@lumencast/runtime 0.4.0 → 0.6.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 (135) hide show
  1. package/README.md +57 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/animate/frame-coalescer.d.ts +13 -0
  4. package/dist/animate/frame-coalescer.d.ts.map +1 -0
  5. package/dist/animate/frame-coalescer.js +46 -0
  6. package/dist/animate/frame-coalescer.js.map +1 -0
  7. package/dist/animate/keyframes.d.ts +1 -1
  8. package/dist/animate/keyframes.d.ts.map +1 -1
  9. package/dist/animate/keyframes.js +20 -6
  10. package/dist/animate/keyframes.js.map +1 -1
  11. package/dist/animate/transitions.d.ts +4 -1
  12. package/dist/animate/transitions.d.ts.map +1 -1
  13. package/dist/animate/transitions.js +30 -3
  14. package/dist/animate/transitions.js.map +1 -1
  15. package/dist/{broadcast-DzZ8TVGZ.js → broadcast-DO7jEkix.js} +3 -3
  16. package/dist/{broadcast-DzZ8TVGZ.js.map → broadcast-DO7jEkix.js.map} +1 -1
  17. package/dist/{control-gbDGvdR0.js → control-BSfl4_cO.js} +4 -4
  18. package/dist/{control-gbDGvdR0.js.map → control-BSfl4_cO.js.map} +1 -1
  19. package/dist/{index-oteiocFe.js → index-Crkij3C4.js} +352 -179
  20. package/dist/index-Crkij3C4.js.map +1 -0
  21. package/dist/index.d.ts +5 -2
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.html +1 -1
  24. package/dist/index.js +10 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/lumencast.js +9 -2
  27. package/dist/mount.d.ts.map +1 -1
  28. package/dist/mount.js +16 -1
  29. package/dist/mount.js.map +1 -1
  30. package/dist/render/bind-animate.d.ts +40 -0
  31. package/dist/render/bind-animate.d.ts.map +1 -0
  32. package/dist/render/bind-animate.js +329 -0
  33. package/dist/render/bind-animate.js.map +1 -0
  34. package/dist/render/bundle.d.ts +56 -6
  35. package/dist/render/bundle.d.ts.map +1 -1
  36. package/dist/render/bundle.js +86 -5
  37. package/dist/render/bundle.js.map +1 -1
  38. package/dist/render/color-interp.d.ts +18 -0
  39. package/dist/render/color-interp.d.ts.map +1 -0
  40. package/dist/render/color-interp.js +303 -0
  41. package/dist/render/color-interp.js.map +1 -0
  42. package/dist/render/css-color.d.ts +16 -0
  43. package/dist/render/css-color.d.ts.map +1 -0
  44. package/dist/render/css-color.js +130 -0
  45. package/dist/render/css-color.js.map +1 -0
  46. package/dist/render/diagnostics.d.ts +26 -0
  47. package/dist/render/diagnostics.d.ts.map +1 -0
  48. package/dist/render/diagnostics.js +58 -0
  49. package/dist/render/diagnostics.js.map +1 -0
  50. package/dist/render/fill.d.ts +15 -3
  51. package/dist/render/fill.d.ts.map +1 -1
  52. package/dist/render/fill.js +81 -14
  53. package/dist/render/fill.js.map +1 -1
  54. package/dist/render/filter-clamp.d.ts +35 -0
  55. package/dist/render/filter-clamp.d.ts.map +1 -0
  56. package/dist/render/filter-clamp.js +90 -0
  57. package/dist/render/filter-clamp.js.map +1 -0
  58. package/dist/render/keyframe-player.d.ts +4 -1
  59. package/dist/render/keyframe-player.d.ts.map +1 -1
  60. package/dist/render/keyframe-player.js +2 -2
  61. package/dist/render/keyframe-player.js.map +1 -1
  62. package/dist/render/primitives/frame.d.ts +16 -1
  63. package/dist/render/primitives/frame.d.ts.map +1 -1
  64. package/dist/render/primitives/frame.js +42 -7
  65. package/dist/render/primitives/frame.js.map +1 -1
  66. package/dist/render/primitives/image.d.ts +1 -1
  67. package/dist/render/primitives/image.d.ts.map +1 -1
  68. package/dist/render/primitives/image.js +6 -3
  69. package/dist/render/primitives/image.js.map +1 -1
  70. package/dist/render/primitives/index.d.ts +3 -0
  71. package/dist/render/primitives/index.d.ts.map +1 -1
  72. package/dist/render/primitives/index.js.map +1 -1
  73. package/dist/render/primitives/instance.d.ts +1 -1
  74. package/dist/render/primitives/instance.d.ts.map +1 -1
  75. package/dist/render/primitives/instance.js +10 -13
  76. package/dist/render/primitives/instance.js.map +1 -1
  77. package/dist/render/primitives/shape.d.ts +9 -3
  78. package/dist/render/primitives/shape.d.ts.map +1 -1
  79. package/dist/render/primitives/shape.js +56 -12
  80. package/dist/render/primitives/shape.js.map +1 -1
  81. package/dist/render/primitives/text.d.ts +35 -4
  82. package/dist/render/primitives/text.d.ts.map +1 -1
  83. package/dist/render/primitives/text.js +179 -7
  84. package/dist/render/primitives/text.js.map +1 -1
  85. package/dist/render/prop-allowlist.d.ts +10 -0
  86. package/dist/render/prop-allowlist.d.ts.map +1 -0
  87. package/dist/render/prop-allowlist.js +112 -0
  88. package/dist/render/prop-allowlist.js.map +1 -0
  89. package/dist/render/svg-path.d.ts +35 -0
  90. package/dist/render/svg-path.d.ts.map +1 -0
  91. package/dist/render/svg-path.js +211 -0
  92. package/dist/render/svg-path.js.map +1 -0
  93. package/dist/render/tree.d.ts.map +1 -1
  94. package/dist/render/tree.js +30 -5
  95. package/dist/render/tree.js.map +1 -1
  96. package/dist/{status-pill-Cgdl9FtP.js → status-pill-BT5b-yET.js} +2 -2
  97. package/dist/{status-pill-Cgdl9FtP.js.map → status-pill-BT5b-yET.js.map} +1 -1
  98. package/dist/{test-CAnkHA0n.js → test-_hh1JvAd.js} +4 -4
  99. package/dist/{test-CAnkHA0n.js.map → test-_hh1JvAd.js.map} +1 -1
  100. package/dist/transport/ws.d.ts +5 -0
  101. package/dist/transport/ws.d.ts.map +1 -1
  102. package/dist/transport/ws.js +7 -0
  103. package/dist/transport/ws.js.map +1 -1
  104. package/dist/tree-DBj9SJgs.js +1230 -0
  105. package/dist/tree-DBj9SJgs.js.map +1 -0
  106. package/dist/types.d.ts +26 -0
  107. package/dist/types.d.ts.map +1 -1
  108. package/package.json +5 -4
  109. package/src/animate/frame-coalescer.ts +63 -0
  110. package/src/animate/keyframes.ts +24 -5
  111. package/src/animate/transitions.ts +33 -3
  112. package/src/index.ts +24 -0
  113. package/src/mount.ts +17 -1
  114. package/src/render/bind-animate.tsx +370 -0
  115. package/src/render/bundle.ts +124 -11
  116. package/src/render/color-interp.ts +303 -0
  117. package/src/render/css-color.ts +145 -0
  118. package/src/render/diagnostics.ts +75 -0
  119. package/src/render/fill.tsx +85 -14
  120. package/src/render/filter-clamp.ts +99 -0
  121. package/src/render/keyframe-player.tsx +10 -2
  122. package/src/render/primitives/frame.tsx +47 -7
  123. package/src/render/primitives/image.tsx +6 -2
  124. package/src/render/primitives/index.ts +3 -0
  125. package/src/render/primitives/instance.tsx +14 -15
  126. package/src/render/primitives/shape.tsx +76 -12
  127. package/src/render/primitives/text.tsx +224 -7
  128. package/src/render/prop-allowlist.ts +119 -0
  129. package/src/render/svg-path.ts +215 -0
  130. package/src/render/tree.tsx +41 -6
  131. package/src/transport/ws.ts +8 -0
  132. package/src/types.ts +27 -0
  133. package/dist/index-oteiocFe.js.map +0 -1
  134. package/dist/tree-DVYXwItH.js +0 -512
  135. package/dist/tree-DVYXwItH.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree-DBj9SJgs.js","sources":["../src/render/primitives/stack.tsx","../src/render/primitives/grid.tsx","../src/render/css-color.ts","../src/render/fill.tsx","../src/render/primitives/frame.tsx","../src/render/primitives/text.tsx","../src/render/primitives/image.tsx","../src/render/svg-path.ts","../src/render/primitives/shape.tsx","../src/render/primitives/media.tsx","../src/render/primitives/instance.tsx","../src/render/primitives/index.ts","../src/render/scope.tsx","../src/render/universal-wrapper.tsx","../src/animate/keyframes.ts","../src/render/stagger-context.tsx","../src/render/keyframe-player.tsx","../src/animate/frame-coalescer.ts","../src/render/color-interp.ts","../src/render/bind-animate.tsx","../src/render/tree.tsx"],"sourcesContent":["import type { CSSProperties } from \"react\";\nimport type { PrimitiveProps } from \"./index\";\n\n/** Vertical or horizontal flex container. Layout-only — bindings\n * here are unusual but tolerated.\n *\n * LSML 1.1 §4.1 adds `wrap` (boolean) and `crossGap` (number) :\n * - wrap: true sets `flex-wrap: wrap` so children flow onto the\n * next row / column when they overflow the main axis.\n * - crossGap is the spacing between rows / columns when wrapping.\n * Mapped to CSS `row-gap` (horizontal stack) or `column-gap`\n * (vertical stack). Ignored when `wrap` is false.\n */\nexport function Stack({ resolved, children }: PrimitiveProps) {\n const direction = (resolved.direction as string) ?? \"vertical\";\n const gap = numberOr(resolved.gap, 0);\n const wrap = resolved.wrap === true;\n const crossGap = numberOr(resolved.crossGap, 0);\n const align = (resolved.align as string) ?? \"stretch\";\n const justify = (resolved.justify as string) ?? \"flex-start\";\n const isHorizontal = direction === \"horizontal\";\n\n const style: CSSProperties = {\n display: \"flex\",\n flexDirection: isHorizontal ? \"row\" : \"column\",\n alignItems: align,\n justifyContent: justify,\n };\n\n if (wrap) {\n style.flexWrap = \"wrap\";\n if (isHorizontal) {\n style.columnGap = gap;\n style.rowGap = crossGap;\n } else {\n style.rowGap = gap;\n style.columnGap = crossGap;\n }\n } else {\n style.gap = gap;\n }\n\n return <div style={style}>{children}</div>;\n}\n\nfunction numberOr(v: unknown, fallback: number): number {\n return typeof v === \"number\" && Number.isFinite(v) ? v : fallback;\n}\n","import type { PrimitiveProps } from \"./index\";\n\n/** CSS Grid container with declared rows / cols. */\nexport function Grid({ resolved, children }: PrimitiveProps) {\n const cols = (resolved.cols as string) ?? \"1fr\";\n const rows = (resolved.rows as string) ?? \"auto\";\n const gap = (resolved.gap as number | string | undefined) ?? 0;\n return (\n <div\n style={{\n display: \"grid\",\n gridTemplateColumns: cols,\n gridTemplateRows: rows,\n gap,\n }}\n >\n {children}\n </div>\n );\n}\n","// Strict CSS colour parser — the ONLY gate through which untrusted\n// colour values (bundle props AND live LSDP deltas, see `resolveProps`\n// in tree.tsx) may reach an inline CSS style.\n//\n// ADR 001 §6 RC#11 (CSS strict) + RC#12 (anti-ReDoS), threat model\n// Bastion 2026-06-10, issue #35. Sites #30/#31/#33 must reuse this\n// module — never re-implement colour validation locally.\n//\n// Accepted grammar (LSML 1.1 §6.5 colour forms, nothing more) :\n// - hex : #RGB | #RGBA | #RRGGBB | #RRGGBBAA\n// - rgb() : rgb(R, G, B) | rgba(R, G, B, A) — 0-255 or percentages\n// - hsl() : hsl(H, S%, L%) | hsla(H, S%, L%, A)\n// - named : canonical CSS named colours + `transparent` + `currentcolor`\n// Anything else — including `url(`, `;`, `}`, `expression(`, var(),\n// calc(), whitespace tricks — is REJECTED (null). Never passthrough.\n//\n// ── Linear-time justification (RC#12, written per Bastion) ──────────\n// 1. Inputs longer than MAX_LEN (64) are rejected before any regex\n// runs, so every step below operates on a bounded string.\n// 2. The charset pre-scan is a single O(n) pass over one character\n// class — it rejects `;`, `}`, `:`, `/`, quotes, backslashes and\n// control characters outright, so no later step ever sees them.\n// 3. Every regex is anchored (`^…$`) and built exclusively from\n// literals, character classes and BOUNDED quantifiers ({m,n}, ?).\n// There are no nested unbounded quantifiers ((a+)+ style), no\n// overlapping alternations under a quantifier — i.e. no input can\n// trigger super-linear backtracking. Combined with the 64-char cap,\n// total work is O(64) per value regardless of payload shape.\n// ─────────────────────────────────────────────────────────────────────\n\nimport { emitDiagnostic } from \"./diagnostics\";\n\nconst MAX_LEN = 64;\n\n// Single-pass charset allowlist. Only characters that can appear in\n// the accepted grammar. Notably ABSENT : `;` `}` `{` `:` `/` `\"` `'`\n// `\\` `<` `>` `-` and all control chars — the injection metacharacters.\nconst CHARSET_RE = /^[#a-zA-Z0-9(),.% ]{1,64}$/;\n\n// hex — 3/4/6/8 hex digits. Alternation of fixed-width character-class\n// runs : strictly linear.\nconst HEX_RE = /^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;\n\n// Number tokens. All quantifiers bounded ; `(?:\\.\\d{1,4})?` is an\n// optional bounded group — one possible match per position, no\n// ambiguity, no backtracking blow-up.\nconst NUM = String.raw`\\d{1,3}(?:\\.\\d{1,4})?`; // 0 … 999.9999\nconst ALPHA = String.raw`(?:0|1|0?\\.\\d{1,4}|${NUM}%)`; // 0-1 or %\nconst SP = String.raw`[ ]{0,4}`; // bounded optional spaces\n\n// rgb(R, G, B[, A]) — channels all plain numbers or all percentages\n// are both accepted (range-checked numerically after the match).\nconst RGB_RE = new RegExp(\n `^rgba?\\\\(${SP}(${NUM})(%?)${SP},${SP}(${NUM})(%?)${SP},${SP}(${NUM})(%?)${SP}` +\n `(?:,${SP}${ALPHA}${SP})?\\\\)$`,\n);\n\n// hsl(H[deg], S%, L%[, A])\nconst HSL_RE = new RegExp(\n `^hsla?\\\\(${SP}(${NUM})(?:deg)?${SP},${SP}(${NUM})%${SP},${SP}(${NUM})%${SP}` +\n `(?:,${SP}${ALPHA}${SP})?\\\\)$`,\n);\n\n// Canonical CSS named colours (CSS Color 4 §6.1) + the two keywords\n// that behave like colours in every site we render.\nconst NAMED = new Set(\n (\n \"aliceblue antiquewhite aqua aquamarine azure beige bisque black blanchedalmond blue \" +\n \"blueviolet brown burlywood cadetblue chartreuse chocolate coral cornflowerblue cornsilk \" +\n \"crimson cyan darkblue darkcyan darkgoldenrod darkgray darkgreen darkgrey darkkhaki \" +\n \"darkmagenta darkolivegreen darkorange darkorchid darkred darksalmon darkseagreen \" +\n \"darkslateblue darkslategray darkslategrey darkturquoise darkviolet deeppink deepskyblue \" +\n \"dimgray dimgrey dodgerblue firebrick floralwhite forestgreen fuchsia gainsboro ghostwhite \" +\n \"gold goldenrod gray green greenyellow grey honeydew hotpink indianred indigo ivory khaki \" +\n \"lavender lavenderblush lawngreen lemonchiffon lightblue lightcoral lightcyan \" +\n \"lightgoldenrodyellow lightgray lightgreen lightgrey lightpink lightsalmon lightseagreen \" +\n \"lightskyblue lightslategray lightslategrey lightsteelblue lightyellow lime limegreen \" +\n \"linen magenta maroon mediumaquamarine mediumblue mediumorchid mediumpurple \" +\n \"mediumseagreen mediumslateblue mediumspringgreen mediumturquoise mediumvioletred \" +\n \"midnightblue mintcream mistyrose moccasin navajowhite navy oldlace olive olivedrab \" +\n \"orange orangered orchid palegoldenrod palegreen paleturquoise palevioletred papayawhip \" +\n \"peachpuff peru pink plum powderblue purple rebeccapurple red rosybrown royalblue \" +\n \"saddlebrown salmon sandybrown seagreen seashell sienna silver skyblue slateblue \" +\n \"slategray slategrey snow springgreen steelblue tan teal thistle tomato turquoise violet \" +\n \"wheat white whitesmoke yellow yellowgreen transparent currentcolor\"\n ).split(\" \"),\n);\n\n/**\n * Validate an untrusted colour value against the strict grammar above.\n *\n * Returns the validated string (trimmed ; named colours lowercased) or\n * `null` on rejection. A `null` MUST be handled as \"omit the style /\n * use the primitive's safe default\" — never interpolate the raw input.\n */\nexport function parseCssColor(value: unknown): string | null {\n if (typeof value !== \"string\") return null;\n const v = value.trim();\n if (v.length === 0 || v.length > MAX_LEN) return null;\n // Contractual explicit rejects (Bastion RC#11) — redundant with the\n // charset scan below, kept as belt-and-braces so the contract holds\n // even if the grammar is ever extended.\n const lower = v.toLowerCase();\n if (lower.includes(\"url(\") || v.includes(\";\") || v.includes(\"}\")) return null;\n // Single-pass charset allowlist (kills every CSS metacharacter).\n if (!CHARSET_RE.test(v)) return null;\n\n if (v.startsWith(\"#\")) return HEX_RE.test(v) ? v : null;\n\n if (lower.startsWith(\"rgb\")) {\n const m = RGB_RE.exec(lower);\n if (!m) return null;\n // Range check : percent channels ≤ 100, plain channels ≤ 255, no mixing.\n const pct = [m[2], m[4], m[6]];\n if (!(pct.every((p) => p === \"%\") || pct.every((p) => p === \"\"))) return null;\n const max = pct[0] === \"%\" ? 100 : 255;\n for (const ch of [m[1], m[3], m[5]]) {\n if (Number(ch) > max) return null;\n }\n return lower;\n }\n\n if (lower.startsWith(\"hsl\")) {\n const m = HSL_RE.exec(lower);\n if (!m) return null;\n if (Number(m[1]) > 360 || Number(m[2]) > 100 || Number(m[3]) > 100) return null;\n return lower;\n }\n\n return NAMED.has(lower) ? lower : null;\n}\n\n/**\n * Diagnostic for a rejected value. Bastion R9 (ADR 001 §5.1) : the\n * rejected VALUE is never logged nor forwarded — only `node.id` (RC#7,\n * issue #34), the field name and a static reason. Routed through the\n * structured diagnostics channel (events, no logs in `broadcast`).\n */\nexport function warnRejectedColor(field: string, nodeId?: string): void {\n emitDiagnostic(\n nodeId,\n field,\n \"rejected unsafe colour : not a strict hex/rgb()/hsl()/named colour\",\n );\n}\n","// Fill rendering helpers (LSML 1.1 §4.12).\n//\n// A Fill is a discriminated union :\n// - solid : { kind: \"solid\", color, opacity? }\n// - linear-gradient : { kind: \"linear-gradient\", angle_deg?, stops, opacity? }\n// - radial-gradient : { kind: \"radial-gradient\", center?, radius?, stops, opacity? }\n//\n// shape.fills[] and frame.backgrounds[] both use this shape. Each fill\n// renders as a separate SVG element layered top-to-bottom (first entry\n// renders on top per §4.12).\n\nimport type { CSSProperties, ReactElement } from \"react\";\nimport { parseCssColor, warnRejectedColor } from \"./css-color\";\nimport { emitDiagnostic } from \"./diagnostics\";\n\nexport interface FillStop {\n offset: number;\n color: string;\n opacity?: number;\n}\n\nexport type Fill =\n | { kind: \"solid\"; color: string; opacity?: number }\n | {\n kind: \"linear-gradient\";\n angle_deg?: number;\n stops: FillStop[];\n opacity?: number;\n }\n | {\n kind: \"radial-gradient\";\n center?: { x: number; y: number };\n radius?: number;\n stops: FillStop[];\n opacity?: number;\n };\n\nlet gradientIdSeq = 0;\nfunction nextGradientId(): string {\n gradientIdSeq = (gradientIdSeq + 1) % 1_000_000;\n return `lumen-grad-${gradientIdSeq.toString(36)}`;\n}\n\nexport interface FillRenderResult {\n /** SVG <defs> contributions (gradient definitions). */\n defs: ReactElement[];\n /** Reference to use as the `fill` attribute on the shape. */\n ref: string;\n}\n\n/** Compile a Fill into an SVG `<defs>` entry + a `fill=\"url(#…)\"` ref.\n * Solid fills produce no defs and return the colour directly. */\nexport function renderFill(fill: Fill): FillRenderResult {\n if (fill.kind === \"solid\") {\n // Solid fill — no defs needed, just hand the colour to fill.\n // SVG fill-opacity composes with element opacity multiplicatively\n // so we apply both consistently.\n return { defs: [], ref: fill.color };\n }\n const id = nextGradientId();\n if (fill.kind === \"linear-gradient\") {\n // angle_deg : 0 = bottom-to-top per §4.12 (matches CSS `linear-gradient`)\n const angle = fill.angle_deg ?? 0;\n // Translate angle (degrees from up) to SVG x1/y1/x2/y2 in user space.\n const rad = ((angle - 90) * Math.PI) / 180; // 0° → x1=0,y1=1 (bottom-up)\n const x1 = 0.5 - 0.5 * Math.cos(rad);\n const y1 = 0.5 - 0.5 * Math.sin(rad);\n const x2 = 0.5 + 0.5 * Math.cos(rad);\n const y2 = 0.5 + 0.5 * Math.sin(rad);\n const defs = [\n <linearGradient\n key={id}\n id={id}\n x1={`${x1 * 100}%`}\n y1={`${y1 * 100}%`}\n x2={`${x2 * 100}%`}\n y2={`${y2 * 100}%`}\n >\n {fill.stops.map((s, i) => (\n <stop\n key={i}\n offset={s.offset}\n stopColor={s.color}\n {...(s.opacity !== undefined ? { stopOpacity: s.opacity } : {})}\n />\n ))}\n </linearGradient>,\n ];\n return { defs, ref: `url(#${id})` };\n }\n // radial-gradient\n const cx = fill.center?.x ?? 0.5;\n const cy = fill.center?.y ?? 0.5;\n const r = fill.radius ?? 0.5;\n const defs = [\n <radialGradient key={id} id={id} cx={`${cx * 100}%`} cy={`${cy * 100}%`} r={`${r * 100}%`}>\n {fill.stops.map((s, i) => (\n <stop\n key={i}\n offset={s.offset}\n stopColor={s.color}\n {...(s.opacity !== undefined ? { stopOpacity: s.opacity } : {})}\n />\n ))}\n </radialGradient>,\n ];\n return { defs, ref: `url(#${id})` };\n}\n\n/** Compile an array of Fill into a CSS `background-image` value usable\n * on a `<div>` (frame backgrounds — non-SVG context). Returns the CSS\n * string + opacity. Stops use percentages in CSS gradient syntax. */\nexport function backgroundsToCss(fills: Fill[], nodeId?: string): CSSProperties {\n // Per §4.12, fills[0] renders on top — CSS background-image stacks\n // first → top-most. Match by passing in the same order.\n const layers = fills.map((f) => fillToCss(f, nodeId)).filter(Boolean) as string[];\n if (layers.length === 0) return {};\n return { backgroundImage: layers.join(\", \") };\n}\n\nfunction fillToCss(fill: Fill, nodeId?: string): string | null {\n // RC#11 — every colour interpolated into an inline CSS string MUST\n // pass the strict parser first (fills/stops arrive from untrusted\n // bundles AND live LSDP deltas). A rejected colour drops the whole\n // layer : never passthrough, never a half-built gradient.\n if (fill.kind === \"solid\") {\n const color = parseCssColor(fill.color);\n if (color === null) {\n warnRejectedColor(\"fill.color\", nodeId);\n return null;\n }\n // Wrap solid in linear-gradient so it can stack with other layers.\n return `linear-gradient(${color}, ${color})`;\n }\n const safeStops: string[] = [];\n for (const s of fill.stops) {\n const color = parseCssColor(s.color);\n if (color === null) {\n warnRejectedColor(\"fill.stops.color\", nodeId);\n return null;\n }\n const c = s.opacity !== undefined ? cssWithOpacity(color, s.opacity) : color;\n safeStops.push(`${c} ${(s.offset * 100).toFixed(2)}%`);\n }\n const stops = safeStops.join(\", \");\n if (fill.kind === \"linear-gradient\") {\n const angle = fill.angle_deg ?? 0;\n return `linear-gradient(${angle}deg, ${stops})`;\n }\n // radial-gradient\n const cx = (fill.center?.x ?? 0.5) * 100;\n const cy = (fill.center?.y ?? 0.5) * 100;\n return `radial-gradient(circle at ${cx}% ${cy}%, ${stops})`;\n}\n\n/** Apply a stop opacity to an ALREADY-VALIDATED colour (callers must\n * have run `parseCssColor` first — fillToCss is the single entry).\n * For 6-digit hex we append the alpha byte ; every other accepted\n * form goes through color-mix, which is safe because the interpolated\n * string can only be a strict-grammar colour (RC#11 fix : this used\n * to interpolate the raw, unparsed input). */\nfunction cssWithOpacity(color: string, opacity: number): string {\n const hex = color.match(/^#([0-9a-f]{6})$/i);\n if (hex) {\n const a = Math.round(opacity * 255)\n .toString(16)\n .padStart(2, \"0\");\n return `#${hex[1]}${a}`;\n }\n return `color-mix(in srgb, ${color} ${opacity * 100}%, transparent)`;\n}\n\n/** Validate every colour carried by a Fill array through the strict\n * parser (RC#11 — issue #30 contractual comment : SVG `fill`/`stroke`\n * attributes and `<stop stop-color>` are injection sites too, since\n * fills arrive from untrusted bundles AND live LSDP deltas). A fill\n * whose solid colour — or ANY gradient stop colour — is rejected drops\n * the whole layer with a diagnostic : never passthrough, never a\n * half-built gradient. Returned fills carry canonicalised colours. */\nexport function sanitizeFills(fills: Fill[], field: string, nodeId?: string): Fill[] {\n const out: Fill[] = [];\n for (const fill of fills) {\n if (fill.kind === \"solid\") {\n const color = parseCssColor(fill.color);\n if (color === null) {\n warnRejectedColor(`${field}.color`, nodeId);\n continue;\n }\n out.push({ ...fill, color });\n continue;\n }\n const stops: FillStop[] = [];\n let rejected = false;\n for (const s of fill.stops ?? []) {\n const color = parseCssColor(s.color);\n if (color === null) {\n warnRejectedColor(`${field}.stops.color`, nodeId);\n rejected = true;\n break;\n }\n stops.push({ ...s, color });\n }\n if (rejected) continue;\n out.push({ ...fill, stops });\n }\n return out;\n}\n\n/** Coerce loose JSON into a Fill array. Returns [] for non-arrays.\n * A structurally-valid fill entry whose `kind` is not renderable by\n * this runtime (e.g. `angular-gradient` / `diamond-gradient`, promoted\n * to core by the LSML 1.2 RFC) is dropped WITH a diagnostic — never\n * silently (ADR 001 §3.4, issue #34). */\nexport function parseFills(value: unknown, field?: string, nodeId?: string): Fill[] {\n if (!Array.isArray(value)) return [];\n if (field !== undefined) {\n for (const v of value) {\n if (!isFill(v)) {\n emitDiagnostic(\n nodeId,\n `${field}.kind`,\n \"fill kind is not renderable by this runtime ; layer dropped (angular/diamond gradients land with LSML 1.2)\",\n );\n }\n }\n }\n return value.filter(isFill) as Fill[];\n}\n\nfunction isFill(v: unknown): v is Fill {\n if (typeof v !== \"object\" || v === null) return false;\n const k = (v as { kind?: unknown }).kind;\n return k === \"solid\" || k === \"linear-gradient\" || k === \"radial-gradient\";\n}\n","import { motion } from \"framer-motion\";\nimport type { CSSProperties } from \"react\";\nimport type { PrimitiveProps } from \"./index\";\nimport { toFramer, mountPlay, resolveTransition } from \"../../animate/transitions\";\nimport { backgroundsToCss, parseFills } from \"../fill\";\nimport { parseCssColor, warnRejectedColor } from \"../css-color\";\nimport { emitDiagnostic } from \"../diagnostics\";\n\n/** Absolute-positioned container with size + transform + opacity.\n * Animatable on `transform` and `opacity` only — width/height/position\n * changes are intentionally *not* animatable to keep the broadcast\n * off the layout path.\n *\n * LSML 1.1 §4.3 + §4.12 add `backgrounds[]` as an alternative to the\n * legacy `background` (single color). The array form supports stacked\n * fills with linear / radial gradients ; first entry renders on top.\n *\n * LSML 1.1 §4.3 `clipsContent` (default `true`) clips children outside\n * the frame's bounds via `overflow: hidden` (ADR 001 §3.2.5, RC#5).\n */\nexport function Frame({\n resolved,\n nodeId,\n transitionFor,\n animateInitial,\n children,\n}: PrimitiveProps) {\n const x = numberOr(resolved.x, 0);\n const y = numberOr(resolved.y, 0);\n const width = sizeProp(resolved.width);\n const height = sizeProp(resolved.height);\n const opacity = numberOr(resolved.opacity, 1);\n const scale = numberOr(resolved.scale, 1);\n const rotate = numberOr(resolved.rotate, 0);\n\n // 1.0 single-fill prop — used as fallback when 1.1 `backgrounds[]`\n // is empty. RC#11 : the value is untrusted (static prop OR live LSDP\n // delta) and lands in inline CSS — strict-parse, never passthrough.\n const rawBackground = resolved.background;\n const legacyBackground = rawBackground === undefined ? undefined : parseCssColor(rawBackground);\n if (rawBackground !== undefined && legacyBackground === null) {\n warnRejectedColor(\"frame.background\", nodeId);\n }\n const backgrounds = parseFills(resolved.backgrounds, \"frame.backgrounds\", nodeId);\n const clipsContent = resolveClipsContent(resolved.clipsContent, nodeId);\n\n // Pick the most expressive declared transition among the animated\n // bindings (transform / opacity). If none, no animation.\n const tx = resolveTransition(\n transitionFor,\n [\"opacity\", \"scale\", \"rotate\", \"x\", \"y\"],\n animateInitial,\n );\n\n const style: CSSProperties = {\n position: \"absolute\",\n left: 0,\n top: 0,\n width,\n height,\n willChange: \"transform, opacity\",\n // LSML 1.1 §4.3 `clipsContent` (default `true`) — children outside\n // the frame's `size` are clipped. Static layout property : it never\n // animates, so it stays off the 0-layout-event hot path (ADR 001\n // §3.2.5). `false` => omit the declaration (CSS initial = visible).\n ...(clipsContent ? { overflow: \"hidden\" } : {}),\n };\n if (backgrounds.length > 0) {\n Object.assign(style, backgroundsToCss(backgrounds, nodeId));\n } else if (legacyBackground !== undefined && legacyBackground !== null) {\n style.background = legacyBackground;\n }\n\n const play = mountPlay({ opacity, x, y, scale, rotate }, animateInitial, nodeId);\n\n return (\n <motion.div\n style={style}\n initial={play.initial}\n animate={play.animate}\n transition={toFramer(tx)}\n >\n {children}\n </motion.div>\n );\n}\n\n/**\n * Resolve `clipsContent` (LSML 1.1 §4.3, schema default `true`).\n *\n * The prop is wire-drivable (static bundle prop OR live LSDP delta via\n * `resolveProps`, tree.tsx), so a non-boolean is treated as hostile :\n * R9 diagnostic (value withheld) + fall back to the spec default\n * (`true`, i.e. clipped — the safe state for broadcast). The returned\n * value only ever selects between two literal style fragments — no\n * untrusted value can reach inline CSS through this path (RC#11 by\n * construction). Exported for boundary testing.\n */\nexport function resolveClipsContent(v: unknown, nodeId?: string): boolean {\n if (v === undefined) return true;\n if (typeof v === \"boolean\") return v;\n emitDiagnostic(nodeId, \"frame.clipsContent\", \"rejected value : not a boolean\");\n return true;\n}\n\nfunction numberOr(v: unknown, fallback: number): number {\n return typeof v === \"number\" && Number.isFinite(v) ? v : fallback;\n}\n\nfunction sizeProp(v: unknown): number | string | undefined {\n if (typeof v === \"number\" && Number.isFinite(v)) return v;\n if (typeof v === \"string\" && v.length > 0) return v;\n return undefined;\n}\n","import { motion } from \"framer-motion\";\nimport type { PrimitiveProps } from \"./index\";\nimport { toFramer, mountPlay, resolveTransition } from \"../../animate/transitions\";\nimport { parseCssColor, warnRejectedColor } from \"../css-color\";\nimport { emitDiagnostic } from \"../diagnostics\";\n\n// ── Typography grammars (LSML 1.1 TextStyle, schema.json) ───────────\n// Every typo prop is wire-drivable (static bundle prop OR live LSDP\n// delta via `resolveProps`, tree.tsx) and lands in inline CSS, so each\n// value is validated against the field's spec'd grammar before it may\n// reach the style object. Enum fields go through a closed allowlist\n// (the emitted string is always one of these constants — never the\n// input), numeric fields through finite-number checks. There is NO\n// string passthrough on any of these sites (ADR 001 RC#11 by\n// construction : no untrusted string ever reaches the style object).\nconst TEXT_TRANSFORMS = new Set([\"none\", \"uppercase\", \"lowercase\", \"capitalize\"]);\nconst TEXT_DECORATIONS = new Set([\"none\", \"underline\", \"line-through\"]);\nconst FONT_STYLES = new Set([\"normal\", \"italic\", \"oblique\"]);\n\n// ── Defence-in-depth upper bounds (issue #34, Bastion follow-up on\n// PR #38) ─────────────────────────────────────────────────────────────\n// The numeric typo fields were type-validated but unbounded ; an absurd\n// value pushed by a hostile bundle or live delta could degrade layout /\n// rendering (e.g. a 10⁹-line clamp or a kilometric letter spacing).\n// Policy : a value beyond its cap is REJECTED (diagnostic + omit → CSS\n// initial), consistent with the existing typo grammar gates — NOT\n// clamped, unlike the R8 filter caps where the spec explicitly blesses\n// clamping. Rationale : there is no \"nearest sensible rendering\" for an\n// absurd typographic value, the author's intent is unknowable ; safe\n// default beats silently-altered output.\n/** Max `maxLines` accepted (Bastion suggested ≤ 1000 on PR #38). */\nexport const MAX_MAX_LINES = 1000;\n/** Max unitless `lineHeight` multiplier (100× the font size is already\n * far beyond any broadcast layout). */\nexport const MAX_LINE_HEIGHT = 100;\n/** Max |letterSpacing| in px, both directions (±1000 px covers any\n * legitimate broadcast typography). */\nexport const MAX_LETTER_SPACING_PX = 1000;\n\n// ── fontFamily policy (issue #34, Bastion follow-up on PR #38) ───────\n// Decision : SHAPE validation, not a font allowlist. `fontFamily` is\n// assigned through the React style object (per-property assignment via\n// CSSStyleDeclaration), which cannot break out of the declaration — so\n// the residual risk is malformed CSS, not injection. A font allowlist\n// would couple the runtime to a host-specific font inventory (a spec /\n// RFC matter — flagged for Atlas in the PR), whereas shape validation\n// keeps any legitimate family name working. The grammar accepts\n// comma-separated family lists with optional quotes ; the injection\n// metacharacters (`;` `}` `{` `:` `\\` `<` `>` `(` `)`) and `url(` are\n// rejected by construction (none of their characters are allowed).\n// Anchored, single character class with a bounded quantifier — linear\n// time (RC#12).\nconst FONT_FAMILY_RE = /^[a-zA-Z0-9 ,.'\"_-]{1,256}$/;\n\n/** Validate an untrusted `fontFamily` value. Returns the string or\n * `null` on rejection (handled as \"omit → inherit\", with diagnostic). */\nexport function parseFontFamily(value: unknown): string | null {\n if (typeof value !== \"string\") return null;\n const v = value.trim();\n if (v.length === 0) return null;\n return FONT_FAMILY_RE.test(v) ? v : null;\n}\n\n/** Text leaf. Value renders as the displayed string ; style props\n * cover the full LSML TextStyle (size / font / weight / colour /\n * alignment / lineHeight / letterSpacing / textTransform /\n * textDecoration / fontStyle) plus `maxLines` (§4.4 ellipsis\n * truncation). Opacity is animated when a transition is declared on\n * `opacity` or `value`. An `animate.from` makes it mount-play\n * (initial → target) on mount. */\nexport function Text({ resolved, nodeId, transitionFor, animateInitial }: PrimitiveProps) {\n const value = resolved.value === undefined ? \"\" : String(resolved.value);\n const size = (resolved.size as string | number | undefined) ?? \"1rem\";\n const weight = (resolved.weight as number | undefined) ?? 400;\n // Issue #34 — `font` is untrusted and lands in inline CSS : shape-\n // validate (see fontFamily policy above) ; rejected → inherit.\n let font: string | undefined;\n if (resolved.font !== undefined) {\n const parsed = parseFontFamily(resolved.font);\n if (parsed === null) {\n emitDiagnostic(nodeId, \"text.font\", \"rejected fontFamily : outside the family-list grammar\");\n } else {\n font = parsed;\n }\n }\n // RC#11 : `colour` is untrusted (static prop OR live LSDP delta) and\n // lands in inline CSS — strict-parse ; rejected → safe default.\n let colour = \"currentColor\";\n if (resolved.colour !== undefined) {\n const parsed = parseCssColor(resolved.colour);\n if (parsed === null) {\n warnRejectedColor(\"text.colour\", nodeId);\n } else {\n colour = parsed;\n }\n }\n const align = (resolved.align as string | undefined) ?? \"start\";\n const opacity = numberOr(resolved.opacity, 1);\n const typography = resolveTypography(resolved, nodeId);\n\n const tx = resolveTransition(transitionFor, [\"opacity\", \"value\"], animateInitial);\n const play = mountPlay({ opacity }, animateInitial, nodeId);\n\n return (\n <motion.span\n style={{\n display: \"inline-block\",\n fontSize: size,\n // `font` carries LSML text.style.fontFamily (spec'd in schema.json).\n // Omitted => inherit the host/container font.\n ...(font !== undefined ? { fontFamily: font } : {}),\n fontWeight: weight,\n color: colour,\n textAlign: align as React.CSSProperties[\"textAlign\"],\n ...typography,\n willChange: \"opacity, transform\",\n }}\n initial={play.initial}\n animate={play.animate}\n transition={toFramer(tx)}\n >\n {value}\n </motion.span>\n );\n}\n\nfunction numberOr(v: unknown, fallback: number): number {\n return typeof v === \"number\" && Number.isFinite(v) ? v : fallback;\n}\n\n/**\n * Resolve the LSML 1.1 TextStyle typography props (`lineHeight`,\n * `letterSpacing`, `textTransform`, `textDecoration`, `fontStyle`) and\n * `maxLines` (§4.4) into a validated React style fragment.\n *\n * Exported for boundary testing : happy-dom drops `-webkit-*`\n * declarations from `CSSStyleDeclaration`, so the line-clamp pattern is\n * asserted on the exact object handed to React's inline style (same\n * approach as `backgroundsToCss` for `color-mix`).\n *\n * Defaults = omit the declaration (inherit / CSS initial). A\n * non-conforming value → R9 diagnostic + omit ; the returned object\n * only ever contains allowlisted constants or validated finite\n * numbers — never the raw input. Numeric fields additionally enforce\n * the defence-in-depth caps above (issue #34).\n */\nexport function resolveTypography(\n resolved: Record<string, unknown>,\n nodeId?: string,\n): React.CSSProperties {\n // schema.json : lineHeight is a unitless multiplier ≥ 0 ;\n // letterSpacing is a number (px) ; the three enums are closed sets.\n const lineHeight = boundedNumber(\n resolved.lineHeight,\n 0,\n MAX_LINE_HEIGHT,\n \"text.lineHeight\",\n nodeId,\n );\n const letterSpacing = boundedNumber(\n resolved.letterSpacing,\n -MAX_LETTER_SPACING_PX,\n MAX_LETTER_SPACING_PX,\n \"text.letterSpacing\",\n nodeId,\n );\n const textTransform = enumValue(\n resolved.textTransform,\n TEXT_TRANSFORMS,\n \"text.textTransform\",\n nodeId,\n );\n const textDecoration = enumValue(\n resolved.textDecoration,\n TEXT_DECORATIONS,\n \"text.textDecoration\",\n nodeId,\n );\n const fontStyle = enumValue(resolved.fontStyle, FONT_STYLES, \"text.fontStyle\", nodeId);\n // §4.4 maxLines — truncation with ellipsis after N lines, via the\n // standard line-clamp pattern (display:-webkit-box overrides the\n // base inline-block ; this fragment is spread after it so it wins).\n const maxLines = positiveInteger(resolved.maxLines, MAX_MAX_LINES, \"text.maxLines\", nodeId);\n\n return {\n ...(lineHeight !== undefined ? { lineHeight } : {}),\n // Built from a validated finite number — no string passthrough.\n ...(letterSpacing !== undefined ? { letterSpacing: `${letterSpacing}px` } : {}),\n ...(textTransform !== undefined\n ? { textTransform: textTransform as React.CSSProperties[\"textTransform\"] }\n : {}),\n ...(textDecoration !== undefined ? { textDecoration } : {}),\n ...(fontStyle !== undefined ? { fontStyle } : {}),\n ...(maxLines !== undefined\n ? {\n display: \"-webkit-box\",\n WebkitBoxOrient: \"vertical\" as const,\n WebkitLineClamp: maxLines,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n }\n : {}),\n };\n}\n\n/** Closed-allowlist enum gate. Returns the canonical constant from the\n * allowlist (NEVER the raw input) or `undefined` (field omitted →\n * CSS initial). Non-conforming value → R9 diagnostic, no passthrough. */\nfunction enumValue(\n v: unknown,\n allow: ReadonlySet<string>,\n field: string,\n nodeId?: string,\n): string | undefined {\n if (v === undefined) return undefined;\n if (typeof v === \"string\" && allow.has(v)) return v;\n warnRejectedTypo(field, nodeId);\n return undefined;\n}\n\n/** Finite number within [min, max] or omit (R9 diagnostic on a\n * non-conforming or out-of-cap input — rejected, never clamped). */\nfunction boundedNumber(\n v: unknown,\n min: number,\n max: number,\n field: string,\n nodeId?: string,\n): number | undefined {\n if (v === undefined) return undefined;\n if (typeof v === \"number\" && Number.isFinite(v) && v >= min && v <= max) return v;\n warnRejectedTypo(field, nodeId);\n return undefined;\n}\n\n/** Integer in [1, max] or omit (schema : maxLines is a line count ;\n * capped per the issue #34 defence-in-depth bounds). */\nfunction positiveInteger(\n v: unknown,\n max: number,\n field: string,\n nodeId?: string,\n): number | undefined {\n if (v === undefined) return undefined;\n if (typeof v === \"number\" && Number.isInteger(v) && v >= 1 && v <= max) return v;\n warnRejectedTypo(field, nodeId);\n return undefined;\n}\n\n/**\n * Diagnostic for a typo value outside its spec'd grammar or caps.\n * Bastion R9 (ADR 001 §5.1) : the rejected VALUE is never logged nor\n * forwarded — only `node.id` (RC#7), the field name and a static\n * reason. Routed through the structured diagnostics channel.\n */\nfunction warnRejectedTypo(field: string, nodeId?: string): void {\n emitDiagnostic(\n nodeId,\n field,\n \"rejected typography value : outside the field's spec'd grammar or caps\",\n );\n}\n","import { motion } from \"framer-motion\";\nimport type { PrimitiveProps } from \"./index\";\nimport { toFramer, mountPlay, resolveTransition } from \"../../animate/transitions\";\n\n/** Image leaf. `src`, `fit` (cover/contain/fill), `position`,\n * `opacity`. Opacity is animated when a transition is declared. When an\n * `animate.from` is lowered onto the node, it mounts at that state and\n * plays to its target on mount (mount-play). */\nexport function Image({ resolved, nodeId, transitionFor, animateInitial }: PrimitiveProps) {\n const src = resolved.src as string | undefined;\n if (!src) return null;\n // LSML §4.5 `alt` is required and was silently unrendered until\n // issue #34's allowlist audit surfaced it — now forwarded to the DOM.\n const alt = typeof resolved.alt === \"string\" ? resolved.alt : \"\";\n const fit = (resolved.fit as string | undefined) ?? \"contain\";\n const position = (resolved.position as string | undefined) ?? \"center\";\n const opacity = numberOr(resolved.opacity, 1);\n // `width`/`height` carry LSML image.size (compiler maps size.w/.h → width/height).\n // When present, honour the intrinsic image dimensions; otherwise fill the\n // container (the prior behaviour — a sized parent drives the layout).\n const width = dimOr(resolved.width, \"100%\");\n const height = dimOr(resolved.height, \"100%\");\n\n const tx = resolveTransition(transitionFor, [\"opacity\", \"src\"], animateInitial);\n const play = mountPlay({ opacity }, animateInitial, nodeId);\n\n return (\n <motion.img\n src={src}\n alt={alt}\n style={{\n objectFit: fit as React.CSSProperties[\"objectFit\"],\n objectPosition: position,\n width,\n height,\n willChange: \"opacity, transform\",\n }}\n initial={play.initial}\n animate={play.animate}\n transition={toFramer(tx)}\n draggable={false}\n />\n );\n}\n\nfunction numberOr(v: unknown, fallback: number): number {\n return typeof v === \"number\" && Number.isFinite(v) ? v : fallback;\n}\n\n/** A render dimension: a finite number → px, a non-empty string → verbatim\n * (e.g. \"100%\"), anything else → the fallback. */\nfunction dimOr(v: unknown, fallback: string): string {\n if (typeof v === \"number\" && Number.isFinite(v)) return `${v}px`;\n if (typeof v === \"string\" && v.length > 0) return v;\n return fallback;\n}\n","// Strict SVG path-data validator — the ONLY gate through which\n// untrusted `d` strings (bundle props AND live LSDP deltas, see\n// `resolveProps` in tree.tsx) may reach a rendered `<path d>` attribute.\n//\n// ADR 001 §6 RC#10 (allowlisted `d` grammar) + RC#12 (anti-ReDoS),\n// threat model Bastion 2026-06-10, issue #30. Validation runs at EVERY\n// render — props are wire-drivable, so a hostile delta arriving after\n// mount goes through the exact same gate as a static prop.\n//\n// Accepted grammar (SVG path data, LSML 1.1 §4.6, nothing more) :\n// - command letters : M m L l H h V v C c S s Q q T t A a Z z\n// - numbers : [+-]? digits [. digits]? ([eE] [+-]? digits)?\n// - separators : space, tab, CR, LF, comma\n// Anything else — including `url(`, `data:`, `<`, `&`, parentheses,\n// quotes, semicolons, braces — is REJECTED (null). Never passthrough.\n//\n// ── Linear-time justification (RC#12, written per Bastion) ──────────\n// 1. Inputs longer than MAX_SUBPATH_LEN (8 KiB) are rejected on a\n// single O(1) length check before any scanning — a 10⁶-command\n// payload never reaches the scanner.\n// 2. The scanner is a hand-written single forward pass : every loop\n// iteration advances `i` by at least one character and there is no\n// regex anywhere, so total work is O(min(n, 8192)) per value\n// regardless of payload shape. No backtracking is possible by\n// construction.\n// 3. The command cap (MAX_SUBPATH_COMMANDS) additionally bounds the\n// number of path segments the SVG engine will ever be asked to\n// tessellate, independently of string length.\n// ─────────────────────────────────────────────────────────────────────\n\nimport { emitDiagnostic } from \"./diagnostics\";\n\n/** RC#10 — hard cap : 8 KiB per subpath `d` string. */\nexport const MAX_SUBPATH_LEN = 8192;\n/**\n * RC#10 — hard cap on commands per subpath. Aligned on the compiler's\n * MAX_PATH_COMMANDS = 4000 (PR #39, deliberate authoring cap) so a live\n * LSDP delta can never carry more commands than the authoring pipeline\n * accepts. A shared constant module is tracked in issue #41 — do not\n * diverge these two values in the meantime.\n */\nexport const MAX_SUBPATH_COMMANDS = 4000;\n/** RC#10 — hard cap on subpaths per shape. */\nexport const MAX_SUBPATHS = 64;\n\nconst CMD_CHARS = new Set(\"MmLlHhVvCcSsQqTtAaZz\");\n\nfunction isDigit(code: number): boolean {\n return code >= 0x30 && code <= 0x39; // 0-9\n}\n\nfunction isSeparator(code: number): boolean {\n // space, tab, CR, LF, comma\n return code === 0x20 || code === 0x09 || code === 0x0d || code === 0x0a || code === 0x2c;\n}\n\n/**\n * Validate an untrusted SVG path `d` string against the strict grammar\n * above. Returns the validated (trimmed) string or `null` on rejection.\n * A `null` MUST be handled as \"omit the subpath + emit a diagnostic\" —\n * never interpolate the raw input into the DOM.\n */\nexport function validatePathData(value: unknown): string | null {\n if (typeof value !== \"string\") return null;\n // O(1) length gate BEFORE any per-character work (RC#12).\n if (value.length === 0 || value.length > MAX_SUBPATH_LEN) return null;\n const d = value.trim();\n if (d.length === 0) return null;\n\n // Contractual explicit rejects (Bastion RC#10) — redundant with the\n // allowlist scan below, kept as belt-and-braces so the contract\n // holds even if the grammar is ever extended.\n const lower = d.toLowerCase();\n if (lower.includes(\"url(\") || lower.includes(\"data:\")) return null;\n if (d.includes(\"<\") || d.includes(\"&\")) return null;\n\n // Single forward pass — tokenizes commands and numbers, rejects any\n // character outside the allowlist, counts commands.\n const n = d.length;\n let i = 0;\n let commands = 0;\n let sawCommand = false;\n while (i < n) {\n const code = d.charCodeAt(i);\n if (isSeparator(code)) {\n i++;\n continue;\n }\n const ch = d[i];\n if (CMD_CHARS.has(ch)) {\n // Path data must start with a moveto (M/m).\n if (!sawCommand && ch !== \"M\" && ch !== \"m\") return null;\n sawCommand = true;\n commands++;\n if (commands > MAX_SUBPATH_COMMANDS) return null;\n i++;\n continue;\n }\n // Anything else must be a well-formed number token — and numbers\n // can only appear after the leading moveto.\n if (!sawCommand) return null;\n if (ch === \"+\" || ch === \"-\") i++;\n let digits = 0;\n while (i < n && isDigit(d.charCodeAt(i))) {\n i++;\n digits++;\n }\n if (i < n && d[i] === \".\") {\n i++;\n let fracDigits = 0;\n while (i < n && isDigit(d.charCodeAt(i))) {\n i++;\n fracDigits++;\n }\n // A token ending in a bare \".\" (empty decimal part, e.g. \"1.\")\n // must not be immediately followed by another \".\" — \"1..2\" is not\n // valid SVG path data and would otherwise be mis-scanned as the\n // two tokens \"1.\" + \".2\" (Probe bug report, issue #30).\n if (fracDigits === 0 && i < n && d[i] === \".\") return null;\n digits += fracDigits;\n }\n if (digits === 0) return null; // bare sign / dot / forbidden char\n if (i < n && (d[i] === \"e\" || d[i] === \"E\")) {\n i++;\n if (i < n && (d[i] === \"+\" || d[i] === \"-\")) i++;\n let expDigits = 0;\n while (i < n && isDigit(d.charCodeAt(i))) {\n i++;\n expDigits++;\n }\n if (expDigits === 0) return null;\n }\n }\n if (commands === 0) return null;\n return d;\n}\n\nexport interface SubPath {\n d: string;\n fillRule: \"nonzero\" | \"evenodd\";\n}\n\n/**\n * Resolve a shape's `pathData` / `paths[]` props (LSML 1.1 §4.6) into\n * validated subpaths ready for rendering — one `<path>` element per\n * entry (ADR 001 §3.2.3). `pathData` is equivalent to\n * `paths: [{ data: pathData, windingRule: \"NONZERO\" }]`.\n *\n * Re-runs at every render (RC#10 — props are live via LSDP deltas).\n * Every rejected or unrendered field emits a diagnostic (ADR 001 §3.4,\n * anti-silent-drop) that NEVER contains the value (R9).\n */\nexport function parseShapePaths(resolved: Record<string, unknown>, nodeId?: string): SubPath[] {\n const rawPaths = resolved.paths;\n const rawPathData = resolved.pathData;\n\n if (Array.isArray(rawPaths)) {\n if (rawPathData !== undefined) {\n // Spec §4.6 : mutually exclusive. Tolerate with paths[] winning\n // (mirrors the fills[]/fill precedence), but never silently.\n warnPath(nodeId, \"shape.pathData\", \"mutually exclusive with paths[] ; paths[] wins\");\n }\n const out: SubPath[] = [];\n for (let idx = 0; idx < rawPaths.length; idx++) {\n if (out.length >= MAX_SUBPATHS) {\n warnPath(nodeId, \"shape.paths\", \"subpath cap exceeded ; remaining entries dropped\");\n break;\n }\n const entry = rawPaths[idx] as { data?: unknown; windingRule?: unknown } | null;\n const d = validatePathData(\n typeof entry === \"object\" && entry !== null ? entry.data : undefined,\n );\n if (d === null) {\n warnPath(nodeId, \"shape.paths.data\", \"not a strict SVG path grammar (allowlist/caps)\");\n continue;\n }\n out.push({ d, fillRule: toFillRule(entry?.windingRule, nodeId) });\n }\n if (out.length === 0 && rawPaths.length > 0) {\n warnPath(nodeId, \"shape.paths\", \"no renderable subpath ; shape geometry omitted\");\n }\n return out;\n }\n\n if (rawPathData !== undefined) {\n const d = validatePathData(rawPathData);\n if (d === null) {\n warnPath(nodeId, \"shape.pathData\", \"not a strict SVG path grammar (allowlist/caps)\");\n return [];\n }\n return [{ d, fillRule: \"nonzero\" }];\n }\n\n // geometry:\"path\" with neither prop — spec'd field combination we\n // cannot render. Diagnostic, never a silent no-op (ADR 001 §3.4).\n warnPath(nodeId, \"shape.paths\", \"geometry is path but neither pathData nor paths[] is present\");\n return [];\n}\n\nfunction toFillRule(windingRule: unknown, nodeId?: string): \"nonzero\" | \"evenodd\" {\n if (windingRule === undefined || windingRule === \"NONZERO\") return \"nonzero\";\n if (windingRule === \"EVENODD\") return \"evenodd\";\n warnPath(nodeId, \"shape.paths.windingRule\", \"unknown winding rule ; defaulting to nonzero\");\n return \"nonzero\";\n}\n\n/**\n * Diagnostic for a rejected / unrendered path field. Bastion R9\n * (ADR 001 §5.1) : the rejected VALUE is never logged nor forwarded —\n * only `node.id` (RC#7, issue #34), the field name and a static reason.\n * Routed through the structured diagnostics channel.\n */\nfunction warnPath(nodeId: string | undefined, field: string, reason: string): void {\n emitDiagnostic(nodeId, field, reason);\n}\n","import { motion } from \"framer-motion\";\nimport type { ReactElement } from \"react\";\nimport type { PrimitiveProps } from \"./index\";\nimport { toFramer, mountPlay, resolveTransition } from \"../../animate/transitions\";\nimport { parseFills, renderFill, sanitizeFills } from \"../fill\";\nimport { parseCssColor, warnRejectedColor } from \"../css-color\";\nimport { parseShapePaths, type SubPath } from \"../svg-path\";\n\ninterface StrokeSpec {\n color?: string;\n width?: number;\n}\n\n/** Rectangle / circle / line / path. Renders as SVG so stroke + fill\n * behave predictably across hosts. Opacity animatable.\n *\n * LSML 1.1 §4.6 + §4.12 add `fills[]` / `strokes[]` arrays as the\n * preferred way to declare multi-layer fills with linear/radial\n * gradients. The legacy single `fill` / `stroke` props remain\n * accepted for 1.0 bundles ; when both are present the array form\n * wins (the spec forbids mixing, but we tolerate to ease migration).\n *\n * Security (ADR 001 §6 RC#10 + RC#11, issue #30) : every colour that\n * reaches an SVG `fill`/`stroke`/`stop-color` attribute goes through\n * the strict `parseCssColor` gate, and every path `d` goes through\n * `validatePathData` — at EVERY render, because props are wire-\n * drivable live via LSDP deltas (`resolveProps`, tree.tsx).\n */\nexport function Shape({ resolved, nodeId, transitionFor, animateInitial }: PrimitiveProps) {\n // Canonical prop name is `geometry` (LSML §4.6 — what the compiler\n // emits) ; `kind` is kept as a fallback for hand-rolled Solar-lineage\n // RenderNodes that predate the compiler.\n const kind =\n (resolved.geometry as string | undefined) ?? (resolved.kind as string | undefined) ?? \"rect\";\n const legacyFill = safeColor(resolved.fill, \"shape.fill\", nodeId) ?? \"transparent\";\n const legacyStroke = safeColor(resolved.stroke, \"shape.stroke\", nodeId) ?? \"transparent\";\n const legacyStrokeWidth = numberOr(resolved.stroke_width, 0);\n const width = numberOr(resolved.width, 100);\n const height = numberOr(resolved.height, 100);\n const radius = numberOr(resolved.radius, 0);\n const opacity = numberOr(resolved.opacity, 1);\n // LSML §4.6 `ariaLabel` was silently unrendered until issue #34's\n // allowlist audit surfaced it — now forwarded as the SVG label.\n const ariaLabel = typeof resolved.ariaLabel === \"string\" ? resolved.ariaLabel : undefined;\n\n const tx = resolveTransition(transitionFor, [\"opacity\"], animateInitial);\n const transition = toFramer(tx);\n const play = mountPlay({ opacity }, animateInitial, nodeId);\n\n // LSML 1.1 §4.6 — `fills[]` is the preferred multi-fill form. Fall\n // back to the singular `fill` for 1.0 bundles. Colours are strict-\n // validated (a rejected colour drops its layer, with diagnostic).\n const fills = sanitizeFills(\n parseFills(resolved.fills, \"shape.fills\", nodeId),\n \"shape.fills\",\n nodeId,\n );\n const strokes = parseStrokes(resolved.strokes);\n\n // LSML 1.1 §4.6 — `geometry:\"path\"` : validated subpaths, one\n // `<path>` element per entry (ADR 001 §3.2.3). Re-validated at every\n // render — see module header of svg-path.ts (RC#10).\n const subpaths = kind === \"path\" ? parseShapePaths(resolved, nodeId) : [];\n\n // Each fill compiles to a (defs, ref) pair. We render the shape\n // outline once per fill, layered top-to-bottom (first entry → on\n // top, per §4.12). The defs are aggregated for a single <defs>.\n const fillRenders = fills.map(renderFill);\n const allDefs = fillRenders.flatMap((r) => r.defs);\n const fillRefs = fillRenders.length > 0 ? fillRenders.map((r) => r.ref) : [legacyFill];\n\n // Strokes : same layered approach, but solid colours only (gradient\n // strokes are out of scope for §4.6 1.1). Each stroke is rendered\n // as an additional pass over the same shape outline.\n const strokeLayers =\n strokes.length > 0\n ? strokes.map((s) => ({\n color: safeColor(s.color, \"shape.strokes.color\", nodeId) ?? \"transparent\",\n width: s.width ?? 0,\n }))\n : [{ color: legacyStroke, width: legacyStrokeWidth }];\n\n // Stack order : fillRefs are emitted top-to-bottom per §4.12. SVG\n // paints later siblings on top, so we reverse here so the first\n // entry in fills[] ends up rendered last (visually on top).\n const stackedFills = [...fillRefs].reverse();\n const stackedStrokes = [...strokeLayers].reverse();\n // For paths, a zero-width / transparent stroke pass would only emit\n // invisible duplicate <path> elements — skip it.\n const effectiveStrokes =\n kind === \"path\"\n ? stackedStrokes.filter((s) => s.width > 0 && s.color !== \"transparent\")\n : stackedStrokes;\n\n const renderShape = (\n fill: string,\n stroke: { color: string; width: number },\n keyPrefix: string,\n ): ReactElement => {\n if (kind === \"path\") {\n // §4.6 — fills and strokes apply to the union of all subpaths ;\n // each subpath keeps its own winding rule (fill-rule).\n return (\n <g key={keyPrefix}>\n {subpaths.map((p: SubPath, i: number) => (\n <path\n key={i}\n d={p.d}\n fillRule={p.fillRule}\n fill={fill}\n stroke={stroke.color}\n strokeWidth={stroke.width}\n />\n ))}\n </g>\n );\n }\n if (kind === \"circle\") {\n return (\n <circle\n key={keyPrefix}\n cx={width / 2}\n cy={height / 2}\n r={Math.min(width, height) / 2 - stroke.width / 2}\n fill={fill}\n stroke={stroke.color}\n strokeWidth={stroke.width}\n />\n );\n }\n if (kind === \"line\") {\n return (\n <line\n key={keyPrefix}\n x1=\"0\"\n y1={height / 2}\n x2={width}\n y2={height / 2}\n stroke={stroke.color || fill}\n strokeWidth={stroke.width || 1}\n />\n );\n }\n // rect default\n return (\n <rect\n key={keyPrefix}\n x={stroke.width / 2}\n y={stroke.width / 2}\n width={Math.max(0, width - stroke.width)}\n height={Math.max(0, height - stroke.width)}\n rx={radius}\n ry={radius}\n fill={fill}\n stroke={stroke.color}\n strokeWidth={stroke.width}\n />\n );\n };\n\n return (\n <motion.svg\n width={width}\n height={height}\n viewBox={`0 0 ${width} ${height}`}\n {...(ariaLabel !== undefined ? { \"aria-label\": ariaLabel, role: \"img\" } : {})}\n initial={play.initial}\n animate={play.animate}\n transition={transition}\n style={{ willChange: \"opacity, transform\" }}\n >\n {allDefs.length > 0 && <defs>{allDefs}</defs>}\n {stackedFills.map((ref, i) =>\n renderShape(ref, { color: \"transparent\", width: 0 }, `fill-${i}`),\n )}\n {effectiveStrokes.map((s, i) => renderShape(\"none\", s, `stroke-${i}`))}\n </motion.svg>\n );\n}\n\n/** Strict-validate a colour prop (RC#11 — SVG attributes are injection\n * sites too once values are wire-drivable). Non-strings resolve to\n * null silently (absent prop) ; a string that fails the strict grammar\n * is rejected with a diagnostic (value withheld per R9). */\nfunction safeColor(value: unknown, field: string, nodeId?: string): string | null {\n if (typeof value !== \"string\") return null;\n const color = parseCssColor(value);\n if (color === null) warnRejectedColor(field, nodeId);\n return color;\n}\n\nfunction parseStrokes(value: unknown): StrokeSpec[] {\n if (!Array.isArray(value)) return [];\n return value.filter(\n (v): v is StrokeSpec => typeof v === \"object\" && v !== null && (\"color\" in v || \"width\" in v),\n );\n}\n\nfunction numberOr(v: unknown, fallback: number): number {\n return typeof v === \"number\" && Number.isFinite(v) ? v : fallback;\n}\n","import type { PrimitiveProps } from \"./index\";\n\n/** Embedded video. `src`, `loop`, `mute`, `autoplay`. Audio is muted\n * by default — broadcast audio is Pulsar-side, not from the browser\n * source. */\nexport function Media({ resolved }: PrimitiveProps) {\n const src = resolved.src as string | undefined;\n if (!src) return null;\n const loop = (resolved.loop as boolean | undefined) ?? true;\n const mute = (resolved.mute as boolean | undefined) ?? true;\n const autoplay = (resolved.autoplay as boolean | undefined) ?? true;\n const fit = (resolved.fit as string | undefined) ?? \"cover\";\n\n return (\n <video\n src={src}\n autoPlay={autoplay}\n loop={loop}\n muted={mute}\n playsInline\n style={{\n width: \"100%\",\n height: \"100%\",\n objectFit: fit as React.CSSProperties[\"objectFit\"],\n }}\n />\n );\n}\n","// LSML 1.1 §4.9 — `instance` primitive (composite-instance reuse).\n//\n// Mounts a sub-scene by `scene_id` + `scene_version`. The sub-scene's\n// state is exposed to its tree under the `__params.*` reserved\n// namespace ; resolution happens via the runtime's bundle fetcher.\n//\n// This implementation is a SCAFFOLD : the visual slot is rendered\n// (size/position honoured) but the sub-tree is replaced by a\n// \"deferred load\" placeholder until the async bundle-fetch path is\n// wired. The composite-reuse rendering is the next iteration's work.\n//\n// What this primitive does today :\n// - parse scene_id, scene_version, params, fit, size, position\n// - reserve the slot in the parent layout\n// - log a one-time warning so authors know it's a scaffold\n//\n// What it does NOT do (yet) :\n// - fetch the inner bundle via the runtime's bundle resolver\n// - render the inner tree with __params.* injected into the store\n// - cycle detection (LSML 1.1 §4.9.2) — depth-8 limit applied at the\n// resolver layer rather than the renderer\n\nimport type { ReactElement } from \"react\";\nimport type { PrimitiveProps } from \"./index\";\nimport { emitDiagnostic } from \"../diagnostics\";\n\nconst warned = new Set<string>();\n\nexport function Instance({ resolved, nodeId }: PrimitiveProps): ReactElement | null {\n const sceneId = resolved.scene_id as string | undefined;\n const sceneVersion = resolved.scene_version as string | undefined;\n if (!sceneId || !sceneVersion) {\n // Structured diagnostic — never dump `resolved` (R9 : prop values,\n // including bound params, must not transit a diagnostic channel).\n emitDiagnostic(nodeId, \"instance.scene_id\", \"missing scene_id or scene_version ; not rendered\");\n return null;\n }\n\n // One-time diagnostic per (sceneId,version) so authors know the\n // scaffold limitation.\n const key = `${sceneId}:${sceneVersion}`;\n if (!warned.has(key)) {\n warned.add(key);\n emitDiagnostic(\n nodeId,\n \"instance\",\n \"scaffold render — async bundle fetch + __params.* injection are not yet wired (LSML 1.1 §4.9)\",\n );\n }\n\n const size = resolved.size as { w?: number; h?: number } | undefined;\n const position = resolved.position as { x?: number; y?: number } | undefined;\n\n return (\n <div\n data-lumencast-instance={sceneId}\n data-lumencast-version={sceneVersion}\n style={{\n position: position ? \"absolute\" : \"relative\",\n left: position?.x,\n top: position?.y,\n width: size?.w,\n height: size?.h,\n outline: import.meta.env.DEV ? \"1px dashed rgba(255,180,0,0.5)\" : \"none\",\n boxSizing: \"border-box\",\n }}\n />\n );\n}\n","// Primitive component registry. Tree dispatch uses this map to look\n// up the React component for each `kind` ; user components are inlined\n// at compile time so Lumencast's runtime never sees them.\n\nimport type { ComponentType, ReactNode } from \"react\";\nimport type { RenderKind } from \"../bundle\";\nimport type { Transition } from \"../../animate/transitions\";\nimport { Stack } from \"./stack\";\nimport { Grid } from \"./grid\";\nimport { Frame } from \"./frame\";\nimport { Text } from \"./text\";\nimport { Image } from \"./image\";\nimport { Shape } from \"./shape\";\nimport { Media } from \"./media\";\nimport { Instance } from \"./instance\";\n// `repeat` is dispatched specially in the tree (it iterates a bound\n// array and provides a path scope to its children) ; it does not\n// appear here as a regular primitive.\n\nexport interface PrimitiveProps {\n resolved: Record<string, unknown>;\n /** `RenderNode.id` of the node being rendered — threaded into every\n * diagnostic the primitive emits (ADR 001 RC#7, issue #34). */\n nodeId?: string;\n transitionFor: (key: string) => Transition | undefined;\n /** LSML 1.1 `animate.from` lowered to a flat framer `initial` map\n * (keys: `opacity`, `scale`, `rotate`, `x`, `y`). When present, a\n * motion primitive passes it as framer-motion `initial={...}` so the\n * element mounts in this state and animates to its rendered target on\n * mount (mount-play). `undefined` → no `initial` (no mount-play). */\n animateInitial?: Record<string, number | string>;\n children?: ReactNode;\n}\n\nexport const PRIMITIVES: Partial<Record<RenderKind, ComponentType<PrimitiveProps>>> = {\n stack: Stack,\n grid: Grid,\n frame: Frame,\n text: Text,\n image: Image,\n shape: Shape,\n media: Media,\n instance: Instance,\n};\n","import { createContext, useContext, type ReactNode } from \"react\";\n\n/** Path-scope context. Children inside a `repeat` get a `prefix` that\n * is prepended to their declared bindings, so a single template can\n * bind to per-item paths like `items.{i}.score`. */\nconst PathScopeContext = createContext<string>(\"\");\n\nexport function PathScopeProvider({ prefix, children }: { prefix: string; children: ReactNode }) {\n const parent = useContext(PathScopeContext);\n const next = parent ? `${parent}.${prefix}` : prefix;\n return <PathScopeContext.Provider value={next}>{children}</PathScopeContext.Provider>;\n}\n\n/** Returns the current path prefix, or \"\" if there is no scope. */\nexport function usePathScope(): string {\n return useContext(PathScopeContext);\n}\n\n/** Resolve a binding path under the current scope. */\nexport function scopedPath(prefix: string, path: string): string {\n if (!prefix) return path;\n // Path may itself start with a literal prefix (e.g. `__system.*`),\n // which should NOT be scoped — only paths that are clearly relative\n // get prefixed.\n if (path.startsWith(\"__\")) return path;\n return `${prefix}.${path}`;\n}\n","// Universal-props wrapper (LSML 1.1 §5.4).\n//\n// Every primitive renders inside this wrapper, which applies the four\n// universal props uniformly :\n//\n// - `visible: false` → display: none (slot collapses in flex layouts)\n// - `opacity` → CSS opacity, multiplicative with whatever animation\n// a primitive may apply via framer-motion (browsers compose them)\n// - `rotation` → CSS transform: rotate(<deg>)\n// - `sizing.x`/`sizing.y` → flex shorthand on the wrapping div, lets\n// a primitive participate in its parent flex layout's auto-sizing\n//\n// `bindUniversal` is resolved by the Tree renderer before the wrapper\n// sees its values, so this component only deals with concrete numbers\n// and booleans.\n\nimport type { ReactNode, CSSProperties } from \"react\";\n\nexport type SizingMode = \"fixed\" | \"hug\" | \"fill\";\n\nexport interface UniversalProps {\n visible?: boolean;\n opacity?: number;\n rotation?: number;\n sizing?: { x?: SizingMode; y?: SizingMode };\n}\n\nexport interface UniversalWrapperProps extends UniversalProps {\n children: ReactNode;\n}\n\n/**\n * Maps a SizingMode onto a flex shorthand. Per LSML 1.1 §5.4.1 :\n * - fixed : the primitive honours its declared size verbatim\n * - hug : the primitive shrinks to its intrinsic content size\n * - fill : the primitive grows to fill available space\n */\nfunction flexFor(mode: SizingMode | undefined): string | undefined {\n switch (mode) {\n case \"fixed\":\n return \"0 0 auto\";\n case \"hug\":\n return \"0 1 auto\";\n case \"fill\":\n return \"1 1 auto\";\n default:\n return undefined;\n }\n}\n\nexport function UniversalWrapper({\n visible,\n opacity,\n rotation,\n sizing,\n children,\n}: UniversalWrapperProps) {\n if (visible === false) {\n return null; // slot collapses in flex/grid layouts (§5.4)\n }\n\n // No-op fast path — when no universal props are set, render children\n // directly. Lets simple bundles avoid an extra DOM node per primitive.\n const hasOpacity = typeof opacity === \"number\" && opacity !== 1;\n const hasRotation = typeof rotation === \"number\" && rotation !== 0;\n const hasSizing = sizing?.x !== undefined || sizing?.y !== undefined;\n if (!hasOpacity && !hasRotation && !hasSizing) {\n return <>{children}</>;\n }\n\n const style: CSSProperties = {};\n if (hasOpacity) style.opacity = opacity;\n if (hasRotation) style.transform = `rotate(${rotation}deg)`;\n\n // sizing.x / sizing.y map to flex / row-flex behaviour. The\n // x-axis applies along the main axis of a horizontal stack ; the\n // y-axis along a vertical stack. We emit `flex` (covers both via\n // CSS's flex-direction) and rely on the parent stack for orientation.\n if (hasSizing) {\n const x = flexFor(sizing?.x);\n const y = flexFor(sizing?.y);\n // Emit a single flex declaration when both axes agree, otherwise\n // ship explicit grow/shrink/basis based on the dominant intent.\n if (x === y && x !== undefined) {\n style.flex = x;\n } else {\n // Heuristic : honour x for horizontal stacks (most common in\n // broadcast UIs). Renderer doesn't know the parent's axis here ;\n // a future iteration could thread that through context.\n style.flex = x ?? y;\n }\n }\n\n return <div style={style}>{children}</div>;\n}\n","// LSML 1.1 §6.6 — keyframe sequence playback.\n//\n// A primitive's `keyframes` block describes a path through animatable\n// property values over time, applied once on (re)mount or whenever the\n// `key` LeafPath value changes. The shapes here mirror the spec verbatim\n// ; `compileForFramer` flattens them into the per-property arrays\n// (`scale: [0.8, 1.05, 1]`) plus `times: [0, 0.6, 1]` that framer-motion\n// expects on its `animate` / `transition` props.\n\nimport {\n FILTER_IDENTITY,\n sanitizeCssFilterString,\n warnRejectedFilter,\n} from \"../render/filter-clamp\";\n\nexport type KeyframeEasing = \"linear\" | \"ease-in\" | \"ease-out\" | \"ease-in-out\";\n\nexport interface KeyframeStep {\n /** Timeline position in [0, 1]. First step is 0 ; last step is 1. */\n at: number;\n /** Optional transform target at this waypoint. */\n transform?: KeyframeTransform;\n /** Optional opacity in [0, 1]. */\n opacity?: number;\n /** Optional CSS filter string. */\n filter?: string;\n}\n\nexport interface KeyframeTransform {\n scale?: number;\n translateX?: number;\n translateY?: number;\n rotate?: number;\n}\n\nexport interface Keyframes {\n /** LeafPath whose value-change replays the sequence. Omitted = mount-only. */\n key?: string;\n steps: KeyframeStep[];\n duration_ms: number;\n easing?: KeyframeEasing;\n}\n\nconst FRAMER_EASE_MAP: Record<KeyframeEasing, \"linear\" | \"easeIn\" | \"easeOut\" | \"easeInOut\"> = {\n linear: \"linear\",\n \"ease-in\": \"easeIn\",\n \"ease-out\": \"easeOut\",\n \"ease-in-out\": \"easeInOut\",\n};\n\nexport interface CompiledKeyframes {\n /** Per-CSS-property animate target (array of values, one per step). */\n animate: Record<string, (number | string)[]>;\n /** Framer transition config — duration in seconds, ease curve, times[]. */\n transition: {\n duration: number;\n ease: \"linear\" | \"easeIn\" | \"easeOut\" | \"easeInOut\";\n times: number[];\n };\n}\n\n/**\n * Flatten a 1.1 keyframe sequence into the per-property arrays + times[]\n * shape framer-motion expects. Returns `undefined` when `steps` is empty\n * or invariants are violated (first.at !== 0 or last.at !== 1) — the\n * caller then falls back to no animation.\n */\nexport function compileForFramer(kf: Keyframes, nodeId?: string): CompiledKeyframes | undefined {\n const steps = kf.steps;\n if (!Array.isArray(steps) || steps.length < 2) return undefined;\n const first = steps[0];\n const last = steps[steps.length - 1];\n if (first.at !== 0 || last.at !== 1) return undefined;\n\n const times = steps.map((s) => s.at);\n const animate: Record<string, (number | string)[]> = {};\n\n // For each animatable property, pull the value at every step. When a\n // step omits the property, we fall back to the previous step's value\n // (last-known-good) so framer-motion sees a coherent waypoint chain.\n pullChannel(steps, \"opacity\", animate, nodeId);\n pullChannel(steps, \"filter\", animate, nodeId);\n pullTransform(steps, \"scale\", animate);\n pullTransform(steps, \"translateX\", animate);\n pullTransform(steps, \"translateY\", animate);\n pullTransform(steps, \"rotate\", animate);\n\n return {\n animate,\n transition: {\n duration: kf.duration_ms / 1000,\n ease: FRAMER_EASE_MAP[kf.easing ?? \"linear\"],\n times,\n },\n };\n}\n\nfunction pullChannel(\n steps: KeyframeStep[],\n prop: \"opacity\" | \"filter\",\n out: Record<string, (number | string)[]>,\n nodeId?: string,\n): void {\n let any = false;\n const values: (number | string)[] = [];\n let last: number | string | undefined;\n for (const s of steps) {\n let v: number | string | undefined = s[prop];\n // R8 runtime half (ADR 001 §5.1, issue #42) — keyframe filter\n // strings in a hand-crafted bundle bypass the compiler clamps.\n // Re-gate here ; rejected → treat as omitted (last-known-good).\n if (prop === \"filter\" && v !== undefined) {\n const safe = sanitizeCssFilterString(v);\n if (safe === null) {\n warnRejectedFilter(\"keyframes.steps[].filter\", nodeId);\n v = undefined;\n } else {\n v = safe;\n }\n }\n if (v !== undefined) {\n any = true;\n last = v;\n values.push(v);\n } else {\n values.push(last ?? (prop === \"opacity\" ? 1 : FILTER_IDENTITY));\n }\n }\n if (any) out[prop] = values;\n}\n\nfunction pullTransform(\n steps: KeyframeStep[],\n prop: keyof KeyframeTransform,\n out: Record<string, (number | string)[]>,\n): void {\n let any = false;\n const values: number[] = [];\n let last: number | undefined;\n for (const s of steps) {\n const v = s.transform?.[prop];\n if (typeof v === \"number\") {\n any = true;\n last = v;\n values.push(v);\n } else {\n values.push(last ?? defaultFor(prop));\n }\n }\n if (any) {\n if (prop === \"rotate\") {\n out.rotate = values.map((n) => `${n}deg`);\n } else {\n out[prop] = values;\n }\n }\n}\n\nfunction defaultFor(prop: keyof KeyframeTransform): number {\n return prop === \"scale\" ? 1 : 0;\n}\n","// LSML 1.1 §6.7 — stagger context.\n//\n// `repeat.stagger_ms` produces wave-like reveals : iteration N's\n// animations start `N * stagger_ms` after iteration 0. The Repeat\n// renderer computes the per-iteration delay and threads it through\n// React context so KeyframePlayer (and future animate-aware primitives)\n// can pick it up without per-primitive wiring.\n\nimport { createContext } from \"react\";\n\n/** Per-iteration stagger delay in milliseconds. `0` means no offset\n * (the implicit default outside a staggered repeat). */\nexport const StaggerContext = createContext<number>(0);\n\n/** Spec hint : runtimes MAY cap effective stagger to avoid pathological\n * wait times on large lists. We cap at 2 s. */\nexport const STAGGER_CAP_MS = 2000;\n\n/** Compute the effective per-iteration delay, applying the runtime cap. */\nexport function computeStaggerDelayMs(index: number, staggerMs: number): number {\n if (staggerMs <= 0) return 0;\n const raw = index * staggerMs;\n return raw > STAGGER_CAP_MS ? STAGGER_CAP_MS : raw;\n}\n","// LSML 1.1 §6.6 — keyframe sequence playback wrapper.\n//\n// Wraps a primitive subtree in a framer-motion `motion.div` that plays\n// out the compiled keyframe arrays once on (re)mount, or whenever the\n// bound `key` LeafPath changes. We trigger replay via React's `key=`\n// reconciliation — bumping a counter when the keyframe key value flips\n// remounts the motion subtree, restarting the animation from `at: 0`.\n//\n// LSML 1.1 §6.7 — when this player runs inside a `repeat` iteration, a\n// `staggerDelay` (ms) is provided through `StaggerContext` and added to\n// framer's transition.delay so each iteration starts `index * stagger_ms`\n// after the previous one.\n\nimport { motion } from \"framer-motion\";\nimport { useContext, useEffect, useRef, type ReactNode } from \"react\";\nimport { useSignals } from \"@preact/signals-react/runtime\";\nimport type { Store } from \"../state/store\";\nimport { compileForFramer, type Keyframes } from \"../animate/keyframes\";\nimport { StaggerContext } from \"./stagger-context\";\nimport { scopedPath, usePathScope } from \"./scope\";\n\nexport interface KeyframePlayerProps {\n keyframes: Keyframes;\n store: Store;\n /** `RenderNode.id` of the owning node — threaded into keyframe\n * diagnostics (ADR 001 RC#7, issue #34). */\n nodeId?: string;\n children: ReactNode;\n}\n\nexport function KeyframePlayer({\n keyframes,\n store,\n nodeId,\n children,\n}: KeyframePlayerProps): ReactNode {\n useSignals();\n const scope = usePathScope();\n const staggerDelayMs = useContext(StaggerContext);\n\n // Pull the latest `key` LeafPath value and remount whenever it\n // changes. We track via a ref + counter so React's reconciliation\n // gives us a fresh motion.div (and thus a fresh animation pass).\n const lastKeyValue = useRef<unknown>(undefined);\n const replayTokenRef = useRef(0);\n if (keyframes.key !== undefined) {\n const v = store.signal(scopedPath(scope, keyframes.key)).value;\n if (lastKeyValue.current !== v) {\n lastKeyValue.current = v;\n replayTokenRef.current += 1;\n }\n }\n\n const compiled = compileForFramer(keyframes, nodeId);\n if (!compiled) {\n return <>{children}</>;\n }\n\n const transition =\n staggerDelayMs > 0\n ? { ...compiled.transition, delay: staggerDelayMs / 1000 }\n : compiled.transition;\n\n return (\n <motion.div\n key={replayTokenRef.current}\n style={{ display: \"contents\" }}\n initial={firstFrame(compiled.animate)}\n animate={compiled.animate}\n transition={transition}\n >\n <ReplayOnMount />\n {children}\n </motion.div>\n );\n}\n\n/** No-op effect placeholder — kept for symmetry / future hooks like\n * reporting playback completion to the renderer. */\nfunction ReplayOnMount(): null {\n useEffect(() => {\n // intentional no-op\n }, []);\n return null;\n}\n\n/** Pluck the `at: 0` waypoint values into a framer-motion `initial` prop\n * so the very first frame matches the start of the keyframe path. Without\n * this, framer interpolates from the element's current style which can\n * produce a visible jump on mount. */\nfunction firstFrame(animate: Record<string, (number | string)[]>): Record<string, number | string> {\n const out: Record<string, number | string> = {};\n for (const [k, arr] of Object.entries(animate)) {\n if (arr.length > 0) out[k] = arr[0];\n }\n return out;\n}\n","// Per-frame delta coalescing (ADR 001 RC#13 — bindAnimate anti-DoS).\n//\n// A live LSDP producer may push deltas at arbitrary frequency (the\n// threat model assumes 1 kHz). Retargeting a Framer animation per delta\n// would start hundreds of redundant animations per displayed frame. The\n// coalescer buffers the LATEST value per binding key and flushes once\n// per animation frame : one retarget max per rAF per binding,\n// independently of the producer's rate.\n//\n// Pure scheduling logic, injectable rAF — unit-testable without a\n// browser loop.\n\nexport interface FrameCoalescer {\n /** Buffer `value` for `key` ; schedules a flush on the next frame.\n * Multiple pushes on the same key within one frame keep only the\n * last value (the previous targets are obsolete by construction). */\n push(key: string, value: unknown): void;\n /** Cancel any scheduled flush and drop pending values. */\n dispose(): void;\n}\n\ntype Schedule = (cb: () => void) => number;\ntype Cancel = (id: number) => void;\n\nexport function createFrameCoalescer(\n flush: (key: string, value: unknown) => void,\n schedule: Schedule = (cb) => requestAnimationFrame(cb),\n cancel: Cancel = (id) => cancelAnimationFrame(id),\n): FrameCoalescer {\n const pending = new Map<string, unknown>();\n let frameId: number | null = null;\n let disposed = false;\n\n const onFrame = (): void => {\n frameId = null;\n // Swap-before-flush : a push() re-entrant from a flush callback\n // lands in a fresh map and schedules the NEXT frame (never the\n // current one) — the one-retarget-per-rAF bound holds.\n const entries = [...pending.entries()];\n pending.clear();\n for (const [key, value] of entries) {\n flush(key, value);\n }\n };\n\n return {\n push(key, value): void {\n if (disposed) return;\n pending.set(key, value);\n if (frameId === null) {\n frameId = schedule(onFrame);\n }\n },\n dispose(): void {\n disposed = true;\n pending.clear();\n if (frameId !== null) {\n cancel(frameId);\n frameId = null;\n }\n },\n };\n}\n","// sRGB colour interpolation (LSML 1.1 §6.5) — issue #33.\n//\n// Both endpoints of a colour animation are first CANONICALISED through\n// the strict shared parser (`parseCssColor`, css-color.ts — ADR 001\n// RC#11/RC#12, never a raw string), then converted to RGBA channels in\n// [0, 1] and interpolated component-wise :\n//\n// out_c = a_c + t * (b_c - a_c) for c ∈ {r, g, b, a}\n//\n// with `t` produced by the easing curve. The output is serialised back\n// to `rgba()` form — which itself round-trips through `parseCssColor`\n// at the consuming primitive (belt and braces).\n//\n// All conversions are constant-time per value (the parser already\n// bounds inputs to 64 chars) ; the named-colour table is a flat map\n// lookup.\n\nimport { parseCssColor } from \"./css-color\";\n\n/** RGBA channels, each in [0, 1]. */\nexport type Rgba = readonly [number, number, number, number];\n\n// CSS Color 4 §6.1 named colours → packed 0xRRGGBB. Kept numeric (not\n// hex strings) to minimise bundle weight ; the set of NAMES here is\n// exactly the set accepted by css-color.ts (`transparent` and\n// `currentcolor` are handled separately — `currentcolor` cannot be\n// interpolated without computed-style context and is rejected).\nconst NAMED_RGB: Record<string, number> = {\n aliceblue: 0xf0f8ff,\n antiquewhite: 0xfaebd7,\n aqua: 0x00ffff,\n aquamarine: 0x7fffd4,\n azure: 0xf0ffff,\n beige: 0xf5f5dc,\n bisque: 0xffe4c4,\n black: 0x000000,\n blanchedalmond: 0xffebcd,\n blue: 0x0000ff,\n blueviolet: 0x8a2be2,\n brown: 0xa52a2a,\n burlywood: 0xdeb887,\n cadetblue: 0x5f9ea0,\n chartreuse: 0x7fff00,\n chocolate: 0xd2691e,\n coral: 0xff7f50,\n cornflowerblue: 0x6495ed,\n cornsilk: 0xfff8dc,\n crimson: 0xdc143c,\n cyan: 0x00ffff,\n darkblue: 0x00008b,\n darkcyan: 0x008b8b,\n darkgoldenrod: 0xb8860b,\n darkgray: 0xa9a9a9,\n darkgreen: 0x006400,\n darkgrey: 0xa9a9a9,\n darkkhaki: 0xbdb76b,\n darkmagenta: 0x8b008b,\n darkolivegreen: 0x556b2f,\n darkorange: 0xff8c00,\n darkorchid: 0x9932cc,\n darkred: 0x8b0000,\n darksalmon: 0xe9967a,\n darkseagreen: 0x8fbc8f,\n darkslateblue: 0x483d8b,\n darkslategray: 0x2f4f4f,\n darkslategrey: 0x2f4f4f,\n darkturquoise: 0x00ced1,\n darkviolet: 0x9400d3,\n deeppink: 0xff1493,\n deepskyblue: 0x00bfff,\n dimgray: 0x696969,\n dimgrey: 0x696969,\n dodgerblue: 0x1e90ff,\n firebrick: 0xb22222,\n floralwhite: 0xfffaf0,\n forestgreen: 0x228b22,\n fuchsia: 0xff00ff,\n gainsboro: 0xdcdcdc,\n ghostwhite: 0xf8f8ff,\n gold: 0xffd700,\n goldenrod: 0xdaa520,\n gray: 0x808080,\n green: 0x008000,\n greenyellow: 0xadff2f,\n grey: 0x808080,\n honeydew: 0xf0fff0,\n hotpink: 0xff69b4,\n indianred: 0xcd5c5c,\n indigo: 0x4b0082,\n ivory: 0xfffff0,\n khaki: 0xf0e68c,\n lavender: 0xe6e6fa,\n lavenderblush: 0xfff0f5,\n lawngreen: 0x7cfc00,\n lemonchiffon: 0xfffacd,\n lightblue: 0xadd8e6,\n lightcoral: 0xf08080,\n lightcyan: 0xe0ffff,\n lightgoldenrodyellow: 0xfafad2,\n lightgray: 0xd3d3d3,\n lightgreen: 0x90ee90,\n lightgrey: 0xd3d3d3,\n lightpink: 0xffb6c1,\n lightsalmon: 0xffa07a,\n lightseagreen: 0x20b2aa,\n lightskyblue: 0x87cefa,\n lightslategray: 0x778899,\n lightslategrey: 0x778899,\n lightsteelblue: 0xb0c4de,\n lightyellow: 0xffffe0,\n lime: 0x00ff00,\n limegreen: 0x32cd32,\n linen: 0xfaf0e6,\n magenta: 0xff00ff,\n maroon: 0x800000,\n mediumaquamarine: 0x66cdaa,\n mediumblue: 0x0000cd,\n mediumorchid: 0xba55d3,\n mediumpurple: 0x9370db,\n mediumseagreen: 0x3cb371,\n mediumslateblue: 0x7b68ee,\n mediumspringgreen: 0x00fa9a,\n mediumturquoise: 0x48d1cc,\n mediumvioletred: 0xc71585,\n midnightblue: 0x191970,\n mintcream: 0xf5fffa,\n mistyrose: 0xffe4e1,\n moccasin: 0xffe4b5,\n navajowhite: 0xffdead,\n navy: 0x000080,\n oldlace: 0xfdf5e6,\n olive: 0x808000,\n olivedrab: 0x6b8e23,\n orange: 0xffa500,\n orangered: 0xff4500,\n orchid: 0xda70d6,\n palegoldenrod: 0xeee8aa,\n palegreen: 0x98fb98,\n paleturquoise: 0xafeeee,\n palevioletred: 0xdb7093,\n papayawhip: 0xffefd5,\n peachpuff: 0xffdab9,\n peru: 0xcd853f,\n pink: 0xffc0cb,\n plum: 0xdda0dd,\n powderblue: 0xb0e0e6,\n purple: 0x800080,\n rebeccapurple: 0x663399,\n red: 0xff0000,\n rosybrown: 0xbc8f8f,\n royalblue: 0x4169e1,\n saddlebrown: 0x8b4513,\n salmon: 0xfa8072,\n sandybrown: 0xf4a460,\n seagreen: 0x2e8b57,\n seashell: 0xfff5ee,\n sienna: 0xa0522d,\n silver: 0xc0c0c0,\n skyblue: 0x87ceeb,\n slateblue: 0x6a5acd,\n slategray: 0x708090,\n slategrey: 0x708090,\n snow: 0xfffafa,\n springgreen: 0x00ff7f,\n steelblue: 0x4682b4,\n tan: 0xd2b48c,\n teal: 0x008080,\n thistle: 0xd8bfd8,\n tomato: 0xff6347,\n turquoise: 0x40e0d0,\n violet: 0xee82ee,\n wheat: 0xf5deb3,\n white: 0xffffff,\n whitesmoke: 0xf5f5f5,\n yellow: 0xffff00,\n yellowgreen: 0x9acd32,\n};\n\n/**\n * Canonicalise + convert an untrusted colour value to RGBA channels in\n * [0, 1]. The value passes through `parseCssColor` FIRST — anything the\n * strict parser rejects converts to `null` here (never interpolate a\n * raw string, §6.5 step 1 / RC#11). `currentcolor` is also rejected :\n * it has no concrete channels without computed-style context.\n */\nexport function cssColorToRgba(value: unknown): Rgba | null {\n const v = parseCssColor(value);\n if (v === null) return null;\n\n if (v.startsWith(\"#\")) return hexToRgba(v);\n\n if (v.startsWith(\"rgb\")) {\n const body = v.slice(v.indexOf(\"(\") + 1, -1);\n const parts = body.split(\",\").map((p) => p.trim());\n if (parts.length < 3) return null;\n const pct = parts[0]!.endsWith(\"%\");\n const scale = pct ? 100 : 255;\n const r = channel(parts[0]!, scale);\n const g = channel(parts[1]!, scale);\n const b = channel(parts[2]!, scale);\n const a = parts.length > 3 ? alphaChannel(parts[3]!) : 1;\n if (r === null || g === null || b === null || a === null) return null;\n return [r, g, b, a];\n }\n\n if (v.startsWith(\"hsl\")) {\n const body = v.slice(v.indexOf(\"(\") + 1, -1);\n const parts = body.split(\",\").map((p) => p.trim());\n if (parts.length < 3) return null;\n const h = Number(parts[0]!.replace(\"deg\", \"\"));\n const s = Number(parts[1]!.replace(\"%\", \"\")) / 100;\n const l = Number(parts[2]!.replace(\"%\", \"\")) / 100;\n const a = parts.length > 3 ? alphaChannel(parts[3]!) : 1;\n if (![h, s, l].every(Number.isFinite) || a === null) return null;\n const [r, g, b] = hslToRgb(h, s, l);\n return [r, g, b, a];\n }\n\n if (v === \"transparent\") return [0, 0, 0, 0];\n if (v === \"currentcolor\") return null;\n\n const packed = NAMED_RGB[v];\n if (packed === undefined) return null;\n return [((packed >> 16) & 0xff) / 255, ((packed >> 8) & 0xff) / 255, (packed & 0xff) / 255, 1];\n}\n\nfunction hexToRgba(v: string): Rgba | null {\n const h = v.slice(1);\n if (h.length === 3 || h.length === 4) {\n const r = parseInt(h[0]! + h[0]!, 16);\n const g = parseInt(h[1]! + h[1]!, 16);\n const b = parseInt(h[2]! + h[2]!, 16);\n const a = h.length === 4 ? parseInt(h[3]! + h[3]!, 16) : 255;\n return [r / 255, g / 255, b / 255, a / 255];\n }\n if (h.length === 6 || h.length === 8) {\n const r = parseInt(h.slice(0, 2), 16);\n const g = parseInt(h.slice(2, 4), 16);\n const b = parseInt(h.slice(4, 6), 16);\n const a = h.length === 8 ? parseInt(h.slice(6, 8), 16) : 255;\n return [r / 255, g / 255, b / 255, a / 255];\n }\n return null;\n}\n\nfunction channel(token: string, scale: number): number | null {\n const n = Number(token.replace(\"%\", \"\"));\n if (!Number.isFinite(n)) return null;\n return clamp01(n / scale);\n}\n\nfunction alphaChannel(token: string): number | null {\n const pct = token.endsWith(\"%\");\n const n = Number(token.replace(\"%\", \"\"));\n if (!Number.isFinite(n)) return null;\n return clamp01(pct ? n / 100 : n);\n}\n\n/** Standard HSL → RGB (CSS Color 4 §7.1). h in degrees, s/l in [0,1]. */\nfunction hslToRgb(h: number, s: number, l: number): [number, number, number] {\n const hue = ((h % 360) + 360) % 360;\n const c = (1 - Math.abs(2 * l - 1)) * s;\n const hp = hue / 60;\n const x = c * (1 - Math.abs((hp % 2) - 1));\n let r = 0;\n let g = 0;\n let b = 0;\n if (hp < 1) [r, g, b] = [c, x, 0];\n else if (hp < 2) [r, g, b] = [x, c, 0];\n else if (hp < 3) [r, g, b] = [0, c, x];\n else if (hp < 4) [r, g, b] = [0, x, c];\n else if (hp < 5) [r, g, b] = [x, 0, c];\n else [r, g, b] = [c, 0, x];\n const m = l - c / 2;\n return [clamp01(r + m), clamp01(g + m), clamp01(b + m)];\n}\n\n/** Component-wise sRGB lerp (§6.5 step 2). `t` may overshoot (springs) ;\n * channels clamp back into [0, 1] after mixing. */\nexport function mixRgba(a: Rgba, b: Rgba, t: number): Rgba {\n return [\n clamp01(a[0] + t * (b[0] - a[0])),\n clamp01(a[1] + t * (b[1] - a[1])),\n clamp01(a[2] + t * (b[2] - a[2])),\n clamp01(a[3] + t * (b[3] - a[3])),\n ];\n}\n\n/** Serialise RGBA back to `rgba()` form (§6.5 step 3). The output is\n * always re-accepted by `parseCssColor` (integer channels, alpha with\n * at most 4 decimals). */\nexport function serializeRgba(rgba: Rgba): string {\n const r = Math.round(clamp01(rgba[0]) * 255);\n const g = Math.round(clamp01(rgba[1]) * 255);\n const b = Math.round(clamp01(rgba[2]) * 255);\n // 4 decimals max so the alpha token matches the strict grammar.\n const a = Math.round(clamp01(rgba[3]) * 10000) / 10000;\n return `rgba(${r}, ${g}, ${b}, ${a})`;\n}\n\nfunction clamp01(n: number): number {\n return n < 0 ? 0 : n > 1 ? 1 : n;\n}\n","// LSML 1.1 §6.3 `bindAnimate` — continuous interpolation toward a live\n// leaf value (ADR 001 §3.3, issue #33).\n//\n// Per binding, the hook subscribes the existing leaf-grain signal and\n// retargets a Framer motion value on change — NO remount, the DOM node\n// is identical before/after (RC#6). Scalar channels (§6.1) ride motion\n// values attached to a wrapping `motion.div` ; colour channels (§6.5)\n// are interpolated component-wise in sRGB through the strict shared\n// parser and flow back into the primitive's resolved prop (which\n// re-validates them — RC#11 belt and braces).\n//\n// Anti-DoS (Bastion RC#13) : deltas are coalesced per frame — one\n// retarget max per rAF per binding, whatever the producer's rate\n// (1 kHz tested in E2E). Retargets interrupt in-flight springs with\n// velocity carry (§6.2/§6.4 — framer preserves a motion value's\n// velocity when a spring animation is replaced ; no snap).\n//\n// R8 runtime half (issue #42) : `filter.blur` / `filter.brightness`\n// values arriving live re-pass the same caps as the compiler before\n// they may touch the composed CSS filter (see filter-clamp.ts).\n//\n// Stagger (§6.7) : inside a `repeat` iteration the FIRST animated\n// retarget per binding is delayed by the ambient StaggerContext delay ;\n// steady-state retargets are never delayed (a permanently-lagging gauge\n// would defeat the purpose of a live binding). Documented hypothesis —\n// the spec only constrains animation *starts*.\n\nimport {\n animate,\n useMotionValue,\n useTransform,\n type AnimationPlaybackControls,\n type MotionValue,\n type MotionStyle,\n} from \"framer-motion\";\nimport { effect } from \"@preact/signals-react\";\nimport { useContext, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { Store } from \"../state/store\";\nimport type { RenderNode } from \"./bundle\";\nimport { scopedPath } from \"./scope\";\nimport { StaggerContext } from \"./stagger-context\";\nimport { toFramer, type FramerTransition, type Transition } from \"../animate/transitions\";\nimport { createFrameCoalescer } from \"../animate/frame-coalescer\";\nimport { clampFilterChannel, warnRejectedFilter } from \"./filter-clamp\";\nimport { warnRejectedColor } from \"./css-color\";\nimport { emitDiagnostic } from \"./diagnostics\";\nimport { cssColorToRgba, mixRgba, serializeRgba, type Rgba } from \"./color-interp\";\n\n/** §6.5 colour-typed bindAnimate keys → the runtime prop name the\n * primitive reads (and re-validates through `parseCssColor`). */\nexport const BIND_ANIMATE_COLOR_PROPS: Readonly<Record<string, string>> = {\n \"style.color\": \"colour\",\n fill: \"fill\",\n background: \"background\",\n};\n\n/** Scalar motion channels a bindAnimate key drives. */\ntype ScalarChannel = \"opacity\" | \"x\" | \"y\" | \"scaleX\" | \"scaleY\" | \"rotate\" | \"blur\" | \"brightness\";\n\n/**\n * Validate + normalise one live bindAnimate value into per-channel\n * numeric targets. Returns `null` on rejection (wrong JSON shape per\n * §6.3, non-finite numbers, filter values outside the R8 caps when the\n * channel rejects rather than clamps). A `null` keeps the last\n * known-good target — the raw input never reaches a style.\n *\n * Exported pure for the hostile-delta fixture suite (issue #42).\n */\nexport function resolveScalarTargets(\n key: string,\n raw: unknown,\n): Partial<Record<ScalarChannel, number>> | null {\n switch (key) {\n case \"opacity\": {\n const v = toFiniteScalar(raw);\n if (v === null) return null;\n return { opacity: v < 0 ? 0 : v > 1 ? 1 : v };\n }\n case \"transform.translate\": {\n if (!Array.isArray(raw) || raw.length !== 2) return null;\n const tx = toFiniteScalar(raw[0]);\n const ty = toFiniteScalar(raw[1]);\n if (tx === null || ty === null) return null;\n return { x: tx, y: ty };\n }\n case \"transform.scale\": {\n const s = toFiniteScalar(raw);\n if (s !== null) return { scaleX: s, scaleY: s };\n if (Array.isArray(raw) && raw.length === 2) {\n const sx = toFiniteScalar(raw[0]);\n const sy = toFiniteScalar(raw[1]);\n if (sx === null || sy === null) return null;\n return { scaleX: sx, scaleY: sy };\n }\n return null;\n }\n case \"transform.rotate\": {\n const v = toFiniteScalar(raw);\n if (v === null) return null;\n return { rotate: v };\n }\n case \"filter.blur\": {\n const v = clampFilterChannel(\"blur\", raw);\n return v === null ? null : { blur: v };\n }\n case \"filter.brightness\": {\n const v = clampFilterChannel(\"brightness\", raw);\n return v === null ? null : { brightness: v };\n }\n default:\n return null;\n }\n}\n\n// ── IEEE-754 `-0` policy (R8 / PR #39 coherence, issue #33) ───────────\n// `-0 < 0` is FALSE, so a plain sign clamp lets -0 through. Uniform\n// rule : no -0 ever reaches a motion value or a style. Per channel :\n// · opacity — the negative case CLAMPS to 0 (`-5 → 0`), so -0 follows\n// the same line and normalises to +0 (rejecting -0 while clamping\n// -5 would be incoherent within the channel) ;\n// · translate / scale / rotate — negatives are valid values and\n// `-0 == 0` is mathematically neutral, so -0 normalises to +0 (a\n// producer computing `-x` at x = 0 must not be rejected into a\n// stale last-good) ;\n// · filter.blur / filter.brightness — REJECTED (null → last-good) by\n// `clampFilterChannel` : the R8 gate treats any negative sign,\n// including -0, as hostile input (compiler mirror, issues #39/#41\n// — do not relax here).\n/** Finite-number gate + `-0 → +0` normalisation for the generic scalar\n * channels (filter channels go through `clampFilterChannel` instead). */\nfunction toFiniteScalar(v: unknown): number | null {\n if (typeof v !== \"number\" || !Number.isFinite(v)) return null;\n return Object.is(v, -0) ? 0 : v;\n}\n\n/** Default retarget transition when neither a per-leaf wire directive\n * nor a compiled `transitions` entry resolves : the §6.2 default\n * spring (stiffness 170, damping 26, mass 1). A spring is the only\n * curve with well-defined retarget semantics (velocity carry) — a\n * documented hypothesis, see the PR for issue #33. */\nexport const DEFAULT_BIND_ANIMATE_TRANSITION: Transition = {\n kind: \"spring\",\n stiffness: 170,\n damping: 26,\n mass: 1,\n};\n\n/** node.transitions lookup key for each bindAnimate key (mirrors the\n * compiler's `transitionKeysForBindAnimate`). */\nfunction transitionLookupKey(key: string): string {\n switch (key) {\n case \"opacity\":\n return \"opacity\";\n case \"transform.translate\":\n return \"x\";\n case \"transform.scale\":\n return \"scale\";\n case \"transform.rotate\":\n return \"rotate\";\n case \"filter.blur\":\n case \"filter.brightness\":\n return \"filter\";\n default:\n return BIND_ANIMATE_COLOR_PROPS[key] ?? key;\n }\n}\n\nexport interface BindAnimateHandle {\n /** Motion-value style for the wrapping `motion.div` — `null` when no\n * scalar channel is bound (no wrapper needed). */\n motionStyle: MotionStyle | null;\n /** Live-interpolated colour values, keyed by the primitive prop name\n * (`colour` / `fill` / `background`). Merged over `resolved`. */\n colorProps: Record<string, string>;\n}\n\nconst NO_COLORS: Record<string, string> = {};\n\n/**\n * Drive a node's `animateBindings`. Must be called unconditionally\n * (hook) ; cheap no-op when the node has no bindings.\n */\nexport function useBindAnimate(node: RenderNode, store: Store, scope: string): BindAnimateHandle {\n const bindings = node.animateBindings;\n const staggerDelayMs = useContext(StaggerContext);\n\n // Fixed channel set — created unconditionally so hook order is\n // stable ; unbound channels stay at their identity value.\n const opacity = useMotionValue(1);\n const x = useMotionValue(0);\n const y = useMotionValue(0);\n const scaleX = useMotionValue(1);\n const scaleY = useMotionValue(1);\n const rotate = useMotionValue(0);\n const blur = useMotionValue(0);\n const brightness = useMotionValue(1);\n // Composed CSS filter — both functions always present so framer\n // interpolates structurally-identical lists (same form as the\n // compiler emits, clamped per R8).\n const filter = useTransform(\n [blur, brightness] as [MotionValue<number>, MotionValue<number>],\n ([b, br]: number[]) => `blur(${b}px) brightness(${br})`,\n );\n\n const [colorProps, setColorProps] = useState<Record<string, string>>(NO_COLORS);\n\n const channels = useRef<Record<ScalarChannel, MotionValue<number>>>({\n opacity,\n x,\n y,\n scaleX,\n scaleY,\n rotate,\n blur,\n brightness,\n });\n\n useEffect(() => {\n if (!bindings || Object.keys(bindings).length === 0) return;\n\n const mvs = channels.current;\n const controls = new Map<string, AnimationPlaybackControls>();\n const colorState = new Map<string, { current: Rgba }>();\n const animatedOnce = new Set<string>();\n let mounted = false;\n\n const transitionFor = (key: string, fullPath: string): FramerTransition => {\n const live = store.transitionSignal(fullPath).peek();\n const declared = live ?? node.transitions?.[transitionLookupKey(key)];\n const base = toFramer(declared ?? DEFAULT_BIND_ANIMATE_TRANSITION);\n // §6.7 — stagger delays only the first animated retarget.\n if (staggerDelayMs > 0 && !animatedOnce.has(key)) {\n return { ...base, delay: staggerDelayMs / 1000 } as FramerTransition;\n }\n return base;\n };\n\n const dispatch = (key: string, value: unknown, instant: boolean): void => {\n const colorProp = BIND_ANIMATE_COLOR_PROPS[key];\n const fullPath = scopedPath(scope, bindings[key] as string);\n\n if (colorProp !== undefined) {\n // §6.5 — canonicalise BOTH endpoints through the strict parser\n // before interpolating ; never a raw string.\n const end = cssColorToRgba(value);\n if (end === null) {\n warnRejectedColor(`bindAnimate.${key}`, node.id);\n return;\n }\n const prev = colorState.get(key);\n if (instant || prev === undefined) {\n colorState.set(key, { current: end });\n setColorProps((p) => ({ ...p, [colorProp]: serializeRgba(end) }));\n return;\n }\n const start = prev.current;\n const tx = transitionFor(key, fullPath);\n animatedOnce.add(key);\n controls.get(`color:${key}`)?.stop();\n controls.set(\n `color:${key}`,\n animate(0, 1, {\n ...tx,\n onUpdate: (t) => {\n const mixed = mixRgba(start, end, t);\n prev.current = mixed;\n setColorProps((p) => ({ ...p, [colorProp]: serializeRgba(mixed) }));\n },\n }),\n );\n return;\n }\n\n const targets = resolveScalarTargets(key, value);\n if (targets === null) {\n // R9 — the offending value is never logged.\n if (key.startsWith(\"filter.\")) warnRejectedFilter(`bindAnimate.${key}`, node.id);\n else warnRejectedBindValue(key, node.id);\n return;\n }\n if (instant) {\n // §6.3.1 — on mount the rendered state initialises from the\n // bound value instantly (there is no previous state).\n for (const [ch, v] of Object.entries(targets)) {\n mvs[ch as ScalarChannel].jump(v as number);\n }\n return;\n }\n const tx = transitionFor(key, fullPath);\n animatedOnce.add(key);\n for (const [ch, v] of Object.entries(targets)) {\n // framer's animate() replaces any in-flight animation on the\n // motion value and seeds the new spring with the value's\n // current velocity — §6.2 velocity carry, no snap.\n controls.set(ch, animate(mvs[ch as ScalarChannel], v as number, tx));\n }\n };\n\n // RC#13 — one retarget max per rAF per binding.\n const coalescer = createFrameCoalescer((key, value) => dispatch(key, value, false));\n\n const disposers = Object.entries(bindings).map(([key, path]) =>\n effect(() => {\n const v = store.signal(scopedPath(scope, path)).value;\n if (v === undefined) return;\n if (!mounted) dispatch(key, v, true);\n else coalescer.push(key, v);\n }),\n );\n mounted = true;\n\n return () => {\n for (const d of disposers) d();\n coalescer.dispose();\n for (const c of controls.values()) c.stop();\n };\n // node/store/scope identity changes re-wire every subscription ;\n // staggerDelayMs is stable per repeat iteration.\n }, [node, bindings, store, scope, staggerDelayMs]);\n\n const motionStyle = useMemo<MotionStyle | null>(() => {\n if (!bindings) return null;\n const style: MotionStyle = {};\n let any = false;\n for (const key of Object.keys(bindings)) {\n switch (key) {\n case \"opacity\":\n style.opacity = opacity;\n any = true;\n break;\n case \"transform.translate\":\n style.x = x;\n style.y = y;\n any = true;\n break;\n case \"transform.scale\":\n style.scaleX = scaleX;\n style.scaleY = scaleY;\n any = true;\n break;\n case \"transform.rotate\":\n style.rotate = rotate;\n any = true;\n break;\n case \"filter.blur\":\n case \"filter.brightness\":\n style.filter = filter;\n any = true;\n break;\n default:\n break; // colour keys flow through colorProps, not the wrapper\n }\n }\n if (!any) return null;\n style.willChange = \"transform, opacity, filter\";\n return style;\n }, [bindings, opacity, x, y, scaleX, scaleY, rotate, filter]);\n\n return { motionStyle, colorProps };\n}\n\n/** R9 diagnostic — shape-invalid bindAnimate value (non-filter\n * channels). Structured event, value withheld. */\nfunction warnRejectedBindValue(key: string, nodeId?: string): void {\n emitDiagnostic(\n nodeId,\n `bindAnimate.${key}`,\n \"rejected bound value : JSON shape does not match the property type (LSML §6.3)\",\n );\n}\n","// Recursive tree renderer — resolves bindings, dispatches to\n// primitives, handles `repeat` specially.\n\nimport { useSignals } from \"@preact/signals-react/runtime\";\nimport { motion } from \"framer-motion\";\nimport { useMemo, type ReactNode } from \"react\";\nimport type { Store } from \"../state/store\";\nimport type { Transition } from \"../animate/transitions\";\nimport { PRIMITIVES } from \"./primitives\";\nimport { PathScopeProvider, scopedPath, usePathScope } from \"./scope\";\nimport type { RenderNode } from \"./bundle\";\nimport { UniversalWrapper, type SizingMode } from \"./universal-wrapper\";\nimport { KeyframePlayer } from \"./keyframe-player\";\nimport { StaggerContext, computeStaggerDelayMs } from \"./stagger-context\";\nimport { useBindAnimate } from \"./bind-animate\";\nimport { checkNodeProps } from \"./prop-allowlist\";\nimport { emitDiagnostic } from \"./diagnostics\";\n\nexport interface TreeProps {\n node: RenderNode;\n store: Store;\n}\n\nexport function Tree({ node, store }: TreeProps): ReactNode {\n if (node.kind === \"repeat\") {\n return <Repeat node={node} store={store} />;\n }\n return <Node node={node} store={store} />;\n}\n\nfunction Node({ node, store }: TreeProps): ReactNode {\n // useSignals() lets the surrounding component subscribe to any\n // signal read during render. Each leaf path has its own signal so\n // re-renders only fire on touched paths.\n useSignals();\n const scope = usePathScope();\n\n // Hooks must run unconditionally — the early-return for unknown\n // kinds happens *after* every hook has fired.\n const resolved = useMemo(\n () => resolveProps(node, store, scope),\n // We re-build per render — signals re-render cheaply, and the\n // resolution itself is O(bindings) which is small. The memo is a\n // micro-optimisation to keep object identity stable across renders\n // when the inputs haven't changed.\n [node, store, scope, ...readBindingValues(node, store, scope)],\n );\n\n // LSML 1.1 §6.3 — bindAnimate : continuous interpolation toward live\n // leaf values, no remount (issue #33). Scalar channels ride motion\n // values on a wrapping motion.div ; colour channels (§6.5) flow back\n // into the primitive's resolved prop as interpolated, re-validated\n // colour strings.\n const bindAnimate = useBindAnimate(node, store, scope);\n\n // ADR 001 §3.4 (issue #34) — audit static props + binding keys\n // against the primitive's allowlist ; unknown props diagnose instead\n // of dropping silently. Key sets are static per node (a live delta\n // can only change values), so the check is once-per-node.\n checkNodeProps(node);\n\n const Primitive = PRIMITIVES[node.kind as keyof typeof PRIMITIVES];\n if (!Primitive) {\n emitDiagnostic(node.id, \"kind\", \"unknown render kind ; node not rendered\");\n return null;\n }\n\n // LSDP/1.1 §3.2.2 — a per-leaf transition on the most recent delta\n // takes precedence over the bundle-level default. Only bound props\n // can carry a wire transition (a static prop never moves). Snapshots\n // clear the directive, so the bundle default reapplies after a reset.\n //\n // We resolve here in the parent's render (useSignals() above tracks\n // these reads) rather than inside the primitive's callback — that way\n // a transition signal change re-renders this Node, which in turn re-\n // renders the primitive with the new transition prop.\n const liveTransitions: Record<string, Transition | undefined> = {};\n if (node.bindings) {\n for (const [key, path] of Object.entries(node.bindings)) {\n const ts = store.transitionSignal(scopedPath(scope, path)).value;\n if (ts !== undefined) liveTransitions[key] = ts;\n }\n }\n const transitionFor = (key: string): Transition | undefined => {\n if (key in liveTransitions) return liveTransitions[key];\n return node.transitions?.[key];\n };\n\n const children = node.children?.map((child, idx) => (\n <Tree key={child.id ?? idx} node={child} store={store} />\n ));\n\n // LSML 1.1 §5.4 — universal props applied uniformly across all\n // primitives. Pulled out of `resolved` so primitives can ignore\n // them ; the wrapper composes with whatever transform/opacity the\n // primitive's own framer-motion may apply.\n const universal = {\n visible: typeof resolved.visible === \"boolean\" ? resolved.visible : undefined,\n opacity:\n typeof resolved.universal_opacity === \"number\" ? resolved.universal_opacity : undefined,\n rotation: typeof resolved.rotation === \"number\" ? resolved.rotation : undefined,\n sizing: extractSizing(resolved.sizing),\n };\n\n // Merge live-interpolated colour values (§6.5) over the resolved\n // props — the primitive re-validates them through `parseCssColor`.\n const resolvedWithColors =\n Object.keys(bindAnimate.colorProps).length > 0\n ? { ...resolved, ...bindAnimate.colorProps }\n : resolved;\n\n let body = (\n <UniversalWrapper {...universal}>\n <Primitive\n resolved={resolvedWithColors}\n nodeId={node.id}\n transitionFor={transitionFor}\n animateInitial={node.animate_initial}\n >\n {children}\n </Primitive>\n </UniversalWrapper>\n );\n\n // Scalar bindAnimate channels apply on a wrapping motion.div (same\n // composition model as UniversalWrapper). Motion values mutate the\n // style directly — zero React re-render per frame on the hot path.\n if (bindAnimate.motionStyle) {\n body = (\n <motion.div data-lumencast-bind-animate={node.id ?? \"\"} style={bindAnimate.motionStyle}>\n {body}\n </motion.div>\n );\n }\n\n // LSML 1.1 §6.6 — when a primitive declares keyframes, wrap the\n // rendered subtree in a player that drives framer-motion through the\n // step path. The player handles replay-on-key-change and reads any\n // ambient stagger delay from StaggerContext (§6.7).\n if (node.keyframes) {\n return (\n <KeyframePlayer keyframes={node.keyframes} store={store} nodeId={node.id}>\n {body}\n </KeyframePlayer>\n );\n }\n return body;\n}\n\nfunction extractSizing(value: unknown): { x?: SizingMode; y?: SizingMode } | undefined {\n if (typeof value !== \"object\" || value === null) return undefined;\n const obj = value as { x?: unknown; y?: unknown };\n const out: { x?: SizingMode; y?: SizingMode } = {};\n if (obj.x === \"fixed\" || obj.x === \"hug\" || obj.x === \"fill\") out.x = obj.x;\n if (obj.y === \"fixed\" || obj.y === \"hug\" || obj.y === \"fill\") out.y = obj.y;\n return out.x !== undefined || out.y !== undefined ? out : undefined;\n}\n\nfunction Repeat({ node, store }: TreeProps): ReactNode {\n useSignals();\n const scope = usePathScope();\n checkNodeProps(node);\n\n const itemsBinding = node.bindings?.items;\n const items =\n itemsBinding === undefined\n ? []\n : ((store.signal(scopedPath(scope, itemsBinding)).value as unknown[] | undefined) ?? []);\n if (!Array.isArray(items)) return null;\n\n const template = node.children?.[0];\n if (!template) return null;\n\n // LSML 1.1 §6.7 — `stagger_ms` produces wave-like reveals across\n // iterations. We compute the per-iteration delay (capped) and feed\n // it to descendants via StaggerContext so the KeyframePlayer (and\n // future animate-aware primitives) can pick it up without per-\n // iteration scripting. `stagger_ms: 0` (or unset) is a no-op.\n const staggerMs = typeof node.stagger_ms === \"number\" ? node.stagger_ms : 0;\n\n return (\n <>\n {items.map((_item, idx) => {\n const delayMs = computeStaggerDelayMs(idx, staggerMs);\n const tree = (\n <PathScopeProvider key={idx} prefix={`${itemsBinding ?? \"\"}.${idx}`}>\n <Tree node={template} store={store} />\n </PathScopeProvider>\n );\n if (delayMs <= 0) return tree;\n return (\n <StaggerContext.Provider key={idx} value={delayMs}>\n {tree}\n </StaggerContext.Provider>\n );\n })}\n </>\n );\n}\n\nfunction resolveProps(node: RenderNode, store: Store, scope: string): Record<string, unknown> {\n const out: Record<string, unknown> = { ...(node.props ?? {}) };\n if (node.bindings) {\n for (const [propKey, path] of Object.entries(node.bindings)) {\n const fullPath = scopedPath(scope, path);\n out[propKey] = store.signal(fullPath).value;\n }\n }\n return out;\n}\n\n/** Helper for the useMemo deps array — read each bound signal so the\n * memo invalidates when any binding moves. */\nfunction readBindingValues(node: RenderNode, store: Store, scope: string): unknown[] {\n if (!node.bindings) return [];\n const values: unknown[] = [];\n for (const path of Object.values(node.bindings)) {\n values.push(store.signal(scopedPath(scope, path)).value);\n }\n return values;\n}\n"],"names":["Stack","resolved","children","direction","gap","numberOr","wrap","crossGap","align","justify","isHorizontal","style","jsx","v","fallback","Grid","cols","rows","MAX_LEN","CHARSET_RE","HEX_RE","NUM","ALPHA","SP","RGB_RE","HSL_RE","NAMED","parseCssColor","value","lower","m","pct","p","max","ch","warnRejectedColor","field","nodeId","emitDiagnostic","gradientIdSeq","nextGradientId","renderFill","fill","id","rad","x1","y1","x2","y2","s","i","cx","cy","r","backgroundsToCss","fills","layers","f","fillToCss","color","safeStops","c","cssWithOpacity","stops","opacity","hex","a","sanitizeFills","out","rejected","parseFills","isFill","k","Frame","transitionFor","animateInitial","x","y","width","sizeProp","height","scale","rotate","rawBackground","legacyBackground","backgrounds","clipsContent","resolveClipsContent","tx","resolveTransition","play","mountPlay","motion","toFramer","TEXT_TRANSFORMS","TEXT_DECORATIONS","FONT_STYLES","MAX_MAX_LINES","MAX_LINE_HEIGHT","MAX_LETTER_SPACING_PX","FONT_FAMILY_RE","parseFontFamily","Text","size","weight","font","parsed","colour","typography","resolveTypography","lineHeight","boundedNumber","letterSpacing","textTransform","enumValue","textDecoration","fontStyle","maxLines","positiveInteger","allow","warnRejectedTypo","min","Image","src","alt","fit","position","dimOr","MAX_SUBPATH_LEN","MAX_SUBPATH_COMMANDS","MAX_SUBPATHS","CMD_CHARS","isDigit","code","isSeparator","validatePathData","d","n","commands","sawCommand","digits","fracDigits","expDigits","parseShapePaths","rawPaths","rawPathData","warnPath","idx","entry","toFillRule","windingRule","reason","Shape","kind","legacyFill","safeColor","legacyStroke","legacyStrokeWidth","radius","ariaLabel","transition","strokes","parseStrokes","subpaths","fillRenders","allDefs","fillRefs","strokeLayers","stackedFills","stackedStrokes","effectiveStrokes","renderShape","stroke","keyPrefix","jsxs","ref","Media","loop","mute","autoplay","warned","Instance","sceneId","sceneVersion","key","PRIMITIVES","PathScopeContext","createContext","PathScopeProvider","prefix","parent","useContext","next","usePathScope","scopedPath","path","flexFor","mode","UniversalWrapper","visible","rotation","sizing","hasOpacity","hasRotation","hasSizing","FRAMER_EASE_MAP","compileForFramer","kf","steps","first","last","times","animate","pullChannel","pullTransform","prop","any","values","safe","sanitizeCssFilterString","warnRejectedFilter","FILTER_IDENTITY","defaultFor","StaggerContext","STAGGER_CAP_MS","computeStaggerDelayMs","index","staggerMs","raw","KeyframePlayer","keyframes","store","useSignals","scope","staggerDelayMs","lastKeyValue","useRef","replayTokenRef","compiled","firstFrame","ReplayOnMount","useEffect","arr","createFrameCoalescer","flush","schedule","cb","cancel","pending","frameId","disposed","onFrame","entries","NAMED_RGB","cssColorToRgba","hexToRgba","parts","channel","g","b","alphaChannel","h","hslToRgb","packed","token","clamp01","l","hue","hp","mixRgba","t","serializeRgba","rgba","BIND_ANIMATE_COLOR_PROPS","resolveScalarTargets","toFiniteScalar","ty","sx","sy","clampFilterChannel","DEFAULT_BIND_ANIMATE_TRANSITION","transitionLookupKey","NO_COLORS","useBindAnimate","node","bindings","useMotionValue","scaleX","scaleY","blur","brightness","filter","useTransform","br","colorProps","setColorProps","useState","channels","mvs","controls","colorState","animatedOnce","mounted","fullPath","declared","base","dispatch","instant","colorProp","end","prev","start","mixed","targets","warnRejectedBindValue","coalescer","disposers","effect","useMemo","Tree","Repeat","Node","resolveProps","readBindingValues","bindAnimate","checkNodeProps","Primitive","liveTransitions","ts","child","universal","extractSizing","resolvedWithColors","body","obj","itemsBinding","items","template","Fragment","_item","delayMs","tree","propKey"],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -14,6 +14,14 @@ export interface LumencastMetric {
14
14
  name: "delta_received" | "delta_applied" | "frame_dropped" | "reconnect" | "snapshot_received" | "scene_changed";
15
15
  [key: string]: unknown;
16
16
  }
17
+ /** Anti-silent-drop render diagnostic (ADR 001 §3.4, issue #34).
18
+ * Carries node identity + field + static reason — NEVER a leaf or
19
+ * prop value (Bastion R9). */
20
+ export interface LumencastDiagnostic {
21
+ nodeId: string;
22
+ field: string;
23
+ reason: string;
24
+ }
17
25
  export interface MountOptions {
18
26
  target: HTMLElement;
19
27
  /** WebSocket URL of the LSDP/1 server (wss://... in production). */
@@ -24,9 +32,27 @@ export interface MountOptions {
24
32
  testSession?: string;
25
33
  /** Required when mode === "test". */
26
34
  scene?: string;
35
+ /** Resolve the absolute URL of a scene's render bundle. Use this when the
36
+ * server is not at the default host-root LSDP/1 layout — e.g. reached
37
+ * through a gateway prefix. Given `(sceneId, sceneVersion)`, return the
38
+ * full URL to fetch, including query string. When omitted, the runtime
39
+ * derives `https://<host>/lsdp/v1/scenes/{id}/bundle?v={hash}` from
40
+ * `serverUrl` (unchanged v0.4.0 behaviour).
41
+ *
42
+ * @example
43
+ * // Orion behind ZabGate:
44
+ * resolveBundleUrl: (id, v) =>
45
+ * `https://zabgate.cyell.dev/orion/api/v1/scenes/${id}/render-bundle?v=${v}`
46
+ */
47
+ resolveBundleUrl?: (sceneId: string, sceneVersion: string) => string;
27
48
  onStatus?: (status: LumencastStatus) => void;
28
49
  onError?: (err: LumencastError) => void;
29
50
  onMetric?: (metric: LumencastMetric) => void;
51
+ /** Anti-silent-drop diagnostics stream (ADR 001 §3.4) : rejected
52
+ * values, unknown props, spec'd-but-unrendered fields. Events, not
53
+ * logs — `broadcast` builds stay console-silent. When omitted, the
54
+ * runtime falls back to a DEV-only console.warn. */
55
+ onDiagnostic?: (diagnostic: LumencastDiagnostic) => void;
30
56
  }
31
57
  export interface LumencastHandle {
32
58
  /** Tear down the WS, unmount the React tree, release timers. Idempotent. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM,CAAC;AAE7D,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,YAAY,GAAG,MAAM,CAAC;AAErE,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CAC9B;AAED,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,sBAAsB,CAAC;AAE7D,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EACA,gBAAgB,GAChB,eAAe,GACf,eAAe,GACf,WAAW,GACX,mBAAmB,GACnB,eAAe,CAAC;IACpB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,cAAc,CAAC;IACtB,IAAI,EAAE,aAAa,CAAC;IACpB,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qCAAqC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IAC7C,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;CAC9C;AAED,MAAM,WAAW,eAAe;IAC9B,4EAA4E;IAC5E,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;CAC3C;AAED,YAAY,EAAE,SAAS,EAAE,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM,CAAC;AAE7D,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,YAAY,GAAG,MAAM,CAAC;AAErE,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CAC9B;AAED,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,sBAAsB,CAAC;AAE7D,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EACA,gBAAgB,GAChB,eAAe,GACf,eAAe,GACf,WAAW,GACX,mBAAmB,GACnB,eAAe,CAAC;IACpB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;+BAE+B;AAC/B,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,cAAc,CAAC;IACtB,IAAI,EAAE,aAAa,CAAC;IACpB,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qCAAqC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;OAWG;IACH,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,MAAM,CAAC;IACrE,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IAC7C,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IAC7C;;;yDAGqD;IACrD,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,mBAAmB,KAAK,IAAI,CAAC;CAC1D;AAED,MAAM,WAAW,eAAe;IAC9B,4EAA4E;IAC5E,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;CAC3C;AAED,YAAY,EAAE,SAAS,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumencast/runtime",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Browser runtime for Lumencast — mount(), LSDP/1 transport, leaf-grain store, LSML render, animations, overlays.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -36,7 +36,7 @@
36
36
  "framer-motion": "^12.0.0",
37
37
  "react": "^19.0.0",
38
38
  "react-dom": "^19.0.0",
39
- "@lumencast/protocol": "0.4.0"
39
+ "@lumencast/protocol": "0.6.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@playwright/test": "^1.49.1",
@@ -44,13 +44,14 @@
44
44
  "@types/react-dom": "^19.0.4",
45
45
  "@types/ws": "^8.5.13",
46
46
  "@vitejs/plugin-react": "^4.3.4",
47
+ "@vitest/coverage-v8": "^4.1.8",
47
48
  "happy-dom": "^20.9.0",
48
49
  "vite": "^6.1.0",
49
50
  "vite-plugin-dts": "^4.5.0",
50
51
  "vitest": "^4.1.5",
51
52
  "ws": "^8.18.0",
52
- "@lumencast/server": "0.4.0",
53
- "@lumencast/dev-server": "0.4.0"
53
+ "@lumencast/dev-server": "0.6.0",
54
+ "@lumencast/server": "0.6.0"
54
55
  },
55
56
  "scripts": {
56
57
  "dev": "vite",
@@ -0,0 +1,63 @@
1
+ // Per-frame delta coalescing (ADR 001 RC#13 — bindAnimate anti-DoS).
2
+ //
3
+ // A live LSDP producer may push deltas at arbitrary frequency (the
4
+ // threat model assumes 1 kHz). Retargeting a Framer animation per delta
5
+ // would start hundreds of redundant animations per displayed frame. The
6
+ // coalescer buffers the LATEST value per binding key and flushes once
7
+ // per animation frame : one retarget max per rAF per binding,
8
+ // independently of the producer's rate.
9
+ //
10
+ // Pure scheduling logic, injectable rAF — unit-testable without a
11
+ // browser loop.
12
+
13
+ export interface FrameCoalescer {
14
+ /** Buffer `value` for `key` ; schedules a flush on the next frame.
15
+ * Multiple pushes on the same key within one frame keep only the
16
+ * last value (the previous targets are obsolete by construction). */
17
+ push(key: string, value: unknown): void;
18
+ /** Cancel any scheduled flush and drop pending values. */
19
+ dispose(): void;
20
+ }
21
+
22
+ type Schedule = (cb: () => void) => number;
23
+ type Cancel = (id: number) => void;
24
+
25
+ export function createFrameCoalescer(
26
+ flush: (key: string, value: unknown) => void,
27
+ schedule: Schedule = (cb) => requestAnimationFrame(cb),
28
+ cancel: Cancel = (id) => cancelAnimationFrame(id),
29
+ ): FrameCoalescer {
30
+ const pending = new Map<string, unknown>();
31
+ let frameId: number | null = null;
32
+ let disposed = false;
33
+
34
+ const onFrame = (): void => {
35
+ frameId = null;
36
+ // Swap-before-flush : a push() re-entrant from a flush callback
37
+ // lands in a fresh map and schedules the NEXT frame (never the
38
+ // current one) — the one-retarget-per-rAF bound holds.
39
+ const entries = [...pending.entries()];
40
+ pending.clear();
41
+ for (const [key, value] of entries) {
42
+ flush(key, value);
43
+ }
44
+ };
45
+
46
+ return {
47
+ push(key, value): void {
48
+ if (disposed) return;
49
+ pending.set(key, value);
50
+ if (frameId === null) {
51
+ frameId = schedule(onFrame);
52
+ }
53
+ },
54
+ dispose(): void {
55
+ disposed = true;
56
+ pending.clear();
57
+ if (frameId !== null) {
58
+ cancel(frameId);
59
+ frameId = null;
60
+ }
61
+ },
62
+ };
63
+ }
@@ -7,6 +7,12 @@
7
7
  // (`scale: [0.8, 1.05, 1]`) plus `times: [0, 0.6, 1]` that framer-motion
8
8
  // expects on its `animate` / `transition` props.
9
9
 
10
+ import {
11
+ FILTER_IDENTITY,
12
+ sanitizeCssFilterString,
13
+ warnRejectedFilter,
14
+ } from "../render/filter-clamp";
15
+
10
16
  export type KeyframeEasing = "linear" | "ease-in" | "ease-out" | "ease-in-out";
11
17
 
12
18
  export interface KeyframeStep {
@@ -59,7 +65,7 @@ export interface CompiledKeyframes {
59
65
  * or invariants are violated (first.at !== 0 or last.at !== 1) — the
60
66
  * caller then falls back to no animation.
61
67
  */
62
- export function compileForFramer(kf: Keyframes): CompiledKeyframes | undefined {
68
+ export function compileForFramer(kf: Keyframes, nodeId?: string): CompiledKeyframes | undefined {
63
69
  const steps = kf.steps;
64
70
  if (!Array.isArray(steps) || steps.length < 2) return undefined;
65
71
  const first = steps[0];
@@ -72,8 +78,8 @@ export function compileForFramer(kf: Keyframes): CompiledKeyframes | undefined {
72
78
  // For each animatable property, pull the value at every step. When a
73
79
  // step omits the property, we fall back to the previous step's value
74
80
  // (last-known-good) so framer-motion sees a coherent waypoint chain.
75
- pullChannel(steps, "opacity", animate);
76
- pullChannel(steps, "filter", animate);
81
+ pullChannel(steps, "opacity", animate, nodeId);
82
+ pullChannel(steps, "filter", animate, nodeId);
77
83
  pullTransform(steps, "scale", animate);
78
84
  pullTransform(steps, "translateX", animate);
79
85
  pullTransform(steps, "translateY", animate);
@@ -93,18 +99,31 @@ function pullChannel(
93
99
  steps: KeyframeStep[],
94
100
  prop: "opacity" | "filter",
95
101
  out: Record<string, (number | string)[]>,
102
+ nodeId?: string,
96
103
  ): void {
97
104
  let any = false;
98
105
  const values: (number | string)[] = [];
99
106
  let last: number | string | undefined;
100
107
  for (const s of steps) {
101
- const v = s[prop];
108
+ let v: number | string | undefined = s[prop];
109
+ // R8 runtime half (ADR 001 §5.1, issue #42) — keyframe filter
110
+ // strings in a hand-crafted bundle bypass the compiler clamps.
111
+ // Re-gate here ; rejected → treat as omitted (last-known-good).
112
+ if (prop === "filter" && v !== undefined) {
113
+ const safe = sanitizeCssFilterString(v);
114
+ if (safe === null) {
115
+ warnRejectedFilter("keyframes.steps[].filter", nodeId);
116
+ v = undefined;
117
+ } else {
118
+ v = safe;
119
+ }
120
+ }
102
121
  if (v !== undefined) {
103
122
  any = true;
104
123
  last = v;
105
124
  values.push(v);
106
125
  } else {
107
- values.push(last ?? (prop === "opacity" ? 1 : "none"));
126
+ values.push(last ?? (prop === "opacity" ? 1 : FILTER_IDENTITY));
108
127
  }
109
128
  }
110
129
  if (any) out[prop] = values;