@lumencast/runtime 0.6.0 → 0.8.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 (133) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/broadcast-Gcd-dmC7.js +12 -0
  3. package/dist/broadcast-Gcd-dmC7.js.map +1 -0
  4. package/dist/control-C5TfClga.js +17 -0
  5. package/dist/control-C5TfClga.js.map +1 -0
  6. package/dist/{index-Crkij3C4.js → index-N-VqrIxN.js} +305 -210
  7. package/dist/index-N-VqrIxN.js.map +1 -0
  8. package/dist/index.d.ts +3 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.html +1 -1
  11. package/dist/index.js +13 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/lumencast.js +14 -9
  14. package/dist/modes/broadcast.d.ts.map +1 -1
  15. package/dist/modes/broadcast.js +6 -1
  16. package/dist/modes/broadcast.js.map +1 -1
  17. package/dist/modes/control.d.ts.map +1 -1
  18. package/dist/modes/control.js +6 -1
  19. package/dist/modes/control.js.map +1 -1
  20. package/dist/modes/test.d.ts.map +1 -1
  21. package/dist/modes/test.js +2 -1
  22. package/dist/modes/test.js.map +1 -1
  23. package/dist/render/allowed-hosts.d.ts +41 -0
  24. package/dist/render/allowed-hosts.d.ts.map +1 -0
  25. package/dist/render/allowed-hosts.js +88 -0
  26. package/dist/render/allowed-hosts.js.map +1 -0
  27. package/dist/render/asset-resolve.d.ts +27 -0
  28. package/dist/render/asset-resolve.d.ts.map +1 -0
  29. package/dist/render/asset-resolve.js +86 -0
  30. package/dist/render/asset-resolve.js.map +1 -0
  31. package/dist/render/blend-mode.d.ts +7 -0
  32. package/dist/render/blend-mode.d.ts.map +1 -0
  33. package/dist/render/blend-mode.js +49 -0
  34. package/dist/render/blend-mode.js.map +1 -0
  35. package/dist/render/bundle.d.ts +9 -1
  36. package/dist/render/bundle.d.ts.map +1 -1
  37. package/dist/render/bundle.js.map +1 -1
  38. package/dist/render/fill.d.ts +36 -3
  39. package/dist/render/fill.d.ts.map +1 -1
  40. package/dist/render/fill.js +222 -23
  41. package/dist/render/fill.js.map +1 -1
  42. package/dist/render/headless.d.ts +39 -0
  43. package/dist/render/headless.d.ts.map +1 -0
  44. package/dist/render/headless.js +83 -0
  45. package/dist/render/headless.js.map +1 -0
  46. package/dist/render/mask.d.ts +87 -0
  47. package/dist/render/mask.d.ts.map +1 -0
  48. package/dist/render/mask.js +243 -0
  49. package/dist/render/mask.js.map +1 -0
  50. package/dist/render/primitives/frame.d.ts.map +1 -1
  51. package/dist/render/primitives/frame.js +91 -5
  52. package/dist/render/primitives/frame.js.map +1 -1
  53. package/dist/render/primitives/grid.d.ts +1 -1
  54. package/dist/render/primitives/grid.d.ts.map +1 -1
  55. package/dist/render/primitives/grid.js +4 -1
  56. package/dist/render/primitives/grid.js.map +1 -1
  57. package/dist/render/primitives/image.d.ts +8 -1
  58. package/dist/render/primitives/image.d.ts.map +1 -1
  59. package/dist/render/primitives/image.js +17 -3
  60. package/dist/render/primitives/image.js.map +1 -1
  61. package/dist/render/primitives/index.d.ts +7 -0
  62. package/dist/render/primitives/index.d.ts.map +1 -1
  63. package/dist/render/primitives/index.js.map +1 -1
  64. package/dist/render/primitives/media.d.ts +11 -2
  65. package/dist/render/primitives/media.d.ts.map +1 -1
  66. package/dist/render/primitives/media.js +14 -3
  67. package/dist/render/primitives/media.js.map +1 -1
  68. package/dist/render/primitives/shape.d.ts.map +1 -1
  69. package/dist/render/primitives/shape.js +29 -26
  70. package/dist/render/primitives/shape.js.map +1 -1
  71. package/dist/render/primitives/stack.d.ts +1 -1
  72. package/dist/render/primitives/stack.d.ts.map +1 -1
  73. package/dist/render/primitives/stack.js +5 -1
  74. package/dist/render/primitives/stack.js.map +1 -1
  75. package/dist/render/primitives/text.d.ts.map +1 -1
  76. package/dist/render/primitives/text.js +0 -1
  77. package/dist/render/primitives/text.js.map +1 -1
  78. package/dist/render/prop-allowlist.d.ts.map +1 -1
  79. package/dist/render/prop-allowlist.js +25 -2
  80. package/dist/render/prop-allowlist.js.map +1 -1
  81. package/dist/render/shape-geometry.d.ts +81 -0
  82. package/dist/render/shape-geometry.d.ts.map +1 -0
  83. package/dist/render/shape-geometry.js +199 -0
  84. package/dist/render/shape-geometry.js.map +1 -0
  85. package/dist/render/shape-index.d.ts +28 -0
  86. package/dist/render/shape-index.d.ts.map +1 -0
  87. package/dist/render/shape-index.js +77 -0
  88. package/dist/render/shape-index.js.map +1 -0
  89. package/dist/render/tree.d.ts.map +1 -1
  90. package/dist/render/tree.js +175 -3
  91. package/dist/render/tree.js.map +1 -1
  92. package/dist/render/universal-wrapper.d.ts +27 -1
  93. package/dist/render/universal-wrapper.d.ts.map +1 -1
  94. package/dist/render/universal-wrapper.js +98 -22
  95. package/dist/render/universal-wrapper.js.map +1 -1
  96. package/dist/{status-pill-BT5b-yET.js → status-pill-BaLQoIDl.js} +2 -2
  97. package/dist/{status-pill-BT5b-yET.js.map → status-pill-BaLQoIDl.js.map} +1 -1
  98. package/dist/{test-_hh1JvAd.js → test-CA30C2By.js} +51 -51
  99. package/dist/{test-_hh1JvAd.js.map → test-CA30C2By.js.map} +1 -1
  100. package/dist/tree-1coZ32nd.js +1777 -0
  101. package/dist/tree-1coZ32nd.js.map +1 -0
  102. package/package.json +6 -5
  103. package/src/index.ts +24 -0
  104. package/src/modes/broadcast.tsx +12 -1
  105. package/src/modes/control.tsx +10 -1
  106. package/src/modes/test.tsx +4 -1
  107. package/src/render/allowed-hosts.tsx +100 -0
  108. package/src/render/asset-resolve.ts +97 -0
  109. package/src/render/blend-mode.ts +50 -0
  110. package/src/render/bundle.ts +6 -1
  111. package/src/render/fill.tsx +266 -24
  112. package/src/render/headless.tsx +129 -0
  113. package/src/render/mask.tsx +389 -0
  114. package/src/render/primitives/frame.tsx +101 -5
  115. package/src/render/primitives/grid.tsx +4 -1
  116. package/src/render/primitives/image.tsx +17 -3
  117. package/src/render/primitives/index.ts +7 -0
  118. package/src/render/primitives/media.tsx +14 -3
  119. package/src/render/primitives/shape.tsx +39 -75
  120. package/src/render/primitives/stack.tsx +5 -1
  121. package/src/render/primitives/text.tsx +0 -1
  122. package/src/render/prop-allowlist.ts +25 -2
  123. package/src/render/shape-geometry.tsx +315 -0
  124. package/src/render/shape-index.tsx +90 -0
  125. package/src/render/tree.tsx +214 -12
  126. package/src/render/universal-wrapper.tsx +128 -21
  127. package/dist/broadcast-DO7jEkix.js +0 -11
  128. package/dist/broadcast-DO7jEkix.js.map +0 -1
  129. package/dist/control-BSfl4_cO.js +0 -16
  130. package/dist/control-BSfl4_cO.js.map +0 -1
  131. package/dist/index-Crkij3C4.js.map +0 -1
  132. package/dist/tree-DBj9SJgs.js +0 -1230
  133. package/dist/tree-DBj9SJgs.js.map +0 -1
@@ -1,6 +1,21 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { parseCssColor, warnRejectedColor } from "./css-color";
3
3
  import { emitDiagnostic } from "./diagnostics";
4
+ import { gateSrc } from "./allowed-hosts";
5
+ import { parseBlendMode } from "./blend-mode";
6
+ /** LSML 1.2 §3.2 closed `objectFit` enum, re-validated at the RUNTIME (the
7
+ * compiler is the other arm of the double-gate, Bastion T4). These are
8
+ * exactly the legal CSS `object-fit` / `background-size`-mappable values ;
9
+ * anything else is omitted + diagnosed, never passed through to inline CSS.
10
+ * Kept local to the runtime — the runtime must not import from the
11
+ * compiler (the dependency edge points the other way). */
12
+ const OBJECT_FITS = new Set(["cover", "contain", "fill", "none", "scale-down"]);
13
+ /** Validate an `objectFit` against the closed enum at render. Returns the
14
+ * value or `undefined` (caller falls back to the default + diagnoses).
15
+ * Never passthrough. */
16
+ export function parseObjectFitRuntime(value) {
17
+ return typeof value === "string" && OBJECT_FITS.has(value) ? value : undefined;
18
+ }
4
19
  let gradientIdSeq = 0;
5
20
  function nextGradientId() {
6
21
  gradientIdSeq = (gradientIdSeq + 1) % 1_000_000;
@@ -9,26 +24,61 @@ function nextGradientId() {
9
24
  /** Compile a Fill into an SVG `<defs>` entry + a `fill="url(#…)"` ref.
10
25
  * Solid fills produce no defs and return the colour directly. */
11
26
  export function renderFill(fill) {
27
+ // #L — re-validate the per-fill blend mode once (runtime T4 arm). An absent
28
+ // or out-of-enum value yields `undefined` → the layer renders `normal`.
29
+ const mixBlendMode = parseBlendMode(fill.blendMode);
12
30
  if (fill.kind === "solid") {
13
- // Solid fill — no defs needed, just hand the colour to fill.
14
- // SVG fill-opacity composes with element opacity multiplicatively
15
- // so we apply both consistently.
16
- return { defs: [], ref: fill.color };
31
+ // Solid fill — no defs needed, just hand the colour to fill. A solid fill
32
+ // carries its OWN opacity (Figma per-paint alpha, e.g. the bg-texture tiles
33
+ // at 6% white) ; fold it into the colour so the SVG path actually renders
34
+ // at that alpha instead of full-strength (the tiles came out 16× too bright
35
+ // pre-mask, near-black post-mask).
36
+ const ref = fill.opacity !== undefined ? cssWithOpacity(fill.color, fill.opacity) : fill.color;
37
+ return { defs: [], ref, mixBlendMode };
38
+ }
39
+ if (fill.kind === "image") {
40
+ // LSML 1.2 §3.2 — image-fill on a shape. Rendered as an SVG <pattern>
41
+ // holding a single <image> that fills the object bounding box ;
42
+ // `preserveAspectRatio` reproduces the closed-enum `objectFit`. `src`
43
+ // is pre-gated (T1/T2) by `gateImageFills`, so it is safe to place on
44
+ // the SVG <image href>. No bundle-derived markup is interpolated — only
45
+ // the URL string and closed-enum-derived attribute values.
46
+ const imgId = nextGradientId();
47
+ const par = objectFitToPreserveAspectRatio(fill.objectFit);
48
+ const defs = [
49
+ _jsx("pattern", { id: imgId, patternContentUnits: "objectBoundingBox", width: "1", height: "1", children: _jsx("image", { href: fill.src, width: "1", height: "1", preserveAspectRatio: par }) }, imgId),
50
+ ];
51
+ return { defs, ref: `url(#${imgId})`, mixBlendMode };
17
52
  }
18
53
  const id = nextGradientId();
19
54
  if (fill.kind === "linear-gradient") {
20
- // angle_deg : 0 = bottom-to-top per §4.12 (matches CSS `linear-gradient`)
21
- const angle = fill.angle_deg ?? 0;
22
- // Translate angle (degrees from up) to SVG x1/y1/x2/y2 in user space.
23
- const rad = ((angle - 90) * Math.PI) / 180; // 0° → x1=0,y1=1 (bottom-up)
24
- const x1 = 0.5 - 0.5 * Math.cos(rad);
25
- const y1 = 0.5 - 0.5 * Math.sin(rad);
26
- const x2 = 0.5 + 0.5 * Math.cos(rad);
27
- const y2 = 0.5 + 0.5 * Math.sin(rad);
55
+ let x1, y1, x2, y2;
56
+ // Honour the Figma `gradientTransform` : the gradient axis (offset 0 → 1) is
57
+ // column 0 = (a, b) of the matrix, in the SVG's y-down space. `angle_deg`
58
+ // alone ignored it and mis-oriented the picto/caramel gradients (too red).
59
+ const t = fill.transform;
60
+ if (Array.isArray(t) && t.length === 6 && Number.isFinite(t[0]) && Number.isFinite(t[1])) {
61
+ const len = Math.hypot(t[0], t[1]) || 1;
62
+ const an = t[0] / len;
63
+ const bn = t[1] / len;
64
+ x1 = 0.5 - 0.5 * an;
65
+ y1 = 0.5 - 0.5 * bn;
66
+ x2 = 0.5 + 0.5 * an;
67
+ y2 = 0.5 + 0.5 * bn;
68
+ }
69
+ else {
70
+ // angle_deg : 0 = bottom-to-top per §4.12.
71
+ const angle = fill.angle_deg ?? 0;
72
+ const rad = ((angle - 90) * Math.PI) / 180; // 0° → x1=0,y1=1 (bottom-up)
73
+ x1 = 0.5 - 0.5 * Math.cos(rad);
74
+ y1 = 0.5 - 0.5 * Math.sin(rad);
75
+ x2 = 0.5 + 0.5 * Math.cos(rad);
76
+ y2 = 0.5 + 0.5 * Math.sin(rad);
77
+ }
28
78
  const defs = [
29
79
  _jsx("linearGradient", { id: id, x1: `${x1 * 100}%`, y1: `${y1 * 100}%`, x2: `${x2 * 100}%`, y2: `${y2 * 100}%`, children: fill.stops.map((s, i) => (_jsx("stop", { offset: s.offset, stopColor: s.color, ...(s.opacity !== undefined ? { stopOpacity: s.opacity } : {}) }, i))) }, id),
30
80
  ];
31
- return { defs, ref: `url(#${id})` };
81
+ return { defs, ref: `url(#${id})`, mixBlendMode };
32
82
  }
33
83
  // radial-gradient
34
84
  const cx = fill.center?.x ?? 0.5;
@@ -37,20 +87,102 @@ export function renderFill(fill) {
37
87
  const defs = [
38
88
  _jsx("radialGradient", { id: id, cx: `${cx * 100}%`, cy: `${cy * 100}%`, r: `${r * 100}%`, children: fill.stops.map((s, i) => (_jsx("stop", { offset: s.offset, stopColor: s.color, ...(s.opacity !== undefined ? { stopOpacity: s.opacity } : {}) }, i))) }, id),
39
89
  ];
40
- return { defs, ref: `url(#${id})` };
90
+ return { defs, ref: `url(#${id})`, mixBlendMode };
41
91
  }
42
- /** Compile an array of Fill into a CSS `background-image` value usable
43
- * on a `<div>` (frame backgrounds non-SVG context). Returns the CSS
44
- * string + opacity. Stops use percentages in CSS gradient syntax. */
92
+ /** Map a closed-enum `objectFit` to the CSS `background-size` keyword that
93
+ * reproduces the same fit for a `background-image`. `fill`/`none`/`scale-
94
+ * down` have no exact 1:1 `background-size` keyword we approximate with
95
+ * the nearest safe keyword (all from the closed enum, never free input). */
96
+ function objectFitToBackgroundSize(fit) {
97
+ switch (fit) {
98
+ case "contain":
99
+ case "scale-down":
100
+ return "contain";
101
+ case "none":
102
+ return "auto";
103
+ case "fill":
104
+ return "100% 100%";
105
+ case "cover":
106
+ default:
107
+ return "cover";
108
+ }
109
+ }
110
+ /** Map a closed-enum `objectFit` to the SVG `<image preserveAspectRatio>`
111
+ * value that reproduces the same fit inside a pattern tile. Every returned
112
+ * value is a fixed literal (closed enum → fixed mapping) — never free
113
+ * input reaching an SVG attribute. */
114
+ function objectFitToPreserveAspectRatio(fit) {
115
+ switch (fit) {
116
+ case "contain":
117
+ case "scale-down":
118
+ return "xMidYMid meet";
119
+ case "fill":
120
+ return "none";
121
+ case "none":
122
+ return "xMidYMid meet";
123
+ case "cover":
124
+ default:
125
+ return "xMidYMid slice";
126
+ }
127
+ }
128
+ /** Compile an array of Fill into background CSS usable on a `<div>` (frame
129
+ * backgrounds — non-SVG context). Returns `backgroundImage` plus, when an
130
+ * image-fill is present, the matching `backgroundSize`/`backgroundPosition`/
131
+ * `backgroundRepeat`. Stops use percentages in CSS gradient syntax.
132
+ *
133
+ * Image-fill `src` MUST already be host/scheme-gated (`gateImageFills`) —
134
+ * `backgroundsToCss` assumes the URL is trusted at this point and only
135
+ * CSS-escapes it for safe interpolation into `url("…")`. */
45
136
  export function backgroundsToCss(fills, nodeId) {
46
137
  // Per §4.12, fills[0] renders on top — CSS background-image stacks
47
138
  // first → top-most. Match by passing in the same order.
48
- const layers = fills.map((f) => fillToCss(f, nodeId)).filter(Boolean);
139
+ // #L keep each layer's validated blend keyword aligned with its CSS
140
+ // layer (a rejected colour drops the layer → drop its blend too), so
141
+ // `background-blend-mode` stays positionally correct.
142
+ const kept = [];
143
+ const layers = [];
144
+ for (const f of fills) {
145
+ const css = fillToCss(f, nodeId);
146
+ if (css) {
147
+ layers.push(css);
148
+ kept.push(f);
149
+ }
150
+ }
49
151
  if (layers.length === 0)
50
152
  return {};
51
- return { backgroundImage: layers.join(", ") };
153
+ const css = { backgroundImage: layers.join(", ") };
154
+ // #L — per-fill-layer blend on a frame background uses CSS
155
+ // `background-blend-mode` (one keyword per layer, same order). Each value is
156
+ // re-validated against the closed enum (runtime T4 arm) ; an absent/rejected
157
+ // value falls back to `normal`. Emitted only when at least one layer carries
158
+ // a non-`normal` blend, to keep pre-#L output byte-identical (rétro-compat).
159
+ const blends = kept.map((f) => parseBlendMode(f.blendMode) ?? "normal");
160
+ if (blends.some((b) => b !== "normal")) {
161
+ css.backgroundBlendMode = blends.join(", ");
162
+ }
163
+ // When any layer is an image-fill, drive its sizing from the (already
164
+ // validated) objectFit. A single image-fill is the common cover case ;
165
+ // for the first image-fill we set the background sizing for the whole box.
166
+ const firstImage = fills.find((f) => f.kind === "image");
167
+ if (firstImage) {
168
+ css.backgroundSize = objectFitToBackgroundSize(firstImage.objectFit);
169
+ css.backgroundPosition = "center";
170
+ css.backgroundRepeat = "no-repeat";
171
+ }
172
+ return css;
173
+ }
174
+ /** CSS-escape a (already host-gated) URL for safe interpolation into a
175
+ * `url("…")` token — escape backslash and the double-quote that would
176
+ * otherwise break out of the quoted string. */
177
+ function cssUrl(src) {
178
+ return `url("${src.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}")`;
52
179
  }
53
180
  function fillToCss(fill, nodeId) {
181
+ if (fill.kind === "image") {
182
+ // `src` is pre-gated (T1/T2) by `gateImageFills` ; only escape it for
183
+ // the CSS string context here.
184
+ return cssUrl(fill.src);
185
+ }
54
186
  // RC#11 — every colour interpolated into an inline CSS string MUST
55
187
  // pass the strict parser first (fills/stops arrive from untrusted
56
188
  // bundles AND live LSDP deltas). A rejected colour drops the whole
@@ -61,8 +193,12 @@ function fillToCss(fill, nodeId) {
61
193
  warnRejectedColor("fill.color", nodeId);
62
194
  return null;
63
195
  }
196
+ // A solid fill carries its OWN opacity (Figma layer-fill alpha, e.g. a 14%
197
+ // white pill) — apply it like a gradient stop's, else the layer renders
198
+ // fully opaque and hides whatever it overlays.
199
+ const c = fill.opacity !== undefined ? cssWithOpacity(color, fill.opacity) : color;
64
200
  // Wrap solid in linear-gradient so it can stack with other layers.
65
- return `linear-gradient(${color}, ${color})`;
201
+ return `linear-gradient(${c}, ${c})`;
66
202
  }
67
203
  const safeStops = [];
68
204
  for (const s of fill.stops) {
@@ -76,7 +212,17 @@ function fillToCss(fill, nodeId) {
76
212
  }
77
213
  const stops = safeStops.join(", ");
78
214
  if (fill.kind === "linear-gradient") {
79
- const angle = fill.angle_deg ?? 0;
215
+ let angle = fill.angle_deg ?? 0;
216
+ // Honour the Figma `gradientTransform` when present : the gradient's main
217
+ // axis (offset 0 → 1) is column 0 = (a, b) of the 2×3 matrix. CSS `Ndeg`
218
+ // measures clockwise from "up" and screen-y points down, so that direction
219
+ // maps to `atan2(a, -b)`. `angle_deg` alone ignored the matrix and rendered
220
+ // the Cover's warm base as a 270° (horizontal) wash instead of the real 180°
221
+ // (warm at top) — leaving the top-right black under the Ruby20 hard-light.
222
+ const t = fill.transform;
223
+ if (Array.isArray(t) && t.length === 6 && Number.isFinite(t[0]) && Number.isFinite(t[1])) {
224
+ angle = ((Math.atan2(t[0], -t[1]) * 180) / Math.PI + 360) % 360;
225
+ }
80
226
  return `linear-gradient(${angle}deg, ${stops})`;
81
227
  }
82
228
  // radial-gradient
@@ -110,6 +256,13 @@ function cssWithOpacity(color, opacity) {
110
256
  export function sanitizeFills(fills, field, nodeId) {
111
257
  const out = [];
112
258
  for (const fill of fills) {
259
+ // Image-fills carry no colour — they are colour-clean by construction.
260
+ // Their `src` is gated separately (`gateImageFills`, T1/T2) ; pass them
261
+ // through here unchanged so `sanitizeFills` only owns colour validation.
262
+ if (fill.kind === "image") {
263
+ out.push(fill);
264
+ continue;
265
+ }
113
266
  if (fill.kind === "solid") {
114
267
  const color = parseCssColor(fill.color);
115
268
  if (color === null) {
@@ -151,12 +304,58 @@ export function parseFills(value, field, nodeId) {
151
304
  }
152
305
  }
153
306
  }
154
- return value.filter(isFill);
307
+ // Image-fill `objectFit` is re-validated against the closed enum here
308
+ // (Bastion T4 runtime arm) : a hostile / unknown value is dropped with a
309
+ // diagnostic and the fill falls back to the default fit — never passed
310
+ // through to inline CSS. `src` is NOT gated here (it needs the host
311
+ // allowlist) — `gateImageFills` does that downstream, before render.
312
+ return value.filter(isFill).map((v) => {
313
+ let fill = v;
314
+ // #L — re-validate a per-fill `blendMode` against the closed enum (runtime
315
+ // T4 arm). An out-of-enum value is diagnosed + stripped (the layer falls
316
+ // back to `normal`), never passed through to inline CSS. Applies to every
317
+ // fill kind.
318
+ if (fill.blendMode !== undefined && parseBlendMode(fill.blendMode) === undefined) {
319
+ emitDiagnostic(nodeId, field !== undefined ? `${field}.blendMode` : "fill.blendMode", "is not a recognised mix-blend-mode ; falling back to normal (ADR 002 §3.2)");
320
+ const { blendMode: _drop, ...rest } = fill;
321
+ fill = rest;
322
+ }
323
+ if (fill.kind !== "image")
324
+ return fill;
325
+ if (fill.objectFit === undefined)
326
+ return fill;
327
+ const fit = parseObjectFitRuntime(fill.objectFit);
328
+ if (fit === undefined) {
329
+ emitDiagnostic(nodeId, field !== undefined ? `${field}.objectFit` : "fill.objectFit", "is not a recognised object-fit ; falling back to default (ADR 002 §3.2)");
330
+ const { objectFit: _drop, ...rest } = fill;
331
+ return rest;
332
+ }
333
+ return { ...fill, objectFit: fit };
334
+ });
155
335
  }
156
336
  function isFill(v) {
157
337
  if (typeof v !== "object" || v === null)
158
338
  return false;
159
339
  const k = v.kind;
160
- return k === "solid" || k === "linear-gradient" || k === "radial-gradient";
340
+ if (k === "solid" || k === "linear-gradient" || k === "radial-gradient")
341
+ return true;
342
+ // An image-fill must carry a string `src` to be structurally valid ; a
343
+ // malformed image entry is dropped like any other unrenderable fill.
344
+ return k === "image" && typeof v.src === "string";
345
+ }
346
+ /**
347
+ * Drop every image-fill whose `src` fails the host/scheme allowlist
348
+ * (Bastion T1/T2), BEFORE any image-fill reaches the DOM. A rejected
349
+ * image-fill is omitted entirely (never a passthrough URL) with an
350
+ * R9-clean diagnostic emitted by `gateSrc`. Non-image fills pass through
351
+ * untouched. Call this once, after `parseFills`, with the active
352
+ * `allowedHosts` from `useAllowedHosts()`.
353
+ */
354
+ export function gateImageFills(fills, allowedHosts, field, nodeId) {
355
+ return fills.filter((fill) => {
356
+ if (fill.kind !== "image")
357
+ return true;
358
+ return gateSrc(fill.src, allowedHosts, `${field}.src`, nodeId) !== undefined;
359
+ });
161
360
  }
162
361
  //# sourceMappingURL=fill.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fill.js","sourceRoot":"","sources":["../../src/render/fill.tsx"],"names":[],"mappings":";AAYA,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAwB/C,IAAI,aAAa,GAAG,CAAC,CAAC;AACtB,SAAS,cAAc;IACrB,aAAa,GAAG,CAAC,aAAa,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC;IAChD,OAAO,cAAc,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;AACpD,CAAC;AASD;iEACiE;AACjE,MAAM,UAAU,UAAU,CAAC,IAAU;IACnC,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC1B,6DAA6D;QAC7D,kEAAkE;QAClE,iCAAiC;QACjC,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;IACvC,CAAC;IACD,MAAM,EAAE,GAAG,cAAc,EAAE,CAAC;IAC5B,IAAI,IAAI,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;QACpC,0EAA0E;QAC1E,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC;QAClC,sEAAsE;QACtE,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,6BAA6B;QACzE,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACrC,MAAM,IAAI,GAAG;YACX,yBAEE,EAAE,EAAE,EAAE,EACN,EAAE,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,EAClB,EAAE,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,EAClB,EAAE,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,EAClB,EAAE,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,YAEjB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CACxB,eAEE,MAAM,EAAE,CAAC,CAAC,MAAM,EAChB,SAAS,EAAE,CAAC,CAAC,KAAK,KACd,CAAC,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,IAH1D,CAAC,CAIN,CACH,CAAC,IAdG,EAAE,CAeQ;SAClB,CAAC;QACF,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC;IACtC,CAAC;IACD,kBAAkB;IAClB,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,GAAG,CAAC;IACjC,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,GAAG,CAAC;IACjC,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,GAAG,CAAC;IAC7B,MAAM,IAAI,GAAG;QACX,yBAAyB,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,GAAG,YACtF,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CACxB,eAEE,MAAM,EAAE,CAAC,CAAC,MAAM,EAChB,SAAS,EAAE,CAAC,CAAC,KAAK,KACd,CAAC,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,IAH1D,CAAC,CAIN,CACH,CAAC,IARiB,EAAE,CASN;KAClB,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC;AACtC,CAAC;AAED;;qEAEqE;AACrE,MAAM,UAAU,gBAAgB,CAAC,KAAa,EAAE,MAAe;IAC7D,mEAAmE;IACnE,wDAAwD;IACxD,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAa,CAAC;IAClF,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACnC,OAAO,EAAE,eAAe,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AAChD,CAAC;AAED,SAAS,SAAS,CAAC,IAAU,EAAE,MAAe;IAC5C,mEAAmE;IACnE,kEAAkE;IAClE,mEAAmE;IACnE,0DAA0D;IAC1D,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,iBAAiB,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;YACxC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,mEAAmE;QACnE,OAAO,mBAAmB,KAAK,KAAK,KAAK,GAAG,CAAC;IAC/C,CAAC;IACD,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACrC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,iBAAiB,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC;YAC9C,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAC7E,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACzD,CAAC;IACD,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,IAAI,CAAC,IAAI,KAAK,iBAAiB,EAAE,CAAC;QACpC,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC;QAClC,OAAO,mBAAmB,KAAK,QAAQ,KAAK,GAAG,CAAC;IAClD,CAAC;IACD,kBAAkB;IAClB,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,GAAG,CAAC,GAAG,GAAG,CAAC;IACzC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,GAAG,CAAC,GAAG,GAAG,CAAC;IACzC,OAAO,6BAA6B,EAAE,KAAK,EAAE,MAAM,KAAK,GAAG,CAAC;AAC9D,CAAC;AAED;;;;;8CAK8C;AAC9C,SAAS,cAAc,CAAC,KAAa,EAAE,OAAe;IACpD,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;IAC7C,IAAI,GAAG,EAAE,CAAC;QACR,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC;aAChC,QAAQ,CAAC,EAAE,CAAC;aACZ,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QACpB,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;IAC1B,CAAC;IACD,OAAO,sBAAsB,KAAK,IAAI,OAAO,GAAG,GAAG,iBAAiB,CAAC;AACvE,CAAC;AAED;;;;;;sEAMsE;AACtE,MAAM,UAAU,aAAa,CAAC,KAAa,EAAE,KAAa,EAAE,MAAe;IACzE,MAAM,GAAG,GAAW,EAAE,CAAC;IACvB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC1B,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACxC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBACnB,iBAAiB,CAAC,GAAG,KAAK,QAAQ,EAAE,MAAM,CAAC,CAAC;gBAC5C,SAAS;YACX,CAAC;YACD,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YAC7B,SAAS;QACX,CAAC;QACD,MAAM,KAAK,GAAe,EAAE,CAAC;QAC7B,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;YACjC,MAAM,KAAK,GAAG,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YACrC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBACnB,iBAAiB,CAAC,GAAG,KAAK,cAAc,EAAE,MAAM,CAAC,CAAC;gBAClD,QAAQ,GAAG,IAAI,CAAC;gBAChB,MAAM;YACR,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAC9B,CAAC;QACD,IAAI,QAAQ;YAAE,SAAS;QACvB,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;yCAIyC;AACzC,MAAM,UAAU,UAAU,CAAC,KAAc,EAAE,KAAc,EAAE,MAAe;IACxE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACrC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACtB,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;gBACf,cAAc,CACZ,MAAM,EACN,GAAG,KAAK,OAAO,EACf,4GAA4G,CAC7G,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,MAAM,CAAC,MAAM,CAAW,CAAC;AACxC,CAAC;AAED,SAAS,MAAM,CAAC,CAAU;IACxB,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,CAAC,GAAI,CAAwB,CAAC,IAAI,CAAC;IACzC,OAAO,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,iBAAiB,IAAI,CAAC,KAAK,iBAAiB,CAAC;AAC7E,CAAC"}
1
+ {"version":3,"file":"fill.js","sourceRoot":"","sources":["../../src/render/fill.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import { type DiagnosticHandler } from "./diagnostics.js";
2
+ import type { RenderBundle } from "./bundle.js";
3
+ export interface HeadlessRenderOptions {
4
+ /** Already-compiled bundle (via `@lumencast/compiler` on the host side). */
5
+ bundle: RenderBundle;
6
+ /** A live, mounted DOM node. Its size is set from `stage` unless the host
7
+ * has already dimensioned it (see `stage`). */
8
+ target: HTMLElement;
9
+ /** Initial leaf-grain store state (`store.reset(defaults)`) — the bound
10
+ * values the bundle reads (`__lit.*`, score, names…). */
11
+ defaults?: Record<string, unknown>;
12
+ /** Stage dimensions in CSS px. Defaults to 1920×1080. Applied to `target`
13
+ * as `width`/`height`/`position:relative`/`overflow:hidden` so the
14
+ * screenshot frame matches the reference exactly. */
15
+ stage?: {
16
+ width: number;
17
+ height: number;
18
+ };
19
+ /** Anti-drop diagnostics channel (ADR 001 §3.4): omitted assets, unhonoured
20
+ * fields surface here as `{ nodeId, field, reason }` (never a value — R9).
21
+ * Wired to the same global channel `mount()` uses. */
22
+ onDiagnostic?: DiagnosticHandler;
23
+ }
24
+ export interface HeadlessRenderHandle {
25
+ /** Resolves after the scene has rendered, two animation frames have passed
26
+ * AND `document.fonts.ready` (ADR 003 §3.3) — i.e. the DOM is laid out and
27
+ * fonts are loaded, so a screenshot taken now is fidelity-faithful. */
28
+ ready: Promise<void>;
29
+ /** Tear down the React root and detach the diagnostics handler. */
30
+ unmount(): void;
31
+ }
32
+ /**
33
+ * Render `bundle` into `target` through the production broadcast path and
34
+ * resolve `ready` once it is settled. The runtime performs NO network fetch and
35
+ * takes NO screenshot — it produces a settled live DOM and a readiness signal,
36
+ * nothing more (ADR 003 D5).
37
+ */
38
+ export declare function renderBundleHeadless(opts: HeadlessRenderOptions): HeadlessRenderHandle;
39
+ //# sourceMappingURL=headless.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headless.d.ts","sourceRoot":"","sources":["../../src/render/headless.tsx"],"names":[],"mappings":"AA0BA,OAAO,EAAyB,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACjF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKhD,MAAM,WAAW,qBAAqB;IACpC,4EAA4E;IAC5E,MAAM,EAAE,YAAY,CAAC;IACrB;oDACgD;IAChD,MAAM,EAAE,WAAW,CAAC;IACpB;8DAC0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC;;0DAEsD;IACtD,KAAK,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C;;2DAEuD;IACvD,YAAY,CAAC,EAAE,iBAAiB,CAAC;CAClC;AAED,MAAM,WAAW,oBAAoB;IACnC;;4EAEwE;IACxE,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACrB,mEAAmE;IACnE,OAAO,IAAI,IAAI,CAAC;CACjB;AAID;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,qBAAqB,GAAG,oBAAoB,CA4DtF"}
@@ -0,0 +1,83 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // Public headless render entry — render an already-compiled `RenderBundle`
3
+ // into a live DOM node, no WebSocket, ready when layout + fonts have settled
4
+ // (ADR 003 §3.1). The host (Playwright / Chromium / a CEF offscreen surface)
5
+ // screenshots `target` once `ready` resolves. The runtime does DOM + readiness
6
+ // ONLY — no screenshot, no fetch (ADR 003 D5/D3).
7
+ //
8
+ // This is the zero-loss harness (ADR 002 #J) generalised: it mounts the EXACT
9
+ // production seam —
10
+ // LumencastRuntimeProvider{ mode:"broadcast", status:"live" } > BroadcastMode
11
+ // — into a real `createRoot(target)`, NOT `renderToStaticMarkup` (which yields
12
+ // unlaid-out markup: unmeasured fonts, uncomposited masks → an infidel PNG,
13
+ // ADR 003 §3.1). `BroadcastMode` is dynamically imported so the headless
14
+ // function adds no weight to the eager `mount`/broadcast path (ADR 003 §4,
15
+ // RC6); the heavy render code already lives in the broadcast/tree chunks.
16
+ //
17
+ // Asset resolution is the HOST's job, done in the bundle BEFORE this call
18
+ // (ADR 003 §3.2): the runtime renders the bundle as-is, gating every remaining
19
+ // `src` through the unchanged deny-by-default host-allow gate inside
20
+ // `BroadcastMode` (`AllowedHostsProvider`). A `src` on a host not in the
21
+ // bundle's `allowedHosts` is omitted + a diagnostic is emitted — never faked
22
+ // (ADR 002 borne, D4). Use `render/asset-resolve` helpers to pre-resolve.
23
+ import { StrictMode } from "react";
24
+ import { createRoot } from "react-dom/client";
25
+ import { createStore } from "../state/store.js";
26
+ import { LumencastRuntimeProvider } from "../overlay/runtime-context.js";
27
+ import { addDiagnosticsHandler } from "./diagnostics.js";
28
+ /** Default stage size — the Figma 817:3 cover frame, the SSIM reference. */
29
+ const DEFAULT_STAGE = { width: 1920, height: 1080 };
30
+ const noop = () => { };
31
+ /**
32
+ * Render `bundle` into `target` through the production broadcast path and
33
+ * resolve `ready` once it is settled. The runtime performs NO network fetch and
34
+ * takes NO screenshot — it produces a settled live DOM and a readiness signal,
35
+ * nothing more (ADR 003 D5).
36
+ */
37
+ export function renderBundleHeadless(opts) {
38
+ const stage = opts.stage ?? DEFAULT_STAGE;
39
+ const target = opts.target;
40
+ // Pose the stage so the screenshot frame is exact (mirrors harness.html).
41
+ target.style.position ||= "relative";
42
+ target.style.width = `${stage.width}px`;
43
+ target.style.height = `${stage.height}px`;
44
+ target.style.overflow = "hidden";
45
+ const removeDiagnostics = opts.onDiagnostic
46
+ ? addDiagnosticsHandler(opts.onDiagnostic)
47
+ : undefined;
48
+ const store = createStore();
49
+ store.reset(opts.defaults ?? {});
50
+ const root = createRoot(target);
51
+ const ready = new Promise((resolve) => {
52
+ // BroadcastMode is dynamically imported so its (and the tree's) weight is
53
+ // not pulled into the eager `mount` entry chunk (RC6). It is already a
54
+ // separate chunk reused from the broadcast path.
55
+ void import("../modes/broadcast.js").then(({ BroadcastMode }) => {
56
+ root.render(_jsx(StrictMode, { children: _jsx(LumencastRuntimeProvider, { value: {
57
+ mode: "broadcast",
58
+ store,
59
+ bundle: opts.bundle,
60
+ status: "live",
61
+ sendInput: noop,
62
+ }, children: _jsx(BroadcastMode, {}) }) }));
63
+ // Settle: two animation frames (layout) AND fonts loaded (ADR 003 §3.3).
64
+ // Both must complete before `ready` resolves, so a screenshot taken on
65
+ // `ready` uses the brand glyphs, not the fallback font (no FOUT freeze).
66
+ const framesSettled = new Promise((res) => {
67
+ requestAnimationFrame(() => requestAnimationFrame(() => res()));
68
+ });
69
+ const fontsReady = typeof document !== "undefined" && document.fonts
70
+ ? document.fonts.ready.then(() => undefined)
71
+ : Promise.resolve();
72
+ void Promise.all([framesSettled, fontsReady]).then(() => resolve());
73
+ });
74
+ });
75
+ return {
76
+ ready,
77
+ unmount() {
78
+ removeDiagnostics?.();
79
+ root.unmount();
80
+ },
81
+ };
82
+ }
83
+ //# sourceMappingURL=headless.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"headless.js","sourceRoot":"","sources":["../../src/render/headless.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,87 @@
1
+ import type { ReactElement, CSSProperties } from "react";
2
+ /** Resolve a shape `mask.source.ref` to its inlined mask-coverage geometry
3
+ * (#K). The Tree supplies this from its one-pass `id → shape` index ; it
4
+ * returns `null` for a PENDING ref (id absent from the index) so the mask is
5
+ * omitted, never crashing. The resolver inlines ONLY the referenced shape's
6
+ * geometry — never its own mask (anti-cycle, profondeur = 1). */
7
+ export type ShapeRefResolver = (ref: string) => ReactElement | null;
8
+ /** Feather pad (px). The group/shape mask wrapper's `overflow:hidden` box is
9
+ * grown by this on every side (tree.tsx, `inset:-PAD`) and the coverage is
10
+ * shifted back by the same amount (buildMask) so a BLURRED mask rim isn't
11
+ * re-cut into a hard square at the box edge. Generous enough for the
12
+ * bg-texture ellipse's ~3σ (53.88 CSS sigma) feather. Sharp masks are
13
+ * unaffected (their alpha-0 region simply sits inside the grown box). */
14
+ export declare const MASK_FEATHER_PAD = 180;
15
+ /** A typed mask source, the only shapes the builder accepts. Anything else
16
+ * (string, missing discriminant, extra markup) is rejected. A `group` source
17
+ * (#O) references a GROUP/FRAME container by id, composited downstream. */
18
+ export type MaskSource = {
19
+ kind: "shape";
20
+ ref: string;
21
+ } | {
22
+ kind: "image";
23
+ src: string;
24
+ srcRect?: {
25
+ x: number;
26
+ y: number;
27
+ w: number;
28
+ h: number;
29
+ };
30
+ } | {
31
+ kind: "group";
32
+ ref: string;
33
+ };
34
+ /** The typed mask spec as it reaches the runtime (compiler-lowered or a live
35
+ * LSDP delta). All fields are re-validated here — nothing is trusted. */
36
+ export interface MaskSpec {
37
+ source: MaskSource;
38
+ type: "alpha" | "luminance";
39
+ op: "intersect" | "subtract" | "union";
40
+ position?: {
41
+ x: number;
42
+ y: number;
43
+ };
44
+ size?: {
45
+ w: number;
46
+ h: number;
47
+ };
48
+ }
49
+ /** What a successfully-built mask contributes to the render. */
50
+ export interface BuiltMask {
51
+ /** The `<mask>` element to drop into the masked element's SVG `<defs>`. */
52
+ def: ReactElement;
53
+ /** Inline style applying the mask to the masked subtree's wrapper. */
54
+ style: CSSProperties;
55
+ /** The generated mask id (for `url(#…)` wiring and test assertions). */
56
+ id: string;
57
+ /** True when the coverage is FEATHERED (a blurred edge) : the masked wrapper
58
+ * must grow by MASK_FEATHER_PAD (tree.tsx) so the soft rim isn't re-cut into a
59
+ * square, and the coverage here is pre-shifted by the same pad. A sharp mask
60
+ * leaves this false and skips the pad entirely (no structural change). */
61
+ feather: boolean;
62
+ }
63
+ /**
64
+ * Validate a loose `mask` value into a strict {@link MaskSpec}, or `null`.
65
+ * Re-runs the closed-enum gates (T4) and the source-shape discriminant ;
66
+ * a malformed mask is dropped whole (it cannot be partially honoured).
67
+ */
68
+ export declare function parseMaskSpec(value: unknown, nodeId: string | undefined): MaskSpec | null;
69
+ /**
70
+ * Build a `<mask>` element + the CSS reference from a typed mask spec.
71
+ * Returns `null` when the mask must be omitted (bad enum, rejected host,
72
+ * unsafe ref) — the caller renders the subtree unmasked.
73
+ *
74
+ * @param mask the typed spec (already enum-checked by parseMaskSpec,
75
+ * but re-checked here so the builder is safe standalone).
76
+ * @param allowedHosts the bundle's `assets.allowedHosts` ; an image source is
77
+ * gated against it (T1/T2) before reaching `<image href>`.
78
+ * @param nodeId for diagnostics (never carries a value, R9).
79
+ * @param resolveShape resolves a shape `mask.source.ref` to its inlined
80
+ * coverage geometry (#K). Omitted / returns `null` ⇒ the
81
+ * shape source is pending → the whole mask is omitted.
82
+ */
83
+ export declare function buildMask(mask: MaskSpec, allowedHosts: readonly string[] | undefined, nodeId: string | undefined, resolveShape?: ShapeRefResolver, boxSize?: {
84
+ w?: number;
85
+ h?: number;
86
+ }, feather?: boolean): BuiltMask | null;
87
+ //# sourceMappingURL=mask.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mask.d.ts","sourceRoot":"","sources":["../../src/render/mask.tsx"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAIzD;;;;kEAIkE;AAClE,MAAM,MAAM,gBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,YAAY,GAAG,IAAI,CAAC;AAOpE;;;;;0EAK0E;AAC1E,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAKpC;;4EAE4E;AAC5E,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACxF;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnC;0EAC0E;AAC1E,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,OAAO,GAAG,WAAW,CAAC;IAC5B,EAAE,EAAE,WAAW,GAAG,UAAU,GAAG,OAAO,CAAC;IACvC,QAAQ,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,IAAI,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACjC;AAED,gEAAgE;AAChE,MAAM,WAAW,SAAS;IACxB,2EAA2E;IAC3E,GAAG,EAAE,YAAY,CAAC;IAClB,sEAAsE;IACtE,KAAK,EAAE,aAAa,CAAC;IACrB,wEAAwE;IACxE,EAAE,EAAE,MAAM,CAAC;IACX;;;+EAG2E;IAC3E,OAAO,EAAE,OAAO,CAAC;CAClB;AAoBD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,IAAI,CAuDzF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,QAAQ,EACd,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EAC3C,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,YAAY,CAAC,EAAE,gBAAgB,EAC/B,OAAO,CAAC,EAAE;IAAE,CAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,CAAC,EAAE,MAAM,CAAA;CAAE,EACpC,OAAO,UAAQ,GACd,SAAS,GAAG,IAAI,CAsMlB"}