@m0saic/knowledge 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +71 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +7 -0
- package/docs/README.md +60 -0
- package/docs/file-formats/m0-iteration-protocol.md +120 -0
- package/docs/file-formats/m0p-and-custom-field.md +195 -0
- package/docs/handbook/README.md +27 -0
- package/docs/handbook/composition-arithmetic.md +278 -0
- package/docs/handbook/dsl-complexity.md +75 -0
- package/docs/handbook/dsl-rules.md +367 -0
- package/docs/handbook/feasibility-precision-quantization.md +591 -0
- package/docs/handbook/m0-construction-methods.md +201 -0
- package/docs/handbook/precision-tiers.md +84 -0
- package/docs/m0saic-thesis.md +95 -0
- package/docs/runtime/README.md +17 -0
- package/docs/runtime/cli-usage.md +372 -0
- package/docs/runtime/ffmpeg-expression-limits.md +117 -0
- package/docs/runtime/reduce-to-one.md +96 -0
- package/docs/skills/README.md +40 -0
- package/docs/skills/axis-and-geometry.md +103 -0
- package/docs/skills/dsl-stdlib-method-catalog.md +7 -0
- package/docs/skills/identity.md +123 -0
- package/docs/skills/labels-and-masks.md +170 -0
- package/docs/skills/m0saic-string-generation.md +251 -0
- package/docs/skills/more-atoms-not-bigger-atoms.md +77 -0
- package/docs/skills/overlay-semantics.md +194 -0
- package/docs/skills/parse-apis.md +79 -0
- package/docs/skills/passthrough-semantics.md +136 -0
- package/docs/skills/structural-construction.md +86 -0
- package/docs/skills/text-in-templates.md +126 -0
- package/docs/skills/zero-overlay-analysis.md +87 -0
- package/docs/templates/README.md +65 -0
- package/docs/templates/capability-templates.md +72 -0
- package/docs/templates/construction-strategy.md +329 -0
- package/docs/templates/data-pipeline.md +324 -0
- package/docs/templates/emission-patterns.md +130 -0
- package/docs/templates/geometry-recipes.md +248 -0
- package/docs/templates/layout-contract.md +168 -0
- package/docs/templates/output-resolution-tree.md +202 -0
- package/docs/templates/patterns/case-study-lessons.md +69 -0
- package/docs/templates/patterns/perf-authoring-rules.md +100 -0
- package/docs/templates/patterns/primitive-extraction-pattern.md +103 -0
- package/docs/templates/philosophy-and-contract.md +310 -0
- package/docs/templates/recursion-nested-rendering.md +138 -0
- package/docs/templates/reference/grid.md +104 -0
- package/docs/templates/reference/json-prop-type.md +169 -0
- package/docs/templates/reference/mosaic-color.md +81 -0
- package/docs/templates/reference/mosaic-placement-props.md +103 -0
- package/docs/templates/reference/prop-bindings.md +203 -0
- package/docs/templates/reference/template-flags.md +205 -0
- package/docs/templates/render-lifecycle.md +117 -0
- package/docs/templates/rendering-model-contract.md +392 -0
- package/docs/templates/standalone-pack-authoring.md +233 -0
- package/docs/templates/theming.md +81 -0
- package/docs/templates/ui-controls.md +150 -0
- package/package.json +37 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
## Axis & Geometry (Splits, Pixels, Remainders)
|
|
2
|
+
|
|
3
|
+
The math layer of the walk: exact rectangle geometry from `()` and `[]`,
|
|
4
|
+
deterministic pixel-remainder distribution (`splitEven`), and how `0`-runs and `-`
|
|
5
|
+
holes shape final rects.
|
|
6
|
+
|
|
7
|
+
> **Source of truth:** `splitEven` in [https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/parse/m0StringParser.ts](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/parse/m0StringParser.ts)
|
|
8
|
+
> (line 478 as of 2026-07-27). Locked behavior: outside-in remainder distribution.
|
|
9
|
+
> Test fixtures in [https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/parse/outside-in-remainder.test.ts](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/parse/outside-in-remainder.test.ts).
|
|
10
|
+
|
|
11
|
+
## Axis semantics
|
|
12
|
+
|
|
13
|
+
- Column split `N( ... )` — splits **width** into N segments; height stays the
|
|
14
|
+
container's height.
|
|
15
|
+
- Row split `N[ ... ]` — splits **height** into N segments; width stays the
|
|
16
|
+
container's width.
|
|
17
|
+
|
|
18
|
+
Children are laid out in order:
|
|
19
|
+
|
|
20
|
+
- Columns advance **x** (left → right)
|
|
21
|
+
- Rows advance **y** (top → bottom)
|
|
22
|
+
|
|
23
|
+
## splitEven pixel distribution (deterministic)
|
|
24
|
+
|
|
25
|
+
When splitting an integer pixel length (`total`) into `parts`:
|
|
26
|
+
|
|
27
|
+
- `base = floor(total / parts)`
|
|
28
|
+
- `remainder = total - base * parts`
|
|
29
|
+
- The `remainder` extra pixels are distributed **outside-in**: indices
|
|
30
|
+
0, P-1, 1, P-2, 2, P-3, ...
|
|
31
|
+
- The remaining segments get `base`
|
|
32
|
+
|
|
33
|
+
Example:
|
|
34
|
+
|
|
35
|
+
- `total = 1080`, `parts = 7`
|
|
36
|
+
- `base = 154`, `remainder = 2`
|
|
37
|
+
- Outside-in order: index 0 gets +1, then index 6 gets +1
|
|
38
|
+
- Sizes: `[155, 154, 154, 154, 154, 154, 155]`
|
|
39
|
+
|
|
40
|
+
Key consequence: remainders distribute symmetrically from the edges inward, keeping
|
|
41
|
+
layouts visually balanced.
|
|
42
|
+
|
|
43
|
+
## Coordinate propagation (rects)
|
|
44
|
+
|
|
45
|
+
Every node owns a rectangle `(x, y, width, height)`. Splits carve that rect into
|
|
46
|
+
child rects:
|
|
47
|
+
|
|
48
|
+
- Column split: child width from `splitEven(width, N)`; child x = cumulative sum of
|
|
49
|
+
prior child widths; y/height unchanged.
|
|
50
|
+
- Row split: child height from `splitEven(height, N)`; child y = cumulative sum of
|
|
51
|
+
prior child heights; x/width unchanged.
|
|
52
|
+
|
|
53
|
+
Overlays always reuse the **owner rect** (same x/y/width/height) and never escape it.
|
|
54
|
+
|
|
55
|
+
## How `0` and `-` affect geometry
|
|
56
|
+
|
|
57
|
+
### `0` (passthrough)
|
|
58
|
+
|
|
59
|
+
- Consumes a split slot; adds its slot size into "carry"
|
|
60
|
+
- The claimant absorbs carry + its own slot, and snaps back to the run start position
|
|
61
|
+
|
|
62
|
+
Geometry impact: `0` changes the eventual claimant rect size and origin. `0` itself
|
|
63
|
+
creates no visible rect (but may create overlay rects via `0{...}`).
|
|
64
|
+
|
|
65
|
+
### `-` (null)
|
|
66
|
+
|
|
67
|
+
- Consumes a split slot; does NOT donate forward
|
|
68
|
+
- Creates a hole / gutter region; can still host overlays (`-{...}`)
|
|
69
|
+
|
|
70
|
+
Geometry impact: a stable region that does not affect siblings beyond consuming its
|
|
71
|
+
share of split space. Useful for isolating sub-layouts and masks.
|
|
72
|
+
|
|
73
|
+
## Why resolution matters (integer pixels)
|
|
74
|
+
|
|
75
|
+
All geometry is integer-pixel based. Two implications:
|
|
76
|
+
|
|
77
|
+
1. A layout can look "perfect" at one size and slightly biased at another, because
|
|
78
|
+
remainder pixels distribute outside-in (edges first).
|
|
79
|
+
2. Bitmap-like layouts (e.g. 64×64 encodings) require compatible output sizes,
|
|
80
|
+
otherwise meaning drifts due to integer rounding distribution.
|
|
81
|
+
|
|
82
|
+
Guidance: if a layout encodes literal grid art, render at sizes aligned to that grid
|
|
83
|
+
(multiples of the grid resolution). For feasibility guards, quantization strategy,
|
|
84
|
+
and quantization-free construction, see
|
|
85
|
+
[`../handbook/feasibility-precision-quantization.md`](../handbook/feasibility-precision-quantization.md).
|
|
86
|
+
|
|
87
|
+
## Minimal examples
|
|
88
|
+
|
|
89
|
+
Even-ish columns — `3(1,1,1)` at width=10:
|
|
90
|
+
|
|
91
|
+
- sizes = `[4,3,3]` (1 remainder pixel goes to the first edge)
|
|
92
|
+
|
|
93
|
+
Rows — `2[1,1]` at height=5:
|
|
94
|
+
|
|
95
|
+
- sizes = `[3,2]`
|
|
96
|
+
|
|
97
|
+
Donation changes claimant geometry — `3(0,1,1)`:
|
|
98
|
+
|
|
99
|
+
- first tile claims 2 slots (`0` + its own)
|
|
100
|
+
|
|
101
|
+
Hole creates gutter — `3(1,-,1)`:
|
|
102
|
+
|
|
103
|
+
- middle region is an explicit empty gutter
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# dsl-stdlib method catalog → see the package
|
|
2
|
+
|
|
3
|
+
The full `@m0saic/dsl-stdlib` method catalog (builders, transforms, queries, masks, SVG import, specialized generators) is **colocated with the package** so it can't drift from the code:
|
|
4
|
+
|
|
5
|
+
➡️ [https://github.com/m0saic-dsl/m0/blob/main/packages/dsl-stdlib/.ai/method-catalog.md](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl-stdlib/.ai/method-catalog.md)
|
|
6
|
+
|
|
7
|
+
That doc is regenerated/reviewed in the same change that alters the API. This file is only a pointer — don't duplicate the catalog here.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Identity — StableKey, depth axes, and selection
|
|
2
|
+
|
|
3
|
+
Merged 2026-07-27 from four overlapping docs (stablekey-and-overlay-identity,
|
|
4
|
+
identity-model, structural-vs-overlay-depth, structural-identity_visual-identity —
|
|
5
|
+
deleted; git history has the long forms). One doc, one owner: everything about
|
|
6
|
+
*referring to the same node* across parses, edits, and renders.
|
|
7
|
+
|
|
8
|
+
> **Source of truth:** `StableKey` + `M0NodeIdentity` in
|
|
9
|
+
> [https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/types.ts](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/types.ts); identity
|
|
10
|
+
> construction in [`parse/m0StringParser.ts`](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/parse/m0StringParser.ts)
|
|
11
|
+
> (search `buildIdentity`, `makeStableKeySegment`).
|
|
12
|
+
|
|
13
|
+
## 1. The identity contract
|
|
14
|
+
|
|
15
|
+
Every parsed node receives a deterministic `stableKey` — the address of the parser's
|
|
16
|
+
walk through **structure**. Segments encode node kind + split axis + childIndex among
|
|
17
|
+
structural siblings. Segment grammar (verified at runtime): `r` (root) ·
|
|
18
|
+
`g{row|col}c{n}` (group) · `fc{n}` (frame) · `pc{n}` (passthrough) · `nc{n}` (null) ·
|
|
19
|
+
`/ov{d}c{k}` (overlay namespace).
|
|
20
|
+
|
|
21
|
+
Same canonical string → same StableKeys, same structuralDepth, same overlay
|
|
22
|
+
namespace. If the string changes, the tree changes and some keys change — that is
|
|
23
|
+
correct, not a bug.
|
|
24
|
+
|
|
25
|
+
**StableKey is NOT:** `paintOrder` / `stackOrder` (paint depends on overlay-deferral
|
|
26
|
+
rules), display/label numbering, or geometry. Geometry is *derived* —
|
|
27
|
+
`rect = f(m0, width, height)` — and shifts with resolution and `splitEven`
|
|
28
|
+
remainders while keys stay put. The DSL guarantees **structural** identity, never
|
|
29
|
+
**visual** identity.
|
|
30
|
+
|
|
31
|
+
## 2. The five axes — keep them separate
|
|
32
|
+
|
|
33
|
+
| Axis | Increments when | Use it for |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| `structuralDepth` (`meta.structuralDepth`) | entering `N(...)` / `N[...]` | tree indentation, containment reasoning |
|
|
36
|
+
| `overlayDepth` | entering `{...}` | layer filtering |
|
|
37
|
+
| `paintOrder` / `stackOrder` | derived from traversal + overlay deferral | paint simulation ONLY |
|
|
38
|
+
| `logicalIndex` | per rendered leaf, logical order | source assignment (`sources[i]`) |
|
|
39
|
+
| `stableKey` | — (structural address) | persistent identity across edits |
|
|
40
|
+
|
|
41
|
+
Worked example — `2(2(1,1),1)` (verified at runtime): the outermost split **IS the
|
|
42
|
+
root** (`stableKey: r`, structuralDepth 0); the inner group is depth **1**
|
|
43
|
+
(`r/gcolc0`); its tiles are depth **2** (`r/gcolc0/fc0`, `fc1`); the outer `1` is
|
|
44
|
+
depth **1** (`r/fc1`). There is no extra root node above the outermost split.
|
|
45
|
+
|
|
46
|
+
`1{1{1}}` has structuralDepth 0 on all three nodes and overlayDepth 0/1/2 — the two
|
|
47
|
+
depth systems are orthogonal: deep structure with zero overlays, flat structure with
|
|
48
|
+
100 overlays, or both.
|
|
49
|
+
|
|
50
|
+
Never assume `deeper == painted later` — overlay deferral (and area-descending
|
|
51
|
+
zero-overlay stacking) controls paint timing, not depth. See
|
|
52
|
+
[`overlay-semantics.md`](overlay-semantics.md).
|
|
53
|
+
|
|
54
|
+
> ⚠️ overlayDepth is structurally free but NOT render-free: each level is a
|
|
55
|
+
> sequential blend pass, and inline-masks silently drop past ~25 nested layers
|
|
56
|
+
> (engine wall W3 — internal: `.ai/moat/runtime/ffmpeg-limitations.md`).
|
|
57
|
+
|
|
58
|
+
## 3. The overlay invariant
|
|
59
|
+
|
|
60
|
+
Overlay subtrees do NOT affect structural childIndex assignment:
|
|
61
|
+
|
|
62
|
+
- Adding/removing overlays never renumbers the base tree.
|
|
63
|
+
- Overlay frames live in a separate namespace — `<ownerStableKey>/ov<depth>c<k>`
|
|
64
|
+
(`r/fc0` → `r/fc0/ov1c0` → `r/fc0/ov1c0/ov2c0`) — so overlay keys cannot collide
|
|
65
|
+
with or shift structural keys.
|
|
66
|
+
|
|
67
|
+
This is why overlays can be used freely (debug layers, labels, reveals) without
|
|
68
|
+
destabilizing the base layout or any tool keyed on it.
|
|
69
|
+
|
|
70
|
+
## 4. logicalIndex vs paintOrder
|
|
71
|
+
|
|
72
|
+
RenderFrames carry both `paintOrder` (stacking order) and `logicalIndex` (logical
|
|
73
|
+
tile index — same as `LogicalFrame.logicalIndex`; there is **no `sourceIndex`
|
|
74
|
+
field** in https://github.com/m0saic-dsl/m0/blob/main/packages/dsl). Build sources in logical order, paint in stack order —
|
|
75
|
+
the split keeps the source array clean of paint semantics. See
|
|
76
|
+
[`parse-apis.md`](parse-apis.md) for which parse API returns which.
|
|
77
|
+
|
|
78
|
+
## 5. Stability limits — what edits do to keys
|
|
79
|
+
|
|
80
|
+
Keys **WILL change** when you: change split counts, insert/remove siblings in the
|
|
81
|
+
base tree, change axis/group nesting. Keys will **NOT change** when you: add/remove
|
|
82
|
+
overlays, deepen overlay nesting, reorder overlay-only layers.
|
|
83
|
+
|
|
84
|
+
Overlay data attaches to `ownerStableKey`, so across edits: split *inside* the owner
|
|
85
|
+
→ owner survives → overlay stays valid; edit a sibling → unaffected; delete/replace
|
|
86
|
+
the owner → the overlay has no owner and cannot reattach. That last case is correct
|
|
87
|
+
behavior, not data loss to be papered over.
|
|
88
|
+
|
|
89
|
+
**Never reattach by geometry.** Preserving overlays via nearest-rect / similar-size /
|
|
90
|
+
pixel-overlap heuristics introduces non-determinism and unfixable edge cases. If the
|
|
91
|
+
owner is gone, the overlay is gone. Determinism > convenience.
|
|
92
|
+
|
|
93
|
+
## 6. Selection policies (agent workflows)
|
|
94
|
+
|
|
95
|
+
When you need to "pick a tile" — recursive growth, demos, batch transforms — pick by
|
|
96
|
+
StableKey, not geometry or paint order. Deterministic policies:
|
|
97
|
+
|
|
98
|
+
- **Smallest StableKey lexicographically** — stable across edits, cheap.
|
|
99
|
+
- **Earliest leaf in StableKey-sorted order** — a "top-left-most" that survives
|
|
100
|
+
structural change.
|
|
101
|
+
- **`(seed + step) mod leafCount`** over a StableKey-sorted leaf list — round-robin
|
|
102
|
+
demos where each step targets a different tile.
|
|
103
|
+
|
|
104
|
+
Anti-patterns: selecting by pixel position (resolution change → different
|
|
105
|
+
selection), by paint order (adding overlays shifts it), or by unsorted traversal
|
|
106
|
+
order.
|
|
107
|
+
|
|
108
|
+
## 7. Labeling
|
|
109
|
+
|
|
110
|
+
Label tiles by StableKey (`.m0c` labels are StableKey-keyed); use the label `text`
|
|
111
|
+
as the human-meaningful join key (`.m0p` regions reference labels by text). If a
|
|
112
|
+
label's key no longer exists in the current parse, `validateLabels`
|
|
113
|
+
(`@m0saic/dsl-file-formats`) flags it — see
|
|
114
|
+
[`../file-formats/m0p-and-custom-field.md`](../file-formats/m0p-and-custom-field.md) §5.
|
|
115
|
+
|
|
116
|
+
## 8. Common mistakes
|
|
117
|
+
|
|
118
|
+
- Treating overlay tiles as structural leaves — scope "leaves" iterations to the
|
|
119
|
+
base namespace; overlays live under `/ov…`.
|
|
120
|
+
- Confusing overlayDepth for structuralDepth (UI tree depth ≠ "inside another split").
|
|
121
|
+
- Hardcoding StableKey strings — they're stable across *layout* edits but computed
|
|
122
|
+
from structure; always derive them from a parse, never string-match literals.
|
|
123
|
+
- Assuming paintOrder reflects structure — it's derived, overlay rules own it.
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Labels and Masks
|
|
2
|
+
|
|
3
|
+
How per-leaf **labels** and **masks** travel from a dictionary entry, through a
|
|
4
|
+
generator, into the editor, and back to disk. The surface spans four packages; this is
|
|
5
|
+
the single map.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. What lives where
|
|
10
|
+
|
|
11
|
+
| Layer | Shape | Home |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| **On disk** | `M0cFile` — `m0` + `labels` (`Record<StableKey, M0Label>`) + `masks` (`Record<StableKey, M0cMaskEntry \| null>`) + `fill` + `size` | https://github.com/m0saic-dsl/m0/blob/main/packages/dsl-file-formats/src/types.ts |
|
|
14
|
+
| **Dictionary entry** | `MosaicDictionaryEntry.labels` / `.masks` — same maps at runtime | https://github.com/m0saic-project/m0saic-packages/blob/main/packages/types/src/dictionary/dictionary.ts |
|
|
15
|
+
| **Editor (live)** | `m0cExtrasRef` — `{ labels, masks, derive, custom }` for the open canvas | the Mosaic Desktop / Web app source (not published) |
|
|
16
|
+
| **Generator output** | `GeneratorResult.m0c?: string` — optional serialized blob | https://github.com/m0saic-project/m0saic-packages/blob/main/packages/dictionary/src/generators |
|
|
17
|
+
|
|
18
|
+
> **`labels[k].color` ≠ the rect fill.** `labels[k].color` tints the *badge*; `fill[k]`
|
|
19
|
+
> paints the *rect*. The inspector once conflated them — don't re-conflate.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 2. Dictionary entry convention
|
|
24
|
+
|
|
25
|
+
- An entry MAY ship **`m0saic.m0c`** instead of `m0saic.m0`. The `.m0c` is
|
|
26
|
+
authoritative; `loadEntryM0()` auto-detects by filename and **throws if both exist**.
|
|
27
|
+
- **`m0saic_src.m0`** is a read-only sibling auto-regenerated by `validate.js`, carrying
|
|
28
|
+
a `# note:` header. It exists for grep-by-DSL and human inspection. **No runtime reads
|
|
29
|
+
it** — never edit it, never treat it as a source.
|
|
30
|
+
- **`entry.masks`** (StableKey-keyed) is the canonical mask path. `entry.maskSets` /
|
|
31
|
+
`getMaskSet()` is reserved for future *named alternate* sets — no current entry uses
|
|
32
|
+
it. Don't reach for it.
|
|
33
|
+
- Positional lookup goes through `getSourceOrderStableKeys(entry)`
|
|
34
|
+
(https://github.com/m0saic-project/m0saic-packages/blob/main/packages/dictionary/src/sourceOrder.ts#L50, memoized per `(entry.id, m0.length)`).
|
|
35
|
+
See logo v2/v3 and qr-animate.
|
|
36
|
+
⚠️ **It throws when `entry.m0` isn't inlined.** Heavy entries carry a lazy
|
|
37
|
+
`entry.m0File` instead. For those, call `resolveM0saic(entry)` first and pass the
|
|
38
|
+
resolved string to the companion **`getSourceOrderStableKeysForM0(id, m0, w, h)`**.
|
|
39
|
+
The error message says this, but only after you've already hit it.
|
|
40
|
+
- One-shot migrations live in **https://github.com/m0saic-project/m0saic-packages/blob/main/packages/dictionary/tools** (not repo-root `tools/`):
|
|
41
|
+
`migrate-masks-to-m0c.mjs` converts legacy `masks.json` + `.m0` → `.m0c`;
|
|
42
|
+
`normalize-mask-paths.mjs` runs the drop-rect-shaped + snap-to-bounds cleanup. Both
|
|
43
|
+
route through `parseM0StringComplete` to turn positional masks into per-StableKey
|
|
44
|
+
entries.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 3. Generator label tiers
|
|
49
|
+
|
|
50
|
+
Every label-emitting generator exposes `labels: LabelTier`
|
|
51
|
+
(`"silent" | "signposts" | "atlas"`) built via `labelTierParam({ defaultTier })`
|
|
52
|
+
(https://github.com/m0saic-project/m0saic-packages/blob/main/packages/dictionary/src/generators/labelTier.ts).
|
|
53
|
+
|
|
54
|
+
| Tier | Emits |
|
|
55
|
+
|---|---|
|
|
56
|
+
| `silent` | plain m0, no labels |
|
|
57
|
+
| `signposts` | named structural regions only ("hero", "feature") |
|
|
58
|
+
| `atlas` | signposts + per-cell / per-level enumeration |
|
|
59
|
+
|
|
60
|
+
`off` is accepted as a spelling alias for `silent`; **`silent` is the canonical wire
|
|
61
|
+
value**. Resolve untrusted input with `resolveLabelTier(raw, fallback)`.
|
|
62
|
+
|
|
63
|
+
### Per-generator defaults and emission path
|
|
64
|
+
|
|
65
|
+
Two emission paths, and which one a generator uses is not arbitrary:
|
|
66
|
+
|
|
67
|
+
- **`emitLabeledM0c({ m0, size, app, tier, labelsBySourceIndex, keyedLabels })`** —
|
|
68
|
+
use when labels map **positionally** to logical source order. Handles
|
|
69
|
+
source-index → stableKey translation, including a feasibility-fallback probe
|
|
70
|
+
scale-up for high-precision layouts.
|
|
71
|
+
- **Direct `serializeM0cFile`** — use when the generator must **classify nulls by
|
|
72
|
+
position** or carries masks. Parse the m0, compute the placed-content bbox, classify
|
|
73
|
+
each `kind: "null"` editorFrame by centroid offset into `padding-top` /
|
|
74
|
+
`padding-right` / `padding-bottom` / `padding-left`. The same algorithm appears in
|
|
75
|
+
every padding-classifying generator — copy it, don't re-derive it.
|
|
76
|
+
|
|
77
|
+
| Generator | Default tier | Path |
|
|
78
|
+
|---|---|---|
|
|
79
|
+
| `qrCodeGenerator` | **`signposts`** | direct |
|
|
80
|
+
| `barcodeGenerator` | **`signposts`** | direct |
|
|
81
|
+
| `aspectFit` | `silent` | direct (padding classification) |
|
|
82
|
+
| `placeRect` | `silent` | direct (padding classification) |
|
|
83
|
+
| `snapGrid` | `silent` | direct (padding classification) |
|
|
84
|
+
| `aspectSafeGrid` | `silent` | direct (padding classification) |
|
|
85
|
+
| `grid` | `silent` | `emitLabeledM0c` |
|
|
86
|
+
| `spotlightGenerator` | `silent` | `emitLabeledM0c` |
|
|
87
|
+
| `magazineGenerator` | `silent` | `emitLabeledM0c` |
|
|
88
|
+
| `comparisonGenerator` | `silent` | `emitLabeledM0c` |
|
|
89
|
+
| `rankedListGenerator` | `silent` | `emitLabeledM0c` |
|
|
90
|
+
| `goldenLayout` | `silent` | `emitLabeledM0c` |
|
|
91
|
+
| `goldenSpiralGenerator` | `silent` | `emitLabeledM0c` |
|
|
92
|
+
| `safeCanvas` | `silent` | `emitLabeledM0c` |
|
|
93
|
+
|
|
94
|
+
**`silent` is the default everywhere except the two code generators** (QR, barcode),
|
|
95
|
+
which default to `signposts` to preserve their always-on label channel.
|
|
96
|
+
|
|
97
|
+
*Keep this table in sync when adding a generator — verify with
|
|
98
|
+
`grep -A3 "labelTierParam(" https://github.com/m0saic-project/m0saic-packages/blob/main/packages/dictionary/src/generators/*.ts.*
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 4. Editor rendering and drop rules
|
|
103
|
+
|
|
104
|
+
**Chip render gate** — `Frame.tsx`: `canShowLabel = hasLabel && minPx >= 70`. There is
|
|
105
|
+
**no kind filter here**: frames, plain nulls, and passthroughs all wear chips once
|
|
106
|
+
mounted. Visibility moved upstream to the `showNullFrames` toggle, which controls
|
|
107
|
+
whether nulls reach `displayFrames` at all.
|
|
108
|
+
|
|
109
|
+
**Drop-label remap** — `ViewFrame.tsx` `buildDropZone`:
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
const isLabelable = (f) => f.kind === "frame" || f.kind === "null";
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
⚠️ **The root node is deliberately excluded.** Including it shifts every
|
|
116
|
+
position-slice mapping by one — the off-by-one that mislabeled aspectFit's `content`.
|
|
117
|
+
Bare-`F` drops (canvas was `"r"`) take a separate path: the new canvas *is* the dropped
|
|
118
|
+
m0, so labels apply with their original keys.
|
|
119
|
+
|
|
120
|
+
> **Name collision — two different `isLabelable`.** `ViewFrame`'s is **node-kind**
|
|
121
|
+
> based (above). `App.tsx`'s is **format** based
|
|
122
|
+
> (`loadedFormat === "m0c" || loadedFormat === "m0p"` — can this file carry labels at
|
|
123
|
+
> all). Same identifier, unrelated meanings, different files. Check which one you're
|
|
124
|
+
> reading.
|
|
125
|
+
|
|
126
|
+
**Why no `isLogicalOwner` requirement.** aspectFit-style generators label plain `-`
|
|
127
|
+
nulls (border padding). Requiring `-{...}` overlay anchors would force every
|
|
128
|
+
padding-labeling generator to wrap each null in an overlay purely to make it visible.
|
|
129
|
+
The relaxed rule lets any structural node carry a label, matching the `.m0c` format's
|
|
130
|
+
own permissiveness.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 5. Undo participation
|
|
135
|
+
|
|
136
|
+
`useUndoRedoText(initial, { onCommit, onUndo, onRedo })` exposes side-channel
|
|
137
|
+
callbacks. `App.tsx` snapshots `{ labels, masks }` from `m0cExtrasRef` into a parallel
|
|
138
|
+
stack on every text commit, and restores on undo/redo.
|
|
139
|
+
|
|
140
|
+
- Manual `setFrameLabel` edits **between** commits don't push their own entry — they're
|
|
141
|
+
absorbed into the *next* commit's snapshot. So manual label → text edit → Ctrl-Z
|
|
142
|
+
restores the manual label too.
|
|
143
|
+
- Drop-derived labels vanish cleanly on Ctrl-Z because the snapshot at the drop's commit
|
|
144
|
+
is taken **before** the handler merges drop labels (`setProfileText` calls `onCommit`
|
|
145
|
+
synchronously, ahead of the `m0cExtrasRef` mutation). This ordering is load-bearing.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## 6. UI affordances
|
|
150
|
+
|
|
151
|
+
- **The dictionary panel's drag chip is the sole apply path.** Click-drag splices into a
|
|
152
|
+
frame; double-click replaces the canvas (confirm modal when the canvas has structure;
|
|
153
|
+
bare-`F` skips the prompt). The standalone "Use Layout" button was **removed** — it
|
|
154
|
+
duplicated the pipeline and kept drifting.
|
|
155
|
+
- **Save-As gates `.m0`** whenever labels or masks exist (`m0SaveBlocked` in `App.tsx`).
|
|
156
|
+
Both the Cmd/Ctrl+S guard and the format chooser respect it — a `.m0` save would
|
|
157
|
+
silently drop the sidecar maps.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## 7. Settled items (don't re-debate)
|
|
162
|
+
|
|
163
|
+
- `aspectSafeGrid` carries m0c only on the **landscape primary** variant; portrait
|
|
164
|
+
alternates don't support m0c yet. Noted in its labels-param description.
|
|
165
|
+
- `LabelTier` naming is **settled**. Renaming touches ~14 generators plus the panel UI.
|
|
166
|
+
- Outer-gutter nulls in `safeCanvas` are **not** labeled — canvas margin, not
|
|
167
|
+
addressable cells.
|
|
168
|
+
|
|
169
|
+
**Related:** `file-formats/m0p-and-custom-field.md` covers `.m0c`'s sibling format but
|
|
170
|
+
predates this convention — read this doc for the entry convention and tier param.
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# m0 Generator Guidelines
|
|
2
|
+
|
|
3
|
+
Practical algorithm outline for generating valid m0 strings programmatically. **Prefer official helpers over hand-emitting tokens.**
|
|
4
|
+
|
|
5
|
+
> **Source of truth:**
|
|
6
|
+
> - Builders: [https://github.com/m0saic-dsl/m0/blob/main/packages/dsl-stdlib/src/builders](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl-stdlib/src/builders)
|
|
7
|
+
> - Unified transforms: [https://github.com/m0saic-dsl/m0/blob/main/packages/dsl-stdlib/src/transforms/unified](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl-stdlib/src/transforms/unified)
|
|
8
|
+
> - Validation: [https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/validate](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/validate)
|
|
9
|
+
> - Feasibility: [https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/feasibility](https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/feasibility)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 0. Prime directives
|
|
14
|
+
|
|
15
|
+
### 0.1 Prefer library ops over hand-writing strings
|
|
16
|
+
|
|
17
|
+
If an official op/helper exists for the transformation you want, **use it** instead of emitting raw m0 tokens.
|
|
18
|
+
|
|
19
|
+
Three primary sources of safe generation:
|
|
20
|
+
|
|
21
|
+
- **`@m0saic/dsl-stdlib/builders`** — high-level builders (`grid`, `weightedSplit`, `strip`, `container`, `aspectFit`, `placeRect`/`placeRects`, `rectsToM0`, `safeCanvas`, `snapGridFit`, `spotlight`, `comparison`, `rankedList`, `goldenSplit`, `goldenSpiral`, etc.). Full surface: see [`dsl-stdlib-method-catalog.md`](./dsl-stdlib-method-catalog.md).
|
|
22
|
+
- **`@m0saic/dsl-stdlib/transforms`** — unified ops (`split`, `replace`, `addOverlay`, `removeOverlay`, `setTileType`, `measureSplit`, `swapFrames`).
|
|
23
|
+
- **`@m0saic/dsl`** — low-level parsing + validation; never the right tool for *generation*.
|
|
24
|
+
|
|
25
|
+
Raw string emission is a **last resort.**
|
|
26
|
+
|
|
27
|
+
### 0.2 Use `grid` for grids
|
|
28
|
+
|
|
29
|
+
For row/column grids (equal cells, weighted cells, gutters, outer gutters), call:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { grid } from "@m0saic/dsl-stdlib";
|
|
33
|
+
|
|
34
|
+
const result = grid({ rows: 3, cols: 4, gutter: 0.05 });
|
|
35
|
+
// result.m0 — canonical m0 string
|
|
36
|
+
// result.totalX, result.totalY — split factors
|
|
37
|
+
// result.cellW, result.gutterW — resolved weights
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The builder handles weighted-split expansion via `0` carry runs, gutter `-` slots, the illegal `1(...)` / `1[...]` rule, exact classifier-count = child-slot count, and canonical output. **Don't hand-author split trees.**
|
|
41
|
+
|
|
42
|
+
Workflow: build the base layout with a builder, apply targeted edits with unified transforms, validate.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 1. Validation and feasibility
|
|
47
|
+
|
|
48
|
+
### 1.1 Always validate
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { validateM0String } from "@m0saic/dsl";
|
|
52
|
+
|
|
53
|
+
const v = validateM0String(m0);
|
|
54
|
+
if (!v.ok) {
|
|
55
|
+
// v.error is a single structured violation: { code, kind, message, ... }
|
|
56
|
+
// (singular — https://github.com/m0saic-dsl/m0/blob/main/packages/dsl/src/errors/errors.ts M0ValidationResult)
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Boolean shortcut: `isValidM0String(m0)`.
|
|
61
|
+
|
|
62
|
+
The validator enforces grammar, token rules, overlay correctness, child-count exactness, the trailing-passthrough rule (trailing `0` is always invalid, even with overlay), and the no-sources rule (≥1 leaf `1` required somewhere in the layout). Overlay bodies do NOT need their own source — sourceless bodies like `1{-}` are legal (relaxed 2026-06-03; see [`overlay-semantics.md`](overlay-semantics.md)). See [`handbook/dsl-rules.md`](../handbook/dsl-rules.md) for the full grammar.
|
|
63
|
+
|
|
64
|
+
### 1.2 Check feasibility when dimensions are known
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { computeFeasibility } from "@m0saic/dsl";
|
|
68
|
+
|
|
69
|
+
const f = computeFeasibility(m0);
|
|
70
|
+
if (targetWidth < f.minWidthPx || targetHeight < f.minHeightPx) {
|
|
71
|
+
// Layout cannot render at this resolution — would produce 0-size frames.
|
|
72
|
+
throw new Error(`Below feasible: ${f.minWidthPx}×${f.minHeightPx}`);
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
If you parse anyway, `parseM0StringComplete` emits `SPLIT_EXCEEDS_AXIS` at parse time:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { parseM0StringComplete } from "@m0saic/dsl";
|
|
80
|
+
|
|
81
|
+
const parsed = parseM0StringComplete(m0, width, height);
|
|
82
|
+
// parsed.precision, parsed.warnings, optional parsed.error
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 1.3 Feasibility vs. precision (important distinction)
|
|
86
|
+
|
|
87
|
+
- **`computeFeasibility`** — exact `{ minWidthPx, minHeightPx }`. Accounts for nested same-axis splits, passthrough carry chains, overlays. Use for all feasibility decisions.
|
|
88
|
+
- **Precision** is a separate, independent floor (neither bounds the other). For a combined verdict use `evaluateM0` from `@m0saic/dsl-stdlib` — it returns `feasible`, `meetsPrecision`, and `recommendedMin` (the per-axis max of both floors). (`computePrecisionFromString` is an internal dsl helper, not public API.)
|
|
89
|
+
|
|
90
|
+
See [`../handbook/feasibility-precision-quantization.md`](../handbook/feasibility-precision-quantization.md) (canonical) for the two-floor model.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 2. Unified transforms — the canonical API for structural changes
|
|
95
|
+
|
|
96
|
+
When you mutate an existing string, use the unified transforms exported from `@m0saic/dsl-stdlib`. Every transform takes `(m0, target, options) → m0` where `target: TransformTarget` is a discriminated union.
|
|
97
|
+
|
|
98
|
+
### TransformTarget
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
type TransformTarget =
|
|
102
|
+
| { by: "logicalIndex"; index: number } // 0-based index of rendered frames (1/F only)
|
|
103
|
+
| { by: "span"; span: { start: number; end: number } } // exact char span
|
|
104
|
+
| { by: "stableKey"; key: string }; // structural identity path
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Pick the addressing mode that matches your invariant — `logicalIndex` for "the Nth rendered tile," `stableKey` for "this specific node, even after edits," `span` for parser-visible ranges. See [`../file-formats/m0p-and-custom-field.md`](../file-formats/m0p-and-custom-field.md) and [`identity.md`](identity.md) for identity semantics.
|
|
108
|
+
|
|
109
|
+
### 2.1 Split
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import { split } from "@m0saic/dsl-stdlib";
|
|
113
|
+
|
|
114
|
+
// Uniform 3-way column split on the 0th tile
|
|
115
|
+
split(m0, { by: "logicalIndex", index: 0 }, { axis: "col", count: 3 });
|
|
116
|
+
|
|
117
|
+
// Weighted split: weights.length must equal count
|
|
118
|
+
split(m0, { by: "logicalIndex", index: 0 }, { axis: "col", count: 2, weights: [60, 40] });
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
GCD-reduces emitted DSL by default. Pass `weightMode: "literal"` to preserve exact slot count.
|
|
122
|
+
|
|
123
|
+
### 2.2 Measure-mode split
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
import { measureSplit } from "@m0saic/dsl-stdlib";
|
|
127
|
+
|
|
128
|
+
// axis "col" or "row"; count = granularity; ranges are {a, b} objects (inclusive)
|
|
129
|
+
// (MeasureSplitOptions — https://github.com/m0saic-dsl/m0/blob/main/packages/dsl-stdlib/src/transforms/unified/measureSplit.ts#L9)
|
|
130
|
+
measureSplit(m0, { by: "logicalIndex", index: 1 }, { axis: "row", count: 12, ranges: [{ a: 2, b: 8 }] });
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### 2.3 Set leaf type
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
import { setTileType } from "@m0saic/dsl-stdlib";
|
|
137
|
+
|
|
138
|
+
// Change a leaf to "-" (null), ">" (passthrough), "F" (frame), or "1"
|
|
139
|
+
setTileType(m0, { by: "logicalIndex", index: 0 }, "-");
|
|
140
|
+
setTileType(m0, { by: "stableKey", key: "r/growc0/fc1" }, ">");
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 2.4 Add / remove overlay
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
import { addOverlay, removeOverlay } from "@m0saic/dsl-stdlib";
|
|
147
|
+
|
|
148
|
+
addOverlay(m0, { by: "logicalIndex", index: 2 }); // visual overlay
|
|
149
|
+
removeOverlay(m0, { by: "stableKey", key: "r/fc2" });
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 2.5 Replace / swap
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
import { replace, swapFrames } from "@m0saic/dsl-stdlib";
|
|
156
|
+
|
|
157
|
+
replace(m0, { by: "logicalIndex", index: 0 }, "2(F,F)"); // replace subtree at target
|
|
158
|
+
swapFrames(m0, 0, 1); // swap rendered tiles — takes two logicalIndex numbers
|
|
159
|
+
// directly, NOT a TransformTarget (the one exception to §2)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 3. Repair-loop strategy
|
|
165
|
+
|
|
166
|
+
When validation fails, apply targeted repairs instead of regenerating from scratch.
|
|
167
|
+
|
|
168
|
+
### 3.1 ILLEGAL_ONE_SPLIT
|
|
169
|
+
|
|
170
|
+
Problem: `1(...)` or `1[...]` — wrapping a single child as a pseudo-container is illegal.
|
|
171
|
+
|
|
172
|
+
Fix: drop the `1(...)` wrapper or use `0`-runs instead.
|
|
173
|
+
|
|
174
|
+
### 3.2 Trailing passthrough
|
|
175
|
+
|
|
176
|
+
Problem: last child in a split is `0` (even with overlay — `0{...}` trailing is always invalid).
|
|
177
|
+
|
|
178
|
+
Fix:
|
|
179
|
+
- Replace trailing `0` with `-`: `2(1,0)` → `2(1,-)`
|
|
180
|
+
- Or replace trailing `0` with `1`: `2(1,0)` → `2(1,1)`
|
|
181
|
+
|
|
182
|
+
Don't use `2(1,0{1})` — trailing passthrough with overlay is still invalid.
|
|
183
|
+
|
|
184
|
+
### 3.3 TOKEN_COUNT mismatch
|
|
185
|
+
|
|
186
|
+
Problem: classifier `N` doesn't match the number of children.
|
|
187
|
+
|
|
188
|
+
Fix: adjust `N` to match children, or add/remove children to match `N`.
|
|
189
|
+
|
|
190
|
+
### 3.4 SPLIT_EXCEEDS_AXIS
|
|
191
|
+
|
|
192
|
+
Problem: classifier count exceeds available pixels on an axis.
|
|
193
|
+
|
|
194
|
+
Fixes (in order):
|
|
195
|
+
1. Reduce split count.
|
|
196
|
+
2. Collapse subtree to `1`.
|
|
197
|
+
3. Replace with fewer parts.
|
|
198
|
+
|
|
199
|
+
### 3.5 NO_SOURCES
|
|
200
|
+
|
|
201
|
+
Problem: the layout as a whole contains no leaf `1` (overlay bodies are allowed to be
|
|
202
|
+
sourceless — `ZERO_SOURCE_OVERLAY` is no longer raised).
|
|
203
|
+
|
|
204
|
+
Fix: ensure at least one `1` / `F` exists somewhere (a source inside an overlay counts:
|
|
205
|
+
`2(-,-){1}` is valid). See [`overlay-semantics.md`](overlay-semantics.md).
|
|
206
|
+
|
|
207
|
+
### 3.6 INVALID_CHAR / TOKEN_RULE
|
|
208
|
+
|
|
209
|
+
Problem: malformed output. **Generator bug** — fix the generation logic, don't band-aid the output.
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## 4. Overlay body constraints
|
|
214
|
+
|
|
215
|
+
Bodies are parsed as complete m0 sub-expressions: non-empty, not a bare `0`
|
|
216
|
+
(`INVALID_EMPTY`), no trailing passthrough — but they do **NOT** need a leaf `1`
|
|
217
|
+
(sourceless `{-}` / `{2(-,-)}` are legal; relaxed 2026-06-03). Full rules:
|
|
218
|
+
[`overlay-semantics.md`](overlay-semantics.md) and
|
|
219
|
+
[`structural-construction.md`](structural-construction.md) §7.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## 5. Output format options
|
|
224
|
+
|
|
225
|
+
Most ops accept output options:
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
{ output: "canonical" } // 1, 0
|
|
229
|
+
{ output: "pretty" } // F, >
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Agents should prefer canonical output for machine use unless pretty is explicitly required.
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## 6. Precision warnings
|
|
237
|
+
|
|
238
|
+
High classifier counts emit `PRECISION_EXCEEDS_NORM` warnings (informational, never
|
|
239
|
+
blocking; raise the threshold via `parseM0StringComplete(s, w, h, { precisionNorm: 256 })`).
|
|
240
|
+
The two-floor feasibility/precision model:
|
|
241
|
+
[`../handbook/feasibility-precision-quantization.md`](../handbook/feasibility-precision-quantization.md).
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## See also
|
|
246
|
+
|
|
247
|
+
- [`../handbook/dsl-rules.md`](../handbook/dsl-rules.md) — full grammar.
|
|
248
|
+
- [`../handbook/feasibility-precision-quantization.md`](../handbook/feasibility-precision-quantization.md) — feasibility rules (canonical).
|
|
249
|
+
- [`structural-construction.md`](structural-construction.md) — structural-build patterns.
|
|
250
|
+
- [`overlay-semantics.md`](overlay-semantics.md) — overlay rules.
|
|
251
|
+
- [`parse-apis.md`](parse-apis.md) — parser entry points.
|