@homepages/template-kit 0.0.0 → 0.1.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.
Files changed (90) hide show
  1. package/CHANGELOG.md +182 -0
  2. package/README.md +71 -14
  3. package/dist/base.css +85 -0
  4. package/dist/browser.d.ts +26 -0
  5. package/dist/browser.js +55 -0
  6. package/dist/cli.js +1 -1
  7. package/dist/contracts/image-descriptor.d.ts +17 -0
  8. package/dist/contracts/markers.d.ts +20 -0
  9. package/dist/contracts/markers.js +21 -0
  10. package/dist/contracts/slot-catalog.d.ts +237 -0
  11. package/dist/contracts/slot-catalog.js +247 -0
  12. package/dist/contracts/transforms.d.ts +108 -0
  13. package/dist/contracts/transforms.js +117 -0
  14. package/dist/design-system/breakpoints.d.ts +5 -0
  15. package/dist/design-system/breakpoints.js +14 -0
  16. package/dist/design-system/theme.d.ts +307 -0
  17. package/dist/design-system/theme.js +95 -0
  18. package/dist/eslint/is-client-file.js +79 -0
  19. package/dist/eslint/rules/image-bare-needs-reason.js +38 -0
  20. package/dist/eslint/rules/no-client-directive-in-contract.js +26 -0
  21. package/dist/eslint/rules/no-client-runtime-in-server.js +99 -0
  22. package/dist/eslint/rules/no-css-import-from-render-path.js +23 -0
  23. package/dist/eslint/rules/no-hex.js +111 -0
  24. package/dist/eslint/rules/no-inline-style.js +23 -0
  25. package/dist/eslint/rules/no-nondeterminism.js +44 -0
  26. package/dist/eslint/rules/no-raw-element.js +32 -0
  27. package/dist/eslint/rules/props-from-schema.js +32 -0
  28. package/dist/eslint/rules/serializable-island-props.js +108 -0
  29. package/dist/eslint/rules/slot-marker-literal.js +33 -0
  30. package/dist/eslint.d.ts +4 -8
  31. package/dist/eslint.js +75 -11
  32. package/dist/fixtures/sample-property.d.ts +67 -0
  33. package/dist/fixtures/sample-property.js +707 -0
  34. package/dist/fixtures.d.ts +2 -0
  35. package/dist/fixtures.js +3 -0
  36. package/dist/index.d.ts +37 -13
  37. package/dist/index.js +34 -2
  38. package/dist/island-runtime.d.ts +30 -0
  39. package/dist/island-runtime.js +73 -0
  40. package/dist/islands/contract.d.ts +56 -0
  41. package/dist/islands/contract.js +71 -0
  42. package/dist/islands/marker.d.ts +16 -0
  43. package/dist/islands/marker.js +18 -0
  44. package/dist/package.js +5 -0
  45. package/dist/primitives/Image.d.ts +49 -0
  46. package/dist/primitives/Image.js +95 -0
  47. package/dist/primitives/Section.d.ts +22 -0
  48. package/dist/primitives/Section.js +26 -0
  49. package/dist/primitives/Slot.d.ts +50 -0
  50. package/dist/primitives/Slot.js +39 -0
  51. package/dist/primitives/SlotGroup.d.ts +38 -0
  52. package/dist/primitives/SlotGroup.js +24 -0
  53. package/dist/primitives/SlotItem.d.ts +43 -0
  54. package/dist/primitives/SlotItem.js +38 -0
  55. package/dist/primitives/index.d.ts +7 -0
  56. package/dist/primitives/picture-sources.d.ts +22 -0
  57. package/dist/primitives/picture-sources.js +52 -0
  58. package/dist/schema/fill-spec.d.ts +692 -0
  59. package/dist/schema/fill-spec.js +98 -0
  60. package/dist/schema/fixture-schema.d.ts +143 -0
  61. package/dist/schema/fixture-schema.js +113 -0
  62. package/dist/schema/index.d.ts +16 -0
  63. package/dist/schema/manifest.d.ts +527 -0
  64. package/dist/schema/manifest.js +73 -0
  65. package/dist/schema/property-facts.d.ts +44 -0
  66. package/dist/schema/resolve-variants.d.ts +11 -0
  67. package/dist/schema/resolve-variants.js +15 -0
  68. package/dist/schema/runtime-values.d.ts +45 -0
  69. package/dist/schema/section-nav.d.ts +7 -0
  70. package/dist/schema/section-props.d.ts +69 -0
  71. package/dist/schema/section-schema.d.ts +533 -0
  72. package/dist/schema/section-schema.js +72 -0
  73. package/dist/schema/slot-types.d.ts +117 -0
  74. package/dist/schema/slot-types.js +181 -0
  75. package/dist/schema/source.d.ts +13 -0
  76. package/dist/schema/source.js +12 -0
  77. package/dist/schema/synthesize-nullable.d.ts +18 -0
  78. package/dist/schema/synthesize-nullable.js +47 -0
  79. package/dist/styles.css +131 -9
  80. package/docs/INDEX.md +6 -4
  81. package/docs/eslint.md +90 -0
  82. package/docs/islands.md +150 -0
  83. package/docs/llms.txt +30 -3
  84. package/docs/primitives.md +214 -0
  85. package/docs/schema-system.md +240 -0
  86. package/docs/theme-and-css.md +179 -0
  87. package/package.json +30 -6
  88. package/tsconfig.base.json +16 -0
  89. package/tsconfig.json +2 -13
  90. package/dist/src-CrdHXijV.js +0 -23
package/CHANGELOG.md ADDED
@@ -0,0 +1,182 @@
1
+ # @homepages/template-kit
2
+
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 46e13a0: `base.css` no longer applies `content-visibility: auto` to off-screen sections on published
8
+ pages, and no longer defines the `--tr-control-*` tokens.
9
+
10
+ `content-visibility` is now opt-in: it carries a `contain: paint` cost that clips in-section
11
+ overlays, and only a template author knows whether their page needs it. If your page is long
12
+ and image-heavy, see "Recipe: `content-visibility` on long, image-heavy pages" in
13
+ `docs/theme-and-css.md` for the rule to add to your own template stylesheet.
14
+
15
+ The `--tr-control-*` tokens were unreferenced and are removed.
16
+
17
+ - b08117b: `ImageValue` now models the shape a renderer actually receives, instead of a
18
+ CSS-framing model the runtime never implemented.
19
+
20
+ **Removed** `crop` (`{ zoom, ox, oy }`) and `focal`. Their doc comments claimed the
21
+ `<Image>` primitive applied them as a CSS transform / `object-position` — it never
22
+ did, and no renderer or primitive has ever read either field. Framing is baked into a
23
+ **pre-cropped derivative asset**: when an image has been cropped, the platform serves
24
+ that derivative as the slot's `url`, and `<Image>` is a centered `object-cover` over
25
+ whatever `url` points at. A renderer that read `crop` or `focal` was reading fields
26
+ that were always absent from the resolved value.
27
+
28
+ **Added** `crop_key` — the content-addressed key of the baked crop derivative. It was
29
+ already on the wire but undeclared here; it is the _provenance_ of `url`, not a second
30
+ thing to render.
31
+
32
+ `rect` and `ar` remain, now labelled **carried, not rendered**: they are the editor's
33
+ re-edit state (so the cropper can reopen at the last crop), not framing inputs.
34
+
35
+ Renderers read only `url`, `alt`, `mobile`, and `responsive`. This matches
36
+ `ResolvedImage`, the shape the platform emits.
37
+
38
+ - c003008: Added the **schema system** — the section authoring contract, and the first real
39
+ surface of this package.
40
+
41
+ A section is now fully declarable against the kit: `schema.ts` (slots + options,
42
+ `as const satisfies SectionSchemaShape`), `fill-spec.ts` (`FillSpecShape`),
43
+ `fixtures.ts` (`FixtureModule`), and a `Renderer.tsx` whose props are **derived**
44
+ from the schema via `SectionProps<typeof schema>` rather than hand-written — a
45
+ required text slot resolves to `string`, an optional one to `string | null`, an
46
+ enum option to the literal union of its declared values.
47
+
48
+ Slots bind to property facts through a **closed vocabulary**: `direct()` accepts
49
+ only a known fact name and `derived()` only a known transform, so a misspelling is
50
+ a compile error at the call site instead of a runtime surprise. The vocabulary
51
+ tables (`SOURCE_FIELDS`, `TRANSFORMS`, `SLOT_TYPE_DESCRIPTORS`) are exported, and
52
+ changing them is a kit release.
53
+
54
+ Also exported: fact-driven variants (`makeResolveVariants`, `TransformInput`), the
55
+ template composition manifest (`TemplateManifest`), the fixture builders
56
+ (`buildFixtureSet`), and the image/breakpoint value shapes a renderer receives.
57
+
58
+ Everything lands on the package root — `import { … } from "@homepages/template-kit"`.
59
+ There are no subpath imports for the authoring API.
60
+
61
+ Zod is now a runtime dependency (`^3.23.8`).
62
+
63
+ Docs: `docs/schema-system.md`.
64
+
65
+ - 07d5dc8: Added the **contract primitives, theme tokens, and the golden property fixtures** —
66
+ with the schema system, a section is now authorable end to end against the kit.
67
+
68
+ **Primitives.** `Section` (the full-bleed section root), `Slot` and `SlotGroup` (the
69
+ editor's slot markers), `Image` (responsive `<picture>`/srcset from the platform's
70
+ image descriptor, with a deterministic empty-state box), and a new `SlotItem`.
71
+
72
+ `SlotItem` is the per-item handle inside a collection slot (`object_list`,
73
+ `image_collection`). It emits `data-slot-item`, which is what makes per-item
74
+ selection, per-item image cropping, and per-item live surfaces work in the editor —
75
+ a collection without it is editable only as a whole. The attribute is now declared
76
+ in the marker contract (`ATTR_SLOT_ITEM`) and emitted by a primitive rather than
77
+ hand-written as a magic string.
78
+
79
+ The primitives are the _only_ components the kit ships, and that is deliberate:
80
+ each one emits something the platform breaks without. Presentation — headings,
81
+ links, layout wells, icons, lists — is your template's own JSX.
82
+
83
+ **Theme tokens.** `@homepages/template-kit/styles.css` carries the Tailwind v4
84
+ `@theme` alias layer and the breakpoint ladder; `@homepages/template-kit/base.css`
85
+ carries the cascade-layer order, the reset, and `.tr-section`. A template supplies
86
+ the values behind the aliases as a `TokenTheme` (`theme.ts`), compiled by
87
+ `compileThemeToCss` into the `:root { --tr-*: … }` block its bundle ships.
88
+
89
+ Import the token entry **after** Tailwind, and import Tailwind exactly once — a
90
+ second `@import "tailwindcss"` anywhere in the graph double-emits the preflight
91
+ reset:
92
+
93
+ ```css
94
+ @import "tailwindcss";
95
+ @import "@homepages/template-kit/styles.css";
96
+ ```
97
+
98
+ **Golden fixtures.** `sampleProperty` and friends (`sampleContacts`, `samplePois`,
99
+ `sampleAmenities`, `allPhotos`, `mkResponsive`) — a realistic 12-unit building with
100
+ long unit labels, a reserved unit and a missing price. Preview is only as honest as
101
+ the data behind it; build your `fixtures.ts` on these rather than on lorem ipsum.
102
+
103
+ **Breaking within 0.x:** the type formerly exported as `Slot` (the slot-definition
104
+ union) is now `SlotDefinition`. `Slot` is the primitive component.
105
+
106
+ - 35b0939: Added `@homepages/template-kit/eslint` — the flat-config ESLint preset for section
107
+ authoring. Eleven rules, all syntactic (no type-aware linting, so "zero extra config"
108
+ holds), catch the mistakes specific to writing a HomePages section template: reading
109
+ the clock or randomness (`no-nondeterminism`), effects/state/handlers/browser globals
110
+ outside a `"use client"` island (`no-client-runtime-in-server`), a prop crossing to an
111
+ island that can't survive JSON serialization (`serializable-island-props`), a hard-coded
112
+ colour where a theme token belongs (`no-hex`), and seven more covering the contract
113
+ files, slot markers, and raw-CSS boundaries a section must respect.
114
+
115
+ Consume it as:
116
+
117
+ ```js
118
+ import kit from "@homepages/template-kit/eslint";
119
+ export default kit;
120
+ ```
121
+
122
+ `@typescript-eslint/parser` is now a real `dependency` (not only a devDependency) —
123
+ the preset wires it directly so a consumer's `eslint.config.mjs` needs no parser
124
+ setup of its own.
125
+
126
+ - 2a32639: Islands: interactivity is now an ordinary React component marked "use client". Adds the
127
+ island contract and marker to the main entry, a new `./island-runtime` loader that
128
+ hydrates each island as its own React root, and a new `./browser` subpath with the
129
+ site-config and signed-ingest helpers for published pages.
130
+ - 96356b5: The published barrel now tree-shakes, and the golden fixtures move to their own subpath.
131
+
132
+ The build previously collapsed the primitives, the schema system, zod, the theme compiler
133
+ and the sample-property fixtures into a single chunk that `dist/index.js` only re-exported.
134
+ Because a consumer's bundler can only tree-shake at module granularity, importing one
135
+ primitive pulled in all of it: `import { Section }` cost **84,755 B** and inlined zod plus
136
+ the golden fixture dataset — agent phone numbers, listing prices, lot sizes — into every
137
+ section's published renderer.
138
+
139
+ The build is now unbundled (one output module per source module), so the import graph is
140
+ honest. `import { Section }` is **202 B**; `import { makeResolveVariants }` is **179 B**.
141
+ Neither carries zod or fixture data.
142
+
143
+ **Breaking:** `sampleProperty`, `sampleContacts`, `samplePois`, `sampleAmenities`,
144
+ `allPhotos`, `mkResponsive`, `sampleHeroSlots` and the `PhotoFixture` / `PoiFixture` /
145
+ `ContactFixture` / `AmenityItem` types are no longer exported from the root barrel. Import
146
+ them from `@homepages/template-kit/fixtures`:
147
+
148
+ ```diff
149
+ -import { sampleProperty } from "@homepages/template-kit";
150
+ +import { sampleProperty } from "@homepages/template-kit/fixtures";
151
+ ```
152
+
153
+ Dev-only sample data must not be reachable from a renderer's import graph at all: a
154
+ section's published bundle is an immutable, publicly readable artifact. Import fixtures
155
+ from `fixtures.ts`, never from `Renderer.tsx`. Everything else on the root barrel — the
156
+ primitives, the schema types, `makeResolveVariants`, `compileThemeToCss`, `KIT_VERSION`,
157
+ the marker constants, the islands contract — is unchanged.
158
+
159
+ The kit's `styles.css` also narrows its Tailwind `@source` scan to the shipped
160
+ `primitives/` modules, which are the only ones that emit markup. This drops 8 dead
161
+ utilities and 19 stray `@property` registrations that were being compiled out of the CLI's
162
+ and the ESLint preset's doc comments into consumers' stylesheets.
163
+
164
+ ### Patch Changes
165
+
166
+ - bb15161: Hardened the release pipeline that produces this package, with no change to the
167
+ authoring API. Packing or publishing now always rebuilds first, so the tarball
168
+ can never ship a stale `dist/`. The tsconfig preset shipped at
169
+ `@homepages/template-kit/tsconfig` no longer leaks this repo's own `exclude`
170
+ list or forces `noEmit` onto consumers who extend it. `CHANGELOG.md` is now
171
+ included in the published tarball, so you can read what changed in a given
172
+ version without leaving `node_modules`. The docs shipped alongside the package
173
+ no longer reference this project's private internal repo layout.
174
+
175
+ ## 0.0.1
176
+
177
+ ### Patch Changes
178
+
179
+ - c1d3127: Package skeleton: the published boundary and its hygiene gates. Ships the exports
180
+ map (root entry, ESLint flat-config preset, tsconfig preset, Tailwind v4 tokens
181
+ CSS), the `template-kit` CLI with its commands stubbed, and the changesets → npm
182
+ trusted-publishing release pipeline. No authoring surface yet.
package/README.md CHANGED
@@ -3,9 +3,10 @@
3
3
  The authoring surface for HomePages marketing-section templates: schema system,
4
4
  contract primitives, theme tokens, and the `template-kit` CLI.
5
5
 
6
- **Status: 0.0.x package skeleton.** The authoring surface is still being
7
- extracted, and the CLI commands are not implemented yet. What exists today is
8
- the published package boundary and its hygiene gates.
6
+ **Status: 0.0.x.** The **schema system** (the section authoring contract and the closed
7
+ vocabulary it validates against), the **contract primitives**, and the **theme tokens**
8
+ are complete a section is authorable end to end against the kit. The `template-kit`
9
+ CLI's commands are still being extracted.
9
10
 
10
11
  ## Install
11
12
 
@@ -15,35 +16,90 @@ npm install @homepages/template-kit react react-dom
15
16
 
16
17
  React 19 is a **peer dependency** — the kit never bundles its own copy.
17
18
 
19
+ ## Authoring a section
20
+
21
+ A section is four files: `schema.ts` (slots + options), `fill-spec.ts` (how AI
22
+ fills them), `fixtures.ts` (preview content), and `Renderer.tsx` (markup, with
23
+ props **derived** from the schema).
24
+
25
+ ```ts
26
+ import { direct, type SectionProps, type SectionSchemaShape } from "@homepages/template-kit";
27
+
28
+ export const schema = {
29
+ meta: { displayName: "Hero" },
30
+ slots: {
31
+ headline: { type: "text", required: true, produced_by: "headline_text" },
32
+ listing_status: { type: "select", required: false, source: direct("listing_intent") },
33
+ },
34
+ } as const satisfies SectionSchemaShape;
35
+
36
+ export type Props = SectionProps<typeof schema>; // slots.headline is `string`
37
+ ```
38
+
39
+ Property facts come from a closed vocabulary, so `direct("listing_intentt")` is a
40
+ compile error, not a runtime surprise. Full contract:
41
+ **[docs/schema-system.md](docs/schema-system.md)**.
42
+
43
+ Your fixtures build on the kit's golden property dataset (`sampleProperty`, `allPhotos`,
44
+ `samplePois`, …) — a realistic 12-unit building, long labels and missing values included.
45
+
46
+ ## The primitives
47
+
48
+ The kit ships **five components and nothing else**: `Section` (the full-bleed section
49
+ root), `Slot` and `SlotItem` and `SlotGroup` (the editor's marker attributes), and
50
+ `Image` (responsive `<picture>` + a deterministic empty-state box). Each earns its place
51
+ by emitting something the platform breaks without. Presentation — headings, links,
52
+ layout, icons — is your template's own JSX.
53
+
54
+ ```tsx
55
+ import { Image, Section, Slot, SlotItem } from "@homepages/template-kit";
56
+ ```
57
+
58
+ Full contract: **[docs/primitives.md](docs/primitives.md)**.
59
+
18
60
  ## Exports
19
61
 
20
62
  | Entry | Use |
21
63
  |---|---|
22
- | `@homepages/template-kit` | Authoring API (schema, primitives) |
23
- | `@homepages/template-kit/styles.css` | Tailwind v4 theme tokens + kit source registration |
64
+ | `@homepages/template-kit` | Authoring API schema system, primitives, marker contract, theme types, and the golden fixtures |
65
+ | `@homepages/template-kit/island-runtime` | Browser loader that hydrates a page's islands |
66
+ | `@homepages/template-kit/browser` | Browser helpers for published pages (site config, signed ingest) |
67
+ | `@homepages/template-kit/styles.css` | Tailwind v4 theme tokens, the breakpoint ladder, kit source registration |
68
+ | `@homepages/template-kit/base.css` | The page's base stylesheet: layer order, reset, `.tr-section`, universal tokens |
24
69
  | `@homepages/template-kit/eslint` | ESLint flat-config preset |
25
70
  | `@homepages/template-kit/tsconfig` | tsconfig preset (`"extends"`) |
26
71
 
27
- The CSS entry does **not** import Tailwind. Import it *after* Tailwind in your entry:
72
+ The authoring API has **no subpath imports** one specifier for everything you
73
+ write a section against. The two CSS entries are the deliberate exception.
74
+
75
+ `styles.css` does **not** import Tailwind. Import it *after* Tailwind in your entry:
28
76
 
29
77
  ```css
30
78
  @import "tailwindcss";
31
79
  @import "@homepages/template-kit/styles.css";
32
80
  ```
33
81
 
34
- Two `@import "tailwindcss"` in one graph double-emits preflight.
82
+ Two `@import "tailwindcss"` in one graph double-emits preflight. `base.css` is not part
83
+ of that entry — it loads as its own stylesheet on the page. Full contract:
84
+ **[docs/theme-and-css.md](docs/theme-and-css.md)**.
35
85
 
36
86
  ## Development
37
87
 
38
88
  ```bash
39
89
  npm install
40
- npm run check # typecheck + lint + test + build + publint + attw + consumer gate
90
+ npm run check # typecheck + lint + build + island-size gate + test + publint + attw + consumer gate
41
91
  ```
42
92
 
43
93
  `npm run verify:consumer` is the boundary gate: it packs the kit, installs the
44
- tarball into a scratch project, and asserts that types resolve, that Tailwind's
45
- cross-package `@source` scan reaches the shipped `dist`, and that only one React
46
- ends up in the tree. Run it before every release.
94
+ tarball into a scratch project, and asserts that types resolve; that a real section
95
+ triple typechecks against the installed package, with a misspelled property fact or
96
+ slot key still failing to compile; that Tailwind's cross-package `@source` scan
97
+ reaches the shipped `dist`; and that only one React ends up in the tree. Run it
98
+ before every release.
99
+
100
+ The island loader (`@homepages/template-kit/island-runtime`) has its own gate,
101
+ `npm run size:islands`: it must stay under 3072 B gzip and never import
102
+ `react`/`react-dom` statically, so a page with no islands loads no React.
47
103
 
48
104
  ## Releasing
49
105
 
@@ -71,6 +127,7 @@ optional capability is a **minor**.
71
127
 
72
128
  ### Bumping the version by hand
73
129
 
74
- Don't. `KIT_VERSION` in `src/index.ts` is asserted against `package.json` by the
75
- test suite, and changesets owns `package.json`'s version so a hand-bump fails
76
- CI. Update `KIT_VERSION` only in the Version Packages PR, alongside the bump.
130
+ Don't changesets owns `package.json`'s version. There is nothing else to keep
131
+ in step with it: `KIT_VERSION` is read from `package.json` and inlined at build
132
+ time, and the test suite asserts `template-kit --version` against `package.json`
133
+ to prove the inlining happened.
package/dist/base.css ADDED
@@ -0,0 +1,85 @@
1
+ @layer theme, base, components, sections, template, utilities;
2
+
3
+ /* @homepages/template-kit — the base stylesheet every template's page loads.
4
+ *
5
+ * Exported as `@homepages/template-kit/base.css`. It is NOT part of the Tailwind
6
+ * entry (that is styles.css); it ships as its own stylesheet on the page.
7
+ *
8
+ * Contains:
9
+ * - The cascade-layer order declaration above — the one place it is stated
10
+ * - The @layer base reset
11
+ * - Document defaults (html/body)
12
+ * - The section box model (.tr-section, emitted by the <Section> primitive)
13
+ * - Universal tokens: the spacing scale and motion
14
+ *
15
+ * NOT here: anything a template owns. Colors, fonts, type, radii, shadows and
16
+ * layout all come from your theme.ts, compiled by compileThemeToCss into a
17
+ * `:root { --tr-*: … }` block. Component styling is Tailwind utilities written
18
+ * on the elements themselves.
19
+ */
20
+
21
+ /* ----- Reset (lightweight; assumes modern browsers) --------------------- *
22
+ * Wrapped in `@layer base` so Tailwind's utilities (which compile into
23
+ * `@layer utilities`) win over these element defaults. Unlayered, a rule like
24
+ * `a { color: inherit }` would beat the layered `.text-ink` utility outright —
25
+ * unlayered always wins over layered in the cascade.
26
+ */
27
+
28
+ @layer base {
29
+ *, *::before, *::after { box-sizing: border-box; }
30
+ html, body { margin: 0; padding: 0; }
31
+ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
32
+ img, svg, video { display: block; max-width: 100%; }
33
+ button { background: none; border: 0; padding: 0; cursor: pointer; font: inherit; color: inherit; }
34
+ a { color: inherit; text-decoration: none; }
35
+ ul, ol { margin: 0; padding: 0; list-style: none; }
36
+ h1, h2, h3, h4, h5, h6, p { margin: 0; }
37
+ }
38
+
39
+ /* ----- Document defaults ------------------------------------------------ */
40
+
41
+ html { scroll-behavior: smooth; scroll-padding-top: var(--tr-header-height); }
42
+ body {
43
+ font-family: var(--tr-font-sans);
44
+ color: var(--tr-color-ink);
45
+ background: var(--tr-color-background);
46
+ font-size: var(--tr-text-base);
47
+ line-height: 1.5;
48
+ }
49
+
50
+ /* ----- Primitives ------------------------------------------------------- */
51
+
52
+ /* The full-bleed section root, emitted by the <Section> primitive. Always 100%
53
+ * wide with zero padding/margin, so adjacent sections butt edge-to-edge and a page
54
+ * composes as a vertical stack with no framework-injected whitespace. A section
55
+ * brings its own background and internal padding. */
56
+ .tr-section {
57
+ display: block;
58
+ width: 100%;
59
+ padding: 0;
60
+ margin: 0;
61
+ }
62
+
63
+ /* ----- Universal tokens ------------------------------------------------- */
64
+
65
+ :root {
66
+ /* Spacing scale */
67
+ --tr-space-1: 0.25rem;
68
+ --tr-space-2: 0.5rem;
69
+ --tr-space-3: 0.75rem;
70
+ --tr-space-4: 1rem;
71
+ --tr-space-5: 1.25rem;
72
+ --tr-space-6: 1.5rem;
73
+ --tr-space-8: 2rem;
74
+ --tr-space-10: 2.5rem;
75
+ --tr-space-12: 3rem;
76
+ --tr-space-16: 4rem;
77
+ --tr-space-20: 5rem;
78
+ --tr-space-24: 6rem;
79
+ --tr-space-32: 8rem;
80
+
81
+ /* Motion */
82
+ --tr-duration-fast: 150ms;
83
+ --tr-duration-base: 250ms;
84
+ --tr-ease: cubic-bezier(0.2, 0.8, 0.2, 1);
85
+ }
@@ -0,0 +1,26 @@
1
+ //#region src/browser/site-config.d.ts
2
+ interface SiteConfig {
3
+ siteId: string;
4
+ siteSecret: string;
5
+ ingestBase: string;
6
+ }
7
+ /** Read the per-site config a published page embeds as `#tr-site-config`. Null when absent or incomplete. */
8
+ declare function readSiteConfig(): SiteConfig | null;
9
+ /**
10
+ * Decode hex to bytes. Always ArrayBuffer-backed (never SharedArrayBuffer), so the
11
+ * result satisfies the BufferSource parameter of crypto.subtle.importKey — but that
12
+ * generic stays out of this public signature: on TypeScript < 5.7, `Uint8Array` isn't
13
+ * generic at all, and writing `Uint8Array<ArrayBuffer>` here would fail every
14
+ * consumer's build below that floor (TS2315) for a detail only this module's own
15
+ * `crypto.subtle.importKey` call needs.
16
+ */
17
+ declare function hexToBytes(hex: string): Uint8Array;
18
+ declare function bytesToHex(buf: ArrayBuffer): string;
19
+ declare function hmacHex(secretHex: string, message: string): Promise<string>;
20
+ /**
21
+ * The signed header set every ingest request carries:
22
+ * `X-Signature` = hex(hmac_sha256(siteSecret, `${unixSeconds}.${body}`)).
23
+ */
24
+ declare function signedIngestHeaders(config: SiteConfig, body: string): Promise<Record<string, string>>;
25
+ //#endregion
26
+ export { SiteConfig, bytesToHex, hexToBytes, hmacHex, readSiteConfig, signedIngestHeaders };
@@ -0,0 +1,55 @@
1
+ //#region src/browser/site-config.ts
2
+ /** Read the per-site config a published page embeds as `#tr-site-config`. Null when absent or incomplete. */
3
+ function readSiteConfig() {
4
+ const el = document.getElementById("tr-site-config");
5
+ if (!el?.textContent) return null;
6
+ try {
7
+ const config = JSON.parse(el.textContent);
8
+ if (!config.siteId || !config.siteSecret || !config.ingestBase) return null;
9
+ return config;
10
+ } catch {
11
+ return null;
12
+ }
13
+ }
14
+ /**
15
+ * Decode hex to bytes. Always ArrayBuffer-backed (never SharedArrayBuffer), so the
16
+ * result satisfies the BufferSource parameter of crypto.subtle.importKey — but that
17
+ * generic stays out of this public signature: on TypeScript < 5.7, `Uint8Array` isn't
18
+ * generic at all, and writing `Uint8Array<ArrayBuffer>` here would fail every
19
+ * consumer's build below that floor (TS2315) for a detail only this module's own
20
+ * `crypto.subtle.importKey` call needs.
21
+ */
22
+ function hexToBytes(hex) {
23
+ if (hex.length % 2 !== 0) throw new Error("hexToBytes: odd-length hex string");
24
+ if (!/^[0-9a-fA-F]*$/.test(hex)) throw new Error("hexToBytes: invalid hex string");
25
+ const out = new Uint8Array(/* @__PURE__ */ new ArrayBuffer(hex.length / 2));
26
+ for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
27
+ return out;
28
+ }
29
+ function bytesToHex(buf) {
30
+ return Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, "0")).join("");
31
+ }
32
+ async function hmacHex(secretHex, message) {
33
+ const key = await crypto.subtle.importKey("raw", hexToBytes(secretHex), {
34
+ name: "HMAC",
35
+ hash: "SHA-256"
36
+ }, false, ["sign"]);
37
+ return bytesToHex(await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(message)));
38
+ }
39
+ /**
40
+ * The signed header set every ingest request carries:
41
+ * `X-Signature` = hex(hmac_sha256(siteSecret, `${unixSeconds}.${body}`)).
42
+ */
43
+ async function signedIngestHeaders(config, body) {
44
+ const ts = Math.floor(Date.now() / 1e3).toString();
45
+ const signature = await hmacHex(config.siteSecret, `${ts}.${body}`);
46
+ return {
47
+ "Content-Type": "application/json",
48
+ "X-Site-Id": config.siteId,
49
+ "X-Timestamp": ts,
50
+ "X-Signature": signature
51
+ };
52
+ }
53
+
54
+ //#endregion
55
+ export { bytesToHex, hexToBytes, hmacHex, readSiteConfig, signedIngestHeaders };
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as KIT_VERSION } from "./src-CrdHXijV.js";
2
+ import { KIT_VERSION } from "./index.js";
3
3
 
4
4
  //#region src/cli/index.ts
5
5
  /**
@@ -0,0 +1,17 @@
1
+ //#region src/contracts/image-descriptor.d.ts
2
+ interface ResponsiveSource {
3
+ /** Image MIME type: 'image/avif' | 'image/webp' | 'image/jpeg' | 'image/png'. */
4
+ type: string;
5
+ /** "<presigned-url> 320w, … 2048w" — rungs ascending by ACTUAL width. */
6
+ srcset: string;
7
+ }
8
+ interface ResponsiveImage {
9
+ /** Per-format candidate sets, ordered preferred→fallback: avif, webp, jpeg, png. */
10
+ sources: ResponsiveSource[];
11
+ /** Ceiling-rung (MAX edge) actual width — drives <img> width/aspect/sizes. */
12
+ width: number;
13
+ /** Ceiling-rung actual height. */
14
+ height: number;
15
+ }
16
+ //#endregion
17
+ export { ResponsiveImage, ResponsiveSource };
@@ -0,0 +1,20 @@
1
+ //#region src/contracts/markers.d.ts
2
+ /** Wrapper marker the editor resolves a slot from (`Slot`, primitive `slotId`). */
3
+ declare const ATTR_SLOT_ID = "data-slot-id";
4
+ /** Marks the immediate text-leaf child of a `textLeaf` slot. */
5
+ declare const ATTR_SLOT_TEXT_LEAF = "data-slot-text-leaf";
6
+ /** Wraps a set of slots edited/regenerated as one unit (`SlotGroup`). */
7
+ declare const ATTR_SLOT_GROUP = "data-slot-group";
8
+ /**
9
+ * Zero-based index of one item inside a collection slot (`SlotItem`) — the
10
+ * per-item handle for `object_list` / `image_collection` slots. The editor
11
+ * resolves per-item selection, live enhancer surfaces, and per-item image
12
+ * crops against it, scoped to the enclosing `ATTR_SLOT_ID` element.
13
+ */
14
+ declare const ATTR_SLOT_ITEM = "data-slot-item";
15
+ /** Per-section-instance identity on the section root. */
16
+ declare const ATTR_SECTION_INSTANCE_ID = "data-section-instance-id";
17
+ /** Opt-in hook telling an enhancer to attach to this element. */
18
+ declare const ATTR_TR_ENHANCE = "data-tr-enhance";
19
+ //#endregion
20
+ export { ATTR_SECTION_INSTANCE_ID, ATTR_SLOT_GROUP, ATTR_SLOT_ID, ATTR_SLOT_ITEM, ATTR_SLOT_TEXT_LEAF, ATTR_TR_ENHANCE };
@@ -0,0 +1,21 @@
1
+ //#region src/contracts/markers.ts
2
+ /** Wrapper marker the editor resolves a slot from (`Slot`, primitive `slotId`). */
3
+ const ATTR_SLOT_ID = "data-slot-id";
4
+ /** Marks the immediate text-leaf child of a `textLeaf` slot. */
5
+ const ATTR_SLOT_TEXT_LEAF = "data-slot-text-leaf";
6
+ /** Wraps a set of slots edited/regenerated as one unit (`SlotGroup`). */
7
+ const ATTR_SLOT_GROUP = "data-slot-group";
8
+ /**
9
+ * Zero-based index of one item inside a collection slot (`SlotItem`) — the
10
+ * per-item handle for `object_list` / `image_collection` slots. The editor
11
+ * resolves per-item selection, live enhancer surfaces, and per-item image
12
+ * crops against it, scoped to the enclosing `ATTR_SLOT_ID` element.
13
+ */
14
+ const ATTR_SLOT_ITEM = "data-slot-item";
15
+ /** Per-section-instance identity on the section root. */
16
+ const ATTR_SECTION_INSTANCE_ID = "data-section-instance-id";
17
+ /** Opt-in hook telling an enhancer to attach to this element. */
18
+ const ATTR_TR_ENHANCE = "data-tr-enhance";
19
+
20
+ //#endregion
21
+ export { ATTR_SECTION_INSTANCE_ID, ATTR_SLOT_GROUP, ATTR_SLOT_ID, ATTR_SLOT_ITEM, ATTR_SLOT_TEXT_LEAF, ATTR_TR_ENHANCE };