Paste a recipe, get a shopping list. It sounds trivial and it's a genuinely interesting problem: unstructured text in, structured data out, with units that need combining.

1. Parse, don't guess

Ingredient lines are messy — "2 large onions, finely chopped", "a pinch of salt", "1½ cups (200g) flour". You need quantity, unit and item out of each.

This is a good candidate for a model rather than regular expressions, because the variation is enormous and the failure mode of a regex is silent.

parse-ingredients.txt
Extract structured ingredients from this recipe text.

For each line return: quantity (number or null), unit (normalised, or
null), item (singular, lowercase, preparation removed), and the
original line.

Rules:
- "finely chopped", "at room temperature" etc. are not part of the item.
- Ranges like "2-3" become the higher number.
- Vague amounts ("a pinch", "to taste") get null quantity, unit
  "to taste".
- If a line has both imperial and metric, prefer metric.
- If you cannot parse a line confidently, return it with everything
  null rather than guessing.

Return JSON only. Here is the text: <paste>

That last rule is essential. A wrong quantity is worse than an unparsed line, because the user won't notice it.

2. Combine sensibly

Two recipes both wanting onions should produce one line. But 200g flour plus 1 cup flour can't be added without a conversion table, and 2 onions plus 200g onions can't be added at all.

Combine when units match. When they don't, list both — "flour: 200g + 1 cup" is honest and useful. Silently converting with a guessed density is not.

3. Group by aisle

The feature that makes it genuinely better than a notes app. Map items to categories — produce, dairy, tinned, frozen — and sort the list that way. It turns the list into a route round the shop.

4. Make it editable

Every parse will get something wrong eventually. The list must be editable by hand, and items must be tickable. If the user can't fix it, one bad parse means abandoning the tool.

Test on a recipe copied from a real website, complete with the life story above it. That's the actual input, not a clean ingredient list.