@caelo-cms/shared 0.10.22 → 0.10.24

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 (249) hide show
  1. package/dist/ai-tools.d.ts +289 -273
  2. package/dist/ai-tools.d.ts.map +1 -1
  3. package/dist/ai-tools.js +342 -323
  4. package/dist/ai-tools.js.map +1 -1
  5. package/dist/auth-forms.d.ts.map +1 -1
  6. package/dist/auth-forms.js +4 -1
  7. package/dist/auth-forms.js.map +1 -1
  8. package/dist/base-css.d.ts +35 -0
  9. package/dist/base-css.d.ts.map +1 -0
  10. package/dist/base-css.js +40 -0
  11. package/dist/base-css.js.map +1 -0
  12. package/dist/build-page.d.ts +330 -0
  13. package/dist/build-page.d.ts.map +1 -0
  14. package/dist/build-page.js +282 -0
  15. package/dist/build-page.js.map +1 -0
  16. package/dist/content.d.ts +322 -9
  17. package/dist/content.d.ts.map +1 -1
  18. package/dist/content.js +354 -11
  19. package/dist/content.js.map +1 -1
  20. package/dist/css-gradient-scan.d.ts +14 -0
  21. package/dist/css-gradient-scan.d.ts.map +1 -0
  22. package/dist/css-gradient-scan.js +81 -0
  23. package/dist/css-gradient-scan.js.map +1 -0
  24. package/dist/css-var-scan.d.ts +56 -0
  25. package/dist/css-var-scan.d.ts.map +1 -0
  26. package/dist/css-var-scan.js +97 -0
  27. package/dist/css-var-scan.js.map +1 -0
  28. package/dist/design-draft-shell.d.ts +21 -0
  29. package/dist/design-draft-shell.d.ts.map +1 -0
  30. package/dist/design-draft-shell.js +81 -0
  31. package/dist/design-draft-shell.js.map +1 -0
  32. package/dist/design-manifest.d.ts +36 -0
  33. package/dist/design-manifest.d.ts.map +1 -0
  34. package/dist/design-manifest.js +90 -0
  35. package/dist/design-manifest.js.map +1 -0
  36. package/dist/fonts.d.ts +89 -0
  37. package/dist/fonts.d.ts.map +1 -0
  38. package/dist/fonts.js +241 -0
  39. package/dist/fonts.js.map +1 -0
  40. package/dist/genesis-inventory.d.ts +32 -0
  41. package/dist/genesis-inventory.d.ts.map +1 -0
  42. package/dist/genesis-inventory.js +186 -0
  43. package/dist/genesis-inventory.js.map +1 -0
  44. package/dist/genesis.d.ts +102 -0
  45. package/dist/genesis.d.ts.map +1 -0
  46. package/dist/genesis.js +145 -0
  47. package/dist/genesis.js.map +1 -0
  48. package/dist/i18n.d.ts +29 -36
  49. package/dist/i18n.d.ts.map +1 -1
  50. package/dist/i18n.js +53 -128
  51. package/dist/i18n.js.map +1 -1
  52. package/dist/index.d.ts +28 -1
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +28 -1
  55. package/dist/index.js.map +1 -1
  56. package/dist/interactions.d.ts +23 -0
  57. package/dist/interactions.d.ts.map +1 -0
  58. package/dist/interactions.js +44 -0
  59. package/dist/interactions.js.map +1 -0
  60. package/dist/media.d.ts +101 -16
  61. package/dist/media.d.ts.map +1 -1
  62. package/dist/media.js +126 -15
  63. package/dist/media.js.map +1 -1
  64. package/dist/page-log.d.ts +94 -0
  65. package/dist/page-log.d.ts.map +1 -0
  66. package/dist/page-log.js +111 -0
  67. package/dist/page-log.js.map +1 -0
  68. package/dist/preview-compose.d.ts +85 -12
  69. package/dist/preview-compose.d.ts.map +1 -1
  70. package/dist/preview-compose.js +157 -67
  71. package/dist/preview-compose.js.map +1 -1
  72. package/dist/proposal-status.d.ts +40 -0
  73. package/dist/proposal-status.d.ts.map +1 -0
  74. package/dist/proposal-status.js +34 -0
  75. package/dist/proposal-status.js.map +1 -0
  76. package/dist/responsive-images.d.ts +64 -0
  77. package/dist/responsive-images.d.ts.map +1 -0
  78. package/dist/responsive-images.js +98 -0
  79. package/dist/responsive-images.js.map +1 -0
  80. package/dist/safe-keys.d.ts +9 -0
  81. package/dist/safe-keys.d.ts.map +1 -0
  82. package/dist/safe-keys.js +20 -0
  83. package/dist/safe-keys.js.map +1 -0
  84. package/dist/seo.d.ts +11 -16
  85. package/dist/seo.d.ts.map +1 -1
  86. package/dist/seo.js +7 -23
  87. package/dist/seo.js.map +1 -1
  88. package/dist/skills.d.ts +14 -68
  89. package/dist/skills.d.ts.map +1 -1
  90. package/dist/skills.js +19 -113
  91. package/dist/skills.js.map +1 -1
  92. package/dist/strip-cdata.d.ts +7 -0
  93. package/dist/strip-cdata.d.ts.map +1 -0
  94. package/dist/strip-cdata.js +48 -0
  95. package/dist/strip-cdata.js.map +1 -0
  96. package/dist/structured-sets.d.ts +6 -52
  97. package/dist/structured-sets.d.ts.map +1 -1
  98. package/dist/structured-sets.js +6 -68
  99. package/dist/structured-sets.js.map +1 -1
  100. package/dist/subagents.d.ts +105 -3
  101. package/dist/subagents.d.ts.map +1 -1
  102. package/dist/subagents.js +224 -41
  103. package/dist/subagents.js.map +1 -1
  104. package/dist/template-engine.d.ts +85 -0
  105. package/dist/template-engine.d.ts.map +1 -0
  106. package/dist/template-engine.js +403 -0
  107. package/dist/template-engine.js.map +1 -0
  108. package/dist/theme-importers/auto-detect.d.ts +26 -0
  109. package/dist/theme-importers/auto-detect.d.ts.map +1 -0
  110. package/dist/theme-importers/auto-detect.js +42 -0
  111. package/dist/theme-importers/auto-detect.js.map +1 -0
  112. package/dist/theme-importers/css-comments.d.ts +12 -0
  113. package/dist/theme-importers/css-comments.d.ts.map +1 -0
  114. package/dist/theme-importers/css-comments.js +15 -0
  115. package/dist/theme-importers/css-comments.js.map +1 -0
  116. package/dist/theme-importers/dtcg.d.ts +46 -0
  117. package/dist/theme-importers/dtcg.d.ts.map +1 -0
  118. package/dist/theme-importers/dtcg.js +111 -0
  119. package/dist/theme-importers/dtcg.js.map +1 -0
  120. package/dist/theme-importers/loose.d.ts +3 -0
  121. package/dist/theme-importers/loose.d.ts.map +1 -0
  122. package/dist/theme-importers/loose.js +76 -0
  123. package/dist/theme-importers/loose.js.map +1 -0
  124. package/dist/theme-importers/shadcn.d.ts +24 -0
  125. package/dist/theme-importers/shadcn.d.ts.map +1 -0
  126. package/dist/theme-importers/shadcn.js +135 -0
  127. package/dist/theme-importers/shadcn.js.map +1 -0
  128. package/dist/theme-importers/style-dictionary.d.ts +17 -0
  129. package/dist/theme-importers/style-dictionary.d.ts.map +1 -0
  130. package/dist/theme-importers/style-dictionary.js +125 -0
  131. package/dist/theme-importers/style-dictionary.js.map +1 -0
  132. package/dist/theme-importers/tailwind.d.ts +3 -0
  133. package/dist/theme-importers/tailwind.d.ts.map +1 -0
  134. package/dist/theme-importers/tailwind.js +218 -0
  135. package/dist/theme-importers/tailwind.js.map +1 -0
  136. package/dist/theme-literal-binding.d.ts +37 -0
  137. package/dist/theme-literal-binding.d.ts.map +1 -0
  138. package/dist/theme-literal-binding.js +138 -0
  139. package/dist/theme-literal-binding.js.map +1 -0
  140. package/dist/theme-normalize.d.ts +31 -0
  141. package/dist/theme-normalize.d.ts.map +1 -0
  142. package/dist/theme-normalize.js +587 -0
  143. package/dist/theme-normalize.js.map +1 -0
  144. package/dist/theme-ramp.d.ts +55 -0
  145. package/dist/theme-ramp.d.ts.map +1 -0
  146. package/dist/theme-ramp.js +149 -0
  147. package/dist/theme-ramp.js.map +1 -0
  148. package/dist/theme-render.d.ts +105 -0
  149. package/dist/theme-render.d.ts.map +1 -0
  150. package/dist/theme-render.js +441 -0
  151. package/dist/theme-render.js.map +1 -0
  152. package/dist/themes-errors.d.ts +109 -0
  153. package/dist/themes-errors.d.ts.map +1 -0
  154. package/dist/themes-errors.js +170 -0
  155. package/dist/themes-errors.js.map +1 -0
  156. package/dist/themes.d.ts +343 -0
  157. package/dist/themes.d.ts.map +1 -0
  158. package/dist/themes.js +697 -0
  159. package/dist/themes.js.map +1 -0
  160. package/dist/version.d.ts +7 -4
  161. package/dist/version.d.ts.map +1 -1
  162. package/dist/version.js +6 -3
  163. package/dist/version.js.map +1 -1
  164. package/package.json +10 -2
  165. package/src/__tests__/redos-hardening.test.ts +160 -0
  166. package/src/ai-tools-add-module-modes.test.ts +106 -0
  167. package/src/ai-tools-position.test.ts +134 -0
  168. package/src/ai-tools.test.ts +81 -0
  169. package/src/ai-tools.ts +1105 -0
  170. package/src/auth-forms.ts +36 -0
  171. package/src/base-css.ts +42 -0
  172. package/src/build-page.test.ts +228 -0
  173. package/src/build-page.ts +319 -0
  174. package/src/cap-failures.ts +67 -0
  175. package/src/content.test.ts +170 -0
  176. package/src/content.ts +620 -0
  177. package/src/context.ts +43 -0
  178. package/src/css-gradient-scan.ts +88 -0
  179. package/src/css-var-scan.test.ts +96 -0
  180. package/src/css-var-scan.ts +144 -0
  181. package/src/derive-module-type.test.ts +80 -0
  182. package/src/design-draft-shell.test.ts +85 -0
  183. package/src/design-draft-shell.ts +109 -0
  184. package/src/design-manifest.ts +93 -0
  185. package/src/fonts.test.ts +157 -0
  186. package/src/fonts.ts +296 -0
  187. package/src/genesis-inventory.test.ts +86 -0
  188. package/src/genesis-inventory.ts +215 -0
  189. package/src/genesis-sanitize.test.ts +35 -0
  190. package/src/genesis.ts +158 -0
  191. package/src/i18n.test.ts +58 -0
  192. package/src/i18n.ts +91 -0
  193. package/src/index.test.ts +10 -0
  194. package/src/index.ts +59 -0
  195. package/src/interactions.ts +48 -0
  196. package/src/logger.ts +147 -0
  197. package/src/media.test.ts +160 -0
  198. package/src/media.ts +355 -0
  199. package/src/page-log.test.ts +163 -0
  200. package/src/page-log.ts +124 -0
  201. package/src/preview-compose.test.ts +637 -0
  202. package/src/preview-compose.ts +656 -0
  203. package/src/preview-scanner.test.ts +96 -0
  204. package/src/preview-scanner.ts +214 -0
  205. package/src/proposal-status.test.ts +69 -0
  206. package/src/proposal-status.ts +40 -0
  207. package/src/responsive-images.test.ts +104 -0
  208. package/src/responsive-images.ts +151 -0
  209. package/src/result.ts +29 -0
  210. package/src/safe-keys.ts +21 -0
  211. package/src/seo.test.ts +194 -0
  212. package/src/seo.ts +233 -0
  213. package/src/skills.ts +48 -0
  214. package/src/snapshots.test.ts +80 -0
  215. package/src/snapshots.ts +81 -0
  216. package/src/strip-cdata.test.ts +41 -0
  217. package/src/strip-cdata.ts +50 -0
  218. package/src/structured-sets.ts +114 -0
  219. package/src/subagents.test.ts +262 -0
  220. package/src/subagents.ts +432 -0
  221. package/src/template-engine.test.ts +379 -0
  222. package/src/template-engine.ts +520 -0
  223. package/src/theme-gradient.test.ts +92 -0
  224. package/src/theme-importers/__tests__/proto-pollution.test.ts +54 -0
  225. package/src/theme-importers/auto-detect.ts +84 -0
  226. package/src/theme-importers/css-comments.ts +15 -0
  227. package/src/theme-importers/dtcg.ts +106 -0
  228. package/src/theme-importers/loose.ts +76 -0
  229. package/src/theme-importers/shadcn.ts +133 -0
  230. package/src/theme-importers/style-dictionary.ts +125 -0
  231. package/src/theme-importers/tailwind.ts +217 -0
  232. package/src/theme-literal-binding.test.ts +71 -0
  233. package/src/theme-literal-binding.ts +159 -0
  234. package/src/theme-motion.test.ts +115 -0
  235. package/src/theme-normalize-envelope.test.ts +43 -0
  236. package/src/theme-normalize-gradient.test.ts +135 -0
  237. package/src/theme-normalize.ts +661 -0
  238. package/src/theme-ramp.ts +187 -0
  239. package/src/theme-render-sanitize.test.ts +45 -0
  240. package/src/theme-render.test.ts +119 -0
  241. package/src/theme-render.ts +487 -0
  242. package/src/theme-shadow.test.ts +56 -0
  243. package/src/themes-errors.ts +199 -0
  244. package/src/themes.ts +842 -0
  245. package/src/version.ts +66 -0
  246. package/dist/translation.d.ts +0 -127
  247. package/dist/translation.d.ts.map +0 -1
  248. package/dist/translation.js +0 -208
  249. package/dist/translation.js.map +0 -1
@@ -0,0 +1,97 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ const VAR_REFERENCE_RE = /var\(\s*(--[a-zA-Z0-9_-]+)\s*(,)?/g;
3
+ const VAR_DEFINITION_RE = /(?:^|[{;\s])(--[a-zA-Z0-9_-]+)\s*:/g;
4
+ /** Extract every `var(--x[, fallback])` reference (deduped by name+fallback). */
5
+ export function extractCssVarReferences(css) {
6
+ const seen = new Map();
7
+ for (const m of css.matchAll(VAR_REFERENCE_RE)) {
8
+ const name = m[1] ?? "";
9
+ const hasFallback = m[2] === ",";
10
+ const key = `${name}|${hasFallback}`;
11
+ if (!seen.has(key))
12
+ seen.set(key, { name, hasFallback });
13
+ }
14
+ return [...seen.values()];
15
+ }
16
+ /** Custom properties DEFINED in the CSS (`--x: value;`). */
17
+ export function extractCssVarDefinitions(css) {
18
+ const out = new Set();
19
+ for (const m of css.matchAll(VAR_DEFINITION_RE)) {
20
+ out.add(m[1] ?? "");
21
+ }
22
+ return out;
23
+ }
24
+ /** Levenshtein distance, small-string use only (var names). */
25
+ function editDistance(a, b) {
26
+ const prev = new Array(b.length + 1);
27
+ for (let j = 0; j <= b.length; j++)
28
+ prev[j] = j;
29
+ for (let i = 1; i <= a.length; i++) {
30
+ let diag = prev[0];
31
+ prev[0] = i;
32
+ for (let j = 1; j <= b.length; j++) {
33
+ const tmp = prev[j];
34
+ prev[j] = Math.min(prev[j] + 1, prev[j - 1] + 1, diag + (a[i - 1] === b[j - 1] ? 0 : 1));
35
+ diag = tmp;
36
+ }
37
+ }
38
+ return prev[b.length];
39
+ }
40
+ /**
41
+ * Report `var()` references that neither the theme emits nor the
42
+ * bundle itself defines, with a nearest-match suggestion so the fix is
43
+ * one edit, not a research task (CLAUDE.md §11: AI-actionable).
44
+ */
45
+ export function scanCssVars(input) {
46
+ const known = new Set(input.knownVars);
47
+ for (const def of extractCssVarDefinitions(input.css))
48
+ known.add(def);
49
+ const unknown = [];
50
+ const reported = new Set();
51
+ for (const ref of extractCssVarReferences(input.css)) {
52
+ if (known.has(ref.name) || reported.has(ref.name))
53
+ continue;
54
+ reported.add(ref.name);
55
+ let best = null;
56
+ let bestDist = Number.POSITIVE_INFINITY;
57
+ for (const candidate of known) {
58
+ const d = editDistance(ref.name, candidate);
59
+ if (d < bestDist) {
60
+ bestDist = d;
61
+ best = candidate;
62
+ }
63
+ }
64
+ const threshold = Math.max(2, Math.floor(ref.name.length * 0.4));
65
+ unknown.push({
66
+ name: ref.name,
67
+ hasFallback: ref.hasFallback,
68
+ suggestion: best !== null && bestDist <= threshold ? best : null,
69
+ });
70
+ }
71
+ return unknown;
72
+ }
73
+ /**
74
+ * Marker for the preview op's missing-content surface — same
75
+ * convention as `theme-asset-unbound:<slot>` / `theme-font-unresolvable`.
76
+ */
77
+ export function unknownCssVarMarker(name) {
78
+ return `unknown-css-var:${name}`;
79
+ }
80
+ /**
81
+ * One-line, AI-actionable warning for authoring-tool results. Returns
82
+ * null when nothing is unknown (no noise on clean writes).
83
+ */
84
+ export function formatUnknownCssVarWarning(unknown) {
85
+ if (unknown.length === 0)
86
+ return null;
87
+ const parts = unknown.map((u) => {
88
+ const hint = u.suggestion !== null ? ` (did you mean \`${u.suggestion}\`?)` : "";
89
+ const fallback = u.hasFallback
90
+ ? " — its hardcoded fallback is what visitors will see, detached from the theme"
91
+ : " — the declaration is invalid and the property won't apply";
92
+ return `\`${u.name}\`${hint}${fallback}`;
93
+ });
94
+ return (`⚠️ Unknown CSS vars (the active theme does not emit them): ${parts.join("; ")}. ` +
95
+ "Use the exact names from the `## Theme` inventory, or define the custom property in this CSS if it's intentional.");
96
+ }
97
+ //# sourceMappingURL=css-var-scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"css-var-scan.js","sourceRoot":"","sources":["../src/css-var-scan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { type ComposeFonts, type ComposeTheme } from "./preview-compose.js";
2
+ export interface DesignDraftShellInput {
3
+ /** The stored fragment (already script-stripped at the boundary). */
4
+ readonly fragmentHtml: string;
5
+ /** Active theme; undefined renders the shell without theme vars
6
+ * (loud: the draft's `var(--…)` references fall to browser initial
7
+ * values — visibly broken, per CLAUDE.md §2 no-fallbacks). */
8
+ readonly theme?: ComposeTheme;
9
+ /** Resolved web fonts; undefined = system stacks only. */
10
+ readonly fonts?: ComposeFonts;
11
+ /** Document title shown in the iframe (accessibility). */
12
+ readonly title: string;
13
+ }
14
+ export interface DesignDraftShellOutput {
15
+ readonly html: string;
16
+ /** `theme-asset-unbound:<slot>` markers, mirroring the template
17
+ * engine's failure-marker contract. */
18
+ readonly missingSlots: readonly string[];
19
+ }
20
+ export declare function composeDesignDraftShell(input: DesignDraftShellInput): DesignDraftShellOutput;
21
+ //# sourceMappingURL=design-draft-shell.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"design-draft-shell.d.ts","sourceRoot":"","sources":["../src/design-draft-shell.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAqB,MAAM,sBAAsB,CAAC;AAY/F,MAAM,WAAW,qBAAqB;IACpC,qEAAqE;IACrE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;;mEAE+D;IAC/D,QAAQ,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC;IAC9B,0DAA0D;IAC1D,QAAQ,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC;IAC9B,0DAA0D;IAC1D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;4CACwC;IACxC,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AA6BD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,qBAAqB,GAAG,sBAAsB,CAyB5F"}
@@ -0,0 +1,81 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ /**
3
+ * issue #375 — theme shell for growth-time design-draft previews.
4
+ *
5
+ * Page/module-scope drafts are stored as FRAGMENTS bound to the site's
6
+ * `var(--…)` tokens; this composer wraps one in the site's real
7
+ * compiled theme at view time: web fonts (#150), theme CSS variables,
8
+ * and the invisible technical baseline (#151) — in the exact head
9
+ * order `composePagePreview` uses, so a draft previews in the same
10
+ * cascade it will materialise into. Pure / sync / no IO: the caller
11
+ * (the `genesis.render_draft` op) loads the theme row and resolves
12
+ * fonts, mirroring `pages.render_preview`.
13
+ */
14
+ import { BASE_TECHNICAL_CSS } from "./base-css.js";
15
+ import { fontsHeadFragment } from "./preview-compose.js";
16
+ import { caeloMissingComment } from "./template-engine.js";
17
+ import { renderThemeCss } from "./theme-render.js";
18
+ /** Same four placeholders the template engine resolves (v0.11.1, #76). */
19
+ const THEME_ASSET_KEY_TO_SLOT = {
20
+ theme_logo_url: "logo",
21
+ theme_logo_dark_url: "logoDark",
22
+ theme_favicon_url: "favicon",
23
+ theme_social_share_url: "socialShare",
24
+ };
25
+ /**
26
+ * Resolve `{{theme_*_url}}` placeholders in a draft fragment. Bound
27
+ * slots substitute their URL; a placeholder on an unbound slot stays
28
+ * loud-raw IN PLACE (placeholders usually sit inside attribute values,
29
+ * where an appended comment would corrupt the URL string — PR-378
30
+ * review) and lands in `missingSlots`; the caller appends the real
31
+ * `caelo:missing` comment nodes at the end of the body so the marker
32
+ * tooling still sees them. Other `{{…}}` sequences are left untouched:
33
+ * a draft is not a fielded module.
34
+ */
35
+ function substituteThemeAssetUrls(html, theme, missing) {
36
+ return html.replace(/\{\{\s*(theme_[a-z_]+)\s*\}\}/g, (match, key) => {
37
+ const slot = THEME_ASSET_KEY_TO_SLOT[key];
38
+ if (slot === undefined)
39
+ return match;
40
+ const url = theme?.assets[slot]?.url;
41
+ if (url === undefined) {
42
+ missing.push(`theme-asset-unbound:${slot}`);
43
+ return match;
44
+ }
45
+ return url;
46
+ });
47
+ }
48
+ export function composeDesignDraftShell(input) {
49
+ const missing = [];
50
+ const substituted = substituteThemeAssetUrls(input.fragmentHtml, input.theme, missing);
51
+ // Real comment NODES, after the fragment — never inside an attribute.
52
+ const markers = [...new Set(missing)].map((m) => caeloMissingComment(m)).join("");
53
+ const body = substituted + markers;
54
+ // Head order mirrors composePagePreview: fonts first (URL discovery),
55
+ // then theme vars (so fragment CSS can `var(--color-primary)`), then
56
+ // the technical baseline.
57
+ const head = [
58
+ `<meta charset="utf-8">`,
59
+ `<meta name="viewport" content="width=device-width, initial-scale=1">`,
60
+ `<title>${escapeHtml(input.title)}</title>`,
61
+ ];
62
+ const fonts = fontsHeadFragment(input.fonts);
63
+ if (fonts !== null)
64
+ head.push(fonts);
65
+ const themeCss = input.theme !== undefined ? renderThemeCss(input.theme.tokens) : null;
66
+ if (themeCss !== null)
67
+ head.push(`<style data-source="theme">${themeCss}</style>`);
68
+ head.push(`<style data-source="base">${BASE_TECHNICAL_CSS}</style>`);
69
+ return {
70
+ html: `<!doctype html>\n<html><head>${head.join("")}</head><body>${body}</body></html>`,
71
+ missingSlots: missing,
72
+ };
73
+ }
74
+ function escapeHtml(s) {
75
+ return s
76
+ .replaceAll("&", "&amp;")
77
+ .replaceAll("<", "&lt;")
78
+ .replaceAll(">", "&gt;")
79
+ .replaceAll('"', "&quot;");
80
+ }
81
+ //# sourceMappingURL=design-draft-shell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"design-draft-shell.js","sourceRoot":"","sources":["../src/design-draft-shell.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * issue #165 — the Design Manifest: a generated, versioned description
3
+ * of THIS site's design system (epic #149, growth-time convergence).
4
+ *
5
+ * The operator requirement is asymmetric: pages of the SAME site share
6
+ * one visual line, while different sites are free to look nothing
7
+ * alike. A global stylesheet solves the first at the cost of the
8
+ * second; the per-site manifest solves both — it captures what this
9
+ * site's tokens/patterns MEAN so the AI converges toward its own
10
+ * design, not a Caelo default.
11
+ *
12
+ * Written at Genesis materialisation (or curated later via chat);
13
+ * rendered as the `## Design system` system-prompt block.
14
+ */
15
+ import { z } from "zod";
16
+ export declare const designManifestPattern: z.ZodObject<{
17
+ name: z.ZodString;
18
+ moduleType: z.ZodOptional<z.ZodString>;
19
+ spec: z.ZodString;
20
+ }, z.core.$strict>;
21
+ export declare const designManifestSchema: z.ZodObject<{
22
+ tokenRoles: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
23
+ typography: z.ZodOptional<z.ZodString>;
24
+ rhythm: z.ZodOptional<z.ZodString>;
25
+ patterns: z.ZodOptional<z.ZodArray<z.ZodObject<{
26
+ name: z.ZodString;
27
+ moduleType: z.ZodOptional<z.ZodString>;
28
+ spec: z.ZodString;
29
+ }, z.core.$strict>>>;
30
+ imagery: z.ZodOptional<z.ZodString>;
31
+ avoid: z.ZodOptional<z.ZodString>;
32
+ }, z.core.$strict>;
33
+ export type DesignManifest = z.infer<typeof designManifestSchema>;
34
+ /** Render the `## Design system` prompt block. Null when empty. */
35
+ export declare function formatDesignSystemBlock(manifest: DesignManifest | null): string | null;
36
+ //# sourceMappingURL=design-manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"design-manifest.d.ts","sourceRoot":"","sources":["../src/design-manifest.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,qBAAqB;;;;kBASvB,CAAC;AAEZ,eAAO,MAAM,oBAAoB;;;;;;;;;;;kBAYtB,CAAC;AACZ,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,mEAAmE;AACnE,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CA8CtF"}
@@ -0,0 +1,90 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ /**
3
+ * issue #165 — the Design Manifest: a generated, versioned description
4
+ * of THIS site's design system (epic #149, growth-time convergence).
5
+ *
6
+ * The operator requirement is asymmetric: pages of the SAME site share
7
+ * one visual line, while different sites are free to look nothing
8
+ * alike. A global stylesheet solves the first at the cost of the
9
+ * second; the per-site manifest solves both — it captures what this
10
+ * site's tokens/patterns MEAN so the AI converges toward its own
11
+ * design, not a Caelo default.
12
+ *
13
+ * Written at Genesis materialisation (or curated later via chat);
14
+ * rendered as the `## Design system` system-prompt block.
15
+ */
16
+ import { z } from "zod";
17
+ export const designManifestPattern = z
18
+ .object({
19
+ /** Pattern name the AI reasons in ("hero", "card grid", "cta band"). */
20
+ name: z.string().min(1).max(80),
21
+ /** Stable module `type` implementing it — reuse target (#159 place mode). */
22
+ moduleType: z.string().max(64).optional(),
23
+ /** One-line spec: what makes this pattern THIS site's version of it. */
24
+ spec: z.string().min(1).max(500),
25
+ })
26
+ .strict();
27
+ export const designManifestSchema = z
28
+ .object({
29
+ /** `--color-primary` → "CTAs and links only" — usage semantics per var. */
30
+ tokenRoles: z.record(z.string().min(3).max(64), z.string().min(1).max(300)).optional(),
31
+ /** Scale ratio, casing, measure rules. */
32
+ typography: z.string().min(1).max(1000).optional(),
33
+ /** Section padding, container width, grid gaps. */
34
+ rhythm: z.string().min(1).max(1000).optional(),
35
+ patterns: z.array(designManifestPattern).max(24).optional(),
36
+ imagery: z.string().min(1).max(500).optional(),
37
+ avoid: z.string().min(1).max(500).optional(),
38
+ })
39
+ .strict();
40
+ /** Render the `## Design system` prompt block. Null when empty. */
41
+ export function formatDesignSystemBlock(manifest) {
42
+ if (manifest === null)
43
+ return null;
44
+ const lines = [
45
+ "## Design system",
46
+ "",
47
+ "THIS site's own design language — derived from the chosen design. Every module you author or restyle conforms to it unless the operator explicitly asks to break the line. Reuse the pattern's module type (place mode) before minting lookalikes.",
48
+ "",
49
+ ];
50
+ let hasContent = false;
51
+ const roles = manifest.tokenRoles ?? {};
52
+ const roleEntries = Object.entries(roles);
53
+ if (roleEntries.length > 0) {
54
+ hasContent = true;
55
+ lines.push("Token roles:");
56
+ for (const [token, role] of roleEntries.slice(0, 24))
57
+ lines.push(`- \`${token}\` — ${role}`);
58
+ lines.push("");
59
+ }
60
+ if (manifest.typography) {
61
+ hasContent = true;
62
+ lines.push(`Typography: ${manifest.typography}`, "");
63
+ }
64
+ if (manifest.rhythm) {
65
+ hasContent = true;
66
+ lines.push(`Rhythm: ${manifest.rhythm}`, "");
67
+ }
68
+ if (manifest.patterns && manifest.patterns.length > 0) {
69
+ hasContent = true;
70
+ lines.push("Patterns:");
71
+ for (const p of manifest.patterns) {
72
+ lines.push(`- **${p.name}**${p.moduleType ? ` (module type \`${p.moduleType}\`)` : ""} — ${p.spec}`);
73
+ }
74
+ lines.push("");
75
+ }
76
+ if (manifest.imagery) {
77
+ hasContent = true;
78
+ lines.push(`Imagery: ${manifest.imagery}`, "");
79
+ }
80
+ if (manifest.avoid) {
81
+ hasContent = true;
82
+ lines.push(`Never: ${manifest.avoid}`, "");
83
+ }
84
+ if (!hasContent)
85
+ return null;
86
+ while (lines[lines.length - 1] === "")
87
+ lines.pop();
88
+ return lines.join("\n");
89
+ }
90
+ //# sourceMappingURL=design-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"design-manifest.js","sourceRoot":"","sources":["../src/design-manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * issue #150 — web-font resolution primitives.
3
+ *
4
+ * Theme typography tokens name font families (`--font-heading:
5
+ * "Poppins"`), but until #150 nothing delivered the font files to
6
+ * rendered pages — every site silently fell back to system fonts, the
7
+ * exact silent-degradation CLAUDE.md §2 forbids. These pure helpers are
8
+ * the shared half of the fix: extract the families a theme actually
9
+ * uses, classify system stacks (nothing to deliver) vs web fonts
10
+ * (self-hosted at deploy — hotlinking fonts.googleapis.com is both a
11
+ * GDPR liability, LG München I 3 O 17493/20, and a third-party request
12
+ * on the critical path), and build/parse the CSS involved.
13
+ *
14
+ * Network + disk live in `@caelo-cms/admin-core`'s font resolver; the
15
+ * static generator and the preview op share that resolver so preview
16
+ * and production render the same `@font-face` set (parity contract,
17
+ * apps/static-generator/src/generate.ts).
18
+ */
19
+ import type { ThemeDocument } from "./themes.js";
20
+ /** One family the theme needs delivered, with the weights it uses. */
21
+ export interface FontRequest {
22
+ /** Primary family name, unquoted (`Poppins`), first entry of the stack. */
23
+ readonly family: string;
24
+ /** Distinct numeric weights, ascending. Always non-empty. */
25
+ readonly weights: readonly number[];
26
+ /** Typography token names that reference the family (`body`, `heading`). */
27
+ readonly roles: readonly string[];
28
+ }
29
+ /** A single @font-face declaration parsed from a fonts-CSS payload. */
30
+ export interface ParsedFontFace {
31
+ readonly family: string;
32
+ readonly style: string;
33
+ readonly weight: string;
34
+ readonly unicodeRange: string | null;
35
+ /** Remote woff2 URL as served by the fonts source. */
36
+ readonly srcUrl: string;
37
+ }
38
+ /** A parsed face whose bytes now live at a self-hosted public URL. */
39
+ export interface ResolvedFontFace extends Omit<ParsedFontFace, "srcUrl"> {
40
+ readonly publicUrl: string;
41
+ }
42
+ /** True when the family resolves locally on visitors' machines. */
43
+ export declare function isSystemFontFamily(family: string): boolean;
44
+ /**
45
+ * Walk `typography.*` composites and collect every non-system family
46
+ * with the weights the theme declares for it. Alias entries (`$value`
47
+ * as `{other.token}` string) are skipped — they point at a concrete
48
+ * composite that is collected on its own.
49
+ *
50
+ * Only the FIRST family of a declared stack is requested: the rest of
51
+ * the stack is the author's fallback chain, which by definition is not
52
+ * delivered.
53
+ */
54
+ export declare function extractThemeFontRequests(tokens: ThemeDocument): FontRequest[];
55
+ /** css2 request URL for one family (woff2 negotiated via the UA header). */
56
+ export declare function googleFontsCssUrl(request: FontRequest): string;
57
+ /**
58
+ * Parse a fonts-CSS payload (css2 response shape) into face records.
59
+ * Faces without a woff2/woff URL are dropped; a payload yielding zero
60
+ * faces is the caller's loud-failure signal, not a silent empty set.
61
+ * Oversized payloads short-circuit to [] for the same loud path —
62
+ * a legitimate css2 response is orders of magnitude smaller.
63
+ */
64
+ export declare function parseFontsCss(cssText: string): ParsedFontFace[];
65
+ /**
66
+ * Emit self-hosted @font-face CSS. `font-display: swap` always — text
67
+ * must render in the fallback while the woff2 loads.
68
+ */
69
+ export declare function buildFontFaceCss(faces: readonly ResolvedFontFace[]): string;
70
+ /**
71
+ * Pick the faces worth a `<link rel="preload">`: one per family — the
72
+ * latin, normal-style face at the lowest declared weight (the body-copy
73
+ * face; heavier display cuts arrive via normal loading). Capped at two
74
+ * families so the preload budget stays sane on multi-family themes.
75
+ */
76
+ export declare function selectPreloadFaces(faces: readonly ResolvedFontFace[]): ResolvedFontFace[];
77
+ /**
78
+ * Structured marker for a family that could not be resolved. Lands in
79
+ * `missingSlots` (preview, soft) or a thrown deploy error (hard) — the
80
+ * same loud-signal convention as `theme-asset-unbound:<slot>`.
81
+ */
82
+ export declare function fontUnresolvableMarker(family: string): string;
83
+ /**
84
+ * Filesystem/URL-safe slug for a family name. Shared between the font
85
+ * resolver (writes `<cacheDir>/<slug>/…`) and the admin serve route
86
+ * (reads the same path from the URL) so the two can't drift.
87
+ */
88
+ export declare function fontFamilySlug(family: string): string;
89
+ //# sourceMappingURL=fonts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fonts.d.ts","sourceRoot":"","sources":["../src/fonts.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,sEAAsE;AACtE,MAAM,WAAW,WAAW;IAC1B,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED,uEAAuE;AACvE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,sDAAsD;IACtD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,sEAAsE;AACtE,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC;IACtE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAqDD,mEAAmE;AACnE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE1D;AAqBD;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,aAAa,GAAG,WAAW,EAAE,CAiC7E;AAED,4EAA4E;AAC5E,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAI9D;AAmBD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,EAAE,CAyB/D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,gBAAgB,EAAE,GAAG,MAAM,CAW3E;AAOD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,gBAAgB,EAAE,GAAG,gBAAgB,EAAE,CAezF;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAUrD"}