@homepages/template-kit 0.3.0 → 0.4.1

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.
Files changed (109) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/README.md +39 -4
  3. package/dist/asset-modules.d.ts +81 -0
  4. package/dist/cli/check/config.js +5 -1
  5. package/dist/cli/check/css.js +27 -13
  6. package/dist/cli/check/loader.js +4 -0
  7. package/dist/cli/check/section-assets.js +58 -0
  8. package/dist/cli/check/stages/size.js +124 -8
  9. package/dist/cli/check/stages/tree.js +26 -27
  10. package/dist/cli/dev/build-css.js +91 -0
  11. package/dist/cli/dev/catalog.js +35 -0
  12. package/dist/cli/dev/compose-template.js +38 -0
  13. package/dist/cli/dev/content-override.js +58 -0
  14. package/dist/cli/dev/discover.js +35 -0
  15. package/dist/cli/dev/fill-state.js +75 -0
  16. package/dist/cli/dev/index.js +32 -0
  17. package/dist/cli/dev/inspect.js +38 -0
  18. package/dist/cli/dev/island-bootstrap.js +38 -0
  19. package/dist/cli/dev/island-map.js +35 -0
  20. package/dist/cli/dev/island-transform.js +31 -0
  21. package/dist/cli/dev/manifest-instances.js +31 -0
  22. package/dist/cli/dev/render-section.js +24 -0
  23. package/dist/cli/dev/screenshot-target.js +24 -0
  24. package/dist/cli/dev/section-page.js +54 -0
  25. package/dist/cli/dev/server.js +480 -0
  26. package/dist/cli/dev/slot-schema.js +12 -0
  27. package/dist/cli/dev/structure-summary.js +118 -0
  28. package/dist/cli/dev/tailwind.js +32 -0
  29. package/dist/cli/dev/vite-server.js +33 -0
  30. package/dist/cli/dev/workspace.js +63 -0
  31. package/dist/cli/link/index.js +74 -0
  32. package/dist/cli/link/overlay.js +59 -0
  33. package/dist/cli/link/watch.js +25 -0
  34. package/dist/cli/new/emit.js +48 -0
  35. package/dist/cli/new/index.js +69 -0
  36. package/dist/cli/new/scaffold/section/_Renderer.tsx +20 -0
  37. package/dist/cli/new/scaffold/section/_fill-spec.ts +18 -0
  38. package/dist/cli/new/scaffold/section/_fixtures.ts +13 -0
  39. package/dist/cli/new/scaffold/section/_schema.ts +24 -0
  40. package/dist/cli/new/scaffold/template/_manifest.json +9 -0
  41. package/dist/cli/new/scaffold/template/sections/hero/ExpandableText.tsx +45 -0
  42. package/dist/cli/new/scaffold/template/sections/hero/Renderer.tsx +40 -0
  43. package/dist/cli/new/scaffold/template/sections/hero/components/Feature.tsx +14 -0
  44. package/dist/cli/new/scaffold/template/sections/hero/fill-spec.ts +29 -0
  45. package/dist/cli/new/scaffold/template/sections/hero/fixtures.ts +41 -0
  46. package/dist/cli/new/scaffold/template/sections/hero/schema.ts +60 -0
  47. package/dist/cli/new/scaffold/template/theme.css +24 -0
  48. package/dist/cli/new/scaffold/template/theme.ts +27 -0
  49. package/dist/cli/new/scaffold-assets.js +20 -0
  50. package/dist/cli/pack/collect.js +44 -0
  51. package/dist/cli/pack/guards.js +57 -0
  52. package/dist/cli/pack/index.js +66 -0
  53. package/dist/cli/pack/manifest.js +15 -0
  54. package/dist/cli/pack/zip.js +86 -0
  55. package/dist/cli.js +80 -14
  56. package/dist/dev-client/assets/index-DfEfrp6P.css +1 -0
  57. package/dist/dev-client/assets/index-bpMP0b0W.js +50 -0
  58. package/dist/dev-client/index.html +13 -0
  59. package/dist/eslint/is-client-file.d.ts +4 -0
  60. package/dist/index.d.ts +1 -0
  61. package/dist/islands/detect.d.ts +16 -0
  62. package/dist/islands/detect.js +22 -0
  63. package/dist/islands/discover.d.ts +5 -0
  64. package/dist/islands/discover.js +19 -0
  65. package/dist/islands/esbuild-plugin.d.ts +13 -0
  66. package/dist/islands/esbuild-plugin.js +39 -0
  67. package/dist/islands/wrap.d.ts +12 -0
  68. package/dist/islands/wrap.js +48 -0
  69. package/dist/package.js +1 -1
  70. package/dist/ssr.d.ts +31 -0
  71. package/dist/ssr.js +46 -0
  72. package/docs/INDEX.md +9 -1
  73. package/docs/assets.md +128 -0
  74. package/docs/check.md +6 -3
  75. package/docs/dev.md +187 -0
  76. package/docs/islands.md +5 -2
  77. package/docs/llms.txt +41 -3
  78. package/docs/new.md +27 -0
  79. package/docs/overview.md +57 -0
  80. package/docs/pack.md +46 -0
  81. package/docs/primitives.md +5 -6
  82. package/docs/quickstart.md +102 -0
  83. package/docs/recipes/INDEX.md +27 -0
  84. package/docs/recipes/bind-property-fact.md +58 -0
  85. package/docs/recipes/collection-slot.md +100 -0
  86. package/docs/recipes/fill-spec-decision.md +66 -0
  87. package/docs/recipes/fixture-states.md +68 -0
  88. package/docs/recipes/image-slot-crop.md +98 -0
  89. package/docs/recipes/interactive-island.md +100 -0
  90. package/docs/recipes/organize-section-folder.md +72 -0
  91. package/docs/recipes/prepare-submission.md +55 -0
  92. package/docs/recipes/second-template.md +49 -0
  93. package/docs/recipes/select-slot.md +59 -0
  94. package/docs/recipes/static-asset.md +103 -0
  95. package/docs/recipes/third-party-package.md +90 -0
  96. package/docs/rules/INDEX.md +4 -2
  97. package/docs/rules/bundle-binary-asset.md +33 -52
  98. package/docs/rules/bundle-incomplete.md +2 -2
  99. package/docs/rules/css-reason.md +3 -3
  100. package/docs/rules/no-bare-css-import.md +8 -8
  101. package/docs/rules/no-css-import-from-render-path.md +7 -8
  102. package/docs/rules/size-assets.md +88 -0
  103. package/docs/rules/size-island-bundle.md +123 -0
  104. package/docs/rules/size-section-css.md +19 -14
  105. package/docs/schema-system.md +28 -28
  106. package/docs/theme-and-css.md +2 -2
  107. package/docs/vocabulary.md +98 -0
  108. package/package.json +12 -3
  109. package/tsconfig.json +1 -1
package/docs/dev.md ADDED
@@ -0,0 +1,187 @@
1
+ ---
2
+ purpose: The `template-kit dev` command — the local preview server, what it serves, its URLs, and the agent-facing render endpoints.
3
+ status: living
4
+ related: [INDEX.md, theme-and-css.md, schema-system.md, check.md, islands.md]
5
+ updated: 2026-07-16
6
+ ---
7
+ # template-kit dev
8
+
9
+ `template-kit dev` is the local preview server for a workspace's templates. It
10
+ renders every section against its fixtures the same way the platform does —
11
+ server-side, with your template's theme and compiled Tailwind utilities — so you
12
+ see real output while you author, and reload as you edit.
13
+
14
+ ## Usage
15
+
16
+ ```bash
17
+ template-kit dev # serve every template in the workspace
18
+ template-kit dev <template> # preselect one template in the UI
19
+ template-kit dev --port 4000 # choose the port (default 5180)
20
+ ```
21
+
22
+ Run it from the workspace root or from inside a template's own folder — running
23
+ from inside a template preselects it on the home launcher. The server serves every
24
+ template it finds under `templates/`, regardless of where you launched it; the
25
+ command runs until you interrupt it (Ctrl-C).
26
+
27
+ ### What it needs installed
28
+
29
+ ```bash
30
+ npm i -D vite @vitejs/plugin-react tailwindcss @tailwindcss/cli
31
+ ```
32
+
33
+ Like [`check`](check.md), `dev` runs **your** toolchain, resolved from your
34
+ workspace — Vite renders and reloads your sections, and Tailwind compiles your
35
+ theme's utilities. None of these are dependencies of the kit; they resolve from
36
+ your install, so the versions you pinned are the ones that run. A missing one is
37
+ reported by name with the line to install it.
38
+
39
+ Screenshots (below) additionally need Playwright, which is optional:
40
+
41
+ ```bash
42
+ npm i -D @playwright/test && npx playwright install chromium
43
+ ```
44
+
45
+ ## What you get
46
+
47
+ Open the server's URL and you land on the **home launcher** (`/`) — a template
48
+ selector plus two tool cards, each a self-contained view you open in its own
49
+ page and return from by clicking the `template-kit dev` wordmark:
50
+
51
+ - **Full template viewer** — opens `/template/<template>`, the whole page as it
52
+ publishes.
53
+ - **Canvas mirror** — a section and fixture selector, opening
54
+ `/canvas/<template>/<section>/<fixture>`.
55
+
56
+ The home launcher is the only place navigation happens; both tools are
57
+ near-chromeless and URL-keyed, so a link to either reloads and shares exactly
58
+ the view you're looking at.
59
+
60
+ ### Live reload
61
+
62
+ The server watches your template files. Editing a section reloads any open
63
+ preview of it; editing a theme recompiles its Tailwind utilities and hot-swaps
64
+ the stylesheet without a full reload.
65
+
66
+ ### Interactivity (islands)
67
+
68
+ A section's `"use client"` components hydrate in the preview. The server renders
69
+ the section — including each client component's initial HTML — then the browser
70
+ loads React and hydrates only those components, so they become interactive while
71
+ the rest of the page stays static. A section with no `"use client"` component
72
+ loads no React at all. Editing a client component reloads the preview, re-rendered
73
+ and re-hydrated. See [Islands](islands.md) for the client-component contract.
74
+
75
+ ### Overriding content
76
+
77
+ A preview page accepts an `overrides` query parameter — base64-encoded JSON,
78
+ deep-merged onto the fixture's resolved props — so you can probe a section against
79
+ content the fixtures don't carry without editing `fixtures.ts`. The decoded JSON
80
+ shape mirrors the props themselves:
81
+
82
+ ```json
83
+ { "slots": {}, "options": {}, "nav": { "selfAnchor": "", "anchors": {} }, "variants": {} }
84
+ ```
85
+
86
+ All four top-level keys are optional. Merging follows one rule at every level: a
87
+ plain object merges key by key with the fixture's value; anything else — a scalar,
88
+ an array, or `null` — replaces the fixture's value outright. A key the override
89
+ omits keeps the fixture's value; a key set explicitly to `null` replaces it with
90
+ `null`, so you can probe an empty/missing state a fixture doesn't otherwise cover.
91
+ The encoded payload is capped at 16 KB.
92
+
93
+ ## Views: the full template, and the section render behind the canvas
94
+
95
+ The whole template renders at `/template/<template>` — every section in
96
+ `manifest.json` order, at each section's base fixture (with that instance's manifest
97
+ options), with the template theme and page shell applied and islands hydrating, as
98
+ the published site stacks it. Open it from the home launcher's **Full template
99
+ viewer** card, or point a browser tool at the URL directly. (Cross-section
100
+ `reconcile` rules are a publish-time behavior and are not applied here.)
101
+
102
+ Every section also has a standalone render at
103
+ `/sections/<template>/<section>/<fixture>` — the document the `/api/screenshot`
104
+ endpoint targets for a single section. It is not a navigation destination in its
105
+ own right.
106
+
107
+ ## Canvas mirror
108
+
109
+ `/canvas/<template>` — open it from the home launcher's **Canvas mirror** card,
110
+ or directly — mirrors the whole template in an iframe: every section instance
111
+ from `manifest.json`, stacked in one scroll container, exactly as the deliverable
112
+ renders. A paint-only overlay sits on top of it, scoped per section instance —
113
+ the same slot name in two different sections selects independently. Click a slot
114
+ to select it (a gold ring); hover to preview one (a blue ring). Only slots that
115
+ instance's schema marks `editable_by_user: true` select individually — clicking
116
+ anywhere else in a section selects that section instance as a whole. The overlay
117
+ never changes the rendered markup. A mode toggle switches between this canvas
118
+ view and a published view — the clean full-template render, the same page with
119
+ the overlay off. A `template-kit dev` wordmark in the toolbar links back to the
120
+ home launcher (`/`) — the canvas mirror is otherwise chromeless.
121
+
122
+ The current template, mode, and selection are encoded in the URL as
123
+ `/canvas/<template>?mode=&sel=`, where `sel` names a section instance
124
+ (`<instanceId>`), a slot within it (`<instanceId>~<slotId>`), or one item of
125
+ a repeating slot (`<instanceId>~<slotId>~<itemIndex>`) — so a canvas link
126
+ reloads and shares exactly the view you're looking at.
127
+
128
+ Selecting a slot (not the section instance as a whole) docks an **edit panel**
129
+ beside the iframe. One JSON editor handles every slot type — you edit the
130
+ slot's value in its own JSON shape rather than switching between per-type
131
+ widgets — alongside a **fill-state badge** (filled / empty / missing) for the
132
+ selected slot and a per-instance **fixture picker**. The picker lists that
133
+ section's whole fixture corpus, including its synthetic empty-content
134
+ fixtures, so you can drive a slot into an empty or missing state without
135
+ touching `fixtures.ts`. Applying a JSON edit, switching the fixture, or
136
+ resetting the instance re-renders that section instance alone, the same way
137
+ it renders in the stack, and swaps its HTML into the iframe in place — no
138
+ page reload, no other instance disturbed — then re-anchors the selection ring
139
+ to the refreshed markup. The kit dev server is the harness's only backend, so
140
+ this loop never touches fixture files, a database, or another service. Edit
141
+ state lives only in the tab: nothing is written to disk, and reloading the
142
+ page loses it. A re-rendered instance's `"use client"` islands stay static —
143
+ they do not re-hydrate — until you rebuild; the panel notes this so it isn't
144
+ mistaken for a bug.
145
+
146
+ ## Agent-facing endpoints
147
+
148
+ These render a section — or the whole template — without a browser, for an agent
149
+ verifying its work from a terminal.
150
+
151
+ - `/api/inspect` — the low-context semantic view of a section×fixture as JSON: each
152
+ declared slot with its `type`, `required`, `fillState` (`filled` / `empty` /
153
+ `missing`), whether it actually `rendered`, a compact `value` preview, and its
154
+ `schema.ts:<line>`; plus resolved `options` / `variants` and `nullCorpus` (the
155
+ synthetic empty-state fixtures, one per optional slot, you can walk to review every
156
+ missing-content branch). An `empty`/`missing` slot that still shows `rendered: true`
157
+ is a broken empty shell. Takes `template`, `section`, `fixture`, the same base64
158
+ `overrides` parameter as a preview page, and `format=text` to add the indented
159
+ structure outline.
160
+ - `/api/render` — the full structured render payload for one section×fixture
161
+ (`html`, `resolvedProps`, `slotTree`, `islandMap`, `slotSchema` — that
162
+ section's slot types and editability — and `slotFills`, the same per-slot
163
+ fill state `/api/inspect` reports); GET takes `overrides`, POST takes an
164
+ unencoded JSON body. POST also accepts a manifest `instanceId` in place of
165
+ `section`+`fixture`: the server resolves that instance's section, its
166
+ manifest options, and its default fixture the same way the whole-template
167
+ render does, so re-rendering one instance matches how it renders in the
168
+ stack. This is what the canvas mirror's edit panel calls to apply a slot
169
+ edit or switch a fixture.
170
+ - `/api/template-schema?template=<t>` — the editability map for the whole
171
+ template: every manifest section instance with its per-slot `type` and
172
+ whether it's user-editable. The canvas mirror's overlay gates selection on
173
+ this endpoint, not on `/api/render`'s per-section `slotSchema`.
174
+ - `/api/screenshot` — a full-page PNG. Give it `template` + `section` + `fixture` for a
175
+ section, or `template` alone for the whole template. `width=<px>` sets the viewport
176
+ (default 1280). Requires Playwright (`npm i -D @playwright/test && npx playwright
177
+ install chromium`); without it the endpoint answers with the install line.
178
+
179
+ `/api/catalog.json` returns the full **template × section × fixture** matrix.
180
+
181
+ ## See also
182
+
183
+ - [Theme and CSS](theme-and-css.md) — the theme the preview compiles, and the
184
+ `template-kit theme` command that generates it.
185
+ - [Schema system](schema-system.md) — the fixtures the preview renders and the
186
+ props the `overrides` parameter merges onto.
187
+ - [Check](check.md) — the acceptance gate to run once a section previews cleanly.
package/docs/islands.md CHANGED
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  purpose: How interactivity works in a template — client components ("islands"), their props contract, and their editor options.
3
3
  status: living
4
- related: [INDEX.md, eslint.md]
5
- updated: 2026-07-14
4
+ related: [INDEX.md, eslint.md, dev.md]
5
+ updated: 2026-07-15
6
6
  ---
7
7
 
8
8
  # Islands: interactivity in a template
@@ -17,6 +17,9 @@ If a page contains no islands, it loads no React and no island code — every pa
17
17
  still ships the small island loader (a few KB, gzipped), but that loader only ever
18
18
  reaches React through a dynamic import it takes when an island is actually on the page.
19
19
 
20
+ [`template-kit dev`](dev.md) hydrates a section's islands in its preview, so you can
21
+ exercise one as you author it.
22
+
20
23
  ## Writing one
21
24
 
22
25
  ```tsx
package/docs/llms.txt CHANGED
@@ -6,16 +6,30 @@
6
6
 
7
7
  Status: 0.0.x. The schema system, the contract primitives, and the theme system are
8
8
  complete — a section is authorable end to end against the kit. The `template-kit`
9
- CLI's `check` and `theme` commands are implemented — see [Check](check.md) and
10
- [Theme and CSS](theme-and-css.md#generating-themecss); its other commands arrive with
11
- the surfaces they drive.
9
+ CLI's `check`, `theme`, `dev`, `pack`, `new`, and `new-section` commands are
10
+ implemented — see [Check](check.md), [Theme and CSS](theme-and-css.md#generating-themecss),
11
+ [Dev](dev.md), [Pack](pack.md), and [Adding templates](new.md); its remaining
12
+ commands arrive with the surfaces they drive.
12
13
 
13
14
  Everything an author writes a section against is imported from the package root —
14
15
  `@homepages/template-kit`, no subpath imports. The two CSS entries
15
16
  (`./styles.css`, `./base.css`) are the deliberate exception.
16
17
 
18
+ ## Start here
19
+
20
+ To build a section, start at [Quickstart](quickstart.md), then the task
21
+ [Recipes](recipes/INDEX.md) ("how do I X" → the recipe). For the mental model,
22
+ read [Overview](overview.md). For the closed sets — slot types, `direct()`
23
+ sources, `derived()` transforms — see [Vocabulary](vocabulary.md). When a lint
24
+ or `check` prints a rule id, resolve it at `rules/<id>.md`.
25
+
17
26
  ## Docs
18
27
 
28
+ - [Quickstart](quickstart.md): the cold-start tutorial — create a workspace,
29
+ preview the starter's `hero` section, read its four files, make one edit,
30
+ and check it. Owns the author-loop anchor every recipe ends with.
31
+ - [Overview](overview.md): the one-page mental model — workspace → templates →
32
+ sections, the four reserved files, and the server-vs-client mapping.
19
33
  - [Schema system](schema-system.md): the section authoring contract. A section is
20
34
  four files — `schema.ts` (slots + options + `meta.groups`), `fill-spec.ts` (how AI
21
35
  fills them), `fixtures.ts` (preview content, built on the kit's golden property
@@ -23,6 +37,9 @@ Everything an author writes a section against is imported from the package root
23
37
  `SectionProps<typeof schema>`). Covers the `as const satisfies` requirement, binding
24
38
  slots to property facts with `direct()`/`derived()` over a closed vocabulary, and
25
39
  fact-driven variants.
40
+ - [Vocabulary](vocabulary.md): the closed authoring vocabulary — the 9 slot
41
+ types, the `direct()` source fields, and the `derived()` transforms, with
42
+ selection semantics (which one to pick).
26
43
  - [Contract primitives](primitives.md): the only five components the kit ships —
27
44
  `Section` (full-bleed section root), `Slot` (`data-slot-id`), `SlotItem` (per-item
28
45
  handle in an `object_list` / `image_collection`), `SlotGroup` (`data-slot-group`),
@@ -41,6 +58,10 @@ Everything an author writes a section against is imported from the package root
41
58
  rendered inside the section.
42
59
  - [README](../README.md): install, exports, and the release flow.
43
60
  - [Islands](islands.md): interactivity — client components, the serializable-props contract, editor options, and the browser loader.
61
+ - [Static assets](assets.md): shipping a logo, texture, icon, or brand font with a
62
+ template — where an asset file lives (template level, or a section folder if the
63
+ section references it), referencing it from a section's CSS with `url()`/`@font-face`
64
+ or importing it into a component, and the asset-vs-content-image distinction.
44
65
  - [ESLint preset](eslint.md): `@homepages/template-kit/eslint`, the flat-config preset —
45
66
  a two-line config with no parser wiring, over an ESLint + `@typescript-eslint/parser`
46
67
  install of your own (the kit depends on neither — both resolve from your workspace).
@@ -49,7 +70,24 @@ Everything an author writes a section against is imported from the package root
49
70
  TypeScript, ESLint and esbuild, resolved from your workspace), its six per-template
50
71
  stages, the once-per-invocation dependency gates, `--all`/`--json`, the report shape,
51
72
  and the exit code. Thresholds are fixed platform acceptance criteria, not configurable.
73
+ - [Dev](dev.md): the `template-kit dev` CLI command — the local preview server. Its
74
+ home launcher with a full template viewer (`/template/<template>`) and a canvas
75
+ mirror (`/canvas/<template>/<section>/<fixture>`), live reload, the Vite + Tailwind
76
+ toolchain it resolves from your workspace, and the `inspect` / `render` / `screenshot`
77
+ endpoints an agent uses to verify a render from a terminal.
78
+ - [Pack](pack.md): the `template-kit pack <template>` CLI command — builds a template's
79
+ submission zip (the template's source, the workspace lockfile, and a stamped
80
+ `pack-manifest.json`), and the conditions it refuses to pack under: a failing
81
+ `check`, a missing lockfile, or a local `file:`/`link:` dependency.
82
+ - [Adding templates](new.md): the `template-kit new <template>` and
83
+ `template-kit new-section <template>/<section>` CLI commands — scaffolding a new
84
+ template with a working starter section, or adding a bare section (the four
85
+ reserved contract files, no island) to an existing template. Both refuse to
86
+ overwrite an existing folder.
52
87
  - [Rules](rules/INDEX.md): every rule-id a lint or `check` failure can print, under the
53
88
  `template-kit/<id>` namespace, one page each at `docs/rules/<id>.md` — Rule / Reason /
54
89
  Fix / Before→After. **The loop:** see an id in a failure → read `docs/rules/<id>.md` →
55
90
  fix. Nothing else to search for; the id is the filename.
91
+ - [Recipes](recipes/INDEX.md): task recipes routed by "how do I…" — each one a minimal,
92
+ complete delta from the starter's `hero` section (never a from-scratch file), ending in
93
+ the [author loop](quickstart.md#the-author-loop).
package/docs/new.md ADDED
@@ -0,0 +1,27 @@
1
+ ---
2
+ purpose: Add a template or a section to a workspace with template-kit new / new-section.
3
+ status: living
4
+ related: [INDEX.md, dev.md, check.md]
5
+ updated: 2026-07-15
6
+ ---
7
+
8
+ # Adding templates and sections
9
+
10
+ A workspace starts with one template (`templates/starter/`). Add more without
11
+ duplicating any shared context — templates are plain folders, and the kit,
12
+ configs, and docs stay at the workspace root.
13
+
14
+ ## `template-kit new <template>`
15
+
16
+ Creates `templates/<template>/` with a working starter section (a text slot, an
17
+ image slot, a fact-bound slot, an option, fixtures, a fill spec, a nested
18
+ `components/` folder, and a `"use client"` island), plus `theme.ts`/`theme.css`
19
+ and a `manifest.json`. It passes `check` as-is; edit it into your template.
20
+
21
+ ## `template-kit new-section <template>/<section>`
22
+
23
+ Adds `templates/<template>/sections/<section>/` — the four reserved contract
24
+ files (`Renderer.tsx`, `schema.ts`, `fill-spec.ts`, `fixtures.ts`), no island.
25
+ Reference the new section by name in the template's `manifest.json`.
26
+
27
+ Both refuse to overwrite an existing folder.
@@ -0,0 +1,57 @@
1
+ ---
2
+ purpose: The mental model in one page — workspace, templates, sections, and the server-vs-client mapping.
3
+ status: living
4
+ related: [schema-system.md, islands.md, rules/server-vs-client.md]
5
+ updated: 2026-07-15
6
+ ---
7
+ # Overview
8
+
9
+ ## Workspace → templates → sections
10
+
11
+ A **workspace** is the container: one kit version, one shared config. A
12
+ **template** is the authored, submitted unit — a folder of sections plus a
13
+ manifest and theme. A **section** is four reserved files in an otherwise
14
+ free-form folder:
15
+
16
+ ```
17
+ templates/<template>/sections/<section>/
18
+ Renderer.tsx
19
+ schema.ts
20
+ fill-spec.ts
21
+ fixtures.ts
22
+ ```
23
+
24
+ Everything else in that folder — extra components, an island, helpers — is
25
+ yours. See [Adding templates and sections](new.md).
26
+
27
+ ## The four files
28
+
29
+ | File | Declares | Reference |
30
+ |---|---|---|
31
+ | `schema.ts` | The slots and options the section has | [schema-system.md](schema-system.md#schemats--what-the-section-contains) |
32
+ | `fill-spec.ts` | How AI fills those slots | [schema-system.md](schema-system.md#fill-spects--how-ai-fills-the-slots) |
33
+ | `fixtures.ts` | Sample content for local preview | [schema-system.md](schema-system.md#fixturests--content-for-local-preview) |
34
+ | `Renderer.tsx` | The markup | [schema-system.md](schema-system.md#renderertsx--props-derived-never-hand-written) |
35
+
36
+ ## Slots, options, fill, fixtures
37
+
38
+ A **slot** is editable content; an **option** is a behavior switch — see
39
+ [the schema system](schema-system.md#schemats--what-the-section-contains) for
40
+ the distinction and [the closed vocabulary](vocabulary.md) for the types.
41
+ **Fill** is how AI assigns slot values via `fill-spec.ts`'s decisions, covered
42
+ in [schema-system.md](schema-system.md#fill-spects--how-ai-fills-the-slots).
43
+ **Fixtures** are the sample content a section previews against, built on the
44
+ kit's golden property dataset — see
45
+ [schema-system.md](schema-system.md#fixturests--content-for-local-preview).
46
+
47
+ ## Server vs client — your Next.js instinct, mapped
48
+
49
+ A `Renderer.tsx` is a server component: a pure function of props, ships no
50
+ JS. Interactivity goes in a `"use client"` island, hydrated in the browser.
51
+ The directive is **per file**. See [server vs client](rules/server-vs-client.md)
52
+ for the precise rule and [Islands](islands.md) for how to write one.
53
+
54
+ ## The author loop
55
+
56
+ For the write → preview → check cycle, see
57
+ [the author loop](quickstart.md#the-author-loop).
package/docs/pack.md ADDED
@@ -0,0 +1,46 @@
1
+ ---
2
+ purpose: The `template-kit pack` command — what it produces, what it refuses, and why.
3
+ status: living
4
+ related: [INDEX.md, check.md]
5
+ updated: 2026-07-15
6
+ ---
7
+ # template-kit pack
8
+
9
+ Build a template's submission artifact: a zip of the template's source plus the
10
+ workspace lockfile, validated and stamped.
11
+
12
+ ## Usage
13
+
14
+ ```bash
15
+ template-kit pack <template>
16
+ ```
17
+
18
+ Run it from the workspace root, or from inside a template folder (the template is
19
+ inferred). In a workspace with several templates you must name one — the submission
20
+ unit is a single template.
21
+
22
+ ## What it produces
23
+
24
+ `<template>.zip` at the workspace root, containing:
25
+
26
+ - `pack-manifest.json` — the exact kit version the source was validated against, a
27
+ content hash of the template tree, the pack timestamp, and a `check` flag
28
+ recording that validation passed.
29
+ - `package.json` and `package-lock.json` from the workspace root, so the build is
30
+ reproducible with `npm ci`.
31
+ - `templates/<template>/` — the template's source only (sections, manifest, theme,
32
+ README). No `node_modules`, no build output, no other template.
33
+
34
+ ## What it refuses
35
+
36
+ `pack` will not produce a zip when:
37
+
38
+ - `check <template>` is not green — fix the reported problems first.
39
+ - the workspace has no `package-lock.json` — run `npm install` and commit it.
40
+ - a dependency uses a local `file:`/`link:` protocol — depend on published versions.
41
+
42
+ Fix the reported cause and run it again.
43
+
44
+ ## See also
45
+
46
+ - [Check](check.md) — the gate `pack` runs before it will produce a zip.
@@ -2,7 +2,7 @@
2
2
  purpose: The five contract primitives a section renderer must use, and the marker attributes they emit.
3
3
  status: living
4
4
  related: [schema-system.md, theme-and-css.md, eslint.md]
5
- updated: 2026-07-14
5
+ updated: 2026-07-15
6
6
  ---
7
7
  # Contract primitives
8
8
 
@@ -106,8 +106,8 @@ whole — no per-item selection, no per-item image cropping, no per-item live su
106
106
  `index` **must** be the item's zero-based position in the slot's array as rendered. The
107
107
  editor writes edits back by that index, so an index that doesn't match its position edits
108
108
  the wrong row. `index` is an array position, not a schema key, so unlike `Slot`'s and
109
- `SlotGroup`'s `id` it is exempt from the [`template-kit/slot-marker-literal`](eslint.md#markers-and-schema)
110
- lint rule.
109
+ `SlotGroup`'s `id` it is exempt from the
110
+ [`template-kit/slot-marker-literal`](rules/slot-marker-literal.md) lint rule.
111
111
 
112
112
  ## `SlotGroup` — slots edited as one unit
113
113
 
@@ -206,7 +206,6 @@ magic string.
206
206
  | `ATTR_SLOT_ITEM` | `data-slot-item` | one item of a collection slot |
207
207
  | `ATTR_SLOT_GROUP` | `data-slot-group` | the container wrapping a group's members |
208
208
  | `ATTR_SECTION_INSTANCE_ID` | `data-section-instance-id` | the section root — **written by the platform**, not by a primitive |
209
- | `ATTR_TR_ENHANCE` | `data-tr-enhance` | opt-in hook telling an enhancer to attach to this element |
210
209
 
211
210
  Changing what an attribute means is a platform contract break, so these names are stable
212
211
  across a major version of the kit.
@@ -215,5 +214,5 @@ across a major version of the kit.
215
214
 
216
215
  Renderers are pure functions of props: no fetches, no globals, no `useEffect` for data,
217
216
  no `Date.now()` / `Math.random()`. They are rendered to static markup by the publisher
218
- and re-mounted constantly by the editor. Interactivity that needs the browser attaches
219
- through `data-tr-enhance`, not through render-time side effects.
217
+ and re-mounted constantly by the editor. Interactivity that needs the browser moves into
218
+ a `"use client"` island (see [Islands](islands.md)), never into render-time side effects.
@@ -0,0 +1,102 @@
1
+ ---
2
+ purpose: Zero to a rendered, checked section in under an hour, on the scaffolded starter.
3
+ status: living
4
+ related: [overview.md, dev.md, check.md, schema-system.md]
5
+ updated: 2026-07-15
6
+ ---
7
+ # Quickstart
8
+
9
+ This walks the scaffolded starter template end to end: create a workspace, see
10
+ its one section render, read how it's built, make one edit, and check it. It
11
+ does not re-teach the authoring contract — see [Overview](overview.md) for the
12
+ mental model and [Schema system](schema-system.md) for the four files in
13
+ depth.
14
+
15
+ ## Create your workspace
16
+
17
+ ```bash
18
+ npm create homepages-workspace@latest my-workspace
19
+ ```
20
+
21
+ This stamps a workspace with one template, `starter`, and one section in it,
22
+ `hero` — a working section built on the real contract, not a stub. See
23
+ [Overview](overview.md) for what a workspace, template, and section are.
24
+
25
+ ## See it render
26
+
27
+ ```bash
28
+ cd my-workspace
29
+ npm run dev
30
+ ```
31
+
32
+ This opens the **home launcher** in your browser: from there the **canvas
33
+ mirror** renders the template with `starter`'s `hero` in it, and the
34
+ full-template viewer shows the whole page. See [Dev](dev.md) for the launcher,
35
+ the canvas mirror, and live reload.
36
+
37
+ If you're an agent (or otherwise browser-less), hit the inspect endpoint
38
+ instead — it returns each slot's fill state and a text outline of the render:
39
+
40
+ ```bash
41
+ curl 'http://localhost:<port>/api/inspect?template=starter&section=hero&fixture=typical&format=text'
42
+ ```
43
+
44
+ `typical` is the starter hero's baseline fixture; it also has `left` and
45
+ `long-blurb` fixtures exercising an option and a content-length branch. See
46
+ [Dev](dev.md#agent-facing-endpoints) for `/api/inspect` and the other
47
+ agent-facing endpoints.
48
+
49
+ ## Read the worked example
50
+
51
+ Before changing anything, read the four files that make up the starter's
52
+ `hero` section — `templates/starter/sections/hero/`:
53
+
54
+ - `schema.ts` — the slots (`hero_image`, `headline`, `blurb`) and the `align`
55
+ option, each comment explaining why it's declared the way it is.
56
+ - `fill-spec.ts` — how AI fills `hero_image` and `blurb`; `headline` needs no
57
+ decision because it's fact-bound.
58
+ - `fixtures.ts` — the `typical` baseline plus the `left`/`long-blurb` states.
59
+ - `Renderer.tsx` — the markup, including the one interactive part: `blurb`
60
+ wrapped in a `"use client"` island.
61
+
62
+ Each file is annotated in place. For the concepts behind them, see
63
+ [Schema system](schema-system.md).
64
+
65
+ ## Make one edit
66
+
67
+ Open `templates/starter/sections/hero/fixtures.ts` and change the `headline`
68
+ value under `base`:
69
+
70
+ ```diff
71
+ - headline: "101 Queensway, Jamaica Plain, MA 02130",
72
+ + headline: "12 Elm Street, Jamaica Plain, MA 02130",
73
+ ```
74
+
75
+ Save it. If `npm run dev` is running, any open preview of the `hero` section
76
+ reloads with the new headline; re-running the `/api/inspect` call above also
77
+ shows the new value.
78
+
79
+ ## Check it
80
+
81
+ ```bash
82
+ npm run check
83
+ ```
84
+
85
+ ```
86
+ ✓ starter
87
+ check passed
88
+ ```
89
+
90
+ A green `check` is the acceptance gate — see [Check](check.md) for what its
91
+ six stages verify and what a failure looks like.
92
+
93
+ ## The author loop
94
+
95
+ Every section, and every change to one, runs the same three-step loop:
96
+
97
+ 1. **Edit** a section's files.
98
+ 2. **Preview** the result — `npm run dev` in a browser, or `/api/inspect` from
99
+ a terminal.
100
+ 3. **Check** it — `npm run check` — until it's green.
101
+
102
+ Every recipe in this corpus ends by running this loop.
@@ -0,0 +1,27 @@
1
+ ---
2
+ purpose: Task router — how do I… → the recipe.
3
+ status: living
4
+ related: []
5
+ updated: 2026-07-16
6
+ ---
7
+ # Recipes
8
+
9
+ Every recipe is a **delta from the starter's `hero` section** — never a
10
+ from-scratch file — ending in the same [author loop](../quickstart.md#the-author-loop)
11
+ as everything else in this corpus: edit, preview, check. Find your task below and
12
+ copy the shapes from its recipe, not from memory.
13
+
14
+ | How do I… | Recipe |
15
+ |---|---|
16
+ | add an image slot the editor can crop | [`image-slot-crop.md`](image-slot-crop.md) |
17
+ | fill a slot from a property fact without AI | [`bind-property-fact.md`](bind-property-fact.md) |
18
+ | give the user a closed set of choices | [`select-slot.md`](select-slot.md) |
19
+ | add a repeating list the user edits per item (unit table, cards, gallery) | [`collection-slot.md`](collection-slot.md) |
20
+ | have AI write a slot's content | [`fill-spec-decision.md`](fill-spec-decision.md) |
21
+ | preview a section's structural edge cases / states | [`fixture-states.md`](fixture-states.md) |
22
+ | make part of a section interactive (an island) | [`interactive-island.md`](interactive-island.md) |
23
+ | organize a section's files beyond the four reserved | [`organize-section-folder.md`](organize-section-folder.md) |
24
+ | ship a static asset (image/svg/font) with a section | [`static-asset.md`](static-asset.md) |
25
+ | use a third-party npm package in a section | [`third-party-package.md`](third-party-package.md) |
26
+ | add another template to the workspace | [`second-template.md`](second-template.md) |
27
+ | package a template for submission | [`prepare-submission.md`](prepare-submission.md) |
@@ -0,0 +1,58 @@
1
+ ---
2
+ purpose: Fill a slot from a known property fact, no AI.
3
+ status: living
4
+ related: [../vocabulary.md, ../schema-system.md, ../rules/schema-invalid.md]
5
+ updated: 2026-07-15
6
+ ---
7
+ # Bind a slot to a property fact
8
+
9
+ ## Goal
10
+
11
+ Fill a slot from a known property fact, no AI.
12
+
13
+ ## The delta
14
+
15
+ Give the slot a `source: direct("<field>")` instead of a `produced_by` — the
16
+ same pattern the starter's `headline` already uses
17
+ (`source: direct("address")`). Model it on an AI-produced slot you want to
18
+ make fact-bound instead: swap its `produced_by` for `source`, and drop the
19
+ matching decision from `fill-spec.ts`.
20
+
21
+ ```ts
22
+ // schema.ts — was produced_by: "blurb_text"
23
+ blurb: {
24
+ type: "text",
25
+ size: "long",
26
+ editable_by_user: true,
27
+ required: true,
28
+ source: direct("lot_size"),
29
+ display_name: "Blurb",
30
+ },
31
+ ```
32
+
33
+ Then delete the now-unused `blurb_text` decision from `fill-spec.ts`'s
34
+ `decisions` array — a `direct()`-bound slot needs none; the fact is baked in
35
+ at fill time, and the [`schema-invalid`](../rules/schema-invalid.md) check
36
+ rejects a slot that declares both `source` and `produced_by`.
37
+
38
+ The same pattern works for any slot type — pick a field whose runtime shape
39
+ matches. For a `number` slot, `source: direct("beds")`; for `text`,
40
+ `source: direct("address")` or any other text-shaped field in
41
+ [the closed vocabulary](../vocabulary.md#direct-sources).
42
+
43
+ ## Commands
44
+
45
+ None — this is a pure content edit. No CLI command beyond the author loop below.
46
+
47
+ ## Verify
48
+
49
+ Verify with [the author loop](../quickstart.md#the-author-loop).
50
+
51
+ ## See also
52
+
53
+ - [The closed vocabulary](../vocabulary.md#direct-sources) — every `direct()` field,
54
+ grouped by what it resolves against.
55
+ - [The schema system](../schema-system.md#binding-a-slot-to-a-property-fact) — the
56
+ `direct()`/`derived()` contract and why a bad name is a compile error.
57
+ - [`schema-invalid`](../rules/schema-invalid.md) — fires if a slot declares both
58
+ `source` and `produced_by`, or neither.