@a-company/atelier 0.36.0 → 0.38.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 (99) hide show
  1. package/dist/{chunk-JPZ4F4PW.js → chunk-3ARBOSWY.js} +64 -5
  2. package/dist/chunk-3ARBOSWY.js.map +1 -0
  3. package/dist/cli.js +11469 -413
  4. package/dist/cli.js.map +1 -1
  5. package/dist/{dist-M67UZGFQ.js → dist-3YQK6PI6.js} +2 -2
  6. package/dist/index.cjs +3193 -227
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.d.cts +701 -8
  9. package/dist/index.d.ts +701 -8
  10. package/dist/index.js +7237 -72
  11. package/dist/index.js.map +1 -1
  12. package/dist/mcp.js +2898 -507
  13. package/dist/mcp.js.map +1 -1
  14. package/package.json +14 -9
  15. package/src/web/inline-app.ts +55 -4
  16. package/src/web/timeline-state-types.ts +28 -0
  17. package/src/web/timeline-view.test.ts +99 -0
  18. package/src/web/timeline-view.ts +339 -0
  19. package/src/web/workspace-app.ts +3146 -0
  20. package/templates/workspace/.claude/agents/atelier-iris.md +75 -0
  21. package/templates/workspace/.claude/agents/atelier-lux.md +67 -0
  22. package/templates/workspace/.claude/agents/atelier-quill.md +61 -0
  23. package/templates/workspace/.gitignore +30 -0
  24. package/templates/workspace/.paradigm/personas/_shared/cascade-merge.md +172 -0
  25. package/templates/workspace/CLAUDE.md +93 -0
  26. package/templates/workspace/README.md +75 -0
  27. package/templates/workspace/SETUP.md +127 -0
  28. package/templates/workspace/_brand/.atelier-brand.yaml +34 -0
  29. package/templates/workspace/_brand/DESIGN.md +56 -0
  30. package/templates/workspace/_brand/SCRIPT.md +41 -0
  31. package/templates/workspace/_brand/STORYBOARD.md +33 -0
  32. package/templates/workspace/_packs/README.md +54 -0
  33. package/templates/workspace/projects/README.md +49 -0
  34. package/templates/workspace/workspace.atelier +22 -0
  35. package/university/content/notes/N-atel-001-first-render.md +114 -0
  36. package/university/content/notes/N-atel-001-install-and-launch.md +84 -0
  37. package/university/content/notes/N-atel-001-what-is-atelier.md +51 -0
  38. package/university/content/notes/N-atel-101-easings.md +97 -0
  39. package/university/content/notes/N-atel-101-layers.md +106 -0
  40. package/university/content/notes/N-atel-101-states-and-deltas.md +94 -0
  41. package/university/content/notes/N-atel-101-the-atelier-format.md +72 -0
  42. package/university/content/notes/N-atel-201-authoring-tools.md +141 -0
  43. package/university/content/notes/N-atel-201-mcp-overview.md +86 -0
  44. package/university/content/notes/N-atel-201-patterns.md +108 -0
  45. package/university/content/notes/N-atel-201-visual-and-effects.md +125 -0
  46. package/university/content/notes/N-atel-301-composition-and-overlays.md +141 -0
  47. package/university/content/notes/N-atel-301-effects.md +136 -0
  48. package/university/content/notes/N-atel-301-images-and-video.md +126 -0
  49. package/university/content/notes/N-atel-301-shapes-and-text.md +118 -0
  50. package/university/content/notes/N-atel-401-hierarchical-states.md +71 -0
  51. package/university/content/notes/N-atel-401-motion-deep-dive.md +106 -0
  52. package/university/content/notes/N-atel-401-presets-and-templates.md +98 -0
  53. package/university/content/notes/N-atel-401-transitions.md +94 -0
  54. package/university/content/notes/N-atel-501-detected-vs-user-edited.md +76 -0
  55. package/university/content/notes/N-atel-501-layer-tag-isolation.md +62 -0
  56. package/university/content/notes/N-atel-501-silence-trim.md +98 -0
  57. package/university/content/notes/N-atel-501-transcribe-and-captions.md +98 -0
  58. package/university/content/notes/N-atel-601-carousel.md +71 -0
  59. package/university/content/notes/N-atel-601-overlay-rules.md +96 -0
  60. package/university/content/notes/N-atel-601-recipe-tools-and-apply.md +84 -0
  61. package/university/content/notes/N-atel-601-studio-recipe.md +103 -0
  62. package/university/content/notes/N-atel-701-choosing-output.md +68 -0
  63. package/university/content/notes/N-atel-701-png-and-frames.md +84 -0
  64. package/university/content/notes/N-atel-701-vector.md +85 -0
  65. package/university/content/notes/N-atel-701-video.md +88 -0
  66. package/university/content/notes/N-atel-801-editing-surface.md +69 -0
  67. package/university/content/notes/N-atel-801-live-bridge.md +84 -0
  68. package/university/content/notes/N-atel-801-studio-app.md +72 -0
  69. package/university/content/notes/N-atel-801-symbiotic-loop.md +56 -0
  70. package/university/content/paths/LP-atel-001.yaml +21 -0
  71. package/university/content/paths/LP-atel-101.yaml +22 -0
  72. package/university/content/paths/LP-atel-201.yaml +23 -0
  73. package/university/content/paths/LP-atel-301.yaml +22 -0
  74. package/university/content/paths/LP-atel-401.yaml +22 -0
  75. package/university/content/paths/LP-atel-501.yaml +22 -0
  76. package/university/content/paths/LP-atel-601.yaml +22 -0
  77. package/university/content/paths/LP-atel-701.yaml +22 -0
  78. package/university/content/paths/LP-atel-801.yaml +22 -0
  79. package/university/content/quizzes/Q-atel-001-orientation.yaml +66 -0
  80. package/university/content/quizzes/Q-atel-101-document-model.yaml +66 -0
  81. package/university/content/quizzes/Q-atel-201-mcp-authoring.yaml +66 -0
  82. package/university/content/quizzes/Q-atel-301-visual-system.yaml +66 -0
  83. package/university/content/quizzes/Q-atel-401-state-machines.yaml +66 -0
  84. package/university/content/quizzes/Q-atel-501-video-pipeline.yaml +66 -0
  85. package/university/content/quizzes/Q-atel-601-recipes.yaml +66 -0
  86. package/university/content/quizzes/Q-atel-701-export.yaml +66 -0
  87. package/university/content/quizzes/Q-atel-801-studio-loop.yaml +66 -0
  88. package/university/index.yaml +720 -0
  89. package/university/pack.yaml +21 -0
  90. package/dist/chunk-5QQESXI6.js +0 -4432
  91. package/dist/chunk-5QQESXI6.js.map +0 -1
  92. package/dist/chunk-JPZ4F4PW.js.map +0 -1
  93. package/dist/cli.cjs +0 -6313
  94. package/dist/cli.cjs.map +0 -1
  95. package/dist/cli.d.cts +0 -1
  96. package/dist/cli.d.ts +0 -1
  97. package/dist/mcp.cjs +0 -5462
  98. package/dist/mcp.cjs.map +0 -1
  99. /package/dist/{dist-M67UZGFQ.js.map → dist-3YQK6PI6.js.map} +0 -0
@@ -541,12 +541,15 @@ function resolveFrame(doc, stateName, frame, overrideDeltas) {
541
541
  throw new Error(`State "${stateName}" not found in document "${doc.name}"`);
542
542
  }
543
543
  const deltasByLayerProperty = groupDeltas(overrideDeltas ?? state.deltas);
544
+ const ancestorClipStartFrame = computeAncestorClipStartFrames(doc);
544
545
  const resolvedLayers = doc.layers.map((layer) => {
545
546
  const computedProperties = {};
546
547
  const layerDeltas = deltasByLayerProperty.get(layer.id);
547
548
  if (layerDeltas) {
549
+ const offset = ancestorClipStartFrame.get(layer.id) ?? 0;
550
+ const localFrame = frame - offset;
548
551
  for (const [property, deltas] of layerDeltas) {
549
- const value = resolvePropertyAtFrame(deltas, frame);
552
+ const value = resolvePropertyAtFrame(deltas, localFrame);
550
553
  if (value !== void 0) {
551
554
  computedProperties[property] = value;
552
555
  }
@@ -567,6 +570,26 @@ function resolveFrame(doc, stateName, frame, overrideDeltas) {
567
570
  });
568
571
  return { frame, stateName, layers: resolvedLayers };
569
572
  }
573
+ function computeAncestorClipStartFrames(doc) {
574
+ const byId = /* @__PURE__ */ new Map();
575
+ for (const l of doc.layers) byId.set(l.id, l);
576
+ const result = /* @__PURE__ */ new Map();
577
+ for (const layer of doc.layers) {
578
+ let cursor = byId.get(layer.parentId ?? "");
579
+ const visited = /* @__PURE__ */ new Set();
580
+ while (cursor) {
581
+ if (visited.has(cursor.id)) break;
582
+ visited.add(cursor.id);
583
+ if (cursor.visual.type === "video") {
584
+ const v = cursor.visual;
585
+ result.set(layer.id, v.startFrame ?? 0);
586
+ break;
587
+ }
588
+ cursor = byId.get(cursor.parentId ?? "");
589
+ }
590
+ }
591
+ return result;
592
+ }
570
593
  function groupDeltas(deltas) {
571
594
  const map = /* @__PURE__ */ new Map();
572
595
  for (const delta of deltas) {
@@ -985,7 +1008,40 @@ function renderVideo(ctx, eff, sourceTime, provider) {
985
1008
  if (!src) return;
986
1009
  const frame = provider(src, sourceTime, eff.width, eff.height);
987
1010
  if (!frame) return;
988
- ctx.drawImage(frame, 0, 0, eff.width, eff.height);
1011
+ const fit = visual.objectFit ?? "contain";
1012
+ if (fit === "fill") {
1013
+ ctx.drawImage(frame, 0, 0, eff.width, eff.height);
1014
+ return;
1015
+ }
1016
+ const src_w = frame.videoWidth ?? frame.width ?? 0;
1017
+ const src_h = frame.videoHeight ?? frame.height ?? 0;
1018
+ if (src_w <= 0 || src_h <= 0) {
1019
+ ctx.drawImage(frame, 0, 0, eff.width, eff.height);
1020
+ return;
1021
+ }
1022
+ const srcAspect = src_w / src_h;
1023
+ const dstAspect = eff.width / eff.height;
1024
+ let dw, dh, dx, dy;
1025
+ if (fit === "cover") {
1026
+ if (srcAspect > dstAspect) {
1027
+ dh = eff.height;
1028
+ dw = dh * srcAspect;
1029
+ } else {
1030
+ dw = eff.width;
1031
+ dh = dw / srcAspect;
1032
+ }
1033
+ } else {
1034
+ if (srcAspect > dstAspect) {
1035
+ dw = eff.width;
1036
+ dh = dw / srcAspect;
1037
+ } else {
1038
+ dh = eff.height;
1039
+ dw = dh * srcAspect;
1040
+ }
1041
+ }
1042
+ dx = (eff.width - dw) / 2;
1043
+ dy = (eff.height - dh) / 2;
1044
+ ctx.drawImage(frame, dx, dy, dw, dh);
989
1045
  }
990
1046
  function renderRef(ctx, eff, opts, _parentDoc) {
991
1047
  const visual = eff.visual;
@@ -1096,8 +1152,11 @@ function renderFrame(ctx, resolvedFrame, doc, optsOrCache) {
1096
1152
  maxRefDepth = opts.maxRefDepth ?? 4;
1097
1153
  }
1098
1154
  const { width, height } = doc.canvas;
1099
- ctx.fillStyle = doc.canvas.background ?? "transparent";
1100
- ctx.fillRect(0, 0, width, height);
1155
+ ctx.clearRect(0, 0, width, height);
1156
+ if (doc.canvas.background) {
1157
+ ctx.fillStyle = doc.canvas.background;
1158
+ ctx.fillRect(0, 0, width, height);
1159
+ }
1101
1160
  const effMap = /* @__PURE__ */ new Map();
1102
1161
  const effList = [];
1103
1162
  const videoSourceTimeMap = /* @__PURE__ */ new Map();
@@ -1368,4 +1427,4 @@ export {
1368
1427
  renderFrame,
1369
1428
  ImageCache
1370
1429
  };
1371
- //# sourceMappingURL=chunk-JPZ4F4PW.js.map
1430
+ //# sourceMappingURL=chunk-3ARBOSWY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../math/src/easing.ts","../../math/src/spring.ts","../../math/src/lerp.ts","../../math/src/color.ts","../../math/src/path.ts","../../core/src/resolver/delta-resolver.ts","../../core/src/resolver/easing-resolver.ts","../../core/src/expressions/expression-evaluator.ts","../../core/src/resolver/frame-resolver.ts","../../core/src/validation/overlap-validator.ts","../../core/src/builder/document-builder.ts","../../core/src/units/resolve-units.ts","../../core/src/presets/preset-resolver.ts","../../core/src/state/state-machine.ts","../../core/src/templates/template-resolver.ts","../../core/src/audio/audio-timing.ts","../../canvas/src/styles.ts","../../canvas/src/apply-properties.ts","../../canvas/src/renderers/shape-renderer.ts","../../canvas/src/renderers/text-renderer.ts","../../canvas/src/renderers/image-renderer.ts","../../canvas/src/renderers/video-renderer.ts","../../canvas/src/renderers/ref-renderer.ts","../../canvas/src/render-frame.ts","../../canvas/src/image-cache.ts"],"sourcesContent":["/**\n * Linear easing — no acceleration.\n */\nexport function linear(t: number): number {\n return t;\n}\n\n/**\n * Cubic bezier easing — CSS-compatible.\n * Implements the same algorithm as CSS cubic-bezier().\n * @param x1 - first control point x\n * @param y1 - first control point y\n * @param x2 - second control point x\n * @param y2 - second control point y\n */\nexport function cubicBezier(\n x1: number,\n y1: number,\n x2: number,\n y2: number,\n): (t: number) => number {\n // Use binary search to find the parametric t value that gives us our x,\n // then compute the corresponding y. This is the standard algorithm used\n // by browsers for CSS transitions.\n\n return (t: number): number => {\n if (t <= 0) return 0;\n if (t >= 1) return 1;\n\n // Binary search for the parametric t value that gives us our x\n let lo = 0;\n let hi = 1;\n let mid: number = 0;\n\n for (let i = 0; i < 20; i++) {\n mid = (lo + hi) / 2;\n const x = sampleBezier(x1, x2, mid);\n if (Math.abs(x - t) < 1e-6) break;\n if (x < t) lo = mid;\n else hi = mid;\n }\n\n mid = (lo + hi) / 2;\n return sampleBezier(y1, y2, mid);\n };\n}\n\n/** Sample a single axis of a cubic bezier at parametric t */\nfunction sampleBezier(p1: number, p2: number, t: number): number {\n // B(t) = 3(1-t)^2*t*p1 + 3(1-t)*t^2*p2 + t^3\n return 3 * (1 - t) * (1 - t) * t * p1 + 3 * (1 - t) * t * t * p2 + t * t * t;\n}\n\n/** Common CSS easing presets */\nexport const easeIn = cubicBezier(0.42, 0, 1, 1);\nexport const easeOut = cubicBezier(0, 0, 0.58, 1);\nexport const easeInOut = cubicBezier(0.42, 0, 0.58, 1);\n\n/**\n * Step easing — jumps between discrete values.\n * @param steps - number of steps\n * @param position - \"start\" or \"end\" (default \"end\")\n */\nexport function step(\n steps: number,\n position: \"start\" | \"end\" = \"end\",\n): (t: number) => number {\n return (t: number): number => {\n if (t <= 0) return position === \"start\" ? 1 / steps : 0;\n if (t >= 1) return 1;\n\n const s = Math.floor(t * steps);\n if (position === \"start\") {\n return Math.min((s + 1) / steps, 1);\n }\n return s / steps;\n };\n}\n","export interface SpringConfig {\n mass?: number; // default 1\n stiffness?: number; // default 100\n damping?: number; // default 10\n velocity?: number; // default 0\n}\n\n/**\n * Creates a spring easing function.\n * Uses damped harmonic oscillator physics.\n * Returns a function that takes t (0-1) and returns the spring value.\n *\n * The spring always goes from 0 to 1, but may overshoot.\n */\nexport function spring(config: SpringConfig = {}): (t: number) => number {\n const {\n mass = 1,\n stiffness = 100,\n damping = 10,\n velocity = 0,\n } = config;\n\n const w0 = Math.sqrt(stiffness / mass); // natural frequency\n const zeta = damping / (2 * Math.sqrt(stiffness * mass)); // damping ratio\n\n // Determine total duration to normalize t\n // We pre-calculate a reasonable duration where the spring settles\n const duration = estimateSettleTime(zeta, w0);\n\n return (t: number): number => {\n if (t <= 0) return 0;\n if (t >= 1) return 1;\n\n const time = t * duration;\n let value: number;\n\n if (zeta < 1) {\n // Underdamped\n const wd = w0 * Math.sqrt(1 - zeta * zeta);\n const A = 1;\n const B = (zeta * w0 + velocity) / wd;\n value =\n 1 -\n Math.exp(-zeta * w0 * time) *\n (A * Math.cos(wd * time) + B * Math.sin(wd * time));\n } else if (zeta === 1) {\n // Critically damped\n value = 1 - Math.exp(-w0 * time) * (1 + (w0 + velocity) * time);\n } else {\n // Overdamped\n const s1 = -w0 * (zeta - Math.sqrt(zeta * zeta - 1));\n const s2 = -w0 * (zeta + Math.sqrt(zeta * zeta - 1));\n const A = (velocity - s2) / (s1 - s2);\n const B = 1 - A;\n value = 1 - A * Math.exp(s1 * time) - B * Math.exp(s2 * time);\n }\n\n return value;\n };\n}\n\n/**\n * Estimates the time for a spring to settle within 0.1% of target.\n */\nfunction estimateSettleTime(zeta: number, w0: number): number {\n if (zeta >= 1) {\n return 10 / (zeta * w0);\n }\n // For underdamped: settling ~ -ln(0.001) / (zeta * w0)\n return Math.log(1000) / (zeta * w0);\n}\n","/**\n * Linear interpolation between two numbers.\n */\nexport function lerp(a: number, b: number, t: number): number {\n return a + (b - a) * t;\n}\n\n/**\n * Clamp a value between min and max.\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\n/**\n * Interpolate between two multi-dimensional values.\n * Arrays must be the same length.\n */\nexport function lerpArray(a: number[], b: number[], t: number): number[] {\n return a.map((v, i) => lerp(v, b[i], t));\n}\n\n/**\n * Re-maps a value from one range to another.\n */\nexport function remap(\n value: number,\n inMin: number,\n inMax: number,\n outMin: number,\n outMax: number,\n): number {\n const t = (value - inMin) / (inMax - inMin);\n return lerp(outMin, outMax, t);\n}\n","import { lerp, clamp } from \"./lerp.js\";\n\nexport interface RGBA {\n r: number; // 0-255\n g: number; // 0-255\n b: number; // 0-255\n a: number; // 0-1\n}\n\nexport interface HSLA {\n h: number; // 0-360\n s: number; // 0-100\n l: number; // 0-100\n a: number; // 0-1\n}\n\n/**\n * Parse a hex color string to RGBA.\n * Supports: #RGB, #RGBA, #RRGGBB, #RRGGBBAA\n */\nexport function hexToRgba(hex: string): RGBA {\n let h = hex.replace(\"#\", \"\");\n\n if (h.length === 3)\n h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2] + \"ff\";\n else if (h.length === 4)\n h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2] + h[3] + h[3];\n else if (h.length === 6) h = h + \"ff\";\n\n return {\n r: parseInt(h.slice(0, 2), 16),\n g: parseInt(h.slice(2, 4), 16),\n b: parseInt(h.slice(4, 6), 16),\n a: parseInt(h.slice(6, 8), 16) / 255,\n };\n}\n\n/**\n * Convert RGBA to hex string.\n */\nexport function rgbaToHex(color: RGBA): string {\n const r = clamp(Math.round(color.r), 0, 255)\n .toString(16)\n .padStart(2, \"0\");\n const g = clamp(Math.round(color.g), 0, 255)\n .toString(16)\n .padStart(2, \"0\");\n const b = clamp(Math.round(color.b), 0, 255)\n .toString(16)\n .padStart(2, \"0\");\n const a = clamp(Math.round(color.a * 255), 0, 255)\n .toString(16)\n .padStart(2, \"0\");\n return `#${r}${g}${b}${a === \"ff\" ? \"\" : a}`;\n}\n\n/**\n * Interpolate between two RGBA colors.\n */\nexport function lerpRgba(a: RGBA, b: RGBA, t: number): RGBA {\n return {\n r: lerp(a.r, b.r, t),\n g: lerp(a.g, b.g, t),\n b: lerp(a.b, b.b, t),\n a: lerp(a.a, b.a, t),\n };\n}\n\n/**\n * Convert RGBA to HSLA.\n */\nexport function rgbaToHsla(color: RGBA): HSLA {\n const r = color.r / 255;\n const g = color.g / 255;\n const b = color.b / 255;\n\n const max = Math.max(r, g, b);\n const min = Math.min(r, g, b);\n const d = max - min;\n const l = (max + min) / 2;\n\n let h = 0;\n let s = 0;\n\n if (d !== 0) {\n s = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) / 6;\n else if (max === g) h = ((b - r) / d + 2) / 6;\n else h = ((r - g) / d + 4) / 6;\n }\n\n return { h: h * 360, s: s * 100, l: l * 100, a: color.a };\n}\n\n/**\n * Convert HSLA to RGBA.\n */\nexport function hslaToRgba(color: HSLA): RGBA {\n const h = color.h / 360;\n const s = color.s / 100;\n const l = color.l / 100;\n\n let r: number, g: number, b: number;\n\n if (s === 0) {\n r = g = b = l;\n } else {\n const q = l < 0.5 ? l * (1 + s) : l + s - l * s;\n const p = 2 * l - q;\n r = hueToRgb(p, q, h + 1 / 3);\n g = hueToRgb(p, q, h);\n b = hueToRgb(p, q, h - 1 / 3);\n }\n\n return {\n r: Math.round(r * 255),\n g: Math.round(g * 255),\n b: Math.round(b * 255),\n a: color.a,\n };\n}\n\nfunction hueToRgb(p: number, q: number, t: number): number {\n if (t < 0) t += 1;\n if (t > 1) t -= 1;\n if (t < 1 / 6) return p + (q - p) * 6 * t;\n if (t < 1 / 2) return q;\n if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;\n return p;\n}\n\n/**\n * Interpolate between two HSLA colors (shortest hue path).\n */\nexport function lerpHsla(a: HSLA, b: HSLA, t: number): HSLA {\n // Handle hue wrapping — take the shortest path\n let dh = b.h - a.h;\n if (dh > 180) dh -= 360;\n if (dh < -180) dh += 360;\n\n return {\n h: (((a.h + dh * t) % 360) + 360) % 360,\n s: lerp(a.s, b.s, t),\n l: lerp(a.l, b.l, t),\n a: lerp(a.a, b.a, t),\n };\n}\n","/**\n * Path interpolation utilities for motion paths.\n * Evaluates position and tangent along a path defined by points with bezier handles.\n */\n\nexport interface PathPoint2D {\n x: number;\n y: number;\n in?: { x: number; y: number };\n out?: { x: number; y: number };\n}\n\nexport interface PathPosition {\n x: number;\n y: number;\n /** Tangent angle in degrees */\n angle: number;\n}\n\n/**\n * Compute the length of a single cubic bezier segment using subdivision.\n */\nfunction bezierSegmentLength(\n p0x: number, p0y: number,\n c0x: number, c0y: number,\n c1x: number, c1y: number,\n p1x: number, p1y: number,\n steps = 32,\n): number {\n let length = 0;\n let prevX = p0x;\n let prevY = p0y;\n for (let i = 1; i <= steps; i++) {\n const t = i / steps;\n const mt = 1 - t;\n const x = mt * mt * mt * p0x + 3 * mt * mt * t * c0x + 3 * mt * t * t * c1x + t * t * t * p1x;\n const y = mt * mt * mt * p0y + 3 * mt * mt * t * c0y + 3 * mt * t * t * c1y + t * t * t * p1y;\n const dx = x - prevX;\n const dy = y - prevY;\n length += Math.sqrt(dx * dx + dy * dy);\n prevX = x;\n prevY = y;\n }\n return length;\n}\n\n/**\n * Evaluate a cubic bezier at parameter t, returning position and tangent.\n */\nfunction evalBezier(\n p0x: number, p0y: number,\n c0x: number, c0y: number,\n c1x: number, c1y: number,\n p1x: number, p1y: number,\n t: number,\n): PathPosition {\n const mt = 1 - t;\n const x = mt * mt * mt * p0x + 3 * mt * mt * t * c0x + 3 * mt * t * t * c1x + t * t * t * p1x;\n const y = mt * mt * mt * p0y + 3 * mt * mt * t * c0y + 3 * mt * t * t * c1y + t * t * t * p1y;\n\n // Tangent (first derivative)\n const tx = 3 * mt * mt * (c0x - p0x) + 6 * mt * t * (c1x - c0x) + 3 * t * t * (p1x - c1x);\n const ty = 3 * mt * mt * (c0y - p0y) + 6 * mt * t * (c1y - c0y) + 3 * t * t * (p1y - c1y);\n\n const angle = Math.atan2(ty, tx) * (180 / Math.PI);\n\n return { x, y, angle };\n}\n\n/**\n * Build a lookup table of cumulative segment lengths for a path.\n */\nfunction buildSegmentLengths(points: PathPoint2D[], closed: boolean): number[] {\n const segCount = closed ? points.length : points.length - 1;\n const lengths: number[] = [];\n\n for (let i = 0; i < segCount; i++) {\n const p0 = points[i];\n const p1 = points[(i + 1) % points.length];\n\n const c0x = p0.x + (p0.out?.x ?? 0);\n const c0y = p0.y + (p0.out?.y ?? 0);\n const c1x = p1.x + (p1.in?.x ?? 0);\n const c1y = p1.y + (p1.in?.y ?? 0);\n\n lengths.push(bezierSegmentLength(p0.x, p0.y, c0x, c0y, c1x, c1y, p1.x, p1.y));\n }\n\n return lengths;\n}\n\n/**\n * Evaluate a point along a path at a given progress (0–1).\n * Uses arc-length parameterization for uniform speed.\n */\nexport function evaluatePathAtProgress(\n points: PathPoint2D[],\n progress: number,\n closed = false,\n): PathPosition {\n if (points.length < 2) {\n return { x: points[0]?.x ?? 0, y: points[0]?.y ?? 0, angle: 0 };\n }\n\n // Clamp progress\n const t = Math.max(0, Math.min(1, progress));\n\n const segLengths = buildSegmentLengths(points, closed);\n const totalLength = segLengths.reduce((a, b) => a + b, 0);\n\n if (totalLength === 0) {\n return { x: points[0].x, y: points[0].y, angle: 0 };\n }\n\n const targetLength = t * totalLength;\n\n // Find which segment the target length falls in\n let accumulated = 0;\n for (let i = 0; i < segLengths.length; i++) {\n const segLen = segLengths[i];\n if (accumulated + segLen >= targetLength || i === segLengths.length - 1) {\n // Progress within this segment\n const segProgress = segLen === 0 ? 0 : (targetLength - accumulated) / segLen;\n\n const p0 = points[i];\n const p1 = points[(i + 1) % points.length];\n\n const c0x = p0.x + (p0.out?.x ?? 0);\n const c0y = p0.y + (p0.out?.y ?? 0);\n const c1x = p1.x + (p1.in?.x ?? 0);\n const c1y = p1.y + (p1.in?.y ?? 0);\n\n return evalBezier(p0.x, p0.y, c0x, c0y, c1x, c1y, p1.x, p1.y, segProgress);\n }\n accumulated += segLen;\n }\n\n // Fallback (shouldn't reach here)\n const last = points[points.length - 1];\n return { x: last.x, y: last.y, angle: 0 };\n}\n","import type { Delta, FrameRange } from \"@a-company/atelier-types\";\nimport { lerp, clamp, hexToRgba, lerpRgba, rgbaToHex } from \"@a-company/atelier-math\";\nimport { resolveEasing } from \"./easing-resolver.js\";\nimport { isExpression, evaluateExpression, type ExpressionContext } from \"../expressions/expression-evaluator.js\";\n\n/**\n * Check if a frame is within a delta's range (inclusive).\n */\nexport function isFrameInRange(frame: number, range: FrameRange): boolean {\n return frame >= range[0] && frame <= range[1];\n}\n\n/**\n * Compute the progress (0-1) of a frame within a delta's range.\n */\nexport function computeProgress(frame: number, range: FrameRange): number {\n const [start, end] = range;\n if (start === end) return 1; // instantaneous\n return clamp((frame - start) / (end - start), 0, 1);\n}\n\n/**\n * Resolve a single delta's value at a given frame.\n * Returns undefined if the frame is outside the delta's range.\n * Returns the interpolated value if within range.\n *\n * If `from` or `to` is an expression object `{ expr: \"...\" }`,\n * it is evaluated with the current animation context before interpolation.\n */\nexport function resolveDeltaValue(delta: Delta, frame: number): unknown | undefined {\n if (!isFrameInRange(frame, delta.range)) {\n return undefined;\n }\n\n const progress = computeProgress(frame, delta.range);\n const easingFn = resolveEasing(delta.easing);\n const easedProgress = easingFn(progress);\n\n const exprCtx: ExpressionContext = {\n t: easedProgress,\n progress,\n frame,\n duration: delta.range[1] - delta.range[0],\n };\n\n const from = isExpression(delta.from)\n ? evaluateExpression(delta.from.expr, exprCtx)\n : delta.from;\n\n const to = isExpression(delta.to)\n ? evaluateExpression(delta.to.expr, exprCtx)\n : delta.to;\n\n return interpolateValue(from, to, easedProgress);\n}\n\n/**\n * Interpolate between two values based on eased progress.\n * Handles numbers, strings (pass-through at threshold), and nested objects.\n */\nexport function interpolateValue(from: unknown, to: unknown, t: number): unknown {\n // Number interpolation\n if (typeof from === \"number\" && typeof to === \"number\") {\n return lerp(from, to, t);\n }\n\n // Hex color interpolation\n if (typeof from === \"string\" && typeof to === \"string\") {\n if (from.startsWith(\"#\") && to.startsWith(\"#\")) {\n return rgbaToHex(lerpRgba(hexToRgba(from), hexToRgba(to), t));\n }\n // Non-color strings — snap at end\n return t >= 1 ? to : from;\n }\n\n // Boolean interpolation — snap at midpoint for frame-precise control\n if (typeof from === \"boolean\" && typeof to === \"boolean\") {\n return t >= 0.5 ? to : from;\n }\n\n // For unknown types, snap at end\n return t >= 1 ? to : from;\n}\n\n/**\n * Given multiple deltas for the SAME layer+property, find the active one\n * at a given frame and return its resolved value.\n *\n * If no delta is active at this frame, holds the `to` value of the most\n * recent completed delta (the one whose range ended most recently before\n * this frame). This prevents properties from reverting after animation ends.\n *\n * Assumes no overlaps (validated elsewhere).\n */\nexport function resolvePropertyAtFrame(\n deltas: Delta[],\n frame: number,\n): unknown | undefined {\n // Check for an active delta first\n for (const delta of deltas) {\n if (isFrameInRange(frame, delta.range)) {\n return resolveDeltaValue(delta, frame);\n }\n }\n\n // No active delta — hold the `to` value of the most recently completed delta\n let lastCompleted: Delta | undefined;\n for (const delta of deltas) {\n if (frame > delta.range[1]) {\n if (!lastCompleted || delta.range[1] > lastCompleted.range[1]) {\n lastCompleted = delta;\n }\n }\n }\n\n if (!lastCompleted) return undefined;\n\n // If the held `to` value is an expression, evaluate at t=1\n if (isExpression(lastCompleted.to)) {\n return evaluateExpression(lastCompleted.to.expr, {\n t: 1,\n progress: 1,\n frame,\n duration: lastCompleted.range[1] - lastCompleted.range[0],\n });\n }\n\n return lastCompleted.to;\n}\n","import type { Easing } from \"@a-company/atelier-types\";\nimport { linear, cubicBezier, easeIn, easeOut, easeInOut, step, spring } from \"@a-company/atelier-math\";\n\n/**\n * Converts an Easing type definition into an executable easing function.\n * Returns a function (t: number) => number where t is 0-1.\n */\nexport function resolveEasing(easing: Easing | undefined): (t: number) => number {\n if (!easing) return linear;\n\n // String presets\n if (typeof easing === \"string\") {\n switch (easing) {\n case \"linear\": return linear;\n case \"ease-in\": return easeIn;\n case \"ease-out\": return easeOut;\n case \"ease-in-out\": return easeInOut;\n default: return linear;\n }\n }\n\n // Object easing definitions\n switch (easing.type) {\n case \"linear\":\n return linear;\n case \"cubic-bezier\":\n return cubicBezier(easing.x1, easing.y1, easing.x2, easing.y2);\n case \"spring\":\n return spring({\n mass: easing.mass,\n stiffness: easing.stiffness,\n damping: easing.damping,\n velocity: easing.velocity,\n });\n case \"step\":\n return step(easing.steps, easing.position);\n default:\n return linear;\n }\n}\n","/**\n * Safe recursive descent expression evaluator.\n * No eval(), no Function(), no code generation.\n *\n * Supports:\n * - Numbers: 42, 3.14, -1\n * - Operators: + - * / % **\n * - Parentheses: (expr)\n * - Math functions: sin, cos, tan, abs, min, max, floor, ceil, round, sqrt, pow, clamp, sign, log\n * - Constants: pi, tau, e\n * - Context variables: t, frame, duration, progress\n * - Comparison: <, >, <=, >=, ==, !=\n * - Ternary: condition ? trueExpr : falseExpr\n */\n\n/** Context variables available during expression evaluation */\nexport interface ExpressionContext {\n /** Eased progress 0–1 */\n t: number;\n /** Raw progress 0–1 (before easing) */\n progress: number;\n /** Current frame number */\n frame: number;\n /** Delta duration in frames */\n duration: number;\n}\n\n// ── Tokenizer ───────────────────────────────────────────────\n\ntype TokenType =\n | \"number\"\n | \"ident\"\n | \"op\"\n | \"lparen\"\n | \"rparen\"\n | \"comma\"\n | \"question\"\n | \"colon\"\n | \"compare\"\n | \"eof\";\n\ninterface Token {\n type: TokenType;\n value: string;\n}\n\nfunction tokenize(expr: string): Token[] {\n const tokens: Token[] = [];\n let i = 0;\n\n while (i < expr.length) {\n const ch = expr[i];\n\n // Whitespace\n if (ch === \" \" || ch === \"\\t\" || ch === \"\\n\" || ch === \"\\r\") {\n i++;\n continue;\n }\n\n // Numbers\n if ((ch >= \"0\" && ch <= \"9\") || ch === \".\") {\n let num = \"\";\n while (i < expr.length && ((expr[i] >= \"0\" && expr[i] <= \"9\") || expr[i] === \".\")) {\n num += expr[i++];\n }\n tokens.push({ type: \"number\", value: num });\n continue;\n }\n\n // Identifiers (variables, functions, constants)\n if ((ch >= \"a\" && ch <= \"z\") || (ch >= \"A\" && ch <= \"Z\") || ch === \"_\") {\n let id = \"\";\n while (i < expr.length && ((expr[i] >= \"a\" && expr[i] <= \"z\") || (expr[i] >= \"A\" && expr[i] <= \"Z\") || (expr[i] >= \"0\" && expr[i] <= \"9\") || expr[i] === \"_\")) {\n id += expr[i++];\n }\n tokens.push({ type: \"ident\", value: id });\n continue;\n }\n\n // Comparison operators (must check before single-char ops)\n if (ch === \"<\" || ch === \">\" || ch === \"!\" || ch === \"=\") {\n if (i + 1 < expr.length && expr[i + 1] === \"=\") {\n tokens.push({ type: \"compare\", value: ch + \"=\" });\n i += 2;\n continue;\n }\n if (ch === \"<\" || ch === \">\") {\n tokens.push({ type: \"compare\", value: ch });\n i++;\n continue;\n }\n }\n\n // Power operator\n if (ch === \"*\" && i + 1 < expr.length && expr[i + 1] === \"*\") {\n tokens.push({ type: \"op\", value: \"**\" });\n i += 2;\n continue;\n }\n\n // Operators\n if (ch === \"+\" || ch === \"-\" || ch === \"*\" || ch === \"/\" || ch === \"%\") {\n tokens.push({ type: \"op\", value: ch });\n i++;\n continue;\n }\n\n // Grouping and function calls\n if (ch === \"(\") { tokens.push({ type: \"lparen\", value: \"(\" }); i++; continue; }\n if (ch === \")\") { tokens.push({ type: \"rparen\", value: \")\" }); i++; continue; }\n if (ch === \",\") { tokens.push({ type: \"comma\", value: \",\" }); i++; continue; }\n\n // Ternary\n if (ch === \"?\") { tokens.push({ type: \"question\", value: \"?\" }); i++; continue; }\n if (ch === \":\") { tokens.push({ type: \"colon\", value: \":\" }); i++; continue; }\n\n throw new Error(`Expression: unexpected character '${ch}' at position ${i}`);\n }\n\n tokens.push({ type: \"eof\", value: \"\" });\n return tokens;\n}\n\n// ── Parser + Evaluator ──────────────────────────────────────\n\nconst CONSTANTS: Record<string, number> = {\n pi: Math.PI,\n PI: Math.PI,\n tau: Math.PI * 2,\n TAU: Math.PI * 2,\n e: Math.E,\n E: Math.E,\n};\n\nconst FUNCTIONS: Record<string, (...args: number[]) => number> = {\n sin: Math.sin,\n cos: Math.cos,\n tan: Math.tan,\n abs: Math.abs,\n floor: Math.floor,\n ceil: Math.ceil,\n round: Math.round,\n sqrt: Math.sqrt,\n sign: Math.sign,\n log: Math.log,\n min: (...args) => Math.min(...args),\n max: (...args) => Math.max(...args),\n pow: (a, b) => Math.pow(a, b),\n clamp: (v, lo, hi) => Math.min(Math.max(v, lo), hi),\n};\n\nclass Parser {\n private tokens: Token[];\n private pos = 0;\n private ctx: ExpressionContext;\n\n constructor(tokens: Token[], ctx: ExpressionContext) {\n this.tokens = tokens;\n this.ctx = ctx;\n }\n\n private peek(): Token {\n return this.tokens[this.pos];\n }\n\n private consume(expectedType?: TokenType): Token {\n const tok = this.tokens[this.pos++];\n if (expectedType && tok.type !== expectedType) {\n throw new Error(`Expression: expected ${expectedType} but got ${tok.type} '${tok.value}'`);\n }\n return tok;\n }\n\n /** Entry: ternary (lowest precedence) */\n parse(): number {\n const result = this.parseTernary();\n if (this.peek().type !== \"eof\") {\n throw new Error(`Expression: unexpected token '${this.peek().value}'`);\n }\n return result;\n }\n\n /** ternary: comparison ? expr : expr */\n private parseTernary(): number {\n const condition = this.parseComparison();\n if (this.peek().type === \"question\") {\n this.consume(); // ?\n const trueVal = this.parseTernary();\n this.consume(\"colon\"); // :\n const falseVal = this.parseTernary();\n return condition ? trueVal : falseVal;\n }\n return condition;\n }\n\n /** comparison: additive (< | > | <= | >= | == | !=) additive */\n private parseComparison(): number {\n let left = this.parseAdditive();\n while (this.peek().type === \"compare\") {\n const op = this.consume().value;\n const right = this.parseAdditive();\n switch (op) {\n case \"<\": left = left < right ? 1 : 0; break;\n case \">\": left = left > right ? 1 : 0; break;\n case \"<=\": left = left <= right ? 1 : 0; break;\n case \">=\": left = left >= right ? 1 : 0; break;\n case \"==\": left = left === right ? 1 : 0; break;\n case \"!=\": left = left !== right ? 1 : 0; break;\n }\n }\n return left;\n }\n\n /** additive: multiplicative (('+' | '-') multiplicative)* */\n private parseAdditive(): number {\n let left = this.parseMultiplicative();\n while (this.peek().type === \"op\" && (this.peek().value === \"+\" || this.peek().value === \"-\")) {\n const op = this.consume().value;\n const right = this.parseMultiplicative();\n left = op === \"+\" ? left + right : left - right;\n }\n return left;\n }\n\n /** multiplicative: power (('*' | '/' | '%') power)* */\n private parseMultiplicative(): number {\n let left = this.parsePower();\n while (this.peek().type === \"op\" && (this.peek().value === \"*\" || this.peek().value === \"/\" || this.peek().value === \"%\")) {\n const op = this.consume().value;\n const right = this.parsePower();\n if (op === \"*\") left = left * right;\n else if (op === \"/\") left = right !== 0 ? left / right : 0;\n else left = left % right;\n }\n return left;\n }\n\n /** power: unary ('**' unary)* (right-associative) */\n private parsePower(): number {\n const base = this.parseUnary();\n if (this.peek().type === \"op\" && this.peek().value === \"**\") {\n this.consume();\n const exp = this.parsePower(); // right-associative\n return Math.pow(base, exp);\n }\n return base;\n }\n\n /** unary: ('-' | '+') unary | primary */\n private parseUnary(): number {\n if (this.peek().type === \"op\" && (this.peek().value === \"-\" || this.peek().value === \"+\")) {\n const op = this.consume().value;\n const val = this.parseUnary();\n return op === \"-\" ? -val : val;\n }\n return this.parsePrimary();\n }\n\n /** primary: number | ident | function(args) | '(' expr ')' */\n private parsePrimary(): number {\n const tok = this.peek();\n\n // Number literal\n if (tok.type === \"number\") {\n this.consume();\n return parseFloat(tok.value);\n }\n\n // Identifier: constant, context variable, or function\n if (tok.type === \"ident\") {\n this.consume();\n const name = tok.value;\n\n // Function call\n if (this.peek().type === \"lparen\") {\n this.consume(); // (\n const args: number[] = [];\n if (this.peek().type !== \"rparen\") {\n args.push(this.parseTernary());\n while (this.peek().type === \"comma\") {\n this.consume(); // ,\n args.push(this.parseTernary());\n }\n }\n this.consume(\"rparen\"); // )\n\n const fn = FUNCTIONS[name];\n if (!fn) throw new Error(`Expression: unknown function '${name}'`);\n return fn(...args);\n }\n\n // Constant\n if (name in CONSTANTS) return CONSTANTS[name];\n\n // Context variable\n if (name in this.ctx) return (this.ctx as unknown as Record<string, number>)[name];\n\n throw new Error(`Expression: unknown variable '${name}'`);\n }\n\n // Parenthesized expression\n if (tok.type === \"lparen\") {\n this.consume(); // (\n const val = this.parseTernary();\n this.consume(\"rparen\"); // )\n return val;\n }\n\n throw new Error(`Expression: unexpected token '${tok.value}'`);\n }\n}\n\n// ── Public API ──────────────────────────────────────────────\n\n/**\n * Check if a value is an expression object { expr: string }.\n */\nexport function isExpression(value: unknown): value is { expr: string } {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"expr\" in value &&\n typeof (value as Record<string, unknown>).expr === \"string\"\n );\n}\n\n/**\n * Evaluate an expression string with the given context.\n * Returns a number. Throws on syntax errors or unknown identifiers.\n */\nexport function evaluateExpression(expr: string, ctx: ExpressionContext): number {\n const tokens = tokenize(expr);\n const parser = new Parser(tokens, ctx);\n return parser.parse();\n}\n","import type { AtelierDocument, Layer, Delta, AnimatableProperty, VideoVisual } from \"@a-company/atelier-types\";\nimport { resolvePropertyAtFrame } from \"./delta-resolver.js\";\n\n/** A resolved layer at a specific frame — all animated properties computed */\nexport interface ResolvedLayer {\n id: string;\n /** Original layer definition */\n layer: Layer;\n /** Computed property overrides from active deltas */\n computedProperties: Partial<Record<AnimatableProperty, unknown>>;\n /** Seconds into source video to seek to — only present for video visual layers */\n videoSourceTime?: number;\n}\n\n/** The resolved state of an entire document at a specific frame */\nexport interface ResolvedFrame {\n /** Frame number that was resolved */\n frame: number;\n /** State name that was resolved */\n stateName: string;\n /** All layers with their computed properties */\n layers: ResolvedLayer[];\n}\n\n/**\n * Resolve all layers at a given frame within a named state.\n * This is the main entry point for frame resolution.\n * @param overrideDeltas - Optional pre-merged deltas (used for hierarchical state resolution)\n */\nexport function resolveFrame(\n doc: AtelierDocument,\n stateName: string,\n frame: number,\n overrideDeltas?: Delta[],\n): ResolvedFrame {\n const state = doc.states[stateName];\n if (!state) {\n throw new Error(`State \"${stateName}\" not found in document \"${doc.name}\"`);\n }\n\n // Group deltas by layer+property\n const deltasByLayerProperty = groupDeltas(overrideDeltas ?? state.deltas);\n\n // Pre-compute the nearest ancestor video-clip startFrame for every layer.\n // v1.0 multi-clip: captions parented to a clip (parentId = clip-layer-id)\n // author their deltas in clip-local frames (0..clipDuration). The renderer\n // shifts the evaluation frame by the parent clip's startFrame so the deltas\n // fire when the clip is actually playing on the composition timeline. This\n // is what makes \"move a clip and its captions go with it\" work.\n const ancestorClipStartFrame = computeAncestorClipStartFrames(doc);\n\n // Resolve each layer\n const resolvedLayers: ResolvedLayer[] = doc.layers.map((layer) => {\n const computedProperties: Partial<Record<AnimatableProperty, unknown>> = {};\n\n // Find all animated properties for this layer\n const layerDeltas = deltasByLayerProperty.get(layer.id);\n if (layerDeltas) {\n // Clip-local time = composition frame - nearest ancestor video startFrame.\n // For layers with no video ancestor, the offset is 0 (no shift).\n const offset = ancestorClipStartFrame.get(layer.id) ?? 0;\n const localFrame = frame - offset;\n for (const [property, deltas] of layerDeltas) {\n const value = resolvePropertyAtFrame(deltas, localFrame);\n if (value !== undefined) {\n computedProperties[property as AnimatableProperty] = value;\n }\n }\n }\n\n const resolvedLayer: ResolvedLayer = { id: layer.id, layer, computedProperties };\n\n if (layer.visual.type === \"video\") {\n const video = layer.visual;\n const fps = doc.canvas.fps;\n const startFrame = video.startFrame ?? 0;\n const sourceOffset = video.sourceOffset ?? 0;\n const playbackRate = video.playbackRate ?? 1.0;\n const relativeFrame = Math.max(0, frame - startFrame);\n const sourceTime = (relativeFrame / fps) * playbackRate + sourceOffset;\n resolvedLayer.videoSourceTime =\n video.sourceEnd !== undefined ? Math.min(sourceTime, video.sourceEnd) : sourceTime;\n }\n\n return resolvedLayer;\n });\n\n return { frame, stateName, layers: resolvedLayers };\n}\n\n/**\n * For each layer in the document, walk the parentId chain looking for the\n * nearest ancestor whose visual is a VideoVisual; return that ancestor's\n * `startFrame` (or 0 if no video ancestor exists). The result is a\n * layer-id → offset map used to shift delta evaluation into clip-local time.\n *\n * Note: this resolves only on a video clip ancestor, not on Group or other\n * layer types. A Group used as a \"track\" doesn't shift child time — only the\n * clip itself does. This keeps non-clip parenting (groups for positioning,\n * masks, etc.) decoupled from the timeline-shift behavior.\n *\n * Cycle-safe via a visited set; a malformed doc with parentId cycles falls\n * back to offset 0 for affected layers rather than looping.\n */\nfunction computeAncestorClipStartFrames(doc: AtelierDocument): Map<string, number> {\n const byId = new Map<string, Layer>();\n for (const l of doc.layers) byId.set(l.id, l);\n\n const result = new Map<string, number>();\n for (const layer of doc.layers) {\n let cursor: Layer | undefined = byId.get(layer.parentId ?? \"\");\n const visited = new Set<string>();\n while (cursor) {\n if (visited.has(cursor.id)) break;\n visited.add(cursor.id);\n if (cursor.visual.type === \"video\") {\n const v = cursor.visual as VideoVisual;\n result.set(layer.id, v.startFrame ?? 0);\n break;\n }\n cursor = byId.get(cursor.parentId ?? \"\");\n }\n }\n return result;\n}\n\n/**\n * Group deltas by layer ID, then by property name.\n */\nfunction groupDeltas(\n deltas: Delta[],\n): Map<string, Map<string, Delta[]>> {\n const map = new Map<string, Map<string, Delta[]>>();\n\n for (const delta of deltas) {\n let layerMap = map.get(delta.layer);\n if (!layerMap) {\n layerMap = new Map();\n map.set(delta.layer, layerMap);\n }\n\n let propDeltas = layerMap.get(delta.property);\n if (!propDeltas) {\n propDeltas = [];\n layerMap.set(delta.property, propDeltas);\n }\n\n propDeltas.push(delta);\n }\n\n return map;\n}\n","import type { Delta, FrameRange } from \"@a-company/atelier-types\";\n\nexport interface OverlapError {\n layerId: string;\n property: string;\n existingRange: FrameRange;\n newRange: FrameRange;\n message: string;\n}\n\n/**\n * Check if two frame ranges overlap.\n * Boundary-touching ranges (e.g., [0-50] and [50-90]) are allowed —\n * only ranges that share more than a single boundary frame are overlaps.\n */\nexport function rangesOverlap(a: FrameRange, b: FrameRange): boolean {\n return a[0] < b[1] && b[0] < a[1];\n}\n\n/**\n * Validate that a new delta doesn't overlap with existing deltas\n * on the same layer+property.\n */\nexport function validateNoOverlap(\n existing: Delta[],\n newDelta: Delta,\n): OverlapError | null {\n for (const delta of existing) {\n if (\n delta.layer === newDelta.layer &&\n delta.property === newDelta.property &&\n rangesOverlap(delta.range, newDelta.range)\n ) {\n return {\n layerId: newDelta.layer,\n property: newDelta.property,\n existingRange: delta.range,\n newRange: newDelta.range,\n message: `Overlapping delta on layer \"${newDelta.layer}\" property \"${newDelta.property}\": ` +\n `existing [${delta.range[0]}-${delta.range[1]}] overlaps with new [${newDelta.range[0]}-${newDelta.range[1]}]`,\n };\n }\n }\n return null;\n}\n\n/**\n * Validate all deltas in a state have no overlaps.\n * Returns all overlap errors found.\n */\nexport function validateAllDeltas(deltas: Delta[]): OverlapError[] {\n const errors: OverlapError[] = [];\n\n for (let i = 0; i < deltas.length; i++) {\n for (let j = i + 1; j < deltas.length; j++) {\n const a = deltas[i];\n const b = deltas[j];\n\n if (\n a.layer === b.layer &&\n a.property === b.property &&\n rangesOverlap(a.range, b.range)\n ) {\n errors.push({\n layerId: a.layer,\n property: a.property,\n existingRange: a.range,\n newRange: b.range,\n message: `Overlapping deltas on layer \"${a.layer}\" property \"${a.property}\": ` +\n `[${a.range[0]}-${a.range[1]}] overlaps with [${b.range[0]}-${b.range[1]}]`,\n });\n }\n }\n }\n\n return errors;\n}\n","import type {\n AtelierDocument, Canvas, Layer, State, Delta, Preset,\n Variable, Asset,\n} from \"@a-company/atelier-types\";\nimport { validateNoOverlap } from \"../validation/overlap-validator.js\";\n\n/**\n * Fluent builder for constructing AtelierDocument objects.\n * Validates constraints (like no-overlap) as you build.\n */\nexport class DocumentBuilder {\n private doc: AtelierDocument;\n\n constructor(name: string, canvas: Canvas) {\n this.doc = {\n version: \"1.0\",\n name,\n canvas,\n layers: [],\n states: {},\n };\n }\n\n /** Set document description */\n description(desc: string): this {\n this.doc.description = desc;\n return this;\n }\n\n /** Add tags to the document */\n tags(...tags: string[]): this {\n this.doc.tags = [...(this.doc.tags ?? []), ...tags];\n return this;\n }\n\n /** Add a variable definition */\n variable(id: string, variable: Variable): this {\n if (!this.doc.variables) this.doc.variables = {};\n this.doc.variables[id] = variable;\n return this;\n }\n\n /** Add an asset reference */\n asset(id: string, asset: Asset): this {\n if (!this.doc.assets) this.doc.assets = {};\n this.doc.assets[id] = asset;\n return this;\n }\n\n /** Add a preset */\n preset(id: string, preset: Preset): this {\n if (!this.doc.presets) this.doc.presets = {};\n this.doc.presets[id] = preset;\n return this;\n }\n\n /** Add a layer */\n addLayer(layer: Layer): this {\n // Check for duplicate IDs\n if (this.doc.layers.some(l => l.id === layer.id)) {\n throw new Error(`Layer with id \"${layer.id}\" already exists`);\n }\n this.doc.layers.push(layer);\n return this;\n }\n\n /** Add a state */\n addState(name: string, state: Omit<State, \"deltas\"> & { deltas?: Delta[] }): this {\n if (this.doc.states[name]) {\n throw new Error(`State \"${name}\" already exists`);\n }\n this.doc.states[name] = { ...state, deltas: state.deltas ?? [] };\n return this;\n }\n\n /** Add a delta to an existing state, with overlap validation */\n addDelta(stateName: string, delta: Delta): this {\n const state = this.doc.states[stateName];\n if (!state) {\n throw new Error(`State \"${stateName}\" not found`);\n }\n\n // Verify the layer exists\n if (!this.doc.layers.some(l => l.id === delta.layer)) {\n throw new Error(`Layer \"${delta.layer}\" not found — add the layer before adding deltas`);\n }\n\n // Check for overlaps\n const overlap = validateNoOverlap(state.deltas, delta);\n if (overlap) {\n throw new Error(overlap.message);\n }\n\n state.deltas.push(delta);\n return this;\n }\n\n /** Build and return the final document */\n build(): AtelierDocument {\n return JSON.parse(JSON.stringify(this.doc)) as AtelierDocument;\n }\n}\n\n/**\n * Create a new DocumentBuilder.\n * Convenience function for starting a builder chain.\n */\nexport function createDocument(name: string, canvas: Canvas): DocumentBuilder {\n return new DocumentBuilder(name, canvas);\n}\n","import type { UnitValue } from \"@a-company/atelier-types\";\n\n/**\n * Check if a UnitValue is a percentage string.\n */\nexport function isPercentage(value: UnitValue): value is `${number}%` {\n return typeof value === \"string\" && value.endsWith(\"%\");\n}\n\n/**\n * Parse a percentage string to its numeric value (0-100).\n */\nexport function parsePercentage(value: `${number}%`): number {\n return parseFloat(value);\n}\n\n/**\n * Resolve a UnitValue to pixels given a reference dimension.\n * Pixel values pass through unchanged.\n * Percentage values are computed relative to the reference.\n */\nexport function resolveUnit(value: UnitValue, reference: number): number {\n if (isPercentage(value)) {\n return (parsePercentage(value) / 100) * reference;\n }\n return value;\n}\n","import type { Preset, Delta, FrameRange } from \"@a-company/atelier-types\";\n\n/**\n * Expand a preset into concrete deltas for a specific layer and start frame.\n *\n * @param preset - The preset to expand\n * @param layerId - Target layer ID\n * @param startFrame - Frame to start the preset from\n * @param duration - Total duration to map preset offsets into\n */\nexport function expandPreset(\n preset: Preset,\n layerId: string,\n startFrame: number,\n duration: number,\n): Delta[] {\n return preset.deltas.map((pd, index) => {\n let range: FrameRange;\n if (pd.offset) {\n range = [startFrame + pd.offset[0], startFrame + pd.offset[1]];\n } else {\n range = [startFrame, startFrame + duration];\n }\n\n return {\n id: `preset-${layerId}-${index}`,\n layer: layerId,\n property: pd.property,\n range,\n from: pd.from,\n to: pd.to,\n easing: pd.easing,\n };\n });\n}\n","import type { AtelierDocument, Easing, State, Delta } from \"@a-company/atelier-types\";\nimport { resolveFrame, type ResolvedFrame } from \"../resolver/frame-resolver.js\";\nimport { resolveEasing } from \"../resolver/easing-resolver.js\";\n\nexport interface StateTransition {\n from: string;\n to: string;\n at: number; // frame at which transition occurs\n}\n\nexport interface PlaybackState {\n stateName: string;\n frame: number;\n resolved: ResolvedFrame;\n isComplete: boolean;\n}\n\nexport interface ActiveTransition {\n fromState: string;\n toState: string;\n duration: number;\n easingFn: (t: number) => number;\n transitionFrame: number; // how many frames into the transition\n}\n\nexport class StateMachine {\n private currentState: string;\n private currentFrame = 0;\n private transitions: StateTransition[] = [];\n private activeTransition: ActiveTransition | null = null;\n\n constructor(\n private doc: AtelierDocument,\n initialState?: string,\n ) {\n // Default to first state\n const stateNames = Object.keys(doc.states);\n if (stateNames.length === 0) throw new Error(\"Document has no states\");\n this.currentState = initialState ?? stateNames[0];\n if (!doc.states[this.currentState]) {\n throw new Error(`State \"${this.currentState}\" not found`);\n }\n }\n\n /** Get current state name */\n get state(): string {\n return this.currentState;\n }\n\n /** Get current frame */\n get frame(): number {\n return this.currentFrame;\n }\n\n /** Get all state names */\n get stateNames(): string[] {\n return Object.keys(this.doc.states);\n }\n\n /** Get current state duration */\n get duration(): number {\n return this.doc.states[this.currentState].duration;\n }\n\n /** Check if current state playback is complete */\n get isComplete(): boolean {\n return this.currentFrame >= this.duration - 1;\n }\n\n /** Check if currently in a transition blend */\n isTransitioning(): boolean {\n return this.activeTransition !== null;\n }\n\n /** Get transition progress (0–1) with easing applied, or null if not transitioning */\n getTransitionProgress(): number | null {\n if (!this.activeTransition) return null;\n const { transitionFrame, duration, easingFn } = this.activeTransition;\n const rawProgress = Math.min(transitionFrame / duration, 1);\n return easingFn(rawProgress);\n }\n\n // ── Hierarchical State Support ────────────────────────────\n\n /**\n * Resolve the ancestor chain for a state (root → … → parent → self).\n * Throws on circular parent references.\n */\n resolveAncestorChain(stateName: string): string[] {\n const chain: string[] = [];\n const visited = new Set<string>();\n let current: string | undefined = stateName;\n\n while (current) {\n if (visited.has(current)) {\n throw new Error(`Circular parent reference detected: \"${current}\" already in chain [${chain.join(\" → \")}]`);\n }\n visited.add(current);\n chain.unshift(current); // prepend so root is first\n const stateObj: State | undefined = this.doc.states[current];\n if (!stateObj) {\n throw new Error(`Parent state \"${current}\" not found`);\n }\n current = stateObj.parent;\n }\n\n return chain;\n }\n\n /**\n * Collect merged deltas from ancestor chain.\n * Child overrides parent deltas for the same layer+property combination.\n * Deltas for different layer+property pairs are accumulated from all ancestors.\n */\n collectDeltas(stateName: string): Delta[] {\n const chain = this.resolveAncestorChain(stateName);\n // Walk root → child. Later entries override earlier for same layer+property.\n const deltaMap = new Map<string, Delta[]>();\n\n for (const ancestorName of chain) {\n const state = this.doc.states[ancestorName];\n // Group this state's deltas by layer+property\n const stateGroups = new Map<string, Delta[]>();\n for (const delta of state.deltas) {\n const key = `${delta.layer}:${delta.property}`;\n if (!stateGroups.has(key)) stateGroups.set(key, []);\n stateGroups.get(key)!.push(delta);\n }\n // Child replaces parent for same key\n for (const [key, deltas] of stateGroups) {\n deltaMap.set(key, deltas);\n }\n }\n\n // Flatten all deltas\n const result: Delta[] = [];\n for (const deltas of deltaMap.values()) {\n result.push(...deltas);\n }\n return result;\n }\n\n // ── Playback ──────────────────────────────────────────────\n\n /** Advance to next frame, returns resolved frame */\n tick(): PlaybackState {\n // Handle active transition\n if (this.activeTransition) {\n this.activeTransition.transitionFrame++;\n if (this.activeTransition.transitionFrame >= this.activeTransition.duration) {\n // Transition complete\n this.activeTransition = null;\n }\n }\n\n const resolved = this.resolveCurrentFrame();\n const result: PlaybackState = {\n stateName: this.currentState,\n frame: this.currentFrame,\n resolved,\n isComplete: this.isComplete,\n };\n if (this.currentFrame < this.duration - 1) {\n this.currentFrame++;\n }\n return result;\n }\n\n /** Resolve the current frame, blending if in transition */\n private resolveCurrentFrame(): ResolvedFrame {\n if (this.activeTransition) {\n const progress = this.getTransitionProgress()!;\n const fromResolved = resolveFrame(\n this.doc, this.activeTransition.fromState, this.currentFrame,\n this.collectDeltas(this.activeTransition.fromState),\n );\n const toResolved = resolveFrame(\n this.doc, this.activeTransition.toState, this.currentFrame,\n this.collectDeltas(this.activeTransition.toState),\n );\n return blendResolvedFrames(fromResolved, toResolved, progress);\n }\n\n // Check if current state has a parent — use merged deltas\n const state = this.doc.states[this.currentState];\n if (state.parent) {\n const mergedDeltas = this.collectDeltas(this.currentState);\n return resolveFrame(this.doc, this.currentState, this.currentFrame, mergedDeltas);\n }\n\n return resolveFrame(this.doc, this.currentState, this.currentFrame);\n }\n\n /** Transition to a different state (instant or blended) */\n transition(stateName: string, startFrame = 0): void {\n if (!this.doc.states[stateName]) {\n throw new Error(`State \"${stateName}\" not found`);\n }\n\n const fromState = this.currentState;\n const currentStateObj = this.doc.states[fromState];\n const transConfig = currentStateObj.transitions?.[stateName];\n\n this.transitions.push({\n from: fromState,\n to: stateName,\n at: this.currentFrame,\n });\n\n if (transConfig && transConfig.duration > 0) {\n // Start a smooth transition\n this.activeTransition = {\n fromState,\n toState: stateName,\n duration: transConfig.duration,\n easingFn: resolveEasing(transConfig.easing),\n transitionFrame: 0,\n };\n }\n\n this.currentState = stateName;\n this.currentFrame = startFrame;\n }\n\n /**\n * Start a smooth transition to target state with explicit duration/easing.\n * This ignores any transitions config on the state.\n */\n transitionTo(targetState: string, duration: number, easing?: Easing): void {\n if (!this.doc.states[targetState]) {\n throw new Error(`State \"${targetState}\" not found`);\n }\n\n const fromState = this.currentState;\n this.transitions.push({\n from: fromState,\n to: targetState,\n at: this.currentFrame,\n });\n\n this.activeTransition = {\n fromState,\n toState: targetState,\n duration,\n easingFn: resolveEasing(easing),\n transitionFrame: 0,\n };\n\n this.currentState = targetState;\n this.currentFrame = 0;\n }\n\n /** Seek to a specific frame in current state */\n seek(frame: number): void {\n this.currentFrame = Math.max(0, Math.min(frame, this.duration - 1));\n }\n\n /** Reset to initial state */\n reset(stateName?: string): void {\n if (stateName) {\n if (!this.doc.states[stateName]) throw new Error(`State \"${stateName}\" not found`);\n this.currentState = stateName;\n }\n this.currentFrame = 0;\n this.activeTransition = null;\n }\n\n /** Get transition history */\n get history(): ReadonlyArray<StateTransition> {\n return this.transitions;\n }\n\n /** Resolve a specific frame without advancing */\n resolveAt(stateName: string, frame: number): ResolvedFrame {\n if (!this.doc.states[stateName]) throw new Error(`State \"${stateName}\" not found`);\n const state = this.doc.states[stateName];\n if (state.parent) {\n return resolveFrame(this.doc, stateName, frame, this.collectDeltas(stateName));\n }\n return resolveFrame(this.doc, stateName, frame);\n }\n\n /** Play through entire current state, calling callback on each frame */\n playThrough(onFrame: (state: PlaybackState) => void): void {\n this.currentFrame = 0;\n while (!this.isComplete) {\n onFrame(this.tick());\n }\n onFrame(this.tick()); // last frame\n }\n}\n\n// ── Frame Blending ────────────────────────────────────────────\n\nimport type { ResolvedLayer } from \"../resolver/frame-resolver.js\";\nimport type { AnimatableProperty } from \"@a-company/atelier-types\";\nimport { interpolateValue } from \"../resolver/delta-resolver.js\";\n\n/**\n * Blend two resolved frames together for smooth state transitions.\n * Numeric properties are lerped, colors are color-lerped, discrete values snap at t >= 0.5.\n */\nexport function blendResolvedFrames(\n frameA: ResolvedFrame,\n frameB: ResolvedFrame,\n t: number,\n): ResolvedFrame {\n // Build layer maps\n const mapA = new Map<string, ResolvedLayer>();\n const mapB = new Map<string, ResolvedLayer>();\n for (const l of frameA.layers) mapA.set(l.id, l);\n for (const l of frameB.layers) mapB.set(l.id, l);\n\n // Get all unique layer IDs\n const allIds = new Set([...mapA.keys(), ...mapB.keys()]);\n const blendedLayers: ResolvedLayer[] = [];\n\n for (const id of allIds) {\n const layerA = mapA.get(id);\n const layerB = mapB.get(id);\n\n if (layerA && layerB) {\n // Both exist — blend computed properties\n blendedLayers.push(blendLayers(layerA, layerB, t));\n } else if (layerA && !layerB) {\n // Only in A — fade out (set opacity toward 0)\n blendedLayers.push(fadeLayer(layerA, 1 - t));\n } else if (!layerA && layerB) {\n // Only in B — fade in (set opacity from 0)\n blendedLayers.push(fadeLayer(layerB, t));\n }\n }\n\n return {\n frame: frameB.frame,\n stateName: frameB.stateName,\n layers: blendedLayers,\n };\n}\n\nfunction blendLayers(a: ResolvedLayer, b: ResolvedLayer, t: number): ResolvedLayer {\n const allProps = new Set([\n ...Object.keys(a.computedProperties),\n ...Object.keys(b.computedProperties),\n ]);\n\n const blended: Partial<Record<AnimatableProperty, unknown>> = {};\n\n for (const prop of allProps) {\n const valA = a.computedProperties[prop as AnimatableProperty];\n const valB = b.computedProperties[prop as AnimatableProperty];\n\n if (valA !== undefined && valB !== undefined) {\n blended[prop as AnimatableProperty] = interpolateValue(valA, valB, t);\n } else if (valA !== undefined) {\n blended[prop as AnimatableProperty] = valA;\n } else {\n blended[prop as AnimatableProperty] = valB;\n }\n }\n\n return {\n id: a.id,\n layer: a.layer,\n computedProperties: blended,\n };\n}\n\nfunction fadeLayer(layer: ResolvedLayer, opacity: number): ResolvedLayer {\n const cp = { ...layer.computedProperties };\n const existingOpacity = (cp.opacity as number) ?? 1;\n cp.opacity = existingOpacity * opacity;\n return {\n id: layer.id,\n layer: layer.layer,\n computedProperties: cp,\n };\n}\n","import type { AtelierDocument } from \"@a-company/atelier-types\";\n\nexport interface TemplateBindings {\n [variableName: string]: unknown;\n}\n\nexport interface TemplateError {\n variable: string;\n message: string;\n}\n\nexport type TemplateResult =\n | {\n success: true;\n document: AtelierDocument;\n }\n | {\n success: false;\n errors: TemplateError[];\n };\n\n/**\n * Instantiate a template document by substituting variable values.\n * Walks the document tree, replacing {{variableName}} patterns with bound values.\n */\nexport function instantiateTemplate(\n template: AtelierDocument,\n bindings: TemplateBindings,\n): TemplateResult {\n // 1. Validate all required variables have bindings\n const errors: TemplateError[] = [];\n const variables = template.variables ?? {};\n\n for (const [name, variable] of Object.entries(variables)) {\n if (bindings[name] === undefined && variable.default === undefined) {\n errors.push({ variable: name, message: `Required variable \"${name}\" not provided` });\n }\n }\n\n // Check for unknown bindings\n for (const name of Object.keys(bindings)) {\n if (!variables[name]) {\n errors.push({ variable: name, message: `Unknown variable \"${name}\"` });\n }\n }\n\n if (errors.length > 0) return { success: false, errors };\n\n // 2. Merge bindings with defaults\n const resolved: Record<string, unknown> = {};\n for (const [name, variable] of Object.entries(variables)) {\n resolved[name] = bindings[name] ?? variable.default;\n }\n\n // 3. Deep clone and substitute\n const doc = JSON.parse(JSON.stringify(template)) as AtelierDocument;\n substituteInObject(doc as unknown as Record<string, unknown>, resolved);\n\n // 4. Remove variables section (no longer a template)\n delete doc.variables;\n\n return { success: true, document: doc };\n}\n\n/**\n * Recursively substitute {{variableName}} patterns in string values.\n */\nfunction substituteInObject(obj: Record<string, unknown>, bindings: Record<string, unknown>): void {\n for (const key of Object.keys(obj)) {\n const value = obj[key];\n if (typeof value === \"string\") {\n obj[key] = substituteString(value, bindings);\n } else if (Array.isArray(value)) {\n substituteInArray(value, bindings);\n } else if (value !== null && typeof value === \"object\") {\n substituteInObject(value as Record<string, unknown>, bindings);\n }\n }\n}\n\nfunction substituteInArray(arr: unknown[], bindings: Record<string, unknown>): void {\n for (let i = 0; i < arr.length; i++) {\n const value = arr[i];\n if (typeof value === \"string\") {\n arr[i] = substituteString(value, bindings);\n } else if (Array.isArray(value)) {\n substituteInArray(value, bindings);\n } else if (value !== null && typeof value === \"object\") {\n substituteInObject(value as Record<string, unknown>, bindings);\n }\n }\n}\n\n/**\n * Replace {{variableName}} in a string with the bound value.\n * If the entire string is a single {{var}}, return the raw value (preserving type).\n * Otherwise, interpolate into the string.\n */\nfunction substituteString(str: string, bindings: Record<string, unknown>): unknown {\n // Exact match: entire string is {{varName}} -> return raw value\n const exactMatch = str.match(/^\\{\\{(\\w+)\\}\\}$/);\n if (exactMatch) {\n const name = exactMatch[1];\n return name in bindings ? bindings[name] : str;\n }\n\n // Partial match: replace all {{varName}} occurrences with string values\n return str.replace(/\\{\\{(\\w+)\\}\\}/g, (_, name: string) => {\n return name in bindings ? String(bindings[name]) : `{{${name}}}`;\n });\n}\n\n/**\n * List all variables used in a document (scan for {{variableName}} patterns).\n */\nexport function findTemplateVariables(doc: AtelierDocument): string[] {\n const vars = new Set<string>();\n scanForVariables(doc, vars);\n return Array.from(vars);\n}\n\nfunction scanForVariables(value: unknown, vars: Set<string>): void {\n if (typeof value === \"string\") {\n const matches = value.matchAll(/\\{\\{(\\w+)\\}\\}/g);\n for (const match of matches) {\n vars.add(match[1]);\n }\n } else if (Array.isArray(value)) {\n for (const item of value) scanForVariables(item, vars);\n } else if (value !== null && typeof value === \"object\") {\n for (const v of Object.values(value as Record<string, unknown>)) {\n scanForVariables(v, vars);\n }\n }\n}\n","import type { AudioVisual } from \"@a-company/atelier-types\";\n\n/**\n * Audio-layer playback utilities.\n *\n * v1.0: audio is no longer a per-state field. These utilities now take an\n * `AudioVisual` layer (see `@a-company/atelier-types/layer.ts`) and a\n * composition frame; they answer \"is this layer playing right now, at what\n * source-time, at what volume?\".\n *\n * Pure functions — no I/O, no state. Reusable by the canvas renderer's\n * audio scheduler and by any export pipeline (e.g. an ffmpeg mux step).\n */\n\n/** Audio playback state at a given composition frame */\nexport interface AudioPlaybackState {\n /** Whether audio should be playing at this frame */\n shouldPlay: boolean;\n /** Current position in the audio source (seconds) */\n currentTime: number;\n /** Effective volume (0–1), accounting for fadeIn/fadeOut envelopes */\n volume: number;\n}\n\n/** Convert a frame number to time in seconds. */\nexport function frameToTime(frame: number, fps: number): number {\n return frame / fps;\n}\n\n/** Convert time in seconds to a frame number (floored). */\nexport function timeToFrame(time: number, fps: number): number {\n return Math.floor(time * fps);\n}\n\n/**\n * Compute the playback state of an audio layer at a given composition frame.\n *\n * @param layer The AudioVisual to evaluate.\n * @param frame Current composition frame.\n * @param fps Canvas fps.\n * @param totalFrames Total composition duration in frames (used for fade-out\n * anchor when the layer plays to the end of the comp).\n * @returns Whether the layer is audible at this frame, where the\n * source playhead is, and the effective volume after\n * fade envelopes.\n */\nexport function computeAudioState(\n layer: AudioVisual,\n frame: number,\n fps: number,\n totalFrames: number,\n): AudioPlaybackState {\n const startFrame = layer.startFrame ?? 0;\n const sourceOffset = layer.sourceOffset ?? 0;\n const baseVolume = layer.muted ? 0 : (layer.volume ?? 1);\n const playbackRate = layer.playbackRate ?? 1;\n\n // Before the layer starts on the composition timeline.\n if (frame < startFrame) {\n return { shouldPlay: false, currentTime: 0, volume: baseVolume };\n }\n\n // Layer's effective end on the composition timeline. Derived from sourceEnd\n // (clip-out point) if set, otherwise plays to totalFrames.\n let endFrame = totalFrames;\n if (layer.sourceEnd !== undefined) {\n const segmentSec = (layer.sourceEnd - sourceOffset) / playbackRate;\n endFrame = Math.min(totalFrames, startFrame + timeToFrame(segmentSec, fps));\n }\n\n if (frame >= endFrame) {\n return { shouldPlay: false, currentTime: 0, volume: baseVolume };\n }\n\n const elapsedFrames = frame - startFrame;\n const elapsedTime = frameToTime(elapsedFrames, fps);\n const currentTime = sourceOffset + elapsedTime * playbackRate;\n\n // Apply fade envelopes (in/out are seconds at the LAYER edges, not source edges).\n let volume = baseVolume;\n if (layer.fadeIn && layer.fadeIn > 0) {\n const inFrames = timeToFrame(layer.fadeIn, fps);\n if (elapsedFrames < inFrames && inFrames > 0) {\n volume *= elapsedFrames / inFrames;\n }\n }\n if (layer.fadeOut && layer.fadeOut > 0) {\n const outFrames = timeToFrame(layer.fadeOut, fps);\n const framesUntilEnd = endFrame - frame;\n if (framesUntilEnd < outFrames && outFrames > 0) {\n volume *= framesUntilEnd / outFrames;\n }\n }\n\n return { shouldPlay: true, currentTime, volume };\n}\n\n/**\n * Compute cue points for an audio layer — frames where audio events occur\n * (start, end). Useful for scheduling DOM/audio-context play/stop calls.\n *\n * v1.0: with `loop` removed from the schema (AudioVisual doesn't support\n * looping yet — add when a real use case lands), there are at most two\n * cues per layer: start and end.\n */\nexport function computeAudioCues(\n layer: AudioVisual,\n fps: number,\n totalFrames: number,\n): Array<{ frame: number; event: \"start\" | \"end\" }> {\n const startFrame = layer.startFrame ?? 0;\n const sourceOffset = layer.sourceOffset ?? 0;\n const playbackRate = layer.playbackRate ?? 1;\n const cues: Array<{ frame: number; event: \"start\" | \"end\" }> = [];\n\n if (startFrame >= totalFrames) return cues;\n cues.push({ frame: startFrame, event: \"start\" });\n\n let endFrame = totalFrames;\n if (layer.sourceEnd !== undefined) {\n const segmentSec = (layer.sourceEnd - sourceOffset) / playbackRate;\n endFrame = Math.min(totalFrames, startFrame + timeToFrame(segmentSec, fps));\n }\n if (endFrame > startFrame && endFrame <= totalFrames) {\n cues.push({ frame: endFrame - 1, event: \"end\" });\n }\n\n return cues;\n}\n","import type { Fill, Stroke, Color, RGBAColor, HSLAColor } from \"@a-company/atelier-types\";\nimport type { RenderContext } from \"./canvas-types.js\";\n\n/**\n * Convert an Atelier Color to a CSS color string.\n */\nexport function colorToCSS(color: Color): string {\n if (typeof color === \"string\") return color; // hex string\n\n if (\"r\" in color) {\n const c = color as RGBAColor;\n return `rgba(${Math.round(c.r)}, ${Math.round(c.g)}, ${Math.round(c.b)}, ${c.a})`;\n }\n\n if (\"h\" in color) {\n const c = color as HSLAColor;\n return `hsla(${c.h}, ${c.s}%, ${c.l}%, ${c.a})`;\n }\n\n return \"#000000\";\n}\n\n/**\n * Apply a Fill to the canvas context's fillStyle.\n */\nexport function applyFill(ctx: RenderContext, fill: Fill, width: number, height: number): void {\n switch (fill.type) {\n case \"solid\":\n ctx.fillStyle = colorToCSS(fill.color);\n break;\n\n case \"linear-gradient\": {\n const rad = (fill.angle * Math.PI) / 180;\n const cos = Math.cos(rad);\n const sin = Math.sin(rad);\n const halfW = width / 2;\n const halfH = height / 2;\n const grad = ctx.createLinearGradient(\n halfW - cos * halfW, halfH - sin * halfH,\n halfW + cos * halfW, halfH + sin * halfH,\n );\n for (const stop of fill.stops) {\n grad.addColorStop(stop.offset, colorToCSS(stop.color));\n }\n ctx.fillStyle = grad as unknown as string;\n break;\n }\n\n case \"radial-gradient\": {\n const cx = typeof fill.center.x === \"number\" ? fill.center.x : (parseFloat(fill.center.x) / 100) * width;\n const cy = typeof fill.center.y === \"number\" ? fill.center.y : (parseFloat(fill.center.y) / 100) * height;\n const r = typeof fill.radius === \"number\" ? fill.radius : (parseFloat(fill.radius) / 100) * Math.max(width, height);\n const grad = ctx.createRadialGradient(cx, cy, 0, cx, cy, r);\n for (const stop of fill.stops) {\n grad.addColorStop(stop.offset, colorToCSS(stop.color));\n }\n ctx.fillStyle = grad as unknown as string;\n break;\n }\n }\n}\n\n/**\n * Apply a Stroke to the canvas context.\n * @param pathLength - total perimeter of the shape (needed for strokeStart/strokeEnd trim)\n */\nexport function applyStroke(ctx: RenderContext, stroke: Stroke, pathLength: number): void {\n ctx.strokeStyle = colorToCSS(stroke.color);\n ctx.lineWidth = stroke.width;\n if (stroke.lineCap) ctx.lineCap = stroke.lineCap;\n if (stroke.lineJoin) ctx.lineJoin = stroke.lineJoin;\n\n const start = stroke.strokeStart ?? 0;\n const end = stroke.strokeEnd ?? 1;\n\n if (start !== 0 || end !== 1) {\n const visible = (end - start) * pathLength;\n ctx.setLineDash([Math.max(visible, 0), pathLength + 1]);\n ctx.lineDashOffset = -start * pathLength;\n } else if (stroke.dash) {\n ctx.setLineDash(stroke.dash);\n }\n}\n","import type { Layer, Visual, ShapeVisual, TextVisual, ImageVisual, Color, BlendMode, LinearGradientFill, RadialGradientFill } from \"@a-company/atelier-types\";\nimport { colorToCSS } from \"./styles.js\";\nimport { evaluatePathAtProgress } from \"@a-company/atelier-math\";\nimport type { ResolvedLayer } from \"@a-company/atelier-core\";\n\n/**\n * Effective values for a layer at a given frame,\n * with computed properties merged over defaults.\n */\nexport interface EffectiveLayer {\n /** Original layer */\n layer: Layer;\n /** Visual with animated property overrides applied */\n visual: Visual;\n /** Effective frame position (may be animated) */\n x: number;\n y: number;\n /** Effective bounds */\n width: number;\n height: number;\n /** Effective transform values */\n opacity: number;\n rotation: number;\n scaleX: number;\n scaleY: number;\n anchorX: number;\n anchorY: number;\n /** Resolved shadow (if layer has shadow or shadow is animated) */\n shadow?: {\n color: string;\n blur: number;\n offsetX: number;\n offsetY: number;\n };\n /** Blend mode for compositing */\n blendMode: string;\n /** Motion path auto-rotation in degrees (applied additively to rotation) */\n motionPathAngle: number;\n /** Whether this layer is visible (may be animated) */\n visible: boolean;\n /** Color tint overlay */\n tint?: {\n color: string;\n amount: number;\n };\n}\n\n/**\n * Resolve a UnitValue to pixels. Percentages resolve against a reference dimension.\n */\nfunction resolveUnit(value: number | string, reference: number): number {\n if (typeof value === \"string\" && value.endsWith(\"%\")) {\n return (parseFloat(value) / 100) * reference;\n }\n return value as number;\n}\n\n/**\n * Build the effective layer values by merging computed properties over layer defaults.\n * @param resolved - The resolved layer from frame resolution\n * @param parentWidth - Parent/canvas width for percentage resolution\n * @param parentHeight - Parent/canvas height for percentage resolution\n */\nexport function buildEffectiveLayer(\n resolved: ResolvedLayer,\n parentWidth: number,\n parentHeight: number,\n): EffectiveLayer {\n const { layer, computedProperties } = resolved;\n const cp = computedProperties as Record<string, unknown>;\n\n const hasShadow = layer.shadow || cp[\"shadow.blur\"] !== undefined || cp[\"shadow.color\"] !== undefined;\n const hasTint = layer.tint || cp[\"tint.amount\"] !== undefined || cp[\"tint.color\"] !== undefined;\n\n // Resolve base position\n let x = resolveUnit((cp[\"frame.x\"] ?? layer.frame.x) as number | string, parentWidth);\n let y = resolveUnit((cp[\"frame.y\"] ?? layer.frame.y) as number | string, parentHeight);\n let motionPathAngle = 0;\n\n // Motion path overrides position when progress is animated\n const motionProgress = cp[\"motionPath.progress\"] as number | undefined;\n if (motionProgress !== undefined && layer.motionPath && layer.motionPath.points.length >= 2) {\n const pos = evaluatePathAtProgress(layer.motionPath.points, motionProgress, layer.motionPath.closed);\n x = pos.x;\n y = pos.y;\n if (layer.motionPath.autoRotate) {\n motionPathAngle = pos.angle + (layer.motionPath.autoRotateOffset ?? 0);\n }\n }\n\n return {\n layer,\n visual: buildEffectiveVisual(layer.visual, cp),\n x,\n y,\n width: resolveUnit((cp[\"bounds.width\"] ?? layer.bounds.width) as number | string, parentWidth),\n height: resolveUnit((cp[\"bounds.height\"] ?? layer.bounds.height) as number | string, parentHeight),\n opacity: (cp[\"opacity\"] as number) ?? layer.opacity ?? 1,\n rotation: (cp[\"rotation\"] as number) ?? layer.rotation ?? 0,\n scaleX: (cp[\"scale.x\"] as number) ?? layer.scale?.x ?? 1,\n scaleY: (cp[\"scale.y\"] as number) ?? layer.scale?.y ?? 1,\n anchorX: (cp[\"anchorPoint.x\"] as number) ?? layer.anchorPoint?.x ?? 0,\n anchorY: (cp[\"anchorPoint.y\"] as number) ?? layer.anchorPoint?.y ?? 0,\n shadow: hasShadow ? {\n color: colorToCSS((cp[\"shadow.color\"] ?? layer.shadow?.color ?? \"#00000080\") as Color),\n blur: (cp[\"shadow.blur\"] as number) ?? layer.shadow?.blur ?? 0,\n offsetX: (cp[\"shadow.offsetX\"] as number) ?? layer.shadow?.offsetX ?? 0,\n offsetY: (cp[\"shadow.offsetY\"] as number) ?? layer.shadow?.offsetY ?? 0,\n } : undefined,\n blendMode: (layer.blendMode as BlendMode) ?? \"normal\",\n motionPathAngle,\n visible: (cp[\"visible\"] as boolean) ?? layer.visible ?? true,\n tint: hasTint ? {\n color: colorToCSS((cp[\"tint.color\"] ?? layer.tint?.color ?? \"#FF0000\") as Color),\n amount: (cp[\"tint.amount\"] as number) ?? layer.tint?.amount ?? 0,\n } : undefined,\n };\n}\n\n/**\n * Build an effective visual by applying computed property overrides.\n * Returns the original visual if no visual properties are animated.\n */\nfunction buildEffectiveVisual(visual: Visual, cp: Record<string, unknown>): Visual {\n const hasVisualOverride =\n cp[\"visual.fill.color\"] !== undefined ||\n cp[\"visual.fill.angle\"] !== undefined ||\n cp[\"visual.fill.center.x\"] !== undefined ||\n cp[\"visual.fill.center.y\"] !== undefined ||\n cp[\"visual.fill.radius\"] !== undefined ||\n cp[\"visual.stroke.color\"] !== undefined ||\n cp[\"visual.stroke.width\"] !== undefined ||\n cp[\"visual.stroke.start\"] !== undefined ||\n cp[\"visual.stroke.end\"] !== undefined ||\n cp[\"visual.shape.cornerRadius\"] !== undefined ||\n cp[\"visual.style.fontSize\"] !== undefined ||\n cp[\"visual.style.color\"] !== undefined;\n\n const hasImageOverride =\n cp[\"visual.image.sourceRect.x\"] !== undefined ||\n cp[\"visual.image.sourceRect.y\"] !== undefined ||\n cp[\"visual.image.sourceRect.width\"] !== undefined ||\n cp[\"visual.image.sourceRect.height\"] !== undefined ||\n cp[\"visual.image.frameIndex\"] !== undefined;\n\n if (!hasVisualOverride && !hasImageOverride) return visual;\n\n if (visual.type === \"shape\") {\n const v: ShapeVisual = { ...visual };\n\n if (cp[\"visual.shape.cornerRadius\"] !== undefined && v.shape.type === \"rect\") {\n v.shape = { ...v.shape, cornerRadius: cp[\"visual.shape.cornerRadius\"] as number };\n }\n\n if (v.fill) {\n if (cp[\"visual.fill.color\"] !== undefined && v.fill.type === \"solid\") {\n v.fill = { ...v.fill, color: cp[\"visual.fill.color\"] as string };\n }\n if (cp[\"visual.fill.angle\"] !== undefined && v.fill.type === \"linear-gradient\") {\n v.fill = { ...v.fill, angle: cp[\"visual.fill.angle\"] as number } as LinearGradientFill;\n }\n if (v.fill.type === \"radial-gradient\") {\n const cx = cp[\"visual.fill.center.x\"];\n const cy = cp[\"visual.fill.center.y\"];\n const r = cp[\"visual.fill.radius\"];\n if (cx !== undefined || cy !== undefined || r !== undefined) {\n const f = v.fill as RadialGradientFill;\n v.fill = {\n ...f,\n center: {\n x: cx !== undefined ? (cx as number) : f.center.x,\n y: cy !== undefined ? (cy as number) : f.center.y,\n },\n radius: r !== undefined ? (r as number) : f.radius,\n } as RadialGradientFill;\n }\n }\n }\n\n if (v.stroke) {\n const strokeColor = cp[\"visual.stroke.color\"] ?? v.stroke.color;\n const strokeWidth = (cp[\"visual.stroke.width\"] as number) ?? v.stroke.width;\n const strokeStart = (cp[\"visual.stroke.start\"] as number | undefined) ?? v.stroke.strokeStart;\n const strokeEnd = (cp[\"visual.stroke.end\"] as number | undefined) ?? v.stroke.strokeEnd;\n if (\n strokeColor !== v.stroke.color ||\n strokeWidth !== v.stroke.width ||\n strokeStart !== v.stroke.strokeStart ||\n strokeEnd !== v.stroke.strokeEnd\n ) {\n v.stroke = {\n ...v.stroke,\n color: strokeColor as string,\n width: strokeWidth,\n strokeStart,\n strokeEnd,\n };\n }\n }\n\n return v;\n }\n\n if (visual.type === \"text\") {\n const v: TextVisual = { ...visual };\n const fontSize = (cp[\"visual.style.fontSize\"] as number) ?? v.style.fontSize;\n const color = cp[\"visual.style.color\"] ?? v.style.color;\n if (fontSize !== v.style.fontSize || color !== v.style.color) {\n v.style = { ...v.style, fontSize, color: color as string };\n }\n return v;\n }\n\n if (visual.type === \"image\" && hasImageOverride) {\n const v: ImageVisual = { ...visual };\n\n // Merge animated sourceRect fields\n if (\n cp[\"visual.image.sourceRect.x\"] !== undefined ||\n cp[\"visual.image.sourceRect.y\"] !== undefined ||\n cp[\"visual.image.sourceRect.width\"] !== undefined ||\n cp[\"visual.image.sourceRect.height\"] !== undefined\n ) {\n const base = v.sourceRect ?? { x: 0, y: 0, width: 0, height: 0 };\n v.sourceRect = {\n x: (cp[\"visual.image.sourceRect.x\"] as number) ?? base.x,\n y: (cp[\"visual.image.sourceRect.y\"] as number) ?? base.y,\n width: (cp[\"visual.image.sourceRect.width\"] as number) ?? base.width,\n height: (cp[\"visual.image.sourceRect.height\"] as number) ?? base.height,\n };\n }\n\n // Merge animated frameIndex\n if (cp[\"visual.image.frameIndex\"] !== undefined) {\n v.frameIndex = Math.floor(cp[\"visual.image.frameIndex\"] as number);\n }\n\n return v;\n }\n\n return visual;\n}\n","import type { ShapeVisual } from \"@a-company/atelier-types\";\nimport type { RenderContext } from \"../canvas-types.js\";\nimport type { EffectiveLayer } from \"../apply-properties.js\";\nimport { applyFill, applyStroke } from \"../styles.js\";\n\nexport function renderShape(ctx: RenderContext, eff: EffectiveLayer): void {\n const visual = eff.visual as ShapeVisual;\n const { shape } = visual;\n const { width, height } = eff;\n\n switch (shape.type) {\n case \"rect\":\n renderRect(ctx, width, height, shape.cornerRadius, visual);\n break;\n case \"ellipse\":\n renderEllipse(ctx, width, height, visual);\n break;\n case \"path\":\n renderPath(ctx, shape.points, shape.closed, visual);\n break;\n }\n}\n\n// ── Perimeter helpers ───────────────────────────────────────\n\nfunction rectPerimeter(w: number, h: number): number {\n return 2 * (w + h);\n}\n\nfunction ellipsePerimeter(w: number, h: number): number {\n const a = w / 2;\n const b = h / 2;\n // Ramanujan approximation\n return Math.PI * (3 * (a + b) - Math.sqrt((3 * a + b) * (a + 3 * b)));\n}\n\nfunction dist(x1: number, y1: number, x2: number, y2: number): number {\n const dx = x2 - x1;\n const dy = y2 - y1;\n return Math.sqrt(dx * dx + dy * dy);\n}\n\nfunction pathPerimeter(\n points: { x: number; y: number; in?: { x: number; y: number }; out?: { x: number; y: number } }[],\n closed: boolean | undefined,\n): number {\n let length = 0;\n for (let i = 1; i < points.length; i++) {\n const prev = points[i - 1];\n const curr = points[i];\n if (prev.out && curr.in) {\n // Approximate bezier with chord * 1.2\n length += dist(prev.x, prev.y, curr.x, curr.y) * 1.2;\n } else {\n length += dist(prev.x, prev.y, curr.x, curr.y);\n }\n }\n if (closed && points.length > 1) {\n const first = points[0];\n const last = points[points.length - 1];\n length += dist(last.x, last.y, first.x, first.y);\n }\n return length;\n}\n\n// ── Renderers ───────────────────────────────────────────────\n\nfunction renderRect(\n ctx: RenderContext,\n width: number,\n height: number,\n cornerRadius: number | [number, number, number, number] | undefined,\n visual: ShapeVisual,\n): void {\n if (visual.fill) {\n applyFill(ctx, visual.fill, width, height);\n if (cornerRadius && ctx.roundRect) {\n ctx.beginPath();\n ctx.roundRect(0, 0, width, height, cornerRadius);\n ctx.fill();\n } else {\n ctx.fillRect(0, 0, width, height);\n }\n }\n if (visual.stroke) {\n const perimeter = rectPerimeter(width, height);\n applyStroke(ctx, visual.stroke, perimeter);\n if (cornerRadius && ctx.roundRect) {\n ctx.beginPath();\n ctx.roundRect(0, 0, width, height, cornerRadius);\n ctx.stroke();\n } else {\n ctx.strokeRect(0, 0, width, height);\n }\n }\n}\n\nfunction renderEllipse(\n ctx: RenderContext,\n width: number,\n height: number,\n visual: ShapeVisual,\n): void {\n ctx.beginPath();\n ctx.ellipse(width / 2, height / 2, width / 2, height / 2, 0, 0, Math.PI * 2);\n\n if (visual.fill) {\n applyFill(ctx, visual.fill, width, height);\n ctx.fill();\n }\n if (visual.stroke) {\n const perimeter = ellipsePerimeter(width, height);\n applyStroke(ctx, visual.stroke, perimeter);\n ctx.stroke();\n }\n}\n\nfunction renderPath(\n ctx: RenderContext,\n points: { x: number; y: number; in?: { x: number; y: number }; out?: { x: number; y: number } }[],\n closed: boolean | undefined,\n visual: ShapeVisual,\n): void {\n if (points.length < 2) return;\n\n ctx.beginPath();\n ctx.moveTo(points[0].x, points[0].y);\n\n for (let i = 1; i < points.length; i++) {\n const prev = points[i - 1];\n const curr = points[i];\n\n if (prev.out && curr.in) {\n ctx.bezierCurveTo(\n prev.x + prev.out.x, prev.y + prev.out.y,\n curr.x + curr.in.x, curr.y + curr.in.y,\n curr.x, curr.y,\n );\n } else {\n ctx.lineTo(curr.x, curr.y);\n }\n }\n\n if (closed) ctx.closePath();\n\n if (visual.fill) {\n applyFill(ctx, visual.fill, 0, 0);\n ctx.fill();\n }\n if (visual.stroke) {\n const perimeter = pathPerimeter(points, closed);\n applyStroke(ctx, visual.stroke, perimeter);\n ctx.stroke();\n }\n}\n","import type { TextVisual } from \"@a-company/atelier-types\";\nimport type { RenderContext } from \"../canvas-types.js\";\nimport type { EffectiveLayer } from \"../apply-properties.js\";\nimport { colorToCSS } from \"../styles.js\";\n\nexport function renderText(ctx: RenderContext, eff: EffectiveLayer): void {\n const visual = eff.visual as TextVisual;\n const { style } = visual;\n\n // Build font string\n const fontStyle = style.fontStyle ?? \"normal\";\n const fontWeight = style.fontWeight ?? \"normal\";\n const fontSize = style.fontSize;\n const fontFamily = style.fontFamily;\n ctx.font = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`;\n\n // Set alignment — position text within its bounding box\n const align = style.textAlign ?? \"left\";\n ctx.textAlign = align;\n ctx.textBaseline = \"top\";\n\n // Set color\n ctx.fillStyle = colorToCSS(style.color);\n\n // Canvas 2D textAlign anchors at the draw point, so offset within bounds:\n // \"left\" → draw at x=0 (text flows right from left edge)\n // \"center\" → draw at x=width/2 (text centers within bounds)\n // \"right\" → draw at x=width (text flows left from right edge)\n let textX = 0;\n if (align === \"center\") {\n textX = eff.width / 2;\n } else if (align === \"right\") {\n textX = eff.width;\n }\n\n ctx.fillText(visual.content, textX, 0);\n}\n","import type { ImageVisual } from \"@a-company/atelier-types\";\nimport type { RenderContext } from \"../canvas-types.js\";\nimport type { EffectiveLayer } from \"../apply-properties.js\";\nimport type { ImageCache } from \"../image-cache.js\";\n\n/**\n * Render an image layer to the canvas.\n * Supports sourceRect cropping and spritesheet grid animation.\n * If the image hasn't loaded yet, triggers a cache load and returns\n * (the image will appear on the next re-render).\n */\nexport function renderImage(ctx: RenderContext, eff: EffectiveLayer, imageCache: ImageCache): void {\n const visual = eff.visual as ImageVisual;\n const src = visual.src;\n if (!src) return;\n\n const img = imageCache.get(src);\n if (!img) {\n imageCache.load(src);\n return;\n }\n\n // Spritesheet grid computation takes precedence over manual sourceRect\n if (visual.spritesheet) {\n const { columns, rows, frameWidth, frameHeight, frameCount } = visual.spritesheet;\n const maxFrames = frameCount ?? (columns * rows);\n const idx = Math.max(0, Math.min(Math.floor(visual.frameIndex ?? 0), maxFrames - 1));\n const col = idx % columns;\n const row = Math.floor(idx / columns);\n const sx = col * frameWidth;\n const sy = row * frameHeight;\n (ctx.drawImage as Function)(img, sx, sy, frameWidth, frameHeight, 0, 0, eff.width, eff.height);\n return;\n }\n\n // Manual sourceRect crop\n if (visual.sourceRect) {\n const sr = visual.sourceRect;\n (ctx.drawImage as Function)(img, sr.x, sr.y, sr.width, sr.height, 0, 0, eff.width, eff.height);\n return;\n }\n\n // Default: draw full image to bounds\n ctx.drawImage(img, 0, 0, eff.width, eff.height);\n}\n","import type { VideoVisual } from \"@a-company/atelier-types\";\nimport type { RenderContext, VideoFrameProvider } from \"../canvas-types.js\";\nimport type { EffectiveLayer } from \"../apply-properties.js\";\n\n/**\n * Render a video layer to the canvas at the given source time.\n * Delegates actual frame extraction to the injected VideoFrameProvider,\n * which may be backed by HTMLVideoElement (preview) or WebCodecs (export).\n * Returns without drawing if the provider returns null — caller retries next tick.\n *\n * Honors `visual.objectFit`:\n * - `\"fill\"` (default for legacy behavior) — stretch to layer bounds, may distort.\n * - `\"contain\"` — aspect-preserving fit-inside, letterbox with transparent gutters.\n * - `\"cover\"` — aspect-preserving fill-outside, crop overflow.\n * The default is `contain` when omitted — for a creator authoring a 9:16\n * portrait canvas with a 16:9 source clip, `fill` would squash visibly;\n * `contain` shows the clip in its natural aspect within the layer's box.\n */\nexport function renderVideo(\n ctx: RenderContext,\n eff: EffectiveLayer,\n sourceTime: number,\n provider: VideoFrameProvider,\n): void {\n const visual = eff.visual as VideoVisual;\n const src = visual.src;\n if (!src) return;\n\n const frame = provider(src, sourceTime, eff.width, eff.height);\n if (!frame) return;\n\n const fit = visual.objectFit ?? \"contain\";\n if (fit === \"fill\") {\n ctx.drawImage(frame as unknown, 0, 0, eff.width, eff.height);\n return;\n }\n\n // Frame has natural dimensions — HTMLVideoElement carries videoWidth/Height,\n // ImageBitmap carries width/height. Both surface as `.width` / `.height` /\n // `.videoWidth` / `.videoHeight`. Probe at runtime; fall back to fill if we\n // can't read the source's intrinsic size (e.g. partially-loaded video).\n const src_w = (frame as { videoWidth?: number; width?: number }).videoWidth\n ?? (frame as { width?: number }).width\n ?? 0;\n const src_h = (frame as { videoHeight?: number; height?: number }).videoHeight\n ?? (frame as { height?: number }).height\n ?? 0;\n if (src_w <= 0 || src_h <= 0) {\n ctx.drawImage(frame as unknown, 0, 0, eff.width, eff.height);\n return;\n }\n\n const srcAspect = src_w / src_h;\n const dstAspect = eff.width / eff.height;\n let dw: number, dh: number, dx: number, dy: number;\n\n if (fit === \"cover\") {\n // Fill the box, overflow on the longer axis is cropped.\n if (srcAspect > dstAspect) {\n dh = eff.height;\n dw = dh * srcAspect;\n } else {\n dw = eff.width;\n dh = dw / srcAspect;\n }\n } else {\n // contain — fit inside the box, transparent letterbox on the shorter axis.\n if (srcAspect > dstAspect) {\n dw = eff.width;\n dh = dw / srcAspect;\n } else {\n dh = eff.height;\n dw = dh * srcAspect;\n }\n }\n dx = (eff.width - dw) / 2;\n dy = (eff.height - dh) / 2;\n ctx.drawImage(frame as unknown, dx, dy, dw, dh);\n}\n","import type { AtelierDocument, RefVisual, ImageVisual } from \"@a-company/atelier-types\";\nimport { resolveFrame } from \"@a-company/atelier-core\";\nimport type { RenderContext, DocumentResolver } from \"../canvas-types.js\";\nimport type { EffectiveLayer } from \"../apply-properties.js\";\nimport { buildEffectiveLayer } from \"../apply-properties.js\";\nimport { renderShape } from \"./shape-renderer.js\";\nimport { renderText } from \"./text-renderer.js\";\nimport { renderImage } from \"./image-renderer.js\";\nimport type { ImageCache } from \"../image-cache.js\";\n\nexport interface RefRenderOpts {\n documentResolver?: DocumentResolver;\n maxRefDepth?: number;\n imageCache?: ImageCache;\n /** Internal: tracks visited refs for cycle detection */\n _visitedRefs?: Set<string>;\n /** Internal: current recursion depth */\n _depth?: number;\n}\n\n/**\n * Render a ref layer. If a documentResolver is provided, resolves and renders\n * the sub-document inline. Otherwise falls back to a placeholder rectangle.\n */\nexport function renderRef(\n ctx: RenderContext,\n eff: EffectiveLayer,\n opts?: RefRenderOpts,\n _parentDoc?: AtelierDocument,\n): void {\n const visual = eff.visual as RefVisual;\n const resolver = opts?.documentResolver;\n\n if (!resolver) {\n renderPlaceholder(ctx, eff, `REF: ${visual.src}`);\n return;\n }\n\n const depth = opts?._depth ?? 0;\n const maxDepth = opts?.maxRefDepth ?? 4;\n\n if (depth >= maxDepth) {\n renderPlaceholder(ctx, eff, \"MAX DEPTH\");\n return;\n }\n\n const visitedRefs = opts?._visitedRefs ?? new Set<string>();\n\n if (visitedRefs.has(visual.src)) {\n renderPlaceholder(ctx, eff, \"CYCLE\");\n return;\n }\n\n const subDoc = resolver(visual.src);\n if (!subDoc) {\n renderPlaceholder(ctx, eff, \"NOT FOUND\");\n return;\n }\n\n // Determine which state and frame to render\n const stateNames = Object.keys(subDoc.states);\n if (stateNames.length === 0) {\n renderPlaceholder(ctx, eff, \"NO STATES\");\n return;\n }\n\n const stateName = visual.state ?? stateNames[0];\n const stateObj = subDoc.states[stateName];\n if (!stateObj) {\n renderPlaceholder(ctx, eff, `STATE? ${stateName}`);\n return;\n }\n\n const maxFrame = Math.max(0, stateObj.duration - 1);\n const frame = Math.min(visual.frame ?? 0, maxFrame);\n\n // Resolve the sub-document frame\n const resolved = resolveFrame(subDoc, stateName, frame);\n\n // Mark as visited for cycle detection\n visitedRefs.add(visual.src);\n\n // Scale to fit eff bounds\n const scaleX = eff.width / subDoc.canvas.width;\n const scaleY = eff.height / subDoc.canvas.height;\n\n ctx.save();\n ctx.scale(scaleX, scaleY);\n\n // Build and render sub-doc layers\n const { width: subW, height: subH } = subDoc.canvas;\n\n for (const resolvedLayer of resolved.layers) {\n const subEff = buildEffectiveLayer(resolvedLayer, subW, subH);\n\n if (!subEff.visible) continue;\n if (subEff.opacity <= 0) continue;\n\n // Resolve asset for images\n if (resolvedLayer.layer.visual.type === \"image\") {\n const iv = subEff.visual as ImageVisual;\n if (!iv.src && iv.assetId && subDoc.assets?.[iv.assetId]) {\n iv.src = subDoc.assets[iv.assetId].src;\n }\n }\n\n ctx.save();\n ctx.globalAlpha = subEff.opacity;\n ctx.translate(subEff.x, subEff.y);\n\n switch (resolvedLayer.layer.visual.type) {\n case \"shape\":\n renderShape(ctx, subEff);\n break;\n case \"text\":\n renderText(ctx, subEff);\n break;\n case \"image\":\n if (opts?.imageCache) renderImage(ctx, subEff, opts.imageCache);\n break;\n case \"ref\":\n renderRef(ctx, subEff, {\n ...opts,\n _visitedRefs: visitedRefs,\n _depth: depth + 1,\n }, subDoc);\n break;\n case \"group\":\n break;\n }\n\n ctx.restore();\n }\n\n ctx.restore();\n\n // Remove from visited after rendering (allow same ref in different branches)\n visitedRefs.delete(visual.src);\n}\n\n/** Render a dashed placeholder rectangle with label */\nfunction renderPlaceholder(ctx: RenderContext, eff: EffectiveLayer, label: string): void {\n const { width, height } = eff;\n\n ctx.strokeStyle = \"#888888\";\n ctx.lineWidth = 2;\n ctx.setLineDash([6, 4]);\n ctx.strokeRect(0, 0, width, height);\n ctx.setLineDash([]);\n\n ctx.fillStyle = \"#888888\";\n ctx.font = `${Math.max(12, Math.min(16, height * 0.15))}px sans-serif`;\n ctx.textAlign = \"center\";\n ctx.textBaseline = \"middle\";\n ctx.fillText(label, width / 2, height / 2, width - 8);\n}\n","import type { AtelierDocument, ImageVisual, VideoVisual, Shape } from \"@a-company/atelier-types\";\nimport type { ResolvedFrame } from \"@a-company/atelier-core\";\nimport type { RenderContext, DocumentResolver, VideoFrameProvider } from \"./canvas-types.js\";\nimport { buildEffectiveLayer, type EffectiveLayer } from \"./apply-properties.js\";\nimport { renderShape } from \"./renderers/shape-renderer.js\";\nimport { renderText } from \"./renderers/text-renderer.js\";\nimport { renderImage } from \"./renderers/image-renderer.js\";\nimport { renderVideo } from \"./renderers/video-renderer.js\";\nimport { renderRef } from \"./renderers/ref-renderer.js\";\nimport type { ImageCache } from \"./image-cache.js\";\n\n/** Options for renderFrame */\nexport interface RenderOptions {\n imageCache?: ImageCache;\n documentResolver?: DocumentResolver;\n maxRefDepth?: number;\n videoFrameProvider?: VideoFrameProvider;\n}\n\n/**\n * Render a resolved frame to a Canvas 2D context.\n * Clears the canvas and draws all visible layers in order.\n */\nexport function renderFrame(\n ctx: RenderContext,\n resolvedFrame: ResolvedFrame,\n doc: AtelierDocument,\n optsOrCache?: RenderOptions | ImageCache,\n): void {\n // Backward compatible: detect old ImageCache arg vs new RenderOptions\n let imageCache: ImageCache | undefined;\n let documentResolver: DocumentResolver | undefined;\n let videoFrameProvider: VideoFrameProvider | undefined;\n let maxRefDepth = 4;\n\n if (optsOrCache && typeof (optsOrCache as ImageCache).get === \"function\") {\n imageCache = optsOrCache as ImageCache;\n } else if (optsOrCache) {\n const opts = optsOrCache as RenderOptions;\n imageCache = opts.imageCache;\n documentResolver = opts.documentResolver;\n videoFrameProvider = opts.videoFrameProvider;\n maxRefDepth = opts.maxRefDepth ?? 4;\n }\n const { width, height } = doc.canvas;\n\n // Clear canvas, then apply background if explicitly set.\n //\n // The naive `fillStyle = \"transparent\"; fillRect(...)` was a NO-OP under\n // default `source-over` compositing — transparent pixels source-over\n // existing pixels = existing pixels unchanged. The canvas accumulated\n // content between renders, which was MASKED in the live editor by\n // full-canvas video layers (drawImage overwrites every pixel) but exposed\n // catastrophically in the MP4 exporter, where captions stacked up over\n // every frame because nothing else ever overwrote them (export had no\n // videoFrameProvider wired, so the video layer was a no-op). Owner saw\n // a \"single static frame with all captions overlapping\" in\n // /Users/ascend/Downloads/animation (1).mp4.\n //\n // `clearRect` resets every pixel to fully-transparent black regardless of\n // compositing. Then we fill the explicit background if the doc set one\n // (image/video docs may set \"#000\" or a brand color so letterboxed gutters\n // aren't transparent).\n ctx.clearRect(0, 0, width, height);\n if (doc.canvas.background) {\n ctx.fillStyle = doc.canvas.background;\n ctx.fillRect(0, 0, width, height);\n }\n\n // Build effective layers into a map for ancestor transform lookups\n const effMap = new Map<string, EffectiveLayer>();\n const effList: EffectiveLayer[] = [];\n const videoSourceTimeMap = new Map<string, number>();\n\n for (const resolvedLayer of resolvedFrame.layers) {\n const eff = buildEffectiveLayer(resolvedLayer, width, height);\n effMap.set(resolvedLayer.layer.id, eff);\n effList.push(eff);\n if (resolvedLayer.videoSourceTime !== undefined) {\n videoSourceTimeMap.set(resolvedLayer.layer.id, resolvedLayer.videoSourceTime);\n }\n }\n\n // Render each layer in order (painters algorithm — first = back)\n for (const eff of effList) {\n const { layer } = eff;\n\n // Skip invisible layers\n if (!eff.visible) continue;\n\n // Skip fully transparent layers\n if (eff.opacity <= 0) continue;\n\n // Resolve assetId → src for image visuals\n if (layer.visual.type === \"image\") {\n const iv = eff.visual as ImageVisual;\n if (!iv.src && iv.assetId && doc.assets?.[iv.assetId]) {\n iv.src = doc.assets[iv.assetId].src;\n }\n }\n\n // Resolve assetId → src for video visuals\n if (layer.visual.type === \"video\") {\n const vv = eff.visual as VideoVisual;\n if (!vv.src && vv.assetId && doc.assets?.[vv.assetId]) {\n vv.src = doc.assets[vv.assetId].src;\n }\n }\n\n ctx.save();\n\n // Apply ancestor transforms (group/parent inheritance)\n applyAncestorTransforms(ctx, layer.id, effMap, doc);\n\n // Apply transforms\n ctx.globalAlpha = eff.opacity;\n\n // Apply blend mode\n if (eff.blendMode !== \"normal\") {\n ctx.globalCompositeOperation = blendModeToComposite(eff.blendMode);\n }\n\n ctx.translate(eff.x, eff.y);\n\n // Apply anchor-relative transforms\n const anchorPixelX = eff.anchorX * eff.width;\n const anchorPixelY = eff.anchorY * eff.height;\n\n // Combine explicit rotation with motion path auto-rotation\n const totalRotation = eff.rotation + eff.motionPathAngle;\n\n if (totalRotation !== 0 || eff.scaleX !== 1 || eff.scaleY !== 1) {\n ctx.translate(anchorPixelX, anchorPixelY);\n if (totalRotation !== 0) {\n ctx.rotate((totalRotation * Math.PI) / 180);\n }\n if (eff.scaleX !== 1 || eff.scaleY !== 1) {\n ctx.scale(eff.scaleX, eff.scaleY);\n }\n ctx.translate(-anchorPixelX, -anchorPixelY);\n }\n\n // Apply shadow if present\n if (eff.shadow) {\n ctx.shadowColor = eff.shadow.color;\n ctx.shadowBlur = eff.shadow.blur;\n ctx.shadowOffsetX = eff.shadow.offsetX;\n ctx.shadowOffsetY = eff.shadow.offsetY;\n }\n\n // Apply clip path if present\n if (layer.clipPath) {\n applyClipPath(ctx, layer.clipPath, eff.width, eff.height);\n }\n\n // Tint requires offscreen compositing — draw to offscreen if tint is active\n const hasTint = eff.tint && eff.tint.amount > 0 && ctx.createOffscreen;\n let renderCtx = ctx;\n let offscreen: { ctx: RenderContext; canvas: unknown } | null = null;\n\n if (hasTint) {\n offscreen = ctx.createOffscreen!(eff.width, eff.height);\n renderCtx = offscreen.ctx;\n }\n\n // Ref rendering options (passed through for sub-doc composition)\n const refOpts = { documentResolver, maxRefDepth, imageCache };\n\n // Dispatch to type-specific renderer\n switch (layer.visual.type) {\n case \"shape\":\n renderShape(renderCtx, eff);\n break;\n case \"text\":\n renderText(renderCtx, eff);\n break;\n case \"image\":\n if (imageCache) renderImage(renderCtx, eff, imageCache);\n break;\n case \"video\":\n if (videoFrameProvider) {\n renderVideo(renderCtx, eff, videoSourceTimeMap.get(layer.id) ?? 0, videoFrameProvider);\n }\n break;\n case \"group\":\n // Groups are structural containers — no visual output\n break;\n case \"ref\":\n renderRef(renderCtx, eff, refOpts, doc);\n break;\n }\n\n // Apply tint via multiply composite on offscreen canvas\n if (hasTint && offscreen && eff.tint) {\n const offCtx = offscreen.ctx;\n // Draw tint color over the content using multiply\n offCtx.save();\n offCtx.globalCompositeOperation = \"multiply\";\n offCtx.fillStyle = eff.tint.color;\n offCtx.fillRect(0, 0, eff.width, eff.height);\n offCtx.restore();\n\n // Clip to original content shape using destination-in\n offCtx.save();\n offCtx.globalCompositeOperation = \"destination-in\";\n // Re-render the content to create the alpha mask\n switch (layer.visual.type) {\n case \"shape\": renderShape(offCtx, eff); break;\n case \"text\": renderText(offCtx, eff); break;\n case \"image\": if (imageCache) renderImage(offCtx, eff, imageCache); break;\n case \"video\": if (videoFrameProvider) renderVideo(offCtx, eff, videoSourceTimeMap.get(layer.id) ?? 0, videoFrameProvider); break;\n case \"ref\": renderRef(offCtx, eff, refOpts, doc); break;\n }\n offCtx.restore();\n\n // Blend tinted offscreen back with original at tint.amount alpha\n // First draw original content\n switch (layer.visual.type) {\n case \"shape\": renderShape(ctx, eff); break;\n case \"text\": renderText(ctx, eff); break;\n case \"image\": if (imageCache) renderImage(ctx, eff, imageCache); break;\n case \"video\": if (videoFrameProvider) renderVideo(ctx, eff, videoSourceTimeMap.get(layer.id) ?? 0, videoFrameProvider); break;\n case \"ref\": renderRef(ctx, eff, refOpts, doc); break;\n }\n // Then overlay tinted version at tint amount\n const prevAlpha = ctx.globalAlpha;\n ctx.globalAlpha = eff.tint.amount;\n ctx.drawImage(offscreen.canvas, 0, 0, eff.width, eff.height);\n ctx.globalAlpha = prevAlpha;\n }\n\n ctx.restore();\n }\n}\n\n/** Apply a shape as a clipping region */\nfunction applyClipPath(ctx: RenderContext, shape: Shape, width: number, height: number): void {\n ctx.beginPath();\n switch (shape.type) {\n case \"rect\":\n if (shape.cornerRadius && ctx.roundRect) {\n ctx.roundRect(0, 0, width, height, shape.cornerRadius);\n } else {\n // Manual rect path for clipping (fillRect doesn't create a path)\n ctx.moveTo(0, 0);\n ctx.lineTo(width, 0);\n ctx.lineTo(width, height);\n ctx.lineTo(0, height);\n ctx.closePath();\n }\n break;\n case \"ellipse\":\n ctx.ellipse(width / 2, height / 2, width / 2, height / 2, 0, 0, Math.PI * 2);\n break;\n case \"path\":\n if (shape.points.length >= 2) {\n ctx.moveTo(shape.points[0].x, shape.points[0].y);\n for (let i = 1; i < shape.points.length; i++) {\n const prev = shape.points[i - 1];\n const curr = shape.points[i];\n if (prev.out && curr.in) {\n ctx.bezierCurveTo(\n prev.x + prev.out.x, prev.y + prev.out.y,\n curr.x + curr.in.x, curr.y + curr.in.y,\n curr.x, curr.y,\n );\n } else {\n ctx.lineTo(curr.x, curr.y);\n }\n }\n if (shape.closed) ctx.closePath();\n }\n break;\n }\n ctx.clip();\n}\n\n/** Map Atelier blend mode names to Canvas 2D globalCompositeOperation values */\nfunction blendModeToComposite(mode: string): string {\n const map: Record<string, string> = {\n \"multiply\": \"multiply\",\n \"screen\": \"screen\",\n \"overlay\": \"overlay\",\n \"darken\": \"darken\",\n \"lighten\": \"lighten\",\n \"color-dodge\": \"color-dodge\",\n \"color-burn\": \"color-burn\",\n \"hard-light\": \"hard-light\",\n \"soft-light\": \"soft-light\",\n \"difference\": \"difference\",\n \"exclusion\": \"exclusion\",\n \"hue\": \"hue\",\n \"saturation\": \"saturation\",\n \"color\": \"color\",\n \"luminosity\": \"luminosity\",\n };\n return map[mode] ?? \"source-over\";\n}\n\n/**\n * Walk the parentId chain and apply each ancestor's transforms.\n * This makes child layers inherit parent position/rotation/scale.\n */\nfunction applyAncestorTransforms(\n ctx: RenderContext,\n layerId: string,\n effMap: Map<string, EffectiveLayer>,\n doc: AtelierDocument,\n): void {\n // Collect ancestor chain (excluding self)\n const chain: EffectiveLayer[] = [];\n const layer = doc.layers.find((l) => l.id === layerId);\n let parentId = layer?.parentId;\n const visited = new Set<string>();\n\n while (parentId && !visited.has(parentId)) {\n visited.add(parentId);\n const parentEff = effMap.get(parentId);\n if (!parentEff) break;\n chain.push(parentEff);\n parentId = parentEff.layer.parentId;\n }\n\n // Apply from root ancestor down to direct parent\n for (let i = chain.length - 1; i >= 0; i--) {\n const p = chain[i];\n ctx.translate(p.x, p.y);\n\n const ax = p.anchorX * p.width;\n const ay = p.anchorY * p.height;\n\n if (p.rotation !== 0 || p.scaleX !== 1 || p.scaleY !== 1) {\n ctx.translate(ax, ay);\n if (p.rotation !== 0) ctx.rotate((p.rotation * Math.PI) / 180);\n if (p.scaleX !== 1 || p.scaleY !== 1) ctx.scale(p.scaleX, p.scaleY);\n ctx.translate(-ax, -ay);\n }\n }\n}\n","/**\n * A loaded image entry that can be drawn via RenderContext.drawImage.\n */\nexport interface CachedImage {\n complete: boolean;\n image: unknown;\n}\n\n/**\n * Simple image cache for loading and caching images.\n * Browser-agnostic — the actual Image constructor is injected via createImage.\n * Triggers an onLoad callback when an image finishes loading so the\n * renderer can re-render the current frame.\n */\nexport class ImageCache {\n private cache = new Map<string, CachedImage>();\n private loading = new Set<string>();\n private onLoad?: () => void;\n private createImage?: (src: string, onLoad: () => void, onError: () => void) => unknown;\n\n constructor(opts?: {\n onLoad?: () => void;\n createImage?: (src: string, onLoad: () => void, onError: () => void) => unknown;\n }) {\n this.onLoad = opts?.onLoad;\n this.createImage = opts?.createImage;\n }\n\n get(src: string): unknown | null {\n const entry = this.cache.get(src);\n return entry?.complete ? entry.image : null;\n }\n\n load(src: string): void {\n if (this.cache.has(src) || this.loading.has(src)) return;\n if (!this.createImage) return;\n this.loading.add(src);\n const image = this.createImage(\n src,\n () => {\n this.cache.set(src, { complete: true, image });\n this.loading.delete(src);\n this.onLoad?.();\n },\n () => {\n this.loading.delete(src);\n },\n );\n }\n}\n"],"mappings":"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","names":[]}