- Add @biomejs/biome 2.5.3 with 2-space indent, double quotes, trailing commas - Add lint and format scripts to root package.json - Update CONTEXT.md: resolve Structured Output shape, add Document AST, Block, Inline Node terms - Add ADR 0002 (remark as internal Markdown parser) - Add ADR 0003 (core defines own AST types) - Add spec: core-parser (Parser implementation and Document AST) - Add spec: cli-view (kitchen view <file> command)
137 lines
6.8 KiB
Markdown
137 lines
6.8 KiB
Markdown
# KitchenMD — Domain Glossary
|
||
|
||
A living glossary of canonical terms for this project.
|
||
Implementation details belong in specs or code, not here.
|
||
|
||
---
|
||
|
||
## Annotation
|
||
|
||
An inline sigil-based marker embedded in recipe prose that carries structured data.
|
||
There are exactly three annotation types: **Ingredient**, **Cookware**, and **Timer**.
|
||
An annotation is valid Markdown plain text — it does not break rendering in Obsidian or any standard Markdown renderer.
|
||
Annotations follow CommonMark's inline processing rules: parsed wherever CommonMark processes inline content (paragraphs, headings, list items, blockquotes); ignored wherever CommonMark treats content as literal (code spans, code blocks, HTML comments, raw HTML blocks).
|
||
|
||
## Ingredient Annotation
|
||
|
||
Syntax: `@name{quantity}` or `@name{quantity unit}`
|
||
|
||
The `{` character terminates the name, so multi-word names work without escaping.
|
||
The `{}` wrapper is mandatory — bare `@name` without braces is not valid.
|
||
Quantity is optional; `@black pepper{}` is valid with no quantity.
|
||
Valid quantity formats: integer (`2`), decimal (`0.5`), simple fraction (`1/2`), mixed number (`1 1/2`).
|
||
Unit is optional and free-form — the format places no constraints on what unit string is written.
|
||
When both quantity and unit are present, they are delimited by the last space inside the braces — so `@butter{1 1/2 tbsp}` parses as quantity `1 1/2`, unit `tbsp`.
|
||
Valid name characters: any character except `{`, `}`, `@`, `$`, `~`.
|
||
|
||
## Cookware Annotation
|
||
|
||
Syntax: `$name{}`, `$name{quantity}`, or `$name{quantity unit}`
|
||
|
||
Same `{`-terminated name rule as Ingredient.
|
||
The `{}` wrapper is mandatory.
|
||
Quantity is optional; `$pan{}` is valid with no quantity.
|
||
Quantity and unit follow the same rules as Ingredient: valid formats are integer, decimal, simple fraction, mixed number; unit is free-form; delimited by the last space inside the braces.
|
||
Valid name characters: any character except `{`, `}`, `@`, `$`, `~`.
|
||
|
||
## Timer Annotation
|
||
|
||
Syntax: `~N unit` or `~N-N unit`
|
||
|
||
No braces.
|
||
The `~` sigil is followed immediately by a number or `N–N` range, a space, then a time unit word from the known set.
|
||
N follows the same format rules as ingredient/cookware quantity: integer, decimal, simple fraction, or mixed number.
|
||
Supports natural range syntax: `~10-15 mins`.
|
||
In range form, the `-` separator follows the first quantity expression — since negative quantities don't exist, the `-` is unambiguous. `~1/2-1 hr` parses as a range of 1/2 hr to 1 hr.
|
||
The annotation terminates after the unit word — trailing prose is ignored. `~1 min more` parses as a timer of 1 min; "more" is plain text.
|
||
Known unit set (aliases → canonical abbreviation): `sec`, `secs`, `second`, `seconds` → `s`; `min`, `mins`, `minute`, `minutes` → `min`; `hr`, `hrs`, `hour`, `hours` → `hr`.
|
||
|
||
## Recipe File
|
||
|
||
A valid `.md` file that may contain any combination of Obsidian Flavored Markdown, YAML frontmatter, and Annotations.
|
||
The file renders correctly in Obsidian without a plugin.
|
||
A Recipe File contains prose instructions only — there is no separate ingredients list section.
|
||
Ingredients are derived entirely by the Parser from inline Ingredient Annotations.
|
||
|
||
## Step
|
||
|
||
The atomic unit of a recipe instruction.
|
||
A step is a single non-empty line in a prose context, or a single ordered list item.
|
||
Headings are not steps.
|
||
Step numbers are section-local — they reset at each heading and do not change when steps are added to other sections.
|
||
|
||
## Combined Recipe
|
||
|
||
A Recipe File whose purpose is to sequence steps from two or more other Recipe Files.
|
||
It contains no steps or ingredients of its own — only Step References in the order they should be executed.
|
||
|
||
## Step Reference
|
||
|
||
A KitchenMD extension of Obsidian transclusion syntax for embedding a specific step from another Recipe File.
|
||
|
||
Syntax:
|
||
- `![[filename#section-heading:N]]` — step N within a named section.
|
||
- `![[filename#N]]` — step N in a headingless recipe (bare number, no section prefix).
|
||
|
||
- `filename` — the Recipe File to reference (no `.md` extension, matching Obsidian wikilink convention).
|
||
- `section-heading` — the heading text of the section containing the step.
|
||
- `N` — the 1-based step number within that section or file.
|
||
|
||
Examples:
|
||
- `![[italian meatballs#rolling:2]]` — second step of the "rolling" section.
|
||
- `![[basic brine#2]]` — second step of a headingless recipe.
|
||
|
||
This syntax is a compatibility break with vanilla Obsidian: `#section:N` does not match any Obsidian heading anchor, so Step References will not render correctly without KitchenMD tooling.
|
||
|
||
## Frontmatter
|
||
|
||
YAML metadata block at the top of a Recipe File (standard `---` delimiters).
|
||
The parser passes frontmatter through as-is with no schema enforcement.
|
||
Consumers are responsible for reading whatever fields they need.
|
||
|
||
## Sigil
|
||
|
||
The single punctuation character that opens each annotation type:
|
||
- `@` — Ingredient
|
||
- `$` — Cookware
|
||
- `~` — Timer
|
||
|
||
## Parser
|
||
|
||
The TypeScript library (`@kitchen-md/core`) that accepts a Recipe File string and returns a structured data object.
|
||
It is a pure function of its input string — no filesystem access.
|
||
|
||
## Structured Output
|
||
|
||
The Document AST returned by the Parser for a given Recipe File.
|
||
See **Document AST** for the exact shape.
|
||
|
||
## Document AST
|
||
|
||
The full structured representation of a Recipe File returned by the Parser.
|
||
Top-level shape: `{ frontmatter: Record<string, unknown>, blocks: Block[] }`.
|
||
`frontmatter` is the raw YAML metadata, passed through without schema enforcement.
|
||
`blocks` is a flat, ordered list of Block nodes representing the document body in document order.
|
||
The flat structure means headings and their following content are siblings, not parent/child.
|
||
Consumers that need section grouping derive it by scanning for Heading nodes.
|
||
Core defines its own AST node types; remark (the internal Markdown parser) is a private implementation detail and its types do not appear in the public API.
|
||
See ADR 0002 and ADR 0003.
|
||
|
||
## Block
|
||
|
||
A top-level node in the Document AST's `blocks` array.
|
||
Each Block represents one logical unit of document structure: a heading, a paragraph, a list, a code block, etc.
|
||
Block types are defined by core; the exact catalogue is not yet finalised — see open questions below.
|
||
|
||
## Inline Node
|
||
|
||
A node representing inline content within a Block (e.g. within a paragraph or list item).
|
||
Inline nodes include: plain text, emphasis, strong, code span, link, Wikilink, and the three Annotation types (Ingredient, Cookware, Timer).
|
||
|
||
---
|
||
|
||
## Open / Unresolved
|
||
|
||
- **Block node type catalogue** — the exact set of Block types core defines (heading, paragraph, list, code block, blockquote, etc.) and how unrecognised types are represented.
|
||
- **Unit normalisation** — the parser should normalise known aliases to a canonical abbreviation (`grams` → `g`, `kilograms` → `kg`); exact alias table and canonical forms are TBD. This is a parser concern, not a format constraint.
|