@homepages/template-kit 0.9.0-dev-20260719224319 → 0.9.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 (129) hide show
  1. package/CHANGELOG.md +29 -59
  2. package/README.md +14 -4
  3. package/dist/contracts/fill-treatments.d.ts +23 -0
  4. package/dist/index.d.ts +2 -2
  5. package/dist/index.js +2 -2
  6. package/dist/islands/discover.js +1 -1
  7. package/dist/islands/esbuild-plugin.js +1 -1
  8. package/dist/islands/wrap.d.ts +9 -1
  9. package/dist/media/resolve-fixture-media.d.ts +22 -0
  10. package/dist/package.js +1 -1
  11. package/dist/schema/resolve-section-ref.d.ts +4 -0
  12. package/dist/schema/rows.d.ts +9 -0
  13. package/dist/schema/section-nav.d.ts +5 -1
  14. package/dist/schema/slot-types.d.ts +1 -1
  15. package/dist/ssr.d.ts +7 -2
  16. package/dist/ssr.js +6 -2
  17. package/guide/INDEX.md +5 -0
  18. package/guide/check.md +1 -0
  19. package/guide/dev.md +1 -0
  20. package/guide/llms.txt +11 -3
  21. package/guide/upgrading.md +12 -8
  22. package/package.json +4 -16
  23. package/dist/canvas-core/chrome-css.d.ts +0 -24
  24. package/dist/canvas-core/chrome-css.js +0 -403
  25. package/dist/canvas-core/chrome.d.ts +0 -49
  26. package/dist/canvas-core/chrome.js +0 -147
  27. package/dist/canvas-core/frame.d.ts +0 -20
  28. package/dist/canvas-core/frame.js +0 -23
  29. package/dist/canvas-core/hidden-tabs.d.ts +0 -16
  30. package/dist/canvas-core/hidden-tabs.js +0 -84
  31. package/dist/canvas-core/resolve-target.js +0 -55
  32. package/dist/canvas-core/scroll.d.ts +0 -10
  33. package/dist/canvas-core/scroll.js +0 -15
  34. package/dist/canvas-core/section-dom.d.ts +0 -6
  35. package/dist/canvas-core/section-dom.js +0 -43
  36. package/dist/canvas-core/srcdoc.d.ts +0 -8
  37. package/dist/canvas-core/srcdoc.js +0 -77
  38. package/dist/canvas-core/types.d.ts +0 -76
  39. package/dist/canvas-core.d.ts +0 -9
  40. package/dist/canvas-core.js +0 -9
  41. package/dist/cli/check/config.js +0 -41
  42. package/dist/cli/check/css.js +0 -131
  43. package/dist/cli/check/diagnostics.js +0 -37
  44. package/dist/cli/check/index.js +0 -94
  45. package/dist/cli/check/loader.js +0 -149
  46. package/dist/cli/check/paths.js +0 -15
  47. package/dist/cli/check/relativize.js +0 -18
  48. package/dist/cli/check/render-invariants.js +0 -24
  49. package/dist/cli/check/resolve-tool.js +0 -38
  50. package/dist/cli/check/section-assets.js +0 -58
  51. package/dist/cli/check/stages/deps.js +0 -337
  52. package/dist/cli/check/stages/lint.js +0 -46
  53. package/dist/cli/check/stages/render.js +0 -98
  54. package/dist/cli/check/stages/size.js +0 -274
  55. package/dist/cli/check/stages/tree.js +0 -206
  56. package/dist/cli/check/stages/typecheck.js +0 -46
  57. package/dist/cli/check/stages/validate/catalog-exhaustiveness.js +0 -18
  58. package/dist/cli/check/stages/validate/facts.js +0 -19
  59. package/dist/cli/check/stages/validate/formatter-contract.js +0 -14
  60. package/dist/cli/check/stages/validate/group-contract.js +0 -21
  61. package/dist/cli/check/stages/validate/index.js +0 -66
  62. package/dist/cli/check/stages/validate/list-scalar-contract.js +0 -22
  63. package/dist/cli/check/stages/validate/nav-contract.js +0 -93
  64. package/dist/cli/check/stages/validate/nested-slot-contract.js +0 -12
  65. package/dist/cli/check/stages/validate/object-list-contract.js +0 -31
  66. package/dist/cli/check/stages/validate/orchestrator.js +0 -241
  67. package/dist/cli/check/stages/validate/produced-by-contract.js +0 -18
  68. package/dist/cli/check/stages/validate/row-contract.js +0 -31
  69. package/dist/cli/check/stages/validate/select-contract.js +0 -13
  70. package/dist/cli/check/stages/validate/source-contract.js +0 -21
  71. package/dist/cli/check/stages/validate/variant-contract.js +0 -15
  72. package/dist/cli/check/workspace.js +0 -99
  73. package/dist/cli/dev/build-css.js +0 -97
  74. package/dist/cli/dev/catalog.js +0 -35
  75. package/dist/cli/dev/compose-template.js +0 -39
  76. package/dist/cli/dev/content-override.js +0 -58
  77. package/dist/cli/dev/diagnostics.js +0 -68
  78. package/dist/cli/dev/discover.js +0 -35
  79. package/dist/cli/dev/fill-state.js +0 -77
  80. package/dist/cli/dev/index.js +0 -42
  81. package/dist/cli/dev/inspect.js +0 -38
  82. package/dist/cli/dev/island-bootstrap.js +0 -38
  83. package/dist/cli/dev/island-canvas-bootstrap.js +0 -204
  84. package/dist/cli/dev/island-map.js +0 -35
  85. package/dist/cli/dev/island-transform.js +0 -31
  86. package/dist/cli/dev/manifest-instances.js +0 -31
  87. package/dist/cli/dev/render-section.js +0 -24
  88. package/dist/cli/dev/screenshot-target.js +0 -24
  89. package/dist/cli/dev/section-page.js +0 -91
  90. package/dist/cli/dev/server.js +0 -511
  91. package/dist/cli/dev/slot-schema.js +0 -12
  92. package/dist/cli/dev/structure-summary.js +0 -119
  93. package/dist/cli/dev/tailwind.js +0 -32
  94. package/dist/cli/dev/tsconfig-paths-plugin.js +0 -90
  95. package/dist/cli/dev/vite-server.js +0 -39
  96. package/dist/cli/dev/workspace.js +0 -63
  97. package/dist/cli/link/index.js +0 -74
  98. package/dist/cli/link/overlay.js +0 -59
  99. package/dist/cli/link/watch.js +0 -25
  100. package/dist/cli/new/emit.js +0 -48
  101. package/dist/cli/new/index.js +0 -69
  102. package/dist/cli/new/scaffold/section/_Renderer.tsx +0 -20
  103. package/dist/cli/new/scaffold/section/_fill-spec.ts +0 -18
  104. package/dist/cli/new/scaffold/section/_fixtures.ts +0 -13
  105. package/dist/cli/new/scaffold/section/_schema.ts +0 -24
  106. package/dist/cli/new/scaffold/template/_manifest.json +0 -9
  107. package/dist/cli/new/scaffold/template/sections/hero/ExpandableText.tsx +0 -45
  108. package/dist/cli/new/scaffold/template/sections/hero/Renderer.tsx +0 -40
  109. package/dist/cli/new/scaffold/template/sections/hero/components/Feature.tsx +0 -14
  110. package/dist/cli/new/scaffold/template/sections/hero/fill-spec.ts +0 -29
  111. package/dist/cli/new/scaffold/template/sections/hero/fixtures.ts +0 -41
  112. package/dist/cli/new/scaffold/template/sections/hero/schema.ts +0 -60
  113. package/dist/cli/new/scaffold/template/theme.css +0 -24
  114. package/dist/cli/new/scaffold/template/theme.ts +0 -27
  115. package/dist/cli/new/scaffold-assets.js +0 -20
  116. package/dist/cli/pack/collect.js +0 -44
  117. package/dist/cli/pack/guards.js +0 -57
  118. package/dist/cli/pack/index.js +0 -66
  119. package/dist/cli/pack/manifest.js +0 -15
  120. package/dist/cli/pack/zip.js +0 -86
  121. package/dist/cli/theme/generate.js +0 -43
  122. package/dist/cli/theme/index.js +0 -33
  123. package/dist/cli/theme/load-theme.js +0 -69
  124. package/dist/cli.d.ts +0 -1
  125. package/dist/cli.js +0 -138
  126. package/dist/dev-client/assets/index-CJa9G1Na.css +0 -1
  127. package/dist/dev-client/assets/index-C_1atZs4.js +0 -450
  128. package/dist/dev-client/index.html +0 -13
  129. package/dist/tokens.css +0 -21
@@ -1,84 +0,0 @@
1
- //#region src/canvas-core/hidden-tabs.ts
2
- const WE_TAB_STEP = 42;
3
- const TAB_TUCK = 16;
4
- function placeTabs(anchors, step) {
5
- const clusters = [];
6
- for (const a of anchors) {
7
- clusters.push({
8
- sum: a.y,
9
- n: 1,
10
- items: [a]
11
- });
12
- while (clusters.length > 1) {
13
- const b = clusters[clusters.length - 1];
14
- const p = clusters[clusters.length - 2];
15
- if (!b || !p) break;
16
- if (b.sum / b.n - (b.n - 1) / 2 * step - (p.sum / p.n + (p.n - 1) / 2 * step) < step) clusters.splice(clusters.length - 2, 2, {
17
- sum: p.sum + b.sum,
18
- n: p.n + b.n,
19
- items: p.items.concat(b.items)
20
- });
21
- else break;
22
- }
23
- }
24
- const out = {};
25
- for (const c of clusters) {
26
- const top = c.sum / c.n - (c.n - 1) / 2 * step;
27
- c.items.forEach((it, i) => {
28
- out[it.idx] = top + i * step;
29
- });
30
- }
31
- return out;
32
- }
33
- const SVG_WRAP = (cls, inner) => `<svg class="${cls}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${inner}</svg>`;
34
- const EYE_OFF_SVG = SVG_WRAP("we-htab-eye-off", "<path d=\"M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49\"/><path d=\"M14.084 14.158a3 3 0 0 1-4.242-4.242\"/><path d=\"M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143\"/><path d=\"m2 2 20 20\"/>");
35
- const EYE_SVG = SVG_WRAP("we-htab-eye-on", "<path d=\"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/>");
36
- const revealHandlers = /* @__PURE__ */ new WeakMap();
37
- function buildHiddenTab(doc, instanceId, label, onReveal) {
38
- const btn = doc.createElement("button");
39
- btn.type = "button";
40
- btn.className = "we-htab";
41
- btn.dataset.hiddenTab = instanceId;
42
- btn.title = `Show ${label}`;
43
- btn.setAttribute("aria-label", `Show ${label}`);
44
- btn.innerHTML = `<span class="we-htab-eye">${EYE_OFF_SVG}${EYE_SVG}</span><span class="we-htab-name"></span>`;
45
- const nameEl = btn.querySelector(".we-htab-name");
46
- if (nameEl) nameEl.textContent = label;
47
- revealHandlers.set(btn, onReveal);
48
- btn.addEventListener("click", (event) => {
49
- event.stopPropagation();
50
- revealHandlers.get(btn)?.();
51
- });
52
- return btn;
53
- }
54
- function reconcileHiddenTabs(container, doc, specs, onReveal) {
55
- const existing = /* @__PURE__ */ new Map();
56
- container.querySelectorAll(".we-htab").forEach((el) => {
57
- const id = el.dataset.hiddenTab;
58
- if (id) existing.set(id, el);
59
- });
60
- const keep = /* @__PURE__ */ new Set();
61
- const live = [];
62
- for (const spec of specs) {
63
- let el = existing.get(spec.instanceId);
64
- if (!el) {
65
- el = buildHiddenTab(doc, spec.instanceId, spec.label, () => onReveal(spec.instanceId));
66
- container.appendChild(el);
67
- } else {
68
- const nameEl = el.querySelector(".we-htab-name");
69
- if (nameEl && nameEl.textContent !== spec.label) nameEl.textContent = spec.label;
70
- el.title = `Show ${spec.label}`;
71
- el.setAttribute("aria-label", `Show ${spec.label}`);
72
- revealHandlers.set(el, () => onReveal(spec.instanceId));
73
- }
74
- keep.add(spec.instanceId);
75
- live.push(el);
76
- }
77
- existing.forEach((el, id) => {
78
- if (!keep.has(id)) el.remove();
79
- });
80
- return live;
81
- }
82
-
83
- //#endregion
84
- export { TAB_TUCK, WE_TAB_STEP, buildHiddenTab, placeTabs, reconcileHiddenTabs };
@@ -1,55 +0,0 @@
1
- //#region src/canvas-core/resolve-target.ts
2
- function firstEditableGroupMemberId(groupEl, instanceId, deps) {
3
- for (const el of Array.from(groupEl.querySelectorAll(`[${deps.markers.slotId}]`))) {
4
- const id = el.getAttribute(deps.markers.slotId);
5
- if (id && deps.isSlotEditable(instanceId, id)) return id;
6
- }
7
- return null;
8
- }
9
- function resolveCanvasTarget(hit, sections, deps) {
10
- if (!hit) return null;
11
- const sectionEl = hit.closest(`[${deps.markers.sectionInstanceId}]`);
12
- const instanceId = sectionEl?.getAttribute(deps.markers.sectionInstanceId);
13
- if (!sectionEl || !instanceId) return null;
14
- const section = sections.find((s) => s.instance_id === instanceId);
15
- const sectionTarget = {
16
- selection: {
17
- kind: "section",
18
- instance_id: instanceId
19
- },
20
- element: sectionEl
21
- };
22
- if (!section) return sectionTarget;
23
- const groupEl = hit.closest(`[${deps.markers.slotGroup}]`);
24
- if (groupEl && sectionEl.contains(groupEl)) {
25
- const repId = firstEditableGroupMemberId(groupEl, instanceId, deps);
26
- if (!repId) return sectionTarget;
27
- return {
28
- selection: {
29
- kind: "slot",
30
- instance_id: instanceId,
31
- slot_id: repId
32
- },
33
- element: groupEl
34
- };
35
- }
36
- const slotEl = hit.closest(`[${deps.markers.slotId}]`);
37
- const slotId = slotEl?.getAttribute(deps.markers.slotId);
38
- if (!slotEl || !slotId) return sectionTarget;
39
- if (!deps.isSlotEditable(instanceId, slotId)) return sectionTarget;
40
- const itemEl = hit.closest(`[${deps.markers.slotItem}]`);
41
- const rawItem = itemEl ? Number(itemEl.getAttribute(deps.markers.slotItem)) : NaN;
42
- const itemIndex = Number.isInteger(rawItem) ? rawItem : void 0;
43
- return {
44
- selection: {
45
- kind: "slot",
46
- instance_id: instanceId,
47
- slot_id: slotId,
48
- ...itemIndex !== void 0 ? { item_index: itemIndex } : {}
49
- },
50
- element: itemEl ?? slotEl
51
- };
52
- }
53
-
54
- //#endregion
55
- export { resolveCanvasTarget };
@@ -1,10 +0,0 @@
1
- //#region src/canvas-core/scroll.d.ts
2
- declare const SCROLL_MARGIN_PX = 24;
3
- interface ScrollRect {
4
- top: number;
5
- bottom: number;
6
- height: number;
7
- }
8
- declare function computeScrollTop(rect: ScrollRect, viewportH: number, scrollY: number, margin: number): number | null;
9
- //#endregion
10
- export { SCROLL_MARGIN_PX, ScrollRect, computeScrollTop };
@@ -1,15 +0,0 @@
1
- //#region src/canvas-core/scroll.ts
2
- const SCROLL_MARGIN_PX = 24;
3
- function computeScrollTop(rect, viewportH, scrollY, margin) {
4
- if (rect.height + 2 * margin <= viewportH) {
5
- if (rect.top >= margin && rect.bottom <= viewportH - margin) return null;
6
- if (rect.top < margin) return Math.max(0, scrollY + rect.top - margin);
7
- return Math.max(0, scrollY + rect.bottom - (viewportH - margin));
8
- }
9
- const target = Math.max(0, scrollY + rect.top - margin);
10
- if (Math.abs(target - scrollY) < 1) return null;
11
- return target;
12
- }
13
-
14
- //#endregion
15
- export { SCROLL_MARGIN_PX, computeScrollTop };
@@ -1,6 +0,0 @@
1
- import { CanvasSectionEntry } from "./types.js";
2
- //#region src/canvas-core/section-dom.d.ts
3
- declare function sectionLabel(entry: CanvasSectionEntry): string;
4
- declare function hashHtml(html: string | null): string;
5
- //#endregion
6
- export { hashHtml, sectionLabel };
@@ -1,43 +0,0 @@
1
- //#region src/canvas-core/section-dom.ts
2
- function sectionLabel(entry) {
3
- if (entry.section_display_name) return entry.section_display_name;
4
- return entry.section_key.split("-").map((p) => p ? p[0].toUpperCase() + p.slice(1) : p).join(" ");
5
- }
6
- function hashHtml(html) {
7
- if (!html) return "null";
8
- let h = 2166136261;
9
- for (let i = 0; i < html.length; i++) {
10
- h ^= html.charCodeAt(i);
11
- h = Math.imul(h, 16777619);
12
- }
13
- return (h >>> 0).toString(16);
14
- }
15
- function buildErrorPlaceholder(entry, doc) {
16
- const card = doc.createElement("div");
17
- card.className = "we-render-error";
18
- const title = doc.createElement("p");
19
- title.className = "we-render-error__title";
20
- title.textContent = "Couldn’t render this section.";
21
- const detail = doc.createElement("p");
22
- detail.className = "we-render-error__detail";
23
- detail.textContent = entry.render_error?.message ?? "Unknown render error.";
24
- card.appendChild(title);
25
- card.appendChild(detail);
26
- return card;
27
- }
28
- function buildSectionWrapper(entry, doc, markers, localize) {
29
- const wrapper = doc.createElement("div");
30
- wrapper.setAttribute(markers.sectionInstanceId, entry.instance_id);
31
- wrapper.setAttribute("data-section-label", sectionLabel(entry));
32
- wrapper.dataset.svId = entry.section_version_id;
33
- wrapper.dataset.htmlHash = hashHtml(entry.html);
34
- const body = doc.createElement("div");
35
- body.className = "we-section-body";
36
- if (entry.render_error || entry.html === null) body.appendChild(buildErrorPlaceholder(entry, doc));
37
- else body.innerHTML = localize(entry.html);
38
- wrapper.appendChild(body);
39
- return wrapper;
40
- }
41
-
42
- //#endregion
43
- export { buildErrorPlaceholder, buildSectionWrapper, hashHtml, sectionLabel };
@@ -1,8 +0,0 @@
1
- import { CanvasAssets, MarkerAttrs } from "./types.js";
2
- //#region src/canvas-core/srcdoc.d.ts
3
- declare function localizeSectionAssets(html: string, basePath: string): string;
4
- declare function buildSrcdoc(assets: CanvasAssets, markers: MarkerAttrs): string;
5
- declare function buildSrcdocError(): string;
6
- declare function isCanvasScaffoldReady(doc: Document | null | undefined): boolean;
7
- //#endregion
8
- export { buildSrcdoc, buildSrcdocError, isCanvasScaffoldReady, localizeSectionAssets };
@@ -1,77 +0,0 @@
1
- import { buildChromeCss } from "./chrome-css.js";
2
-
3
- //#region src/canvas-core/srcdoc.ts
4
- function jsonForScript(value) {
5
- return JSON.stringify(value).replaceAll("<", "\\u003c").replaceAll(">", "\\u003e").replaceAll("&", "\\u0026");
6
- }
7
- function islandHead(assets) {
8
- if (Object.keys(assets.islandMap).length === 0) return "";
9
- if (Object.keys(assets.importMap).length === 0 && !assets.hostResolvesOwnImports) {
10
- console.error("canvas: this template version has islands but no import map, so nothing can resolve their React — sections render statically. Republish the template to emit the vendor.");
11
- return "";
12
- }
13
- const host = assets.hostScriptSrc ? `
14
- <script id="tr-island-host" type="module" src="${assets.hostScriptSrc}"><\/script>` : "";
15
- return `${Object.keys(assets.importMap).length === 0 ? "" : `
16
- <script type="importmap">${jsonForScript({ imports: assets.importMap })}<\/script>`}
17
- <script>window.__TR_ISLAND_MAP=${jsonForScript(assets.islandMap)};<\/script>
18
- <!-- id is the contract the host's readiness gate keys off — it waits on THIS
19
- element's load event, not a timeout, so a slow cold first load still
20
- activates without a refresh. -->${host}`;
21
- }
22
- function localizeSectionAssets(html, basePath) {
23
- return html.replaceAll("/section-assets/", `${basePath}runtime/section-asset/`);
24
- }
25
- function buildSrcdoc(assets, markers) {
26
- const links = assets.stylesheets.map((href) => `<link rel="stylesheet" href="${href}">`).join("");
27
- return `<!doctype html>
28
- <html>
29
- <head>
30
- <meta charset="utf-8">
31
- <base href="${assets.basePath}">
32
- ${links}${islandHead(assets)}
33
- <style>${buildChromeCss(markers)}</style>
34
- </head>
35
- <body>
36
- <div id="we-zoom">
37
- <div id="we-page"></div>
38
- </div>
39
- <div id="we-chrome-overlay" aria-hidden="true">
40
- <div class="we-chrome-ring we-chrome-ring--hover"></div>
41
- <div class="we-chrome-ring we-chrome-ring--selection"></div>
42
- <div class="we-chrome-label"></div>
43
- </div>
44
- <div id="we-htabs"></div>
45
- </body>
46
- </html>`;
47
- }
48
- function buildSrcdocError() {
49
- return `<!doctype html>
50
- <html>
51
- <head>
52
- <meta charset="utf-8">
53
- <style>
54
- body { margin: 0; font-family: ui-sans-serif, system-ui, sans-serif; }
55
- .we-assets-error {
56
- margin: 40px; padding: 24px 28px; border-radius: 8px;
57
- border: 1px solid #e6c0c0; background: #fdf4f4; color: #7a2e2e;
58
- font-size: 14px; line-height: 1.5; max-width: 520px;
59
- }
60
- .we-assets-error strong { display: block; margin-bottom: 6px; font-size: 15px; }
61
- </style>
62
- </head>
63
- <body>
64
- <div class="we-assets-error" role="alert">
65
- <strong>Template assets unavailable</strong>
66
- This template has no published static-assets binding. Re-publish
67
- the template registry, then reload the editor.
68
- </div>
69
- </body>
70
- </html>`;
71
- }
72
- function isCanvasScaffoldReady(doc) {
73
- return !!doc?.getElementById("we-page");
74
- }
75
-
76
- //#endregion
77
- export { buildSrcdoc, buildSrcdocError, isCanvasScaffoldReady, localizeSectionAssets };
@@ -1,76 +0,0 @@
1
- //#region src/canvas-core/types.d.ts
2
- /** Marker attribute names the frame stamps and reads. Injected because each
3
- * consuming repo owns its own generated copy of the marker contract. */
4
- interface MarkerAttrs {
5
- sectionInstanceId: string;
6
- slotId: string;
7
- slotGroup: string;
8
- slotItem: string;
9
- }
10
- /** Everything the frame needs to resolve a URL. The app derives this from its
11
- * published static-asset binding; a dev harness derives it from its own server. */
12
- interface CanvasAssets {
13
- /** Prefix for every same-origin URL the frame emits, e.g. "/app/" or "/". */
14
- basePath: string;
15
- /** Stylesheet hrefs, emitted in order. Already absolute or base-relative. */
16
- stylesheets: string[];
17
- /** Bare-specifier -> URL, emitted as an import map. Empty means no islands. */
18
- importMap: Record<string, string>;
19
- /** Island key -> module URL, emitted as window.__TR_ISLAND_MAP. */
20
- islandMap: Record<string, string>;
21
- /** The island host module, or null to emit no host. */
22
- hostScriptSrc: string | null;
23
- /**
24
- * True when the host module resolves its own bare specifiers — a build-time
25
- * transform (a Vite dev server, say) having already rewritten every import
26
- * including `react`. Such a consumer needs no import map, so an empty
27
- * `importMap` is correct rather than broken.
28
- *
29
- * Defaults to false, which keeps the strict gate: for a host that DOES carry
30
- * bare specifiers, an empty import map means its `import "react"` cannot
31
- * resolve, and emitting no host at all beats emitting one that throws.
32
- */
33
- hostResolvesOwnImports?: boolean;
34
- }
35
- /** The subset of a rendered section the frame needs: exactly the fields the frame
36
- * reads, and no more. Field names mirror the consumer's own entry shape, so a
37
- * richer entry type satisfies this structurally and is passed unconverted. */
38
- interface CanvasSectionEntry {
39
- instance_id: string;
40
- section_version_id: string;
41
- section_key: string;
42
- section_display_name?: string | null;
43
- html: string | null;
44
- render_error?: {
45
- code: string;
46
- message: string;
47
- } | null;
48
- }
49
- /** A resolved canvas selection. A discriminated union rather than flat optionals
50
- * so a slot selection structurally always carries its slot_id. */
51
- type CanvasSelection = {
52
- kind: "section";
53
- instance_id: string;
54
- } | {
55
- kind: "slot";
56
- instance_id: string;
57
- slot_id: string;
58
- /** For collection slots: the item the user clicked (the slotItem index). */
59
- item_index?: number;
60
- };
61
- interface CanvasTarget {
62
- selection: CanvasSelection;
63
- element: HTMLElement;
64
- }
65
- /** The frame's injected dependencies. */
66
- interface CanvasFrameDeps {
67
- markers: MarkerAttrs;
68
- assets: CanvasAssets;
69
- /** True when a slot may be selected. Inverted out of the kernel so it does not
70
- * pull in a consumer's slot-editor registry. Keyed on the section INSTANCE id,
71
- * not the section key: two instances of one key can pin different section
72
- * versions, so the key alone does not determine a slot's schema. */
73
- isSlotEditable: (sectionInstanceId: string, slotId: string) => boolean;
74
- }
75
- //#endregion
76
- export { CanvasAssets, CanvasFrameDeps, CanvasSectionEntry, CanvasSelection, CanvasTarget, MarkerAttrs };
@@ -1,9 +0,0 @@
1
- import { CanvasAssets, CanvasFrameDeps, CanvasSectionEntry, CanvasSelection, CanvasTarget, MarkerAttrs } from "./canvas-core/types.js";
2
- import { LABEL_LEFT_INDENT, RING_OFFSET, RingRect, applySelectionKind, clearLabel, clearRing, cssEscape, hasMeasurableRingBox, measureRingRect, positionLabel, positionRing, ringBox } from "./canvas-core/chrome.js";
3
- import { HOST_INJECTED_PROPERTIES, buildChromeCss } from "./canvas-core/chrome-css.js";
4
- import { CanvasFrame, createCanvasFrame } from "./canvas-core/frame.js";
5
- import { HiddenTabSpec, TAB_TUCK, TabAnchor, WE_TAB_STEP, buildHiddenTab, placeTabs, reconcileHiddenTabs } from "./canvas-core/hidden-tabs.js";
6
- import { SCROLL_MARGIN_PX, ScrollRect, computeScrollTop } from "./canvas-core/scroll.js";
7
- import { hashHtml, sectionLabel } from "./canvas-core/section-dom.js";
8
- import { buildSrcdoc, buildSrcdocError, isCanvasScaffoldReady, localizeSectionAssets } from "./canvas-core/srcdoc.js";
9
- export { type CanvasAssets, type CanvasFrame, type CanvasFrameDeps, type CanvasSectionEntry, type CanvasSelection, type CanvasTarget, HOST_INJECTED_PROPERTIES, type HiddenTabSpec, LABEL_LEFT_INDENT, type MarkerAttrs, RING_OFFSET, type RingRect, SCROLL_MARGIN_PX, type ScrollRect, TAB_TUCK, type TabAnchor, WE_TAB_STEP, applySelectionKind, buildChromeCss, buildHiddenTab, buildSrcdoc, buildSrcdocError, clearLabel, clearRing, computeScrollTop, createCanvasFrame, cssEscape, hasMeasurableRingBox, hashHtml, isCanvasScaffoldReady, localizeSectionAssets, measureRingRect, placeTabs, positionLabel, positionRing, reconcileHiddenTabs, ringBox, sectionLabel };
@@ -1,9 +0,0 @@
1
- import { LABEL_LEFT_INDENT, RING_OFFSET, applySelectionKind, clearLabel, clearRing, cssEscape, hasMeasurableRingBox, measureRingRect, positionLabel, positionRing, ringBox } from "./canvas-core/chrome.js";
2
- import { TAB_TUCK, WE_TAB_STEP, buildHiddenTab, placeTabs, reconcileHiddenTabs } from "./canvas-core/hidden-tabs.js";
3
- import { HOST_INJECTED_PROPERTIES, buildChromeCss } from "./canvas-core/chrome-css.js";
4
- import { hashHtml, sectionLabel } from "./canvas-core/section-dom.js";
5
- import { buildSrcdoc, buildSrcdocError, isCanvasScaffoldReady, localizeSectionAssets } from "./canvas-core/srcdoc.js";
6
- import { createCanvasFrame } from "./canvas-core/frame.js";
7
- import { SCROLL_MARGIN_PX, computeScrollTop } from "./canvas-core/scroll.js";
8
-
9
- export { HOST_INJECTED_PROPERTIES, LABEL_LEFT_INDENT, RING_OFFSET, SCROLL_MARGIN_PX, TAB_TUCK, WE_TAB_STEP, applySelectionKind, buildChromeCss, buildHiddenTab, buildSrcdoc, buildSrcdocError, clearLabel, clearRing, computeScrollTop, createCanvasFrame, cssEscape, hasMeasurableRingBox, hashHtml, isCanvasScaffoldReady, localizeSectionAssets, measureRingRect, placeTabs, positionLabel, positionRing, reconcileHiddenTabs, ringBox, sectionLabel };
@@ -1,41 +0,0 @@
1
- //#region src/cli/check/config.ts
2
- const CHECK_CONFIG = {
3
- size: {
4
- /** Gzipped bytes of a section's renderer bundle, React excluded. */
5
- rendererBundleGzip: 50 * 1024,
6
- /** Gzipped bytes of a section's stylesheet, including CSS pulled in from packages. */
7
- sectionCssGzip: 50 * 1024,
8
- /** Gzipped bytes of a section's largest island (browser) bundle, React excluded. */
9
- islandBundleGzip: 50 * 1024,
10
- /** Raw bytes of a section's static assets (svg/images/fonts). RAW, not gzip — images/fonts are already compressed. */
11
- assets: 250 * 1024
12
- },
13
- audit: {
14
- /** A vulnerability at or above this severity fails the check. */
15
- failOn: ["high", "critical"] },
16
- /**
17
- * A package whose license is not on this list fails the check. A missing
18
- * license field, or a bespoke "SEE LICENSE IN …", is a denial — not a pass.
19
- */
20
- licenseAllowlist: [
21
- "MIT",
22
- "ISC",
23
- "Apache-2.0",
24
- "BSD-2-Clause",
25
- "BSD-3-Clause",
26
- "0BSD",
27
- "Unlicense",
28
- "CC0-1.0"
29
- ],
30
- /**
31
- * The license gate walks the *shipped* dependency graph of every third-party
32
- * package a template pulls in — this is the first-party exception to that.
33
- * The kit itself is `UNLICENSED` by design (it is not published for reuse
34
- * outside this platform) and every workspace depends on it, so without this
35
- * exemption the gate would deny the kit on a stock, correct workspace.
36
- */
37
- licenseExemptions: ["@homepages/template-kit"]
38
- };
39
-
40
- //#endregion
41
- export { CHECK_CONFIG };
@@ -1,131 +0,0 @@
1
- import { loadEsbuild } from "./resolve-tool.js";
2
- import { readdir } from "node:fs/promises";
3
- import { join } from "node:path";
4
- import { existsSync } from "node:fs";
5
-
6
- //#region src/cli/check/css.ts
7
- /**
8
- * Every `.css` file anywhere in a section folder (the folder is free-form —
9
- * an author may nest CSS under components/, enhancers/, wherever), in
10
- * published order: `styles.css` first (the conventional root-level home),
11
- * then every other file sorted by path. `snapshots/` and `node_modules/` are
12
- * skipped — generated or not authored, never hand-CSS.
13
- */
14
- async function sectionCssFiles(dir) {
15
- if (!existsSync(dir)) return [];
16
- const found = [];
17
- const walk = async (d) => {
18
- for (const entry of await readdir(d, { withFileTypes: true })) {
19
- const full = join(d, entry.name);
20
- if (entry.isDirectory()) {
21
- if (entry.name === "snapshots" || entry.name === "node_modules") continue;
22
- await walk(full);
23
- } else if (entry.isFile() && entry.name.endsWith(".css")) found.push(full);
24
- }
25
- };
26
- await walk(dir);
27
- const own = join(dir, "styles.css");
28
- const rest = found.filter((f) => f !== own).sort();
29
- return (existsSync(own) ? [own] : []).concat(rest);
30
- }
31
- const ASSET_LOADERS = {
32
- ".svg": "file",
33
- ".png": "file",
34
- ".jpg": "file",
35
- ".jpeg": "file",
36
- ".gif": "file",
37
- ".webp": "file",
38
- ".avif": "file",
39
- ".ico": "file",
40
- ".woff": "file",
41
- ".woff2": "file",
42
- ".ttf": "file",
43
- ".otf": "file",
44
- ".eot": "file",
45
- ".mp4": "file",
46
- ".webm": "file",
47
- ".mov": "file",
48
- ".mp3": "file",
49
- ".wav": "file",
50
- ".pdf": "file",
51
- ".zip": "file"
52
- };
53
- const ASSET_PUBLIC_PATH = "/section-assets";
54
- const ASSET_NAMES = "[name]-[hash]";
55
- /**
56
- * A section's third-party package CSS — discovered by the bundler, not
57
- * declared. An author writes `import "swiper/css"` in Renderer.tsx exactly
58
- * as in React; this runs a CSS-only esbuild pass rooted at Renderer.tsx and
59
- * returns the emitted stylesheet (empty string if there is none).
60
- *
61
- * A relative CSS import in the render path is a lint error
62
- * (`no-css-import-from-render-path`, already enforced by ESLint) — this pass
63
- * does not re-implement that rejection, it only has to not crash on one.
64
- *
65
- * `platform: "neutral"` defaults `mainFields` to `[]`, which cannot resolve a
66
- * package that ships only a CJS `main` (no `exports` map) — `mainFields`
67
- * is set explicitly so such a package still resolves to its ESM `module`
68
- * build where one exists.
69
- */
70
- async function compileSectionPackageCss(workspaceRoot, sectionDir) {
71
- const renderer = join(sectionDir, "Renderer.tsx");
72
- if (!existsSync(renderer)) return "";
73
- const { build } = await loadEsbuild(workspaceRoot);
74
- const css = (await build({
75
- entryPoints: [renderer],
76
- bundle: true,
77
- write: false,
78
- minify: true,
79
- outdir: join(workspaceRoot, "node_modules", ".template-kit", "check-css"),
80
- format: "esm",
81
- target: "es2022",
82
- platform: "neutral",
83
- mainFields: ["module", "main"],
84
- jsx: "automatic",
85
- external: [
86
- "react",
87
- "react-dom",
88
- "react/jsx-runtime"
89
- ],
90
- loader: { ...ASSET_LOADERS },
91
- publicPath: ASSET_PUBLIC_PATH,
92
- assetNames: ASSET_NAMES,
93
- absWorkingDir: workspaceRoot,
94
- logLevel: "silent"
95
- })).outputFiles?.find((f) => f.path.endsWith(".css"));
96
- return css ? css.text : "";
97
- }
98
- /**
99
- * The 1-based line of the first top-level `@import`, or `undefined` if there
100
- * is none. Comment-aware: an `@import` mentioned inside a CSS block comment
101
- * is prose (e.g. a header warning authors not to write one), not a rule — a
102
- * naive line-regex would false-fire on it.
103
- */
104
- function findImportLine(css) {
105
- const lines = css.split("\n");
106
- let inComment = false;
107
- for (let i = 0; i < lines.length; i++) {
108
- let line = lines[i];
109
- if (inComment) {
110
- const close = line.indexOf("*/");
111
- if (close === -1) continue;
112
- line = line.slice(close + 2);
113
- inComment = false;
114
- }
115
- for (;;) {
116
- const open = line.indexOf("/*");
117
- if (open === -1) break;
118
- const close = line.indexOf("*/", open + 2);
119
- if (close === -1) {
120
- line = line.slice(0, open);
121
- inComment = true;
122
- break;
123
- }
124
- line = line.slice(0, open) + line.slice(close + 2);
125
- }
126
- if (/^\s*@import\b/.test(line)) return i + 1;
127
- }
128
- }
129
-
130
- //#endregion
131
- export { ASSET_LOADERS, ASSET_NAMES, ASSET_PUBLIC_PATH, compileSectionPackageCss, findImportLine, sectionCssFiles };
@@ -1,37 +0,0 @@
1
- //#region src/cli/check/diagnostics.ts
2
- function location(d) {
3
- if (!d.file) return "";
4
- return d.line === void 0 ? d.file : `${d.file}:${d.line}`;
5
- }
6
- function formatOne(d) {
7
- const where = location(d);
8
- return `${where ? ` ${where}\n` : ""} [${d.ruleId}] ${d.message}\n fix: ${d.fix}\n`;
9
- }
10
- function formatHuman(report) {
11
- const parts = [];
12
- if (report.linkedKit !== void 0) {
13
- const { kitVersion, linkedFrom, pin } = report.linkedKit;
14
- parts.push(`⚠ linked kit ${kitVersion} from ${linkedFrom} — your pin is ${pin}.\n`);
15
- parts.push(" This result does not reflect your pin. Run `template-kit unlink` before merging.\n");
16
- }
17
- for (const [key, t] of Object.entries(report.templates)) {
18
- if (t.ok) {
19
- parts.push(`✓ ${key}\n`);
20
- continue;
21
- }
22
- parts.push(`✗ ${key} — ${String(t.diagnostics.length)} problem(s)\n`);
23
- for (const d of t.diagnostics) parts.push(formatOne(d));
24
- }
25
- if (!report.workspace.ok) {
26
- parts.push(`✗ workspace — ${String(report.workspace.diagnostics.length)} problem(s)\n`);
27
- for (const d of report.workspace.diagnostics) parts.push(formatOne(d));
28
- }
29
- if (report.ok) parts.push("check passed\n");
30
- return parts.join("");
31
- }
32
- function toJson(report) {
33
- return JSON.stringify(report, null, 2);
34
- }
35
-
36
- //#endregion
37
- export { formatHuman, toJson };