@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,205 @@
|
|
|
1
|
+
# Template status flags: `internal` / `primitive` / `deprecated`
|
|
2
|
+
|
|
3
|
+
Lookup reference for the three orthogonal discovery/visibility flags on
|
|
4
|
+
`MosaicTemplate`. A template may carry any combination; conflating them is the
|
|
5
|
+
standard mistake. Declared in https://github.com/m0saic-project/m0saic-packages/blob/main/packages/types/src/template/template.ts —
|
|
6
|
+
`internal?` (:1385), `primitive?` (:1416), `deprecated?` (:1446), each with its
|
|
7
|
+
truth table in the JSDoc (line numbers verified 2026-07-27; re-verify:
|
|
8
|
+
`grep -n "internal?: boolean\|primitive?: boolean\|deprecated?: {" https://github.com/m0saic-project/m0saic-packages/blob/main/packages/types/src/template/template.ts).
|
|
9
|
+
|
|
10
|
+
| Flag | Question | Renderable alone? | Default UI |
|
|
11
|
+
|---|---|---|---|
|
|
12
|
+
| `primitive?: boolean` | "Is this a base others build on?" | **Yes** | Shown + **PRIMITIVE** badge (teal) |
|
|
13
|
+
| `internal?: boolean` | "Is this intended as a **top-level selection**?" (No → hidden) | **Sometimes** — the flag doesn't decide this | **Hidden**; **INTERNAL** badge (violet) when revealed |
|
|
14
|
+
| `deprecated?: {reason?, replacement?, since?}` | "Is this still the recommended pick?" | Yes | **Hidden**; **DEPRECATED** tag + "use X instead" |
|
|
15
|
+
|
|
16
|
+
**`primitive` is informational — it does NOT hide anything.** `internal` and
|
|
17
|
+
`deprecated` are the visibility gates.
|
|
18
|
+
|
|
19
|
+
## Why `primitive` ≠ `internal`
|
|
20
|
+
|
|
21
|
+
- **`primitive` is a quality** — foundational, widely reused. `charts/donut`,
|
|
22
|
+
`charts/stat-card`, `charts/bar-graph`, `primitives/grid` are primitives *and*
|
|
23
|
+
perfectly good standalone picks.
|
|
24
|
+
- **`internal` is an INTENT/visibility flag, not a capability statement** — it says
|
|
25
|
+
"not intended as a top-level pick": excluded from public listings and template
|
|
26
|
+
pickers by default, but fully available to `renderNestedTemplate()`
|
|
27
|
+
(`internal` JSDoc, `template.ts:1375-1384`). Whether it can render standalone
|
|
28
|
+
varies by template and is NOT what the flag encodes: the ffmpeg-pulse piece
|
|
29
|
+
templates are internal yet render fine on their own; `bar-graph`'s `bar-cell`
|
|
30
|
+
leaf is internal AND genuinely expects parent-injected context. Don't infer
|
|
31
|
+
either way from the flag — read the template.
|
|
32
|
+
- **Both** is a real combination: a shared building block many templates reuse
|
|
33
|
+
that still isn't meant to be picked top-level.
|
|
34
|
+
|
|
35
|
+
(The `primitive` flag's orthogonality table in `template.ts` used to gloss
|
|
36
|
+
`internal` as "can this render on its own?", contradicting the `internal` JSDoc —
|
|
37
|
+
fixed in-code 2026-07-27; both JSDoc now carry the top-level-selection semantics.)
|
|
38
|
+
|
|
39
|
+
## Where the flags surface
|
|
40
|
+
|
|
41
|
+
- **Electron `templates:get`** (the Mosaic Desktop / Web app source (not published)) serializes all
|
|
42
|
+
three into the meta the desktop Templates page consumes.
|
|
43
|
+
- **Desktop Templates page** — PRIMITIVE / INTERNAL badges with explanatory tooltips.
|
|
44
|
+
- **CLI `list-templates` / `browse-templates`** — annotates
|
|
45
|
+
`(primitive) (internal) (deprecated)` in that **fixed order**
|
|
46
|
+
(the CLI source (not published)-33`).
|
|
47
|
+
|
|
48
|
+
## Curation is a content decision
|
|
49
|
+
|
|
50
|
+
`primitive` started as 4 seeded entries and is on **29 templates** as of 2026-07-27
|
|
51
|
+
(re-verify: `grep -rn "primitive: true" https://github.com/m0saic-project/m0saic-packages/blob/main/packages/templates/src/m0saic | wc -l`).
|
|
52
|
+
There is no rule that derives it — like `deprecated`, someone decides. If you're
|
|
53
|
+
adding a template that other templates will compose, set it; don't wait for a sweep.
|
|
54
|
+
|
|
55
|
+
⚠️ **Setting `deprecated` obligates a pruning pass** — repoint the curated registry,
|
|
56
|
+
and remove the id from the CLI E2E cases and the e2e variant list. The full ordered
|
|
57
|
+
procedure is in the maintainers' agent contract §10 (not published) "Deprecating a template"; a deprecated id must
|
|
58
|
+
never linger in those lists. The default e2e sweep auto-excludes deprecated
|
|
59
|
+
templates; opt back in with `M0SAIC_INCLUDE_DEPRECATED=1`.
|
|
60
|
+
|
|
61
|
+
### Deprecation also silences convention advice (2026-09-09)
|
|
62
|
+
|
|
63
|
+
`M0SAIC_INCLUDE_DEPRECATED=1` governs more than the e2e render sweep: it also
|
|
64
|
+
controls **convention findings** in https://github.com/m0saic-project/m0saic-packages/blob/main/packages/templates/tools/check-registry.mjs,
|
|
65
|
+
which gates `npm run build`.
|
|
66
|
+
|
|
67
|
+
By default a deprecated template's definition-time and render-time convention
|
|
68
|
+
warnings/errors are **suppressed** — the template is defunct, so the advice will
|
|
69
|
+
never be acted on and repeating it forever is noise. This makes the intended
|
|
70
|
+
workflow self-cleaning: for a shipped template that violates a convention, **bump
|
|
71
|
+
to vN+1 and deprecate the old one — the old one goes quiet automatically.** There
|
|
72
|
+
is no exclusion list to maintain.
|
|
73
|
+
|
|
74
|
+
The `outputFormat` convention (2026-09-13: a public template declares
|
|
75
|
+
`outputHints.format`; record posture) treats both visibility gates as
|
|
76
|
+
exemptions at the audit itself, not only through this suppression: an
|
|
77
|
+
`internal` building block renders only nested and a `deprecated` template is
|
|
78
|
+
frozen history, so neither owes a deliverable declaration
|
|
79
|
+
(`auditOutputFormat`, https://github.com/m0saic-project/m0saic-packages/blob/main/packages/template-utils/src/template/auditSchemaConventions.ts).
|
|
80
|
+
|
|
81
|
+
The suppressed count is always printed, never silent:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
[check-registry] ℹ 14 finding(s) on 22 deprecated template(s) suppressed
|
|
85
|
+
(advice only — freeze + fingerprints still apply).
|
|
86
|
+
M0SAIC_INCLUDE_DEPRECATED=1 to see them.
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
⭐ **Suppression is ADVICE ONLY.** The freeze gate (`frozen.manifest.json`, every
|
|
90
|
+
template shipped in 0.1.0) and the layout fingerprints still cover deprecated
|
|
91
|
+
templates in full, byte for byte, in both modes. Deprecation means "stop
|
|
92
|
+
suggesting improvements" — it NEVER grants permission to change a shipped
|
|
93
|
+
template. See the maintainers' agent contract §10 (not published).
|
|
94
|
+
|
|
95
|
+
Measured when introduced (103 registered templates, 22 deprecated): render-time
|
|
96
|
+
warnings 21 → 12, definition-time warning knobs 208 → 165 across 30 → 25
|
|
97
|
+
templates; freeze (327 files) and fingerprints (89) identical either way.
|
|
98
|
+
|
|
99
|
+
## The repo's front door — `repo.helloWorld` (2026-09-14)
|
|
100
|
+
|
|
101
|
+
Not a template flag but a **repo-descriptor** field beside them
|
|
102
|
+
(`MosaicTemplateRepoDescriptor.helloWorld?: TemplateId`,
|
|
103
|
+
https://github.com/m0saic-project/m0saic-packages/blob/main/packages/types/src/template-repo/template-repo.ts): the id of the template a
|
|
104
|
+
newcomer renders first. Core names `@m0saic/hello-world/v1`; each starter names
|
|
105
|
+
its own `…/basics/hello-world/v1`. It is deliberately NOT a `TemplateRole` — roles
|
|
106
|
+
say what a template *produces*; this says which one is the door.
|
|
107
|
+
|
|
108
|
+
Readers: the CLI alias (`m0saic hello-world --template-repo <path>`), the manifest
|
|
109
|
+
(the descriptor is embedded, so `template-manifest.json` carries it zero-exec; the
|
|
110
|
+
platform loader passes it through), and — follow-up — Make's "Start here" chip.
|
|
111
|
+
The gate's `repoFrontDoor` convention (record posture) warns when the field is
|
|
112
|
+
unset or names an id the repo does not register; `internal` / `deprecated`
|
|
113
|
+
templates are fine to name, just unusual. Authoring recipe: `standalone-pack-authoring.md`
|
|
114
|
+
§1.
|
|
115
|
+
|
|
116
|
+
## A canvas that is a knob — `resolveOutputHints` (2026-09-15)
|
|
117
|
+
|
|
118
|
+
Also not a flag but a **template field** beside `outputHints`:
|
|
119
|
+
`resolveOutputHints?: (props) => Partial<MosaicTemplateOutputHints>`. The
|
|
120
|
+
static `outputHints` stay what the manifest and the cards show; the resolver
|
|
121
|
+
is what a host asks with the current props before rendering, through the one
|
|
122
|
+
helper `resolveTemplateOutputHints(tmpl, props)` (`@m0saic/template-utils`).
|
|
123
|
+
Readers: CLI `make` (+ wireframe / tutorial paths), Electron preview / cover /
|
|
124
|
+
render-to-file + the `templates:resolveOutputHints` IPC, the web design
|
|
125
|
+
preview, and Make's Device anchor (re-resolved on every prop change). The
|
|
126
|
+
seam's `outputHintsResolve` convention (THROW posture) checks it returns an
|
|
127
|
+
object, is deterministic at `defaultProps`, and agrees with the static hints
|
|
128
|
+
for every field it returns there. Precedence: [`../output-resolution-tree.md`](../output-resolution-tree.md) §size.
|
|
129
|
+
|
|
130
|
+
## The lattice declarations — `lattice` (2026-09-16)
|
|
131
|
+
|
|
132
|
+
**Convention `latticeSmooth` (throw):** every split count above 12 in the rendered
|
|
133
|
+
layout — root document and every nested child, walked as documents (a nested doc
|
|
134
|
+
whose flatten fails below its floor is still measured) — is 5-smooth, `N = 2ᵃ·3ᵇ·5ᶜ`.
|
|
135
|
+
Counts ≤ 12 with a rough factor (7 weekdays, 11 tiles) pass as small-basis ratio
|
|
136
|
+
fill. Measured at the hinted canvas and, under `--sweep`, on the standard canvases.
|
|
137
|
+
Why: all delivery canvases are 5-smooth with family gcd 120, so 5-smooth counts
|
|
138
|
+
divide them (pixel-exact), any two 5-smooth self-lattices LCM to a small number,
|
|
139
|
+
and a child never inherits a rough cell size (handbook
|
|
140
|
+
[`composition-arithmetic.md`](../../handbook/composition-arithmetic.md) §1–3).
|
|
141
|
+
`m0saic doctor <repo>` reports it for external packs at error severity — the
|
|
142
|
+
publish requirement. Plan + numbers: (internal design history).
|
|
143
|
+
|
|
144
|
+
`MosaicTemplate.lattice?` (https://github.com/m0saic-project/m0saic-packages/blob/main/packages/types/src/template/template.ts, beside
|
|
145
|
+
`primitive?` / `deprecated?`) is the declaration surface — every override is typed,
|
|
146
|
+
reasoned, and printed by the gate:
|
|
147
|
+
|
|
148
|
+
| field | when it is honest | effect |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| `mode: "bitmap"` | the split counts ARE a raster (QR modules, barcode bars, a logo trace, the community M) — handbook §3c BITMAP, baked once, never live-composed | the template is skipped with a note |
|
|
151
|
+
| `allow: [{ count, reason }]` | a count above 12 that is content cardinality (53 ISO weeks; a 335-tile fixture grid) | the count is accepted; the reason is printed and carried in `--json` |
|
|
152
|
+
| `canvas: "physical"` | the hinted size is a millimetre spec at a dpi (business card 1130×678 = 2·5·113 × 2·3·113) | the rough-axis finding is not raised, counts the canvas hands down are charged to it (not to the construction), and the `--sweep` canvases are skipped — a print size cannot move |
|
|
153
|
+
|
|
154
|
+
Per DOCUMENT, `MosaicEngineMeta.lattice?: { mode: "bitmap" }` (`doc.engine.lattice`)
|
|
155
|
+
marks a raster a parent assembles itself (the business card's QR child, built from
|
|
156
|
+
`qrToRenderable` pieces) — that subtree is skipped, the parent is still measured.
|
|
157
|
+
(The layout-fingerprint sidecar cannot carry this marker: the fleet lock
|
|
158
|
+
https://github.com/m0saic-project/m0saic-packages/blob/main/packages/templates/src/lattice-audit.test.ts re-measures such a template with
|
|
159
|
+
`auditRenderedTemplate` instead of trusting the flattened string.)
|
|
160
|
+
|
|
161
|
+
**Not honest:** a count that came from your arithmetic — independent rounding to a
|
|
162
|
+
cap (121/119/241/245), a pitch matched to a grid that no longer exists (499),
|
|
163
|
+
`round(side/K)` + `ceil` self-frames (99/101), pixel splits inside an already
|
|
164
|
+
quantized cell. The finding names the nearest 5-smooth neighbours and the LCM cost;
|
|
165
|
+
the neighbour almost always looks identical.
|
|
166
|
+
|
|
167
|
+
## Reading the dictionary from a template — web vs node (2026-09-16)
|
|
168
|
+
|
|
169
|
+
`@m0saic/dictionary` has two entries: node (`index.ts`, reads `.m0`/`.m0c` from
|
|
170
|
+
disk) and browser (`browser.ts`, what the Mosaic Desktop / Web app source (not published) bundles via the package's
|
|
171
|
+
`browser` field). Every jest suite resolves the NODE entry, so a template that reads
|
|
172
|
+
a brand entry synchronously can be green under test and an error mosaic on
|
|
173
|
+
app.m0saic.io — which is exactly what QR Code's centre M, Brand Marks v3 and
|
|
174
|
+
community-m did until 2026-09-16 (the browser entry shipped every brand `m0` as `""`
|
|
175
|
+
and its `getRankSet` threw). Rules that hold on BOTH entries now:
|
|
176
|
+
|
|
177
|
+
- **`entry.m0` is populated for every entry whose canonical m0 is ≤ 40 KB**
|
|
178
|
+
(`INLINE_M0_MAX_CHARS` in https://github.com/m0saic-project/m0saic-packages/blob/main/packages/dictionary/tools/validate.js, which emits a
|
|
179
|
+
committed sibling `m0.json` per entry that `browser.ts` imports). Only
|
|
180
|
+
`brand/m-33_bitmap` (149 KB) ships `m0: ""` on web — a template that can take it
|
|
181
|
+
must guard `if (!entry.m0)` and say "Desktop renders this" (Brand Marks v3 does).
|
|
182
|
+
- **`entry.masks`, `entry.rankSets`, `entry.sourceCount` ride in `metadata.json`** →
|
|
183
|
+
identical on web. `registry.getRankSet(id, name)` works on web (same lookup as
|
|
184
|
+
node). Named mask SETS (`getMaskSet`) are node-only.
|
|
185
|
+
- **The guards, in order of reach:** https://github.com/m0saic-project/m0saic-packages/blob/main/packages/dictionary/src/browser.test.ts
|
|
186
|
+
(browser m0 == node m0 for every inlined entry; only the bitmap lazy) ·
|
|
187
|
+
https://github.com/m0saic-project/m0saic-packages/blob/main/packages/templates/src/webDictionary.test.ts (renders QR Code / Brand Marks /
|
|
188
|
+
community-m against the browser entry via `jest.mock`) · the Playwright web lane
|
|
189
|
+
the Mosaic Desktop / Web app source (not published) (the shipped bundle).
|
|
190
|
+
**Add your template to `webDictionary.test.ts` if it reads a brand entry.**
|
|
191
|
+
|
|
192
|
+
the Mosaic Desktop / Web app source (not published) copies `*.json`, so `m0.json`
|
|
193
|
+
also lands under `public/dictionary/` — harmless (≤ 10 KB each). The light entries in
|
|
194
|
+
`browser.ts` are still hand-written F-form literals; the drift test compares them
|
|
195
|
+
canonically to the generated `m0.json`.
|
|
196
|
+
|
|
197
|
+
## Known wrinkle
|
|
198
|
+
|
|
199
|
+
The Templates page's **"Show layout primitives" toggle** (renamed from "Show
|
|
200
|
+
primitives" on 2026-09-16 so the label says what it does) gates a *tag-derived*
|
|
201
|
+
Primitives **category** (tags `grid` / `primitive` — in practice the `primitives/`
|
|
202
|
+
family) — separate from the cross-cutting `primitive` **flag**, which only ranks and
|
|
203
|
+
badges. They coexist cleanly, but they are two mechanisms with one word. Driving the
|
|
204
|
+
toggle off the flag would hide most of the alpine/charts shelf by default, so the
|
|
205
|
+
founder kept the category gate and fixed the label instead.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Template render lifecycle — the four entry points
|
|
2
|
+
|
|
3
|
+
`render` is required. The other three are optional, opt-in, and **editor-only** —
|
|
4
|
+
none of them is ever on the `m0saic make` / CLI path.
|
|
5
|
+
|
|
6
|
+
> **Source of truth:** the four members + their JSDoc in
|
|
7
|
+
> [https://github.com/m0saic-project/m0saic-packages/blob/main/packages/types/src/template/template.ts](https://github.com/m0saic-project/m0saic-packages/blob/main/packages/types/src/template/template.ts);
|
|
8
|
+
> the wrapper in https://github.com/m0saic-project/m0saic-packages/blob/main/packages/template-utils/src/template/defineMosaicTemplate.ts;
|
|
9
|
+
> the dispatch helpers in https://github.com/m0saic-project/m0saic-packages/blob/main/packages/template-utils/src/render/renderTemplate{Lite,Cover,Tutorial}.ts`;
|
|
10
|
+
> the shipped exemplar https://github.com/m0saic-project/m0saic-packages/blob/main/packages/templates/src/m0saic/media/screencap_grid/v2.
|
|
11
|
+
|
|
12
|
+
| Function | Who calls it | When absent |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| `render` | the engine, for real output | — (required) |
|
|
15
|
+
| `renderLite` | preview hosts, on the design hot path | falls back to `render` |
|
|
16
|
+
| `renderCover` | the editor, on a pure-default first open | **nothing happens** |
|
|
17
|
+
| `renderTutorial` | the editor, when the user clicks the "?" pill | **nothing happens** |
|
|
18
|
+
|
|
19
|
+
Tier is orthogonal. A core-tier template may declare any of the three optional
|
|
20
|
+
members; `renderLite`'s *capability* rationale (and the RenderHero wiring that
|
|
21
|
+
goes with it) lives in
|
|
22
|
+
[`capability-templates.md`](capability-templates.md).
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## The dispatch asymmetry (the thing to get right)
|
|
27
|
+
|
|
28
|
+
`renderTemplateLite(tmpl, props, ctx)` falls back to `render` — historical
|
|
29
|
+
behavior, and correct: a preview must always show *something*.
|
|
30
|
+
|
|
31
|
+
`renderTemplateCover` / `renderTemplateTutorial` return `null` and **never** fall
|
|
32
|
+
back. Absence means "behave exactly as before this feature existed". Hosts must
|
|
33
|
+
not synthesize a generic cover or tutorial; the seam encodes that so no host has
|
|
34
|
+
to remember it.
|
|
35
|
+
|
|
36
|
+
Copying the `renderLite` helper when adding the next surface is the mistake to
|
|
37
|
+
avoid — it silently re-introduces "every template gets a cover", which is
|
|
38
|
+
precisely what the opt-in design forbids.
|
|
39
|
+
|
|
40
|
+
## Error semantics differ per surface, on purpose
|
|
41
|
+
|
|
42
|
+
- **Cover** error or absence → `null` → the host falls through to its normal
|
|
43
|
+
preview path, silently. An error card *about the cover* would recreate the
|
|
44
|
+
broken-first-impression problem the cover exists to fix.
|
|
45
|
+
- **Tutorial** error → `makeErrorMosaic`, rendered inside the tutorial view. The
|
|
46
|
+
user explicitly clicked; a silent no-op reads as a dead button.
|
|
47
|
+
|
|
48
|
+
Both hosts (the Mosaic Desktop / Web app source (not published),
|
|
49
|
+
the Mosaic Desktop / Web app source (not published)) implement this pair
|
|
50
|
+
identically. The desktop bridge methods are **optional** on the channel, so an
|
|
51
|
+
older shell paired with newer web code degrades to "no cover, no pill" rather
|
|
52
|
+
than throwing.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Authoring checklist
|
|
57
|
+
|
|
58
|
+
- **Deterministic and side-effect-free.** No `Date.now`, no `Math.random`.
|
|
59
|
+
- **Browser-safe** if the template ships in https://github.com/m0saic-project/m0saic-packages/blob/main/packages/templates/src/web.ts — no
|
|
60
|
+
node-only imports.
|
|
61
|
+
- **Never read `ctx.media`.** Both hosts pass `{}` (no ffprobe pass), so these
|
|
62
|
+
surfaces are self-contained by contract.
|
|
63
|
+
- **Size to `ctx.target`.** It carries the editor's canvas dimensions.
|
|
64
|
+
- **Real geometry.** A cover page's bands and a tutorial's diagram are known
|
|
65
|
+
rects — carve m0 cells, don't float drawtext over a full-canvas source. The
|
|
66
|
+
Rect Thesis does not get a pass for onboarding content (see
|
|
67
|
+
[`construction-strategy.md`](construction-strategy.md), "Hard rule: real
|
|
68
|
+
geometry first").
|
|
69
|
+
- **ASCII-only copy.** drawtext renders `→ · — ×` from the bundled font as tofu
|
|
70
|
+
— the same reason `makeErrorMosaic` runs an `asciiOnly()` pass. See
|
|
71
|
+
[`../skills/text-in-templates.md`](../skills/text-in-templates.md).
|
|
72
|
+
- **A tutorial owns its timing.** Declare per-step `durationMs`; never read
|
|
73
|
+
`ctx.target.durationMs`. The host passes no form duration, and a tutorial is
|
|
74
|
+
watched, not rendered into a window.
|
|
75
|
+
- **Invocation props**: the tutorial gets the template's OWN `defaultProps`
|
|
76
|
+
(stable regardless of editor state); the cover gets the working props, which at
|
|
77
|
+
the only moment it shows ARE the defaults — so a cover may ignore props
|
|
78
|
+
entirely.
|
|
79
|
+
- `defineMosaicTemplate` wraps all three with the **capability gate ONLY** — no
|
|
80
|
+
autoCompact, no stamping, no `assertTiming`. Ephemeral preview material, like
|
|
81
|
+
`renderLite`. (Stamping a tutorial would be actively wrong: it declares its own
|
|
82
|
+
per-step durations.)
|
|
83
|
+
- **No new CLI E2E case.** These are unit-tested; they never reach `m0saic make`.
|
|
84
|
+
Same posture as `renderLite`.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Host lifecycle (the Make page)
|
|
89
|
+
|
|
90
|
+
- The cover shows iff `hasRenderCover && !coverDismissed`. `coverDismissed`
|
|
91
|
+
starts `true`, is set at template load from "did this open carry
|
|
92
|
+
caller-supplied props" (so `m0saic open --props` skips the cover), and is
|
|
93
|
+
flipped permanently by the first user prop edit. Deliberately **not** derived
|
|
94
|
+
from `props === defaults` — edit-then-undo must not resurrect it.
|
|
95
|
+
- A cover is a **stand-in**, like a `renderLite` card: it must stay out of the
|
|
96
|
+
render-progress hero filmstrip. Note a template can have a cover *without* a
|
|
97
|
+
`renderLite`, so a hero guard written against `hasRenderLite` alone misses it.
|
|
98
|
+
- The tutorial substitutes only the STAGE (a `stageRenderable` + a
|
|
99
|
+
`tutorial:<id>` doc key). The real document keeps feeding save, geometry-edit,
|
|
100
|
+
doc-stats, and `renderRequest`, so view-only holds by construction rather than
|
|
101
|
+
by discipline — the Make button always renders the real template.
|
|
102
|
+
|
|
103
|
+
## Meta flags
|
|
104
|
+
|
|
105
|
+
`templates:get` reports `hasRenderLite` / `hasRenderCover` / `hasRenderTutorial`,
|
|
106
|
+
with field-for-field parity in the web `getTemplate` and in the `TemplateMeta`
|
|
107
|
+
type. UI affordances gate on these flags, never on making the call and seeing
|
|
108
|
+
what comes back.
|
|
109
|
+
|
|
110
|
+
## Exemplar
|
|
111
|
+
|
|
112
|
+
`@m0saic/media/screencap_grid/v2` — `screencap-grid-cover.ts` (a weighted band
|
|
113
|
+
split) and `screencap-grid-tutorial.ts` (six pages, two of them real diagrams:
|
|
114
|
+
an actual gutter-separated 4×4 grid, and an actual pane band over chipped tiles).
|
|
115
|
+
Its `render` keeps its zero-input `makeErrorMosaic` fail-fast contract untouched
|
|
116
|
+
— that is the point. The cover exists so the fail-fast contract can stay strict
|
|
117
|
+
without costing the template its first impression.
|