@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
@@ -1 +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","../src/index.ts","../src/commands/validate.ts","../../schema/src/units.ts","../../schema/src/coordinates.ts","../../schema/src/color.ts","../../schema/src/shape.ts","../../schema/src/easing.ts","../../schema/src/shadow.ts","../../schema/src/layer.ts","../../schema/src/interaction.ts","../../schema/src/delta.ts","../../schema/src/state.ts","../../schema/src/preset.ts","../../schema/src/variable.ts","../../schema/src/asset.ts","../../schema/src/document.ts","../../schema/src/recipe.ts","../../schema/src/validate.ts","../../schema/src/parse.ts","../src/commands/info.ts","../src/commands/still.ts","../src/commands/render.ts","../src/commands/render-pipeline.ts","../src/commands/export-svg.ts","../../svg/src/render-svg.ts","../../svg/src/svg-properties.ts","../../svg/src/svg-gradients.ts","../../svg/src/svg-shapes.ts","../../svg/src/svg-text.ts","../../svg/src/svg-filters.ts","../../svg/src/svg-clip.ts","../src/commands/export-lottie.ts","../../lottie/src/map-colors.ts","../../lottie/src/map-shapes.ts","../../lottie/src/map-easing.ts","../../lottie/src/map-keyframes.ts","../../lottie/src/map-layers.ts","../../lottie/src/warnings.ts","../../lottie/src/export-lottie.ts","../src/commands/export-image.ts","../src/lib/render-image.ts","../src/commands/carousel.ts","../src/lib/recipe.ts","../src/commands/assets.ts","../src/commands/variables.ts","../src/lib/video-project.ts","../src/lib/silence-detect.ts","../src/lib/cut-model.ts","../src/commands/trim.ts","../src/lib/whisper.ts","../src/lib/transcript-model.ts","../src/lib/caption-builder.ts","../src/commands/transcribe.ts","../src/commands/transcript.ts","../src/commands/captions.ts","../src/commands/recipe.ts","../src/commands/apply-recipe.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 } 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 // 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 for (const [property, deltas] of layerDeltas) {\n const value = resolvePropertyAtFrame(deltas, frame);\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 * 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 { Audio } from \"@a-company/atelier-types\";\n\n/** Audio playback state at a given frame */\nexport interface AudioPlaybackState {\n /** Whether audio should be playing at this frame */\n shouldPlay: boolean;\n /** Current position in the audio track (seconds) */\n currentTime: number;\n /** Effective volume (0–1) */\n volume: number;\n}\n\n/**\n * Convert a frame number to time in seconds.\n */\nexport function frameToTime(frame: number, fps: number): number {\n return frame / fps;\n}\n\n/**\n * Convert time in seconds to a frame number (floored).\n */\nexport function timeToFrame(time: number, fps: number): number {\n return Math.floor(time * fps);\n}\n\n/**\n * Compute the audio playback state for a given frame.\n *\n * @param audio - Audio configuration from a state\n * @param frame - Current frame number within the state\n * @param fps - Frames per second from canvas config\n * @param stateDuration - Total state duration in frames\n * @returns AudioPlaybackState with shouldPlay, currentTime, and volume\n */\nexport function computeAudioState(\n audio: Audio,\n frame: number,\n fps: number,\n stateDuration: number,\n): AudioPlaybackState {\n const startFrame = audio.startFrame ?? 0;\n const offset = audio.offset ?? 0;\n const volume = audio.volume ?? 1;\n const loop = audio.loop ?? false;\n\n // Before audio starts\n if (frame < startFrame) {\n return { shouldPlay: false, currentTime: 0, volume };\n }\n\n // Time elapsed since audio started\n const elapsedFrames = frame - startFrame;\n const elapsedTime = frameToTime(elapsedFrames, fps);\n\n // Total available playback time (from startFrame to end of state)\n const availableFrames = stateDuration - startFrame;\n const availableTime = frameToTime(availableFrames, fps);\n\n if (!loop) {\n // Non-looping: audio plays from offset, stops at end of state\n const currentTime = offset + elapsedTime;\n return {\n shouldPlay: frame < stateDuration,\n currentTime,\n volume,\n };\n }\n\n // Looping: wrap elapsed time around available duration\n // The audio segment length is the available time\n const segmentDuration = availableTime;\n if (segmentDuration <= 0) {\n return { shouldPlay: false, currentTime: 0, volume };\n }\n\n const wrappedTime = elapsedTime % segmentDuration;\n const currentTime = offset + wrappedTime;\n\n return {\n shouldPlay: frame < stateDuration,\n currentTime,\n volume,\n };\n}\n\n/**\n * Compute audio cue points — frames where audio events occur.\n *\n * @param audio - Audio configuration\n * @param stateDuration - Total state duration in frames\n * @returns Array of { frame, event } describing audio events\n */\nexport function computeAudioCues(\n audio: Audio,\n stateDuration: number,\n): Array<{ frame: number; event: \"start\" | \"end\" | \"loop\" }> {\n const startFrame = audio.startFrame ?? 0;\n const loop = audio.loop ?? false;\n const cues: Array<{ frame: number; event: \"start\" | \"end\" | \"loop\" }> = [];\n\n if (startFrame < stateDuration) {\n cues.push({ frame: startFrame, event: \"start\" });\n }\n\n if (loop) {\n // In loop mode, audio restarts each full cycle\n const availableFrames = stateDuration - startFrame;\n if (availableFrames > 0) {\n // Loop points at each full cycle\n for (let i = 1; startFrame + i * availableFrames < stateDuration; i++) {\n cues.push({ frame: startFrame + i * availableFrames, event: \"loop\" });\n }\n }\n }\n\n // Audio ends when state ends\n if (stateDuration > startFrame) {\n cues.push({ frame: stateDuration - 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 */\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 ctx.drawImage(frame as unknown, 0, 0, eff.width, eff.height);\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\n ctx.fillStyle = doc.canvas.background ?? \"transparent\";\n ctx.fillRect(0, 0, width, height);\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","// @a-company/atelier-cli — CLI tool: validate, preview, render, info commands\n\n// Command registration functions (for programmatic use)\nexport { validateCommand, validateFile } from \"./commands/validate.js\";\nexport { infoCommand, getInfo } from \"./commands/info.js\";\nexport type { DocumentInfo } from \"./commands/info.js\";\nexport { stillCommand, resolveStill } from \"./commands/still.js\";\nexport { renderCommand } from \"./commands/render.js\";\nexport { exportSvgCommand } from \"./commands/export-svg.js\";\nexport { exportLottieCommand } from \"./commands/export-lottie.js\";\nexport { exportImageCommand, resolveExportDimensions } from \"./commands/export-image.js\";\nexport {\n carouselCommand,\n composeCarouselFrameDoc,\n expandInputs,\n carouselFileName,\n} from \"./commands/carousel.js\";\nexport {\n renderDocumentToPng,\n fitImageToCanvas,\n loadCanvasModule,\n CanvasUnavailableError,\n} from \"./lib/render-image.js\";\nexport { assetsCommand, getAssets } from \"./commands/assets.js\";\nexport type { AssetInfo } from \"./commands/assets.js\";\nexport { variablesCommand, getVariables } from \"./commands/variables.js\";\nexport type { VariableInfo } from \"./commands/variables.js\";\nexport {\n checkFfmpeg,\n buildFfmpegArgs,\n renderDocument,\n} from \"./commands/render-pipeline.js\";\nexport type {\n RenderFormat,\n RenderOptions,\n RenderResult,\n ProgressInfo,\n} from \"./commands/render-pipeline.js\";\n\n// VideoProject — local-first video project folder structure\nexport {\n createVideoProject,\n loadVideoProject,\n readCutList,\n writeCutList,\n readTranscript,\n writeTranscript,\n readComposition,\n writeComposition,\n rewriteCutLayers,\n effectiveSpan,\n VIDEO_PROJECT_VERSION,\n VIDEO_CUTLIST_VERSION,\n} from \"./lib/video-project.js\";\nexport type { VideoProject } from \"./lib/video-project.js\";\n\n// Silence trim — parametric cuts + atelier trim pipeline\nexport { trimCommand, trimProject } from \"./commands/trim.js\";\nexport type { TrimOptions, TrimResult } from \"./commands/trim.js\";\n\n// Transcribe — Whisper + editable transcript + caption layers\nexport { transcribeCommand, transcribeProject } from \"./commands/transcribe.js\";\nexport type { TranscribeOptions, TranscribeResult } from \"./commands/transcribe.js\";\nexport { transcriptCommand } from \"./commands/transcript.js\";\nexport { captionsCommand } from \"./commands/captions.js\";\nexport {\n buildCaptionLayers,\n rewriteCaptionLayers,\n} from \"./lib/caption-builder.js\";\nexport type { CaptionStyle, BuildCaptionsOptions } from \"./lib/caption-builder.js\";\nexport {\n flattenWords,\n groupIntoPhrases,\n mergeTranscriptWithExisting,\n applyTextEdit,\n applyBatchReplace,\n applyHide,\n applyAdd,\n applyMerge,\n applySplit,\n} from \"./lib/transcript-model.js\";\nexport {\n probeWhisper,\n runWhisperCpp,\n parseWhisperCppJson,\n} from \"./lib/whisper.js\";\nexport type { WhisperBackend, WhisperModel, WhisperOptions } from \"./lib/whisper.js\";\nexport { VIDEO_TRANSCRIPT_VERSION } from \"./lib/video-project.js\";\n\n// Studio Recipe — Phase 1\nexport { recipeCommand } from \"./commands/recipe.js\";\nexport { applyRecipeCommand } from \"./commands/apply-recipe.js\";\nexport {\n loadRecipe,\n resolveRecipePath,\n scaffoldRecipeYaml,\n applyRecipeToTrimOptions,\n applyRecipeToTranscribeOptions,\n applyRecipeToCaptionOptions,\n renderRecipeWithDefaults,\n recipeToYaml,\n RECIPE_VERSION,\n} from \"./lib/recipe.js\";\nexport type { LoadedRecipe } from \"./lib/recipe.js\";\n","import { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { Command } from \"commander\";\nimport { parseAtelier } from \"@a-company/atelier-schema\";\nimport { validateAllDeltas } from \"@a-company/atelier-core\";\n\n/**\n * Validate an .atelier file: parse YAML, check schema, check delta overlaps.\n * Returns { valid, errors } for programmatic use.\n */\nexport function validateFile(filePath: string): {\n valid: boolean;\n errors: string[];\n} {\n const absPath = resolve(filePath);\n let content: string;\n try {\n content = readFileSync(absPath, \"utf-8\");\n } catch {\n return { valid: false, errors: [`Cannot read file: ${absPath}`] };\n }\n\n const result = parseAtelier(content);\n if (!result.success) {\n return {\n valid: false,\n errors: result.errors.map(\n (e) => `${e.path}: ${e.message}`,\n ),\n };\n }\n\n // Check all states for delta overlaps\n const overlapErrors: string[] = [];\n for (const [stateName, state] of Object.entries(result.data.states)) {\n const overlaps = validateAllDeltas(state.deltas);\n for (const overlap of overlaps) {\n overlapErrors.push(`State \"${stateName}\": ${overlap.message}`);\n }\n }\n\n if (overlapErrors.length > 0) {\n return { valid: false, errors: overlapErrors };\n }\n\n return { valid: true, errors: [] };\n}\n\n/**\n * Register the `validate` subcommand on the Commander program.\n */\nexport function validateCommand(program: Command): void {\n program\n .command(\"validate <file>\")\n .description(\"Validate an .atelier YAML file\")\n .action((file: string) => {\n const { valid, errors } = validateFile(file);\n if (valid) {\n console.log(\"Valid\");\n } else {\n console.error(\"Validation errors:\");\n for (const error of errors) {\n console.error(` - ${error}`);\n }\n process.exit(1);\n }\n });\n}\n","import { z } from \"zod\";\n\n/** Pixel value — any number */\nexport const PixelSchema = z.number();\n\n/** Percentage string like \"50%\" */\nexport const PercentageSchema = z.string().regex(/^-?\\d+(\\.\\d+)?%$/, {\n message: \"Percentage must be a number followed by %, e.g. \\\"50%\\\"\",\n});\n\n/** Either pixel or percentage */\nexport const UnitValueSchema = z.union([PixelSchema, PercentageSchema]);\n","import { z } from \"zod\";\nimport { UnitValueSchema } from \"./units.js\";\n\nexport const FrameSchema = z.object({\n x: UnitValueSchema,\n y: UnitValueSchema,\n});\n\nexport const BoundsSchema = z.object({\n width: UnitValueSchema,\n height: UnitValueSchema,\n});\n\nexport const AnchorPointSchema = z.object({\n x: z.number().min(0).max(1),\n y: z.number().min(0).max(1),\n});\n","import { z } from \"zod\";\n\nexport const RGBAColorSchema = z.object({\n r: z.number().min(0).max(255),\n g: z.number().min(0).max(255),\n b: z.number().min(0).max(255),\n a: z.number().min(0).max(1),\n});\n\nexport const HSLAColorSchema = z.object({\n h: z.number().min(0).max(360),\n s: z.number().min(0).max(100),\n l: z.number().min(0).max(100),\n a: z.number().min(0).max(1),\n});\n\nexport const HexColorSchema = z.string().regex(/^#([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/, {\n message: \"Color must be a hex string: #RGB, #RGBA, #RRGGBB, or #RRGGBBAA\",\n});\n\nexport const ColorSchema = z.union([RGBAColorSchema, HSLAColorSchema, HexColorSchema]);\n","import { z } from \"zod\";\nimport { ColorSchema } from \"./color.js\";\nimport { UnitValueSchema } from \"./units.js\";\n\nexport const PathPointSchema = z.object({\n x: z.number(),\n y: z.number(),\n in: z.object({ x: z.number(), y: z.number() }).optional(),\n out: z.object({ x: z.number(), y: z.number() }).optional(),\n});\n\nexport const RectShapeSchema = z.object({\n type: z.literal(\"rect\"),\n cornerRadius: z.union([\n z.number().min(0),\n z.tuple([z.number().min(0), z.number().min(0), z.number().min(0), z.number().min(0)]),\n ]).optional(),\n});\n\nexport const EllipseShapeSchema = z.object({\n type: z.literal(\"ellipse\"),\n});\n\nexport const PathShapeSchema = z.object({\n type: z.literal(\"path\"),\n points: z.array(PathPointSchema).min(2, \"Path must have at least 2 points\"),\n closed: z.boolean().optional(),\n});\n\nexport const ShapeSchema = z.discriminatedUnion(\"type\", [\n RectShapeSchema,\n EllipseShapeSchema,\n PathShapeSchema,\n]);\n\nexport const GradientStopSchema = z.object({\n offset: z.number().min(0).max(1),\n color: ColorSchema,\n});\n\nexport const SolidFillSchema = z.object({\n type: z.literal(\"solid\"),\n color: ColorSchema,\n});\n\nexport const LinearGradientFillSchema = z.object({\n type: z.literal(\"linear-gradient\"),\n angle: z.number(),\n stops: z.array(GradientStopSchema).min(2, \"Gradient needs at least 2 stops\"),\n});\n\nexport const RadialGradientFillSchema = z.object({\n type: z.literal(\"radial-gradient\"),\n center: z.object({ x: UnitValueSchema, y: UnitValueSchema }),\n radius: UnitValueSchema,\n stops: z.array(GradientStopSchema).min(2, \"Gradient needs at least 2 stops\"),\n});\n\nexport const FillSchema = z.discriminatedUnion(\"type\", [\n SolidFillSchema,\n LinearGradientFillSchema,\n RadialGradientFillSchema,\n]);\n\nexport const StrokeSchema = z.object({\n color: ColorSchema,\n width: z.number().min(0),\n dash: z.array(z.number().min(0)).optional(),\n lineCap: z.enum([\"butt\", \"round\", \"square\"]).optional(),\n lineJoin: z.enum([\"miter\", \"round\", \"bevel\"]).optional(),\n strokeStart: z.number().min(0).max(1).optional(),\n strokeEnd: z.number().min(0).max(1).optional(),\n});\n\nexport const TextStyleSchema = z.object({\n fontFamily: z.string().min(1, \"fontFamily is required\"),\n fontSize: z.number().positive(\"fontSize must be positive\"),\n fontWeight: z.union([z.number(), z.enum([\"normal\", \"bold\"])]).optional(),\n fontStyle: z.enum([\"normal\", \"italic\"]).optional(),\n textAlign: z.enum([\"left\", \"center\", \"right\"]).optional(),\n lineHeight: z.number().positive().optional(),\n letterSpacing: z.number().optional(),\n color: ColorSchema,\n});\n","import { z } from \"zod\";\n\nexport const LinearEasingSchema = z.object({ type: z.literal(\"linear\") });\n\nexport const CubicBezierEasingSchema = z.object({\n type: z.literal(\"cubic-bezier\"),\n x1: z.number().min(0).max(1),\n y1: z.number(),\n x2: z.number().min(0).max(1),\n y2: z.number(),\n});\n\nexport const SpringEasingSchema = z.object({\n type: z.literal(\"spring\"),\n mass: z.number().positive().optional(),\n stiffness: z.number().positive().optional(),\n damping: z.number().positive().optional(),\n velocity: z.number().optional(),\n});\n\nexport const StepEasingSchema = z.object({\n type: z.literal(\"step\"),\n steps: z.number().int().positive(),\n position: z.enum([\"start\", \"end\"]).optional(),\n});\n\nexport const EasingPresetSchema = z.enum([\"linear\", \"ease-in\", \"ease-out\", \"ease-in-out\"]);\n\nexport const EasingSchema = z.union([\n LinearEasingSchema,\n CubicBezierEasingSchema,\n SpringEasingSchema,\n StepEasingSchema,\n EasingPresetSchema,\n]);\n","import { z } from \"zod\";\nimport { ColorSchema } from \"./color.js\";\n\nexport const ShadowSchema = z.object({\n color: ColorSchema,\n blur: z.number().min(0),\n offsetX: z.number().optional(),\n offsetY: z.number().optional(),\n});\n","import { z } from \"zod\";\nimport { ShapeSchema, FillSchema, StrokeSchema, TextStyleSchema, PathPointSchema } from \"./shape.js\";\nimport { FrameSchema, BoundsSchema, AnchorPointSchema } from \"./coordinates.js\";\nimport { ShadowSchema } from \"./shadow.js\";\nimport { InteractionSchema } from \"./interaction.js\";\n\nexport const BlendModeSchema = z.enum([\n \"normal\", \"multiply\", \"screen\", \"overlay\",\n \"darken\", \"lighten\", \"color-dodge\", \"color-burn\",\n \"hard-light\", \"soft-light\", \"difference\", \"exclusion\",\n \"hue\", \"saturation\", \"color\", \"luminosity\",\n]);\n\nexport const MotionPathSchema = z.object({\n points: z.array(PathPointSchema).min(2, \"Motion path must have at least 2 points\"),\n closed: z.boolean().optional(),\n autoRotate: z.boolean().optional(),\n autoRotateOffset: z.number().optional(),\n});\n\nexport const ShapeVisualSchema = z.object({\n type: z.literal(\"shape\"),\n shape: ShapeSchema,\n fill: FillSchema.optional(),\n stroke: StrokeSchema.optional(),\n});\n\nexport const TextVisualSchema = z.object({\n type: z.literal(\"text\"),\n content: z.string(),\n style: TextStyleSchema,\n});\n\nexport const SpritesheetConfigSchema = z.object({\n columns: z.number().int().positive(),\n rows: z.number().int().positive(),\n frameCount: z.number().int().positive().optional(),\n frameWidth: z.number().positive(),\n frameHeight: z.number().positive(),\n});\n\nexport const SourceRectSchema = z.object({\n x: z.number(),\n y: z.number(),\n width: z.number().positive(),\n height: z.number().positive(),\n});\n\nexport const ImageVisualSchema = z.object({\n type: z.literal(\"image\"),\n assetId: z.string().min(1, \"assetId is required\"),\n src: z.string().optional(),\n sourceRect: SourceRectSchema.optional(),\n spritesheet: SpritesheetConfigSchema.optional(),\n frameIndex: z.number().int().min(0).optional(),\n});\n\nexport const VideoVisualSchema = z.object({\n type: z.literal(\"video\"),\n assetId: z.string().min(1, \"assetId is required\"),\n src: z.string().optional(),\n startFrame: z.number().int().min(0).optional(),\n sourceOffset: z.number().min(0).optional(),\n sourceEnd: z.number().positive().optional(),\n playbackRate: z.number().positive().optional(),\n volume: z.number().min(0).max(1).optional(),\n muted: z.boolean().optional(),\n objectFit: z.enum([\"contain\", \"cover\", \"fill\"]).optional(),\n});\n\nexport const GroupVisualSchema = z.object({\n type: z.literal(\"group\"),\n});\n\nexport const RefVisualSchema = z.object({\n type: z.literal(\"ref\"),\n src: z.string().min(1, \"src is required\"),\n state: z.string().optional(),\n frame: z.number().int().min(0).optional(),\n});\n\nexport const VisualSchema = z.discriminatedUnion(\"type\", [\n ShapeVisualSchema,\n TextVisualSchema,\n ImageVisualSchema,\n VideoVisualSchema,\n GroupVisualSchema,\n RefVisualSchema,\n]);\n\nexport const LayerSchema = z.object({\n id: z.string().min(1, \"Layer id is required\"),\n description: z.string().optional(),\n tags: z.array(z.string()).optional(),\n visual: VisualSchema,\n frame: FrameSchema,\n bounds: BoundsSchema,\n anchorPoint: AnchorPointSchema.optional(),\n parentId: z.string().optional(),\n opacity: z.number().min(0).max(1).optional(),\n rotation: z.number().optional(),\n scale: z.object({ x: z.number(), y: z.number() }).optional(),\n visible: z.boolean().optional(),\n shadow: ShadowSchema.optional(),\n blendMode: BlendModeSchema.optional(),\n motionPath: MotionPathSchema.optional(),\n clipPath: ShapeSchema.optional(),\n tint: z.object({\n color: z.string(),\n amount: z.number().min(0).max(1),\n }).optional(),\n interactions: z.array(InteractionSchema).optional(),\n});\n","import { z } from \"zod\";\n\nexport const TriggerTypeSchema = z.enum([\n \"click\", \"hover\", \"pointerdown\", \"pointerup\", \"timer\", \"signal\",\n]);\n\nexport const TriggerSchema = z.object({\n type: TriggerTypeSchema,\n delay: z.number().nonnegative(\"Timer delay must be non-negative\").optional(),\n signal: z.string().optional(),\n}).refine(\n (t) => t.type !== \"timer\" || t.delay !== undefined,\n { message: \"Timer trigger requires a delay\" },\n).refine(\n (t) => t.type !== \"signal\" || t.signal !== undefined,\n { message: \"Signal trigger requires a signal name\" },\n);\n\nexport const ActionTypeSchema = z.enum([\n \"go-to-state\", \"emit-signal\", \"set-variable\", \"toggle-visibility\",\n]);\n\nexport const ActionSchema = z.object({\n type: ActionTypeSchema,\n state: z.string().optional(),\n signal: z.string().optional(),\n variable: z.string().optional(),\n value: z.unknown().optional(),\n targetLayer: z.string().optional(),\n}).refine(\n (a) => a.type !== \"go-to-state\" || a.state !== undefined,\n { message: \"go-to-state action requires a state name\" },\n).refine(\n (a) => a.type !== \"emit-signal\" || a.signal !== undefined,\n { message: \"emit-signal action requires a signal name\" },\n).refine(\n (a) => a.type !== \"set-variable\" || (a.variable !== undefined && a.value !== undefined),\n { message: \"set-variable action requires variable and value\" },\n);\n\nexport const InteractionSchema = z.object({\n id: z.string().min(1, \"Interaction id is required\"),\n trigger: TriggerSchema,\n action: ActionSchema,\n description: z.string().optional(),\n});\n","import { z } from \"zod\";\nimport { EasingSchema } from \"./easing.js\";\n\nexport const AnimatablePropertySchema = z.enum([\n \"frame.x\",\n \"frame.y\",\n \"bounds.width\",\n \"bounds.height\",\n \"opacity\",\n \"rotation\",\n \"scale.x\",\n \"scale.y\",\n \"anchorPoint.x\",\n \"anchorPoint.y\",\n \"visual.shape.cornerRadius\",\n \"visual.fill.color\",\n \"visual.stroke.color\",\n \"visual.stroke.width\",\n \"visual.stroke.start\",\n \"visual.stroke.end\",\n \"visual.style.fontSize\",\n \"visual.style.color\",\n \"shadow.color\",\n \"shadow.blur\",\n \"shadow.offsetX\",\n \"shadow.offsetY\",\n \"motionPath.progress\",\n \"visual.fill.angle\",\n \"visual.fill.center.x\",\n \"visual.fill.center.y\",\n \"visual.fill.radius\",\n \"visual.image.sourceRect.x\",\n \"visual.image.sourceRect.y\",\n \"visual.image.sourceRect.width\",\n \"visual.image.sourceRect.height\",\n \"visual.image.frameIndex\",\n \"visible\",\n \"tint.color\",\n \"tint.amount\",\n]);\n\nexport const FrameRangeSchema = z.tuple([\n z.number().int().min(0, \"Frame start must be >= 0\"),\n z.number().int().min(0, \"Frame end must be >= 0\"),\n]).refine(([start, end]) => end >= start, {\n message: \"Frame range end must be >= start\",\n});\n\nexport const DeltaSchema = z.object({\n id: z.string().optional(),\n name: z.string().optional(),\n layer: z.string().min(1, \"Delta must reference a layer id\"),\n property: AnimatablePropertySchema,\n range: FrameRangeSchema,\n from: z.unknown(),\n to: z.unknown(),\n easing: EasingSchema.optional(),\n description: z.string().optional(),\n tags: z.array(z.string()).optional(),\n});\n","import { z } from \"zod\";\nimport { DeltaSchema } from \"./delta.js\";\nimport { EasingSchema } from \"./easing.js\";\n\nexport const AudioSchema = z.object({\n src: z.string().min(1, \"Audio src is required\"),\n offset: z.number().min(0, \"Audio offset must be non-negative\").optional(),\n volume: z.number().min(0).max(1, \"Audio volume must be 0–1\").optional(),\n loop: z.boolean().optional(),\n startFrame: z.number().int().min(0, \"Audio startFrame must be a non-negative integer\").optional(),\n});\n\nexport const StateTransitionConfigSchema = z.object({\n duration: z.number().int().positive(\"Transition duration must be a positive integer (frames)\"),\n easing: EasingSchema.optional(),\n});\n\nexport const StateSchema = z.object({\n description: z.string().optional(),\n tags: z.array(z.string()).optional(),\n parent: z.string().optional(),\n duration: z.number().int().positive(\"State duration must be a positive integer (frames)\"),\n deltas: z.array(DeltaSchema),\n audio: AudioSchema.optional(),\n transitions: z.record(z.string(), StateTransitionConfigSchema).optional(),\n});\n","import { z } from \"zod\";\nimport { AnimatablePropertySchema } from \"./delta.js\";\nimport { EasingSchema } from \"./easing.js\";\n\nexport const PresetDeltaSchema = z.object({\n property: AnimatablePropertySchema,\n offset: z.tuple([z.number().int().min(0), z.number().int().min(0)]).optional(),\n from: z.unknown(),\n to: z.unknown(),\n easing: EasingSchema.optional(),\n});\n\nexport const PresetSchema = z.object({\n description: z.string().optional(),\n tags: z.array(z.string()).optional(),\n deltas: z.array(PresetDeltaSchema).min(1, \"Preset must have at least one delta\"),\n});\n","import { z } from \"zod\";\n\nexport const VariableTypeSchema = z.enum([\"string\", \"number\", \"color\", \"asset\", \"boolean\"]);\n\nexport const VariableSchema = z.object({\n type: VariableTypeSchema,\n default: z.unknown().optional(),\n description: z.string().optional(),\n});\n","import { z } from \"zod\";\n\nexport const AssetTypeSchema = z.enum([\"image\", \"svg\", \"font\", \"animation\", \"audio\", \"video\"]);\n\nexport const AssetSchema = z.object({\n type: AssetTypeSchema,\n src: z.string().min(1, \"Asset src is required\"),\n description: z.string().optional(),\n spritesheet: z.object({\n columns: z.number().int().positive(),\n rows: z.number().int().positive(),\n frameCount: z.number().int().positive().optional(),\n frameWidth: z.number().positive(),\n frameHeight: z.number().positive(),\n }).optional(),\n videoMeta: z.object({\n duration: z.number().positive(\"videoMeta.duration must be positive\"),\n fps: z.number().positive(\"videoMeta.fps must be positive\"),\n width: z.number().int().positive(),\n height: z.number().int().positive(),\n }).optional(),\n});\n","import { z } from \"zod\";\nimport { LayerSchema } from \"./layer.js\";\nimport { StateSchema } from \"./state.js\";\nimport { PresetSchema } from \"./preset.js\";\nimport { VariableSchema } from \"./variable.js\";\nimport { AssetSchema } from \"./asset.js\";\n\nexport const CanvasSchema = z.object({\n width: z.number().int().positive(\"Canvas width must be a positive integer\"),\n height: z.number().int().positive(\"Canvas height must be a positive integer\"),\n fps: z.number().int().positive(\"FPS must be a positive integer\"),\n background: z.string().optional(),\n});\n\nexport const AtelierDocumentSchema = z.object({\n version: z.string().min(1, \"Version is required\"),\n name: z.string().min(1, \"Animation name is required\"),\n description: z.string().optional(),\n tags: z.array(z.string()).optional(),\n canvas: CanvasSchema,\n variables: z.record(z.string(), VariableSchema).optional(),\n assets: z.record(z.string(), AssetSchema).optional(),\n presets: z.record(z.string(), PresetSchema).optional(),\n layers: z.array(LayerSchema),\n states: z.record(z.string(), StateSchema),\n});\n","import { z } from \"zod\";\n\n/** Silence-trim policy block */\nexport const SilencePolicySchema = z.object({\n noise: z.string().optional(),\n min_silence: z.number().nonnegative().optional(),\n default_padding_pre: z.number().nonnegative().optional(),\n default_padding_post: z.number().nonnegative().optional(),\n match_tolerance: z.number().nonnegative().optional(),\n}).strict();\n\n/** Caption visual style block */\nexport const CaptionStyleSchema = z.object({\n font_family: z.string().optional(),\n font_size: z.number().positive().optional(),\n font_weight: z.union([z.literal(\"normal\"), z.literal(\"bold\"), z.number()]).optional(),\n text_align: z.enum([\"left\", \"center\", \"right\"]).optional(),\n color: z.string().optional(),\n y_ratio: z.number().min(0).max(1).optional(),\n width_ratio: z.number().min(0).max(1).optional(),\n fade_seconds: z.number().nonnegative().optional(),\n}).strict();\n\n/** Caption phrase grouping block */\nexport const CaptionGroupingSchema = z.object({\n max_words: z.number().int().positive().optional(),\n pause_gap: z.number().nonnegative().optional(),\n}).strict();\n\n/** Anchor enum shared by every overlay sub-rule */\nexport const OverlayAnchorSchema = z.enum([\n \"top-left\",\n \"top-right\",\n \"bottom-left\",\n \"bottom-right\",\n]);\n\n/** Visual subset for overlay text — recipe snake_case convention */\nexport const OverlayTextStyleSchema = z.object({\n font_family: z.string().optional(),\n font_size: z.number().positive().optional(),\n font_weight: z.union([z.literal(\"normal\"), z.literal(\"bold\"), z.number()]).optional(),\n color: z.string().optional(),\n}).strict();\n\n/**\n * Validate a page-number format template:\n * - braces must balance\n * - every {…} group must match (current|total)(:0Nd)? (zero-pad form)\n * - at least one of {current} / {total} must appear\n *\n * Render-time substitution is a separate concern; this only validates syntax.\n */\nfunction validatePageNumberFormat(\n format: string,\n ctx: z.RefinementCtx,\n): void {\n if (format.length === 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: \"format must be a non-empty string\",\n });\n return;\n }\n\n const open = (format.match(/\\{/g) ?? []).length;\n const close = (format.match(/\\}/g) ?? []).length;\n if (open !== close) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `format has unbalanced braces (${open} '{' vs ${close} '}')`,\n });\n return;\n }\n\n const groupRe = /\\{([^{}]*)\\}/g;\n const groupRule = /^(current|total)(:0\\d+d)?$/;\n let m: RegExpExecArray | null;\n let sawCurrent = false;\n let sawTotal = false;\n while ((m = groupRe.exec(format)) !== null) {\n const inner = m[1];\n if (!groupRule.test(inner)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `format placeholder \"{${inner}}\" is not recognized — expected {current}, {total}, {current:0Nd}, or {total:0Nd}`,\n });\n return;\n }\n if (inner.startsWith(\"current\")) sawCurrent = true;\n if (inner.startsWith(\"total\")) sawTotal = true;\n }\n\n if (!sawCurrent && !sawTotal) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: \"format must contain at least one {current} or {total} placeholder\",\n });\n }\n}\n\n/** Handle overlay rule schema — anchored creator-handle text */\nexport const OverlayHandleRuleSchema = z.object({\n text: z.string().min(1),\n anchor: OverlayAnchorSchema,\n margin: z.number().nonnegative().optional(),\n style: OverlayTextStyleSchema.optional(),\n}).strict();\n\n/** Page-number overlay rule schema — anchored \"1/5\"-style indicator */\nexport const OverlayPageNumberRuleSchema = z.object({\n format: z.string().superRefine(validatePageNumberFormat),\n anchor: OverlayAnchorSchema,\n margin: z.number().nonnegative().optional(),\n style: OverlayTextStyleSchema.optional(),\n}).strict();\n\n/** Overlay rules block (Phase 1.5) — both sub-blocks optional */\nexport const OverlayRulesSchema = z.object({\n handle: OverlayHandleRuleSchema.optional(),\n page_number: OverlayPageNumberRuleSchema.optional(),\n}).strict();\n\n/**\n * StudioRecipe — top-level. Reserved fields (caption_highlight, transition_kit,\n * etc.) still accept any shape; Phase 3 will refine them. Validate-time warnings\n * surface reserved-fields-in-use to authors so they aren't caught off-guard\n * when those sections become load-bearing.\n *\n * overlay_rules was promoted from reserved Phase 3 to first-class Phase 1.5.\n */\nexport const StudioRecipeSchema = z.object({\n version: z.string(),\n name: z.string(),\n description: z.string().optional(),\n author: z.string().optional(),\n tags: z.array(z.string()).optional(),\n\n silence_policy: SilencePolicySchema.optional(),\n caption_style: CaptionStyleSchema.optional(),\n caption_grouping: CaptionGroupingSchema.optional(),\n\n // Phase 1.5 — first-class overlay rules\n overlay_rules: OverlayRulesSchema.optional(),\n\n // Reserved — Phase 3 (parse-opaque)\n caption_highlight: z.unknown().optional(),\n transition_kit: z.unknown().optional(),\n palette: z.unknown().optional(),\n audio_policy: z.unknown().optional(),\n aspect_targets: z.array(z.unknown()).optional(),\n}).strict();\n\n/** Names of still-reserved fields — used by validators to emit forward-compat warnings */\nexport const RESERVED_RECIPE_FIELDS = [\n \"caption_highlight\",\n \"transition_kit\",\n \"palette\",\n \"audio_policy\",\n \"aspect_targets\",\n] as const;\n","import type { AtelierDocument, Layer, Delta, VideoVisual, StudioRecipe } from \"@a-company/atelier-types\";\nimport { AtelierDocumentSchema } from \"./document.js\";\nimport { LayerSchema } from \"./layer.js\";\nimport { DeltaSchema } from \"./delta.js\";\nimport { StudioRecipeSchema, RESERVED_RECIPE_FIELDS } from \"./recipe.js\";\nimport type { z } from \"zod\";\n\n/** Validation result — either success with typed data or failure with readable errors */\nexport type ValidationResult<T> =\n | { success: true; data: T }\n | { success: false; errors: ValidationError[] };\n\nexport interface ValidationError {\n path: string;\n message: string;\n}\n\n/** Format Zod errors into flat, AI-readable error messages */\nfunction formatErrors(error: z.ZodError): ValidationError[] {\n return error.issues.map((issue) => ({\n path: issue.path.join(\".\") || \"(root)\",\n message: issue.message,\n }));\n}\n\n/** Validate a complete AtelierDocument */\nexport function validateDocument(input: unknown): ValidationResult<AtelierDocument> {\n const result = AtelierDocumentSchema.safeParse(input);\n if (result.success) {\n return { success: true, data: result.data as AtelierDocument };\n }\n return { success: false, errors: formatErrors(result.error) };\n}\n\n/** Validate a single Layer */\nexport function validateLayer(input: unknown): ValidationResult<Layer> {\n const result = LayerSchema.safeParse(input);\n if (result.success) {\n return { success: true, data: result.data as Layer };\n }\n return { success: false, errors: formatErrors(result.error) };\n}\n\n/** Validate a single Delta */\nexport function validateDelta(input: unknown): ValidationResult<Delta> {\n const result = DeltaSchema.safeParse(input);\n if (result.success) {\n return { success: true, data: result.data as Delta };\n }\n return { success: false, errors: formatErrors(result.error) };\n}\n\n/**\n * Validate a VideoVisual layer against semantic constraints (^valid-video-layer gate).\n * Checks beyond Zod: sourceEnd > sourceOffset, sourceEnd ≤ videoMeta.duration.\n */\nexport function validateVideoLayer(\n visual: VideoVisual,\n videoMetaDuration?: number,\n): ValidationResult<VideoVisual> {\n const errors: ValidationError[] = [];\n\n if (!visual.assetId) {\n errors.push({ path: \"assetId\", message: \"assetId is required\" });\n }\n\n const sourceOffset = visual.sourceOffset ?? 0;\n if (visual.sourceEnd !== undefined) {\n if (visual.sourceEnd <= sourceOffset) {\n errors.push({\n path: \"sourceEnd\",\n message: `sourceEnd (${visual.sourceEnd}) must be greater than sourceOffset (${sourceOffset})`,\n });\n }\n if (videoMetaDuration !== undefined && visual.sourceEnd > videoMetaDuration) {\n errors.push({\n path: \"sourceEnd\",\n message: `sourceEnd (${visual.sourceEnd}) exceeds asset duration (${videoMetaDuration})`,\n });\n }\n }\n\n if (videoMetaDuration !== undefined && sourceOffset >= videoMetaDuration) {\n errors.push({\n path: \"sourceOffset\",\n message: `sourceOffset (${sourceOffset}) is at or beyond asset duration (${videoMetaDuration})`,\n });\n }\n\n if (errors.length > 0) return { success: false, errors };\n return { success: true, data: visual };\n}\n\n/**\n * Validate a StudioRecipe against the schema (^valid-recipe gate).\n * Returns success with the parsed recipe and optional warnings about\n * reserved-but-unimplemented Phase 3 fields that are present.\n */\nexport function validateRecipe(\n recipe: unknown,\n): ValidationResult<StudioRecipe> & { warnings?: string[] } {\n const parsed = StudioRecipeSchema.safeParse(recipe);\n if (!parsed.success) {\n return { success: false, errors: formatErrors(parsed.error) };\n }\n\n const warnings: string[] = [];\n const data = parsed.data as StudioRecipe;\n for (const field of RESERVED_RECIPE_FIELDS) {\n if (data[field] !== undefined) {\n warnings.push(\n `${field} is reserved for Phase 3 and currently has no effect.`,\n );\n }\n }\n\n return { success: true, data, ...(warnings.length > 0 && { warnings }) };\n}\n","import { parse as yamlParse, stringify as yamlStringify } from \"yaml\";\nimport type { AtelierDocument } from \"@a-company/atelier-types\";\nimport { validateDocument } from \"./validate.js\";\nimport type { ValidationResult } from \"./validate.js\";\n\n/**\n * Parse a YAML string into a validated AtelierDocument.\n * Returns validation errors if the YAML is invalid or doesn't match the schema.\n */\nexport function parseAtelier(yamlString: string): ValidationResult<AtelierDocument> {\n let parsed: unknown;\n try {\n parsed = yamlParse(yamlString);\n } catch (err) {\n return {\n success: false,\n errors: [{ path: \"(yaml)\", message: `YAML parse error: ${(err as Error).message}` }],\n };\n }\n return validateDocument(parsed);\n}\n\n/**\n * Serialize an AtelierDocument to YAML string.\n */\nexport function serializeAtelier(doc: AtelierDocument): string {\n return yamlStringify(doc, { indent: 2 });\n}\n","import { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { Command } from \"commander\";\nimport type { AtelierDocument } from \"@a-company/atelier-types\";\nimport { parseAtelier } from \"@a-company/atelier-schema\";\n\n/**\n * Info summary for an AtelierDocument.\n */\nexport interface DocumentInfo {\n name: string;\n description?: string;\n canvas: {\n width: number;\n height: number;\n fps: number;\n background?: string;\n };\n layers: {\n count: number;\n items: { id: string; type: string }[];\n };\n states: {\n count: number;\n items: { name: string; duration: number; deltaCount: number }[];\n };\n presets: {\n count: number;\n };\n}\n\n/**\n * Extract summary info from a parsed AtelierDocument.\n */\nexport function getInfo(doc: AtelierDocument): DocumentInfo {\n return {\n name: doc.name,\n description: doc.description,\n canvas: {\n width: doc.canvas.width,\n height: doc.canvas.height,\n fps: doc.canvas.fps,\n background: doc.canvas.background,\n },\n layers: {\n count: doc.layers.length,\n items: doc.layers.map((layer) => ({\n id: layer.id,\n type: layer.visual.type,\n })),\n },\n states: {\n count: Object.keys(doc.states).length,\n items: Object.entries(doc.states).map(([name, state]) => ({\n name,\n duration: state.duration,\n deltaCount: state.deltas.length,\n })),\n },\n presets: {\n count: doc.presets ? Object.keys(doc.presets).length : 0,\n },\n };\n}\n\n/**\n * Format a DocumentInfo for terminal output.\n */\nfunction formatInfo(info: DocumentInfo): string {\n const lines: string[] = [];\n\n lines.push(`Name: ${info.name}`);\n if (info.description) {\n lines.push(`Description: ${info.description}`);\n }\n\n const bg = info.canvas.background\n ? `, background: ${info.canvas.background}`\n : \"\";\n lines.push(\n `Canvas: ${info.canvas.width}x${info.canvas.height} @ ${info.canvas.fps}fps${bg}`,\n );\n\n lines.push(`Layers: ${info.layers.count}`);\n for (const layer of info.layers.items) {\n lines.push(` - ${layer.id} (${layer.type})`);\n }\n\n lines.push(`States: ${info.states.count}`);\n for (const state of info.states.items) {\n lines.push(\n ` - ${state.name}: ${state.duration} frames, ${state.deltaCount} deltas`,\n );\n }\n\n if (info.presets.count > 0) {\n lines.push(`Presets: ${info.presets.count}`);\n }\n\n return lines.join(\"\\n\");\n}\n\n/** Read and parse an .atelier file, exiting on failure. */\nfunction readAndParse(file: string): AtelierDocument {\n const absPath = resolve(file);\n let content: string;\n try {\n content = readFileSync(absPath, \"utf-8\");\n } catch {\n console.error(`Cannot read file: ${absPath}`);\n return process.exit(1);\n }\n\n const result = parseAtelier(content);\n if (!result.success) {\n console.error(\"Parse errors:\");\n for (const error of result.errors) {\n console.error(` - ${error.path}: ${error.message}`);\n }\n return process.exit(1);\n }\n\n return result.data;\n}\n\n/**\n * Register the `info` subcommand on the Commander program.\n */\nexport function infoCommand(program: Command): void {\n program\n .command(\"info <file>\")\n .description(\"Display summary info for an .atelier file\")\n .action((file: string) => {\n const doc = readAndParse(file);\n const info = getInfo(doc);\n console.log(formatInfo(info));\n });\n}\n","import { readFileSync, writeFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { Command } from \"commander\";\nimport type { AtelierDocument } from \"@a-company/atelier-types\";\nimport { parseAtelier } from \"@a-company/atelier-schema\";\nimport { resolveFrame } from \"@a-company/atelier-core\";\nimport type { ResolvedFrame } from \"@a-company/atelier-core\";\n\n/**\n * Resolve a single frame from a document, returning the resolved frame data.\n * If stateName is not provided, uses the first state.\n * If frame is not provided, uses frame 0.\n */\nexport function resolveStill(\n doc: AtelierDocument,\n stateName?: string,\n frame?: number,\n): ResolvedFrame {\n const stateNames = Object.keys(doc.states);\n if (stateNames.length === 0) {\n throw new Error(\"Document has no states\");\n }\n\n const resolvedStateName = stateName ?? stateNames[0];\n if (!(resolvedStateName in doc.states)) {\n throw new Error(\n `State \"${resolvedStateName}\" not found. Available: ${stateNames.join(\", \")}`,\n );\n }\n\n const resolvedFrame = frame ?? 0;\n return resolveFrame(doc, resolvedStateName, resolvedFrame);\n}\n\n/** Read and parse an .atelier file, exiting on failure. */\nfunction readAndParse(file: string): AtelierDocument {\n const absPath = resolve(file);\n let content: string;\n try {\n content = readFileSync(absPath, \"utf-8\");\n } catch {\n console.error(`Cannot read file: ${absPath}`);\n return process.exit(1);\n }\n\n const result = parseAtelier(content);\n if (!result.success) {\n console.error(\"Parse errors:\");\n for (const error of result.errors) {\n console.error(` - ${error.path}: ${error.message}`);\n }\n return process.exit(1);\n }\n\n return result.data;\n}\n\n/**\n * Register the `still` subcommand on the Commander program.\n */\nexport function stillCommand(program: Command): void {\n program\n .command(\"still <file>\")\n .description(\n \"Resolve a single frame and output as JSON or render as PNG\",\n )\n .option(\"-s, --state <name>\", \"State name (defaults to first state)\")\n .option(\n \"-f, --frame <number>\",\n \"Frame number (defaults to 0)\",\n \"0\",\n )\n .option(\n \"--format <type>\",\n \"Output format: json | png (default: json)\",\n \"json\",\n )\n .option(\"-o, --output <path>\", \"Output file path (default: stdout)\")\n .action(\n async (\n file: string,\n options: { state?: string; frame: string; format: string; output?: string },\n ) => {\n const doc = readAndParse(file);\n\n const frameNumber = parseInt(options.frame, 10);\n if (isNaN(frameNumber) || frameNumber < 0) {\n console.error(\n `Invalid frame number: ${options.frame}`,\n );\n process.exit(1);\n return;\n }\n\n if (options.format !== \"json\" && options.format !== \"png\") {\n console.error(`Unknown format: \"${options.format}\". Use json or png.`);\n process.exit(1);\n return;\n }\n\n try {\n const resolved = resolveStill(\n doc,\n options.state,\n frameNumber,\n );\n\n if (options.format === \"json\") {\n const json = JSON.stringify(resolved, null, 2);\n if (options.output) {\n writeFileSync(resolve(options.output), json, \"utf-8\");\n } else {\n console.log(json);\n }\n } else {\n // PNG format — rasterize via @napi-rs/canvas (prebuilt platform\n // binaries; no node-gyp / system libraries, so PNG output works on\n // a plain install).\n const { createCanvas } = await import(\"@napi-rs/canvas\");\n const { renderFrame } = await import(\"@a-company/atelier-canvas\");\n\n const cvs = createCanvas(doc.canvas.width, doc.canvas.height);\n const ctx = cvs.getContext(\"2d\");\n renderFrame(ctx as unknown as import(\"@a-company/atelier-canvas\").RenderContext, resolved, doc);\n\n const buffer = cvs.toBuffer(\"image/png\");\n if (options.output) {\n writeFileSync(resolve(options.output), buffer);\n } else {\n process.stdout.write(buffer);\n }\n }\n } catch (err) {\n console.error(\n (err as Error).message,\n );\n process.exit(1);\n }\n },\n );\n}\n","/**\n * `atelier render <file>` — render an animation to MP4 or GIF via FFmpeg.\n *\n * Usage:\n * atelier render animation.atelier → animation.mp4\n * atelier render animation.atelier -f gif → animation.gif\n * atelier render animation.atelier -o out/video.mp4 → custom path\n * atelier render animation.atelier -s intro outro → specific states\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { resolve, basename, extname } from \"node:path\";\nimport type { Command } from \"commander\";\nimport type { AtelierDocument } from \"@a-company/atelier-types\";\nimport { parseAtelier } from \"@a-company/atelier-schema\";\nimport {\n checkFfmpeg,\n renderDocument,\n type RenderFormat,\n} from \"./render-pipeline.js\";\n\n/** Read and parse an .atelier file, exiting on failure. */\nfunction readAndParse(file: string): AtelierDocument {\n const absPath = resolve(file);\n let content: string;\n try {\n content = readFileSync(absPath, \"utf-8\");\n } catch {\n console.error(`Cannot read file: ${absPath}`);\n return process.exit(1);\n }\n\n const result = parseAtelier(content);\n if (!result.success) {\n console.error(\"Parse errors:\");\n for (const error of result.errors) {\n console.error(` - ${error.path}: ${error.message}`);\n }\n return process.exit(1);\n }\n\n return result.data;\n}\n\nfunction inferFormat(output: string | undefined): RenderFormat {\n if (!output) return \"mp4\";\n const ext = extname(output).toLowerCase();\n if (ext === \".gif\") return \"gif\";\n return \"mp4\";\n}\n\n/** Register the `render` subcommand on the Commander program. */\nexport function renderCommand(program: Command): void {\n program\n .command(\"render <file>\")\n .description(\"Render animation to MP4 or GIF via FFmpeg\")\n .option(\"-o, --output <path>\", \"Output file path\")\n .option(\"-f, --format <type>\", \"Output format: mp4 | gif\")\n .option(\n \"-s, --state <names...>\",\n \"State(s) to render (default: all in order)\",\n )\n .action(\n async (\n file: string,\n options: {\n output?: string;\n format?: string;\n state?: string[];\n },\n ) => {\n // Check FFmpeg availability\n const hasFfmpeg = await checkFfmpeg();\n if (!hasFfmpeg) {\n console.error(\"FFmpeg is not installed or not in PATH.\");\n console.error(\"Install it:\");\n console.error(\" macOS: brew install ffmpeg\");\n console.error(\" Ubuntu: sudo apt install ffmpeg\");\n console.error(\" Windows: https://ffmpeg.org/download.html\");\n process.exit(1);\n return;\n }\n\n const doc = readAndParse(file);\n\n // Resolve format\n let format: RenderFormat;\n if (options.format) {\n if (options.format !== \"mp4\" && options.format !== \"gif\") {\n console.error(\n `Unknown format: \"${options.format}\". Use mp4 or gif.`,\n );\n process.exit(1);\n return;\n }\n format = options.format;\n } else {\n format = inferFormat(options.output);\n }\n\n // Resolve output path\n const inputName = basename(file, extname(file));\n const output = options.output ?? `${inputName}.${format}`;\n\n const startTime = Date.now();\n\n try {\n const result = await renderDocument(doc, {\n output: resolve(output),\n format,\n states: options.state,\n onProgress: ({ frame, totalFrames, state, percent }) => {\n const elapsed = (Date.now() - startTime) / 1000;\n const rate = elapsed > 0 ? frame / elapsed : 0;\n const remaining =\n rate > 0 ? (totalFrames - frame) / rate : 0;\n process.stderr.write(\n `\\rRendering: frame ${frame}/${totalFrames} (${percent}%) - state \"${state}\" - ETA ${remaining.toFixed(1)}s`,\n );\n },\n });\n\n process.stderr.write(\"\\n\");\n console.log(\n `Done: ${result.totalFrames} frames \\u2192 ${result.output} (${(result.durationMs / 1000).toFixed(1)}s)`,\n );\n } catch (err) {\n process.stderr.write(\"\\n\");\n console.error((err as Error).message);\n process.exit(1);\n }\n },\n );\n}\n","/**\n * Core render pipeline — renders an Atelier document to MP4 or GIF via FFmpeg.\n * Pure pipeline with no Commander dependency; testable in isolation.\n */\n\nimport { spawn } from \"node:child_process\";\nimport type { AtelierDocument } from \"@a-company/atelier-types\";\nimport { resolveFrame } from \"@a-company/atelier-core\";\nimport { renderFrame, ImageCache } from \"@a-company/atelier-canvas\";\n\n// ─── Types ───────────────────────────────────────────────────\n\nexport type RenderFormat = \"mp4\" | \"gif\";\n\nexport interface RenderOptions {\n output: string;\n format: RenderFormat;\n states?: string[];\n onProgress?: (info: ProgressInfo) => void;\n}\n\nexport interface ProgressInfo {\n frame: number;\n totalFrames: number;\n state: string;\n percent: number;\n}\n\nexport interface RenderResult {\n output: string;\n format: RenderFormat;\n totalFrames: number;\n states: string[];\n durationMs: number;\n}\n\n// ─── FFmpeg Helpers ──────────────────────────────────────────\n\n/** Check whether FFmpeg is available on the system PATH. */\nexport async function checkFfmpeg(): Promise<boolean> {\n return new Promise((resolve) => {\n const proc = spawn(\"ffmpeg\", [\"-version\"], { stdio: \"pipe\" });\n proc.on(\"error\", () => resolve(false));\n proc.on(\"close\", (code) => resolve(code === 0));\n });\n}\n\n/** Build the FFmpeg argument array for the given format. Pure function. */\nexport function buildFfmpegArgs(\n width: number,\n height: number,\n fps: number,\n format: RenderFormat,\n output: string,\n): string[] {\n const input = [\n \"-y\",\n \"-f\", \"rawvideo\",\n \"-pix_fmt\", \"bgra\",\n \"-s\", `${width}x${height}`,\n \"-r\", String(fps),\n \"-i\", \"pipe:0\",\n ];\n\n if (format === \"mp4\") {\n return [\n ...input,\n \"-c:v\", \"libx264\",\n \"-pix_fmt\", \"yuv420p\",\n \"-preset\", \"medium\",\n \"-crf\", \"18\",\n \"-movflags\", \"+faststart\",\n output,\n ];\n }\n\n // GIF — single-pass palette generation (stdin-compatible)\n return [\n ...input,\n \"-vf\", \"split[s0][s1];[s0]palettegen=stats_mode=single[p];[s1][p]paletteuse=dither=sierra2_4a\",\n \"-loop\", \"0\",\n output,\n ];\n}\n\n// ─── Image Pre-loading ───────────────────────────────────────\n\n/**\n * Scan document layers for image visuals, pre-load them with node-canvas\n * loadImage, and return a populated ImageCache ready for rendering.\n */\nasync function preloadImages(\n doc: AtelierDocument,\n loadImage: (src: string) => Promise<unknown>,\n): Promise<ImageCache> {\n const sources = new Set<string>();\n\n for (const layer of doc.layers) {\n if (layer.visual.type === \"image\") {\n const iv = layer.visual as { src?: string; assetId?: string };\n if (iv.src) {\n sources.add(iv.src);\n } else if (iv.assetId && doc.assets?.[iv.assetId]) {\n sources.add(doc.assets[iv.assetId].src);\n }\n }\n }\n\n if (sources.size === 0) {\n return new ImageCache();\n }\n\n // Load all images in parallel\n const preloaded = new Map<string, unknown>();\n await Promise.all(\n [...sources].map(async (src) => {\n try {\n preloaded.set(src, await loadImage(src));\n } catch {\n // Images that fail to load will render as blank\n }\n }),\n );\n\n // Create ImageCache pre-populated with loaded images.\n // createImage returns the pre-loaded image and fires onLoad on next tick\n // so that the ImageCache internal `image` variable is assigned first.\n const imageCache = new ImageCache({\n createImage: (src, onLoad, onError) => {\n const img = preloaded.get(src);\n if (img) {\n process.nextTick(onLoad);\n return img;\n }\n process.nextTick(onError);\n return {};\n },\n });\n\n for (const src of preloaded.keys()) {\n imageCache.load(src);\n }\n\n // Wait for nextTick callbacks to populate the cache\n await new Promise<void>((resolve) => process.nextTick(resolve));\n\n return imageCache;\n}\n\n// ─── Main Render Loop ────────────────────────────────────────\n\n/**\n * Render an Atelier document to a video/GIF file via FFmpeg.\n * Dynamically imports `canvas` (node-canvas) — fails with a helpful\n * message if the native dependency is not installed.\n */\nexport async function renderDocument(\n doc: AtelierDocument,\n opts: RenderOptions,\n): Promise<RenderResult> {\n // Dynamic import — canvas requires native compilation.\n // Use a variable to avoid TypeScript resolving the module at compile time.\n const canvasModuleName = \"canvas\";\n let createCanvas: (w: number, h: number) => unknown;\n let loadImage: (src: string) => Promise<unknown>;\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const canvasModule = await import(/* webpackIgnore: true */ canvasModuleName);\n createCanvas = canvasModule.createCanvas;\n loadImage = canvasModule.loadImage;\n } catch {\n throw new Error(\n \"The 'canvas' package is not installed.\\n\" +\n \"Install it with:\\n\" +\n \" npm install canvas\\n\" +\n \"Prerequisites vary by OS:\\n\" +\n \" macOS: brew install pkg-config cairo pango libpng jpeg giflib librsvg pixman\\n\" +\n \" Ubuntu: sudo apt install build-essential libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev\\n\" +\n \"See: https://github.com/Automattic/node-canvas#compiling\",\n );\n }\n\n const { width, height, fps } = doc.canvas;\n const { output, format, states, onProgress } = opts;\n\n // H.264 requires even dimensions\n if (format === \"mp4\" && (width % 2 !== 0 || height % 2 !== 0)) {\n throw new Error(\n `H.264 requires even dimensions. Canvas is ${width}\\u00d7${height}. ` +\n `Try ${width + (width % 2)}\\u00d7${height + (height % 2)}.`,\n );\n }\n\n // Resolve which states to render\n const allStates = Object.keys(doc.states);\n const renderStates = states ?? allStates;\n for (const s of renderStates) {\n if (!(s in doc.states)) {\n throw new Error(\n `State \"${s}\" not found. Available: ${allStates.join(\", \")}`,\n );\n }\n }\n\n // Total frame count across all states\n let totalFrames = 0;\n for (const s of renderStates) {\n totalFrames += doc.states[s].duration;\n }\n\n if (totalFrames === 0) {\n throw new Error(\"Nothing to render \\u2014 all states have duration 0\");\n }\n\n // Pre-load images before the render loop\n const imageCache = await preloadImages(doc, loadImage);\n\n // Create node-canvas\n const canvas = createCanvas(width, height) as { getContext(id: \"2d\"): unknown; toBuffer(format: \"raw\"): Buffer };\n const ctx = canvas.getContext(\"2d\");\n\n // Spawn FFmpeg\n const ffmpegArgs = buildFfmpegArgs(width, height, fps, format, output);\n const ffmpeg = spawn(\"ffmpeg\", ffmpegArgs, {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n let stderrOutput = \"\";\n ffmpeg.stderr?.on(\"data\", (chunk: Buffer) => {\n stderrOutput += chunk.toString();\n });\n\n const startTime = Date.now();\n let frameIndex = 0;\n\n for (const stateName of renderStates) {\n const duration = doc.states[stateName].duration;\n for (let f = 0; f < duration; f++) {\n const resolved = resolveFrame(doc, stateName, f);\n renderFrame(ctx as never, resolved, doc, imageCache);\n\n const raw = canvas.toBuffer(\"raw\");\n const canWrite = ffmpeg.stdin!.write(raw);\n if (!canWrite) {\n await new Promise<void>((resolve) =>\n ffmpeg.stdin!.once(\"drain\", resolve),\n );\n }\n\n frameIndex++;\n onProgress?.({\n frame: frameIndex,\n totalFrames,\n state: stateName,\n percent: Math.round((frameIndex / totalFrames) * 100),\n });\n }\n }\n\n // Close stdin and wait for FFmpeg to exit\n ffmpeg.stdin!.end();\n\n const exitCode = await new Promise<number | null>((resolve) => {\n ffmpeg.on(\"close\", resolve);\n });\n\n if (exitCode !== 0) {\n throw new Error(\n `FFmpeg exited with code ${exitCode}.\\n${stderrOutput.slice(-500)}`,\n );\n }\n\n return {\n output,\n format,\n totalFrames,\n states: renderStates,\n durationMs: Date.now() - startTime,\n };\n}\n","import { readFileSync, writeFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { Command } from \"commander\";\nimport type { AtelierDocument } from \"@a-company/atelier-types\";\nimport { parseAtelier } from \"@a-company/atelier-schema\";\nimport { renderFrameSVG } from \"@a-company/atelier-svg\";\n\n/** Read and parse an .atelier file, exiting on failure. */\nfunction readAndParse(file: string): AtelierDocument {\n const absPath = resolve(file);\n let content: string;\n try {\n content = readFileSync(absPath, \"utf-8\");\n } catch {\n console.error(`Cannot read file: ${absPath}`);\n return process.exit(1);\n }\n\n const result = parseAtelier(content);\n if (!result.success) {\n console.error(\"Parse errors:\");\n for (const error of result.errors) {\n console.error(` - ${error.path}: ${error.message}`);\n }\n return process.exit(1);\n }\n\n return result.data;\n}\n\n/** Register the `export-svg` subcommand on the Commander program. */\nexport function exportSvgCommand(program: Command): void {\n program\n .command(\"export-svg <file>\")\n .description(\"Export a frame as SVG\")\n .option(\"-s, --state <name>\", \"State name (defaults to first state)\")\n .option(\"-f, --frame <number>\", \"Frame number (defaults to 0)\", \"0\")\n .option(\"-o, --output <path>\", \"Output file path (default: stdout)\")\n .option(\"--xml-declaration\", \"Include XML declaration\")\n .action(\n (\n file: string,\n options: { state?: string; frame: string; output?: string; xmlDeclaration?: boolean },\n ) => {\n const doc = readAndParse(file);\n\n const frameNumber = parseInt(options.frame, 10);\n if (isNaN(frameNumber) || frameNumber < 0) {\n console.error(`Invalid frame number: ${options.frame}`);\n process.exit(1);\n return;\n }\n\n const stateNames = Object.keys(doc.states);\n if (stateNames.length === 0) {\n console.error(\"Document has no states\");\n process.exit(1);\n return;\n }\n\n const stateName = options.state ?? stateNames[0];\n if (!doc.states[stateName]) {\n console.error(`State \"${stateName}\" not found. Available: ${stateNames.join(\", \")}`);\n process.exit(1);\n return;\n }\n\n try {\n const svg = renderFrameSVG(doc, stateName, frameNumber, {\n xmlDeclaration: options.xmlDeclaration,\n });\n\n if (options.output) {\n writeFileSync(resolve(options.output), svg, \"utf-8\");\n } else {\n console.log(svg);\n }\n } catch (err) {\n console.error((err as Error).message);\n process.exit(1);\n }\n },\n );\n}\n","import type { AtelierDocument, ImageVisual, RefVisual } from \"@a-company/atelier-types\";\nimport { resolveFrame, type ResolvedFrame } from \"@a-company/atelier-core\";\nimport { buildEffectiveLayer, type EffectiveLayer, type DocumentResolver } from \"@a-company/atelier-canvas\";\nimport { buildTransform, buildStyleAttrs } from \"./svg-properties.js\";\nimport { renderShapeSVG } from \"./svg-shapes.js\";\nimport { renderTextSVG } from \"./svg-text.js\";\nimport { buildShadowFilter, buildTintFilter, resetFilterCounter } from \"./svg-filters.js\";\nimport { resetGradientCounter } from \"./svg-gradients.js\";\nimport { buildClipPathDef, resetClipCounter } from \"./svg-clip.js\";\nimport { escapeXml } from \"./svg-properties.js\";\n\nexport interface RenderSVGOptions {\n /** Include XML declaration (default: false) */\n xmlDeclaration?: boolean;\n /** Include viewBox attribute (default: true) */\n viewBox?: boolean;\n /** Indent size in spaces (default: 2) */\n indent?: number;\n /** Resolves ref layer src paths to loaded AtelierDocuments */\n documentResolver?: DocumentResolver;\n /** Maximum ref nesting depth (default: 4) */\n maxRefDepth?: number;\n}\n\n/**\n * Render a resolved frame as an SVG string.\n * If no resolved frame is provided, resolves the given state and frame.\n */\nexport function renderFrameSVG(\n doc: AtelierDocument,\n stateOrFrame: string | ResolvedFrame,\n frame?: number,\n opts?: RenderSVGOptions,\n): string {\n // Reset counters for deterministic IDs\n resetGradientCounter();\n resetFilterCounter();\n resetClipCounter();\n\n let resolved: ResolvedFrame;\n if (typeof stateOrFrame === \"string\") {\n resolved = resolveFrame(doc, stateOrFrame, frame ?? 0);\n } else {\n resolved = stateOrFrame;\n }\n\n const { width, height } = doc.canvas;\n const indent = opts?.indent ?? 2;\n const pad = \" \".repeat(indent);\n\n // Build effective layers\n const effLayers: EffectiveLayer[] = resolved.layers.map(rl =>\n buildEffectiveLayer(rl, width, height),\n );\n\n // Collect all defs and layer elements\n const allDefs: string[] = [];\n const layerElements: string[] = [];\n\n for (let i = 0; i < effLayers.length; i++) {\n const eff = effLayers[i];\n const layer = resolved.layers[i].layer;\n\n // Skip invisible or fully transparent\n if (!eff.visible) continue;\n if (eff.opacity <= 0) continue;\n\n // Resolve asset for images\n if (layer.visual.type === \"image\") {\n const iv = eff.visual as ImageVisual;\n if (!iv.src && iv.assetId && doc.assets?.[iv.assetId]) {\n (eff.visual as ImageVisual).src = doc.assets[iv.assetId].src;\n }\n }\n\n // Build transform\n const transform = buildTransform(eff);\n const styleAttrs = buildStyleAttrs(eff);\n\n // Build shadow filter\n const filterResult = buildShadowFilter(eff);\n if (filterResult) allDefs.push(filterResult.defs);\n\n // Build tint filter\n const tintResult = buildTintFilter(eff);\n if (tintResult) allDefs.push(tintResult.defs);\n\n // Build clip path\n let clipAttr = \"\";\n if (layer.clipPath) {\n const clipResult = buildClipPathDef(layer.clipPath, eff.width, eff.height);\n if (clipResult.defs) {\n allDefs.push(clipResult.defs);\n clipAttr = ` clip-path=\"${clipResult.clipRef}\"`;\n }\n }\n\n // Build group attributes\n const gAttrs: string[] = [];\n if (transform) gAttrs.push(`transform=\"${transform}\"`);\n if (styleAttrs) gAttrs.push(styleAttrs);\n // Apply combined or individual filters\n if (filterResult && tintResult) {\n // When both shadow and tint exist, we need a combined filter\n // For simplicity, apply shadow filter and tint separately via style\n gAttrs.push(`filter=\"${filterResult.filterRef}\"`);\n // Tint is a second filter — wrap content in nested group\n } else if (filterResult) {\n gAttrs.push(`filter=\"${filterResult.filterRef}\"`);\n } else if (tintResult) {\n gAttrs.push(`filter=\"${tintResult.filterRef}\"`);\n }\n if (clipAttr) gAttrs.push(clipAttr.trim());\n\n // Render visual content\n let content = \"\";\n let layerDefs = \"\";\n\n switch (layer.visual.type) {\n case \"shape\": {\n const result = renderShapeSVG(eff, eff.visual as import(\"@a-company/atelier-types\").ShapeVisual);\n content = result.elements;\n layerDefs = result.defs;\n break;\n }\n case \"text\":\n content = renderTextSVG(eff, eff.visual as import(\"@a-company/atelier-types\").TextVisual);\n break;\n case \"image\": {\n const iv = eff.visual as ImageVisual;\n if (iv.src) {\n if (iv.spritesheet) {\n // Spritesheet: compute source rect and use full sheet dimensions for inner image\n const { columns, rows, frameWidth, frameHeight, frameCount } = iv.spritesheet;\n const maxFrames = frameCount ?? (columns * rows);\n const idx = Math.max(0, Math.min(Math.floor(iv.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 const imgW = columns * frameWidth;\n const imgH = rows * frameHeight;\n content = `<svg viewBox=\"${sx} ${sy} ${frameWidth} ${frameHeight}\" width=\"${eff.width}\" height=\"${eff.height}\">` +\n `<image href=\"${escapeXml(iv.src)}\" width=\"${imgW}\" height=\"${imgH}\" />` +\n `</svg>`;\n } else if (iv.sourceRect) {\n // Manual sourceRect: viewBox crops from image coordinate space\n const sr = iv.sourceRect;\n content = `<svg viewBox=\"${sr.x} ${sr.y} ${sr.width} ${sr.height}\" width=\"${eff.width}\" height=\"${eff.height}\">` +\n `<image href=\"${escapeXml(iv.src)}\" width=\"${eff.width}\" height=\"${eff.height}\" />` +\n `</svg>`;\n } else {\n content = `<image href=\"${escapeXml(iv.src)}\" width=\"${eff.width}\" height=\"${eff.height}\" />`;\n }\n }\n break;\n }\n case \"group\":\n // Groups are structural — no visual content in SVG either\n break;\n case \"ref\": {\n const refVisual = eff.visual as RefVisual;\n const refContent = renderRefSVG(eff, refVisual, doc, opts);\n content = refContent;\n break;\n }\n }\n\n if (layerDefs) allDefs.push(layerDefs);\n\n if (content) {\n const gOpen = gAttrs.length > 0 ? `<g ${gAttrs.join(\" \")}>` : \"<g>\";\n layerElements.push(`${pad}${gOpen}${content}</g>`);\n }\n }\n\n // Build SVG\n const lines: string[] = [];\n\n if (opts?.xmlDeclaration) {\n lines.push('<?xml version=\"1.0\" encoding=\"UTF-8\"?>');\n }\n\n const viewBox = opts?.viewBox !== false ? ` viewBox=\"0 0 ${width} ${height}\"` : \"\";\n const bg = doc.canvas.background;\n\n lines.push(`<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\"${viewBox}>`);\n\n // Defs section\n if (allDefs.length > 0) {\n lines.push(`${pad}<defs>`);\n for (const def of allDefs) {\n lines.push(`${pad}${pad}${def}`);\n }\n lines.push(`${pad}</defs>`);\n }\n\n // Background\n if (bg && bg !== \"transparent\") {\n lines.push(`${pad}<rect width=\"${width}\" height=\"${height}\" fill=\"${bg}\" />`);\n }\n\n // Layers\n lines.push(...layerElements);\n\n lines.push(\"</svg>\");\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Render a ref layer as SVG. Uses documentResolver to inline sub-doc content,\n * or falls back to a dashed placeholder rectangle.\n */\nfunction renderRefSVG(\n eff: EffectiveLayer,\n visual: RefVisual,\n _parentDoc: AtelierDocument,\n opts?: RenderSVGOptions,\n _depth?: number,\n _visitedRefs?: Set<string>,\n): string {\n const resolver = opts?.documentResolver;\n\n if (!resolver) {\n return `<rect width=\"${eff.width}\" height=\"${eff.height}\" fill=\"none\" stroke=\"#999\" stroke-dasharray=\"4 2\" />`;\n }\n\n const depth = _depth ?? 0;\n const maxDepth = opts?.maxRefDepth ?? 4;\n if (depth >= maxDepth) {\n return `<rect width=\"${eff.width}\" height=\"${eff.height}\" fill=\"none\" stroke=\"#c33\" stroke-dasharray=\"4 2\" /><text x=\"${eff.width / 2}\" y=\"${eff.height / 2}\" text-anchor=\"middle\" dominant-baseline=\"middle\" fill=\"#c33\" font-size=\"12\">MAX DEPTH</text>`;\n }\n\n const visitedRefs = _visitedRefs ?? new Set<string>();\n if (visitedRefs.has(visual.src)) {\n return `<rect width=\"${eff.width}\" height=\"${eff.height}\" fill=\"none\" stroke=\"#c33\" stroke-dasharray=\"4 2\" /><text x=\"${eff.width / 2}\" y=\"${eff.height / 2}\" text-anchor=\"middle\" dominant-baseline=\"middle\" fill=\"#c33\" font-size=\"12\">CYCLE</text>`;\n }\n\n const subDoc = resolver(visual.src);\n if (!subDoc) {\n return `<rect width=\"${eff.width}\" height=\"${eff.height}\" fill=\"none\" stroke=\"#999\" stroke-dasharray=\"4 2\" /><text x=\"${eff.width / 2}\" y=\"${eff.height / 2}\" text-anchor=\"middle\" dominant-baseline=\"middle\" fill=\"#999\" font-size=\"12\">NOT FOUND</text>`;\n }\n\n const stateNames = Object.keys(subDoc.states);\n if (stateNames.length === 0) {\n return `<rect width=\"${eff.width}\" height=\"${eff.height}\" fill=\"none\" stroke=\"#999\" stroke-dasharray=\"4 2\" />`;\n }\n\n const stateName = visual.state ?? stateNames[0];\n const stateObj = subDoc.states[stateName];\n if (!stateObj) {\n return `<rect width=\"${eff.width}\" height=\"${eff.height}\" fill=\"none\" stroke=\"#999\" stroke-dasharray=\"4 2\" />`;\n }\n\n const maxFrame = Math.max(0, stateObj.duration - 1);\n const frame = Math.min(visual.frame ?? 0, maxFrame);\n\n visitedRefs.add(visual.src);\n\n // Render sub-doc as nested <svg> with viewBox for scaling\n const subW = subDoc.canvas.width;\n const subH = subDoc.canvas.height;\n const resolved = resolveFrame(subDoc, stateName, frame);\n\n const subParts: string[] = [];\n for (const rl of resolved.layers) {\n const subEff = buildEffectiveLayer(rl, subW, subH);\n if (!subEff.visible || subEff.opacity <= 0) continue;\n\n // Resolve asset\n if (rl.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 const transform = buildTransform(subEff);\n const styleAttrs = buildStyleAttrs(subEff);\n const gAttrs: string[] = [];\n if (transform) gAttrs.push(`transform=\"${transform}\"`);\n if (styleAttrs) gAttrs.push(styleAttrs);\n\n let childContent = \"\";\n switch (rl.layer.visual.type) {\n case \"shape\": {\n const result = renderShapeSVG(subEff, subEff.visual as import(\"@a-company/atelier-types\").ShapeVisual);\n childContent = result.elements;\n break;\n }\n case \"text\":\n childContent = renderTextSVG(subEff, subEff.visual as import(\"@a-company/atelier-types\").TextVisual);\n break;\n case \"image\": {\n const iv = subEff.visual as ImageVisual;\n if (iv.src) {\n childContent = `<image href=\"${escapeXml(iv.src)}\" width=\"${subEff.width}\" height=\"${subEff.height}\" />`;\n }\n break;\n }\n case \"ref\": {\n const refV = subEff.visual as RefVisual;\n childContent = renderRefSVG(subEff, refV, subDoc, opts, depth + 1, visitedRefs);\n break;\n }\n }\n\n if (childContent) {\n const gOpen = gAttrs.length > 0 ? `<g ${gAttrs.join(\" \")}>` : \"<g>\";\n subParts.push(`${gOpen}${childContent}</g>`);\n }\n }\n\n visitedRefs.delete(visual.src);\n\n return `<svg x=\"0\" y=\"0\" width=\"${eff.width}\" height=\"${eff.height}\" viewBox=\"0 0 ${subW} ${subH}\">${subParts.join(\"\")}</svg>`;\n}\n","import type { EffectiveLayer } from \"@a-company/atelier-canvas\";\n\n/** Build SVG transform attribute from effective layer values */\nexport function buildTransform(eff: EffectiveLayer): string {\n const parts: string[] = [];\n\n // Translate to position\n if (eff.x !== 0 || eff.y !== 0) {\n parts.push(`translate(${eff.x}, ${eff.y})`);\n }\n\n const totalRotation = eff.rotation + eff.motionPathAngle;\n if (totalRotation !== 0 || eff.scaleX !== 1 || eff.scaleY !== 1) {\n const ax = eff.anchorX * eff.width;\n const ay = eff.anchorY * eff.height;\n\n // Move to anchor, apply rotation/scale, move back\n if (ax !== 0 || ay !== 0) {\n parts.push(`translate(${ax}, ${ay})`);\n }\n if (totalRotation !== 0) {\n parts.push(`rotate(${totalRotation})`);\n }\n if (eff.scaleX !== 1 || eff.scaleY !== 1) {\n parts.push(`scale(${eff.scaleX}, ${eff.scaleY})`);\n }\n if (ax !== 0 || ay !== 0) {\n parts.push(`translate(${-ax}, ${-ay})`);\n }\n }\n\n return parts.length > 0 ? parts.join(\" \") : \"\";\n}\n\n/** Build common SVG style attributes */\nexport function buildStyleAttrs(eff: EffectiveLayer): string {\n const attrs: string[] = [];\n\n if (eff.opacity < 1) {\n attrs.push(`opacity=\"${eff.opacity}\"`);\n }\n\n if (eff.blendMode !== \"normal\") {\n attrs.push(`style=\"mix-blend-mode: ${eff.blendMode}\"`);\n }\n\n return attrs.join(\" \");\n}\n\n/** Escape XML special characters */\nexport function escapeXml(str: string): string {\n return str\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&apos;\");\n}\n","import type { Fill, LinearGradientFill, RadialGradientFill, Color, RGBAColor, HSLAColor } from \"@a-company/atelier-types\";\n\nlet gradientIdCounter = 0;\n\nexport function resetGradientCounter(): void {\n gradientIdCounter = 0;\n}\n\n/** Convert an Atelier Color to a CSS color string */\nexport function colorToCSS(color: Color): string {\n if (typeof color === \"string\") return color;\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 if (\"h\" in color) {\n const c = color as HSLAColor;\n return `hsla(${c.h}, ${c.s}%, ${c.l}%, ${c.a})`;\n }\n return \"#000000\";\n}\n\n/** Generate SVG gradient defs and return the fill reference */\nexport function buildGradientDef(fill: Fill, width: number, height: number): { defs: string; fillRef: string } {\n if (fill.type === \"solid\") {\n return { defs: \"\", fillRef: colorToCSS(fill.color) };\n }\n\n if (fill.type === \"linear-gradient\") {\n return buildLinearGradient(fill, width, height);\n }\n\n if (fill.type === \"radial-gradient\") {\n return buildRadialGradient(fill, width, height);\n }\n\n return { defs: \"\", fillRef: \"none\" };\n}\n\nfunction buildLinearGradient(fill: LinearGradientFill, _width: number, _height: number): { defs: string; fillRef: string } {\n const id = `grad-${++gradientIdCounter}`;\n const rad = (fill.angle * Math.PI) / 180;\n const cos = Math.cos(rad);\n const sin = Math.sin(rad);\n\n // Map angle to x1,y1,x2,y2 (0-1 percentages)\n const x1 = 0.5 - cos * 0.5;\n const y1 = 0.5 - sin * 0.5;\n const x2 = 0.5 + cos * 0.5;\n const y2 = 0.5 + sin * 0.5;\n\n const stops = fill.stops.map(s =>\n `<stop offset=\"${s.offset}\" stop-color=\"${colorToCSS(s.color)}\" />`\n ).join(\"\");\n\n const def = `<linearGradient id=\"${id}\" x1=\"${x1}\" y1=\"${y1}\" x2=\"${x2}\" y2=\"${y2}\">${stops}</linearGradient>`;\n return { defs: def, fillRef: `url(#${id})` };\n}\n\nfunction buildRadialGradient(fill: RadialGradientFill, width: number, height: number): { defs: string; fillRef: string } {\n const id = `grad-${++gradientIdCounter}`;\n\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\n const stops = fill.stops.map(s =>\n `<stop offset=\"${s.offset}\" stop-color=\"${colorToCSS(s.color)}\" />`\n ).join(\"\");\n\n const def = `<radialGradient id=\"${id}\" cx=\"${cx}\" cy=\"${cy}\" r=\"${r}\" gradientUnits=\"userSpaceOnUse\">${stops}</radialGradient>`;\n return { defs: def, fillRef: `url(#${id})` };\n}\n","import type { ShapeVisual, Shape, RectShape, PathShape, Stroke } from \"@a-company/atelier-types\";\nimport type { EffectiveLayer } from \"@a-company/atelier-canvas\";\nimport { buildGradientDef, colorToCSS } from \"./svg-gradients.js\";\n\n/** Render a shape visual as SVG elements */\nexport function renderShapeSVG(\n eff: EffectiveLayer,\n visual: ShapeVisual,\n): { elements: string; defs: string } {\n const { shape } = visual;\n const defs: string[] = [];\n\n let fillAttr = \"none\";\n if (visual.fill) {\n const gradResult = buildGradientDef(visual.fill, eff.width, eff.height);\n if (gradResult.defs) defs.push(gradResult.defs);\n fillAttr = gradResult.fillRef;\n }\n\n let strokeAttrs = \"\";\n if (visual.stroke) {\n strokeAttrs = buildStrokeAttrs(visual.stroke);\n }\n\n const element = buildShapeElement(shape, eff.width, eff.height, fillAttr, strokeAttrs);\n return { elements: element, defs: defs.join(\"\") };\n}\n\nfunction buildShapeElement(\n shape: Shape,\n width: number,\n height: number,\n fill: string,\n strokeAttrs: string,\n): string {\n switch (shape.type) {\n case \"rect\":\n return buildRectElement(shape, width, height, fill, strokeAttrs);\n case \"ellipse\":\n return buildEllipseElement(width, height, fill, strokeAttrs);\n case \"path\":\n return buildPathElement(shape, fill, strokeAttrs);\n }\n}\n\nfunction buildRectElement(\n shape: RectShape,\n width: number,\n height: number,\n fill: string,\n strokeAttrs: string,\n): string {\n let rx = \"\";\n if (shape.cornerRadius) {\n const r = typeof shape.cornerRadius === \"number\" ? shape.cornerRadius : shape.cornerRadius[0];\n rx = ` rx=\"${r}\" ry=\"${r}\"`;\n }\n return `<rect width=\"${width}\" height=\"${height}\" fill=\"${fill}\"${rx}${strokeAttrs ? \" \" + strokeAttrs : \"\"} />`;\n}\n\nfunction buildEllipseElement(\n width: number,\n height: number,\n fill: string,\n strokeAttrs: string,\n): string {\n const cx = width / 2;\n const cy = height / 2;\n const rx = width / 2;\n const ry = height / 2;\n return `<ellipse cx=\"${cx}\" cy=\"${cy}\" rx=\"${rx}\" ry=\"${ry}\" fill=\"${fill}\"${strokeAttrs ? \" \" + strokeAttrs : \"\"} />`;\n}\n\nfunction buildPathElement(\n shape: PathShape,\n fill: string,\n strokeAttrs: string,\n): string {\n if (shape.points.length < 2) return \"\";\n\n const d: string[] = [];\n d.push(`M ${shape.points[0].x} ${shape.points[0].y}`);\n\n for (let i = 1; i < shape.points.length; i++) {\n const prev = shape.points[i - 1];\n const curr = shape.points[i];\n\n if (prev.out && curr.in) {\n d.push(`C ${prev.x + prev.out.x} ${prev.y + prev.out.y} ${curr.x + curr.in.x} ${curr.y + curr.in.y} ${curr.x} ${curr.y}`);\n } else {\n d.push(`L ${curr.x} ${curr.y}`);\n }\n }\n\n if (shape.closed) d.push(\"Z\");\n\n return `<path d=\"${d.join(\" \")}\" fill=\"${fill}\"${strokeAttrs ? \" \" + strokeAttrs : \"\"} />`;\n}\n\nfunction buildStrokeAttrs(stroke: Stroke): string {\n const parts: string[] = [];\n parts.push(`stroke=\"${colorToCSS(stroke.color)}\"`);\n parts.push(`stroke-width=\"${stroke.width}\"`);\n\n if (stroke.lineCap) parts.push(`stroke-linecap=\"${stroke.lineCap}\"`);\n if (stroke.lineJoin) parts.push(`stroke-linejoin=\"${stroke.lineJoin}\"`);\n if (stroke.dash) parts.push(`stroke-dasharray=\"${stroke.dash.join(\" \")}\"`);\n\n return parts.join(\" \");\n}\n","import type { TextVisual } from \"@a-company/atelier-types\";\nimport type { EffectiveLayer } from \"@a-company/atelier-canvas\";\nimport { colorToCSS } from \"./svg-gradients.js\";\nimport { escapeXml } from \"./svg-properties.js\";\n\n/** Render a text visual as SVG elements */\nexport function renderTextSVG(eff: EffectiveLayer, visual: TextVisual): string {\n const { style } = visual;\n\n const attrs: string[] = [];\n\n // Font attributes\n attrs.push(`font-family=\"${escapeXml(style.fontFamily)}\"`);\n attrs.push(`font-size=\"${style.fontSize}\"`);\n\n if (style.fontWeight && style.fontWeight !== \"normal\") {\n attrs.push(`font-weight=\"${style.fontWeight}\"`);\n }\n if (style.fontStyle && style.fontStyle !== \"normal\") {\n attrs.push(`font-style=\"${style.fontStyle}\"`);\n }\n\n // Color\n attrs.push(`fill=\"${colorToCSS(style.color)}\"`);\n\n // Text alignment\n const align = style.textAlign ?? \"left\";\n let textAnchor = \"start\";\n let x = 0;\n if (align === \"center\") {\n textAnchor = \"middle\";\n x = eff.width / 2;\n } else if (align === \"right\") {\n textAnchor = \"end\";\n x = eff.width;\n }\n attrs.push(`text-anchor=\"${textAnchor}\"`);\n\n // Letter spacing\n if (style.letterSpacing) {\n attrs.push(`letter-spacing=\"${style.letterSpacing}\"`);\n }\n\n // SVG text baseline: use dominant-baseline for top alignment\n attrs.push(`dominant-baseline=\"hanging\"`);\n\n return `<text x=\"${x}\" y=\"0\" ${attrs.join(\" \")}>${escapeXml(visual.content)}</text>`;\n}\n","import type { EffectiveLayer } from \"@a-company/atelier-canvas\";\n\nlet filterIdCounter = 0;\n\nexport function resetFilterCounter(): void {\n filterIdCounter = 0;\n}\n\n/** Build SVG filter definition for shadow effects */\nexport function buildShadowFilter(eff: EffectiveLayer): { defs: string; filterRef: string } | null {\n if (!eff.shadow) return null;\n\n const id = `filter-${++filterIdCounter}`;\n const { color, blur, offsetX, offsetY } = eff.shadow;\n\n const def = [\n `<filter id=\"${id}\" x=\"-50%\" y=\"-50%\" width=\"200%\" height=\"200%\">`,\n `<feDropShadow dx=\"${offsetX}\" dy=\"${offsetY}\" stdDeviation=\"${blur / 2}\" flood-color=\"${color}\" />`,\n `</filter>`,\n ].join(\"\");\n\n return { defs: def, filterRef: `url(#${id})` };\n}\n\n/** Build SVG filter definition for tint effect (feFlood + feBlend multiply) */\nexport function buildTintFilter(eff: EffectiveLayer): { defs: string; filterRef: string } | null {\n if (!eff.tint || eff.tint.amount <= 0) return null;\n\n const id = `filter-${++filterIdCounter}`;\n const { color, amount } = eff.tint;\n\n const def = [\n `<filter id=\"${id}\" x=\"0%\" y=\"0%\" width=\"100%\" height=\"100%\">`,\n `<feFlood flood-color=\"${color}\" flood-opacity=\"${amount}\" result=\"tint\" />`,\n `<feBlend in=\"SourceGraphic\" in2=\"tint\" mode=\"multiply\" />`,\n `</filter>`,\n ].join(\"\");\n\n return { defs: def, filterRef: `url(#${id})` };\n}\n","import type { Shape } from \"@a-company/atelier-types\";\n\nlet clipIdCounter = 0;\n\nexport function resetClipCounter(): void {\n clipIdCounter = 0;\n}\n\n/** Build SVG clipPath definition */\nexport function buildClipPathDef(shape: Shape, width: number, height: number): { defs: string; clipRef: string } {\n const id = `clip-${++clipIdCounter}`;\n let pathContent = \"\";\n\n switch (shape.type) {\n case \"rect\":\n if (shape.cornerRadius) {\n const r = typeof shape.cornerRadius === \"number\" ? shape.cornerRadius : shape.cornerRadius[0];\n pathContent = `<rect width=\"${width}\" height=\"${height}\" rx=\"${r}\" ry=\"${r}\" />`;\n } else {\n pathContent = `<rect width=\"${width}\" height=\"${height}\" />`;\n }\n break;\n case \"ellipse\":\n pathContent = `<ellipse cx=\"${width / 2}\" cy=\"${height / 2}\" rx=\"${width / 2}\" ry=\"${height / 2}\" />`;\n break;\n case \"path\": {\n if (shape.points.length < 2) return { defs: \"\", clipRef: \"\" };\n const d: string[] = [];\n d.push(`M ${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 d.push(`C ${prev.x + prev.out.x} ${prev.y + prev.out.y} ${curr.x + curr.in.x} ${curr.y + curr.in.y} ${curr.x} ${curr.y}`);\n } else {\n d.push(`L ${curr.x} ${curr.y}`);\n }\n }\n if (shape.closed) d.push(\"Z\");\n pathContent = `<path d=\"${d.join(\" \")}\" />`;\n break;\n }\n }\n\n const def = `<clipPath id=\"${id}\">${pathContent}</clipPath>`;\n return { defs: def, clipRef: `url(#${id})` };\n}\n","import { readFileSync, writeFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { Command } from \"commander\";\nimport type { AtelierDocument } from \"@a-company/atelier-types\";\nimport { parseAtelier } from \"@a-company/atelier-schema\";\nimport { exportToLottie } from \"@a-company/atelier-lottie\";\n\n/** Read and parse an .atelier file, exiting on failure. */\nfunction readAndParse(file: string): AtelierDocument {\n const absPath = resolve(file);\n let content: string;\n try {\n content = readFileSync(absPath, \"utf-8\");\n } catch {\n console.error(`Cannot read file: ${absPath}`);\n return process.exit(1);\n }\n\n const result = parseAtelier(content);\n if (!result.success) {\n console.error(\"Parse errors:\");\n for (const error of result.errors) {\n console.error(` - ${error.path}: ${error.message}`);\n }\n return process.exit(1);\n }\n\n return result.data;\n}\n\n/** Register the `export-lottie` subcommand on the Commander program. */\nexport function exportLottieCommand(program: Command): void {\n program\n .command(\"export-lottie <file>\")\n .description(\"Export a document to Lottie JSON format\")\n .option(\"-s, --state <name>\", \"State name (defaults to first state)\")\n .option(\"-o, --output <path>\", \"Output file path (default: stdout)\")\n .action(\n (\n file: string,\n options: { state?: string; output?: string },\n ) => {\n const doc = readAndParse(file);\n\n try {\n const { json, warnings } = exportToLottie(doc, {\n state: options.state,\n });\n\n // Print warnings to stderr\n for (const warning of warnings) {\n console.error(`Warning: ${warning}`);\n }\n\n const output = JSON.stringify(json, null, 2);\n if (options.output) {\n writeFileSync(resolve(options.output), output, \"utf-8\");\n } else {\n console.log(output);\n }\n } catch (err) {\n console.error((err as Error).message);\n process.exit(1);\n }\n },\n );\n}\n","import type { Color, RGBAColor, HSLAColor } from \"@a-company/atelier-types\";\n\n/** Convert an Atelier color to Lottie [r, g, b, a] format (0–1 range) */\nexport function colorToLottie(color: Color): number[] {\n if (typeof color === \"string\") {\n return hexToLottie(color);\n }\n\n if (\"r\" in color) {\n const c = color as RGBAColor;\n return [c.r / 255, c.g / 255, c.b / 255, c.a];\n }\n\n if (\"h\" in color) {\n const c = color as HSLAColor;\n const rgb = hslToRgb(c.h, c.s, c.l);\n return [rgb[0] / 255, rgb[1] / 255, rgb[2] / 255, c.a];\n }\n\n return [0, 0, 0, 1];\n}\n\nfunction hexToLottie(hex: string): number[] {\n const clean = hex.replace(\"#\", \"\");\n if (clean.length === 3 || clean.length === 4) {\n const r = parseInt(clean[0] + clean[0], 16) / 255;\n const g = parseInt(clean[1] + clean[1], 16) / 255;\n const b = parseInt(clean[2] + clean[2], 16) / 255;\n const a = clean.length === 4 ? parseInt(clean[3] + clean[3], 16) / 255 : 1;\n return [r, g, b, a];\n }\n const r = parseInt(clean.slice(0, 2), 16) / 255;\n const g = parseInt(clean.slice(2, 4), 16) / 255;\n const b = parseInt(clean.slice(4, 6), 16) / 255;\n const a = clean.length === 8 ? parseInt(clean.slice(6, 8), 16) / 255 : 1;\n return [r, g, b, a];\n}\n\nfunction hslToRgb(h: number, s: number, l: number): [number, number, number] {\n s = s / 100;\n l = l / 100;\n const a = s * Math.min(l, 1 - l);\n const f = (n: number) => {\n const k = (n + h / 30) % 12;\n return l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);\n };\n return [Math.round(f(0) * 255), Math.round(f(8) * 255), Math.round(f(4) * 255)];\n}\n","import type { ShapeVisual, Fill, Stroke } from \"@a-company/atelier-types\";\nimport type { LottieShapeItem } from \"./lottie-types.js\";\nimport { colorToLottie } from \"./map-colors.js\";\n\n/** Map an Atelier shape visual to Lottie shape items */\nexport function mapShapeVisual(\n visual: ShapeVisual,\n width: number,\n height: number,\n): LottieShapeItem[] {\n const items: LottieShapeItem[] = [];\n\n // Shape geometry\n switch (visual.shape.type) {\n case \"rect\":\n items.push({\n ty: \"rc\",\n nm: \"Rectangle\",\n d: 1,\n s: { a: 0, k: [width, height] },\n p: { a: 0, k: [width / 2, height / 2] },\n r: { a: 0, k: typeof visual.shape.cornerRadius === \"number\" ? visual.shape.cornerRadius : 0 },\n });\n break;\n case \"ellipse\":\n items.push({\n ty: \"el\",\n nm: \"Ellipse\",\n d: 1,\n s: { a: 0, k: [width, height] },\n p: { a: 0, k: [width / 2, height / 2] },\n });\n break;\n case \"path\": {\n const vertices = visual.shape.points.map(p => [p.x, p.y]);\n const inTangents = visual.shape.points.map(p => p.in ? [p.in.x, p.in.y] : [0, 0]);\n const outTangents = visual.shape.points.map(p => p.out ? [p.out.x, p.out.y] : [0, 0]);\n items.push({\n ty: \"sh\",\n nm: \"Path\",\n ks: {\n a: 0,\n k: {\n c: visual.shape.closed ?? false,\n v: vertices,\n i: inTangents,\n o: outTangents,\n },\n },\n });\n break;\n }\n }\n\n // Fill\n if (visual.fill) {\n items.push(mapFill(visual.fill, width, height));\n }\n\n // Stroke\n if (visual.stroke) {\n items.push(mapStroke(visual.stroke));\n }\n\n return items;\n}\n\nfunction mapFill(fill: Fill, _width: number, _height: number): LottieShapeItem {\n if (fill.type === \"solid\") {\n const color = colorToLottie(fill.color);\n return {\n ty: \"fl\",\n nm: \"Fill\",\n c: { a: 0, k: color.slice(0, 3) },\n o: { a: 0, k: (color[3] ?? 1) * 100 },\n r: 1,\n };\n }\n\n if (fill.type === \"linear-gradient\") {\n const stops: number[] = [];\n for (const stop of fill.stops) {\n const c = colorToLottie(stop.color);\n stops.push(stop.offset, c[0], c[1], c[2]);\n }\n return {\n ty: \"gf\",\n nm: \"Gradient Fill\",\n t: 1, // linear\n s: { a: 0, k: [0, 0] },\n e: { a: 0, k: [100, 0] },\n g: { p: fill.stops.length, k: { a: 0, k: stops } },\n r: 1,\n o: { a: 0, k: 100 },\n };\n }\n\n if (fill.type === \"radial-gradient\") {\n const stops: number[] = [];\n for (const stop of fill.stops) {\n const c = colorToLottie(stop.color);\n stops.push(stop.offset, c[0], c[1], c[2]);\n }\n return {\n ty: \"gf\",\n nm: \"Gradient Fill\",\n t: 2, // radial\n s: { a: 0, k: [50, 50] },\n e: { a: 0, k: [100, 50] },\n g: { p: fill.stops.length, k: { a: 0, k: stops } },\n r: 1,\n o: { a: 0, k: 100 },\n };\n }\n\n return { ty: \"fl\", nm: \"Fill\", c: { a: 0, k: [0, 0, 0] }, o: { a: 0, k: 100 }, r: 1 };\n}\n\nfunction mapStroke(stroke: Stroke): LottieShapeItem {\n const color = colorToLottie(stroke.color);\n return {\n ty: \"st\",\n nm: \"Stroke\",\n c: { a: 0, k: color.slice(0, 3) },\n o: { a: 0, k: (color[3] ?? 1) * 100 },\n w: { a: 0, k: stroke.width },\n lc: stroke.lineCap === \"round\" ? 2 : stroke.lineCap === \"square\" ? 3 : 1,\n lj: stroke.lineJoin === \"round\" ? 2 : stroke.lineJoin === \"bevel\" ? 3 : 1,\n };\n}\n","import type { Easing } from \"@a-company/atelier-types\";\n\nexport interface LottieEasing {\n i: { x: number[]; y: number[] };\n o: { x: number[]; y: number[] };\n}\n\n/** Map an Atelier easing to Lottie bezier easing curves */\nexport function mapEasing(easing: Easing | undefined): LottieEasing | { h: 1 } {\n if (!easing) return linearEasing();\n\n if (typeof easing === \"string\") {\n switch (easing) {\n case \"ease-in\":\n return bezierEasing(0.42, 0, 1, 1);\n case \"ease-out\":\n return bezierEasing(0, 0, 0.58, 1);\n case \"ease-in-out\":\n return bezierEasing(0.42, 0, 0.58, 1);\n default:\n return linearEasing();\n }\n }\n\n switch (easing.type) {\n case \"linear\":\n return linearEasing();\n case \"cubic-bezier\":\n return bezierEasing(easing.x1, easing.y1, easing.x2, easing.y2);\n case \"spring\":\n // Approximate spring as a cubic-bezier (lossy)\n return bezierEasing(0.25, 0.1, 0.25, 1);\n case \"step\":\n // Steps map to hold keyframes\n return { h: 1 };\n default:\n return linearEasing();\n }\n}\n\nfunction linearEasing(): LottieEasing {\n return {\n i: { x: [0.833], y: [0.833] },\n o: { x: [0.167], y: [0.167] },\n };\n}\n\nfunction bezierEasing(x1: number, y1: number, x2: number, y2: number): LottieEasing {\n return {\n i: { x: [x2], y: [y2] },\n o: { x: [x1], y: [y1] },\n };\n}\n\n/** Check if an easing is lossy (spring/step → approximated) */\nexport function isLossyEasing(easing: Easing | undefined): string | null {\n if (!easing || typeof easing === \"string\") return null;\n if (easing.type === \"spring\") return \"Spring easing approximated as cubic-bezier\";\n if (easing.type === \"step\") return \"Step easing mapped to hold keyframes\";\n return null;\n}\n","import type { Delta, AnimatableProperty } from \"@a-company/atelier-types\";\nimport type { LottieAnimatedValue, LottieAnimatedMultiValue, LottieKeyframe, LottieMultiKeyframe } from \"./lottie-types.js\";\nimport { mapEasing, isLossyEasing } from \"./map-easing.js\";\n\n/** Map deltas for a single layer+property to Lottie animated value */\nexport function mapDeltasToAnimated(\n deltas: Delta[],\n property: AnimatableProperty,\n warnings: string[],\n): LottieAnimatedValue {\n if (deltas.length === 0) {\n return { a: 0, k: 0 };\n }\n\n // Check for lossy easings\n for (const d of deltas) {\n const lossyMsg = isLossyEasing(d.easing);\n if (lossyMsg) warnings.push(`${property}: ${lossyMsg}`);\n }\n\n // If expressions, warn and use static\n for (const d of deltas) {\n if (isExpression(d.from) || isExpression(d.to)) {\n warnings.push(`${property}: Expression values not supported in Lottie, using static values`);\n return { a: 0, k: typeof d.from === \"number\" ? d.from : 0 };\n }\n }\n\n // Single delta — simple animated value\n if (deltas.length === 1) {\n const d = deltas[0];\n const fromVal = resolveValue(d.from, property);\n const toVal = resolveValue(d.to, property);\n\n if (fromVal === toVal) {\n return { a: 0, k: fromVal };\n }\n\n const easing = mapEasing(d.easing);\n const kfs: LottieKeyframe[] = [];\n\n if (\"h\" in easing) {\n kfs.push({ t: d.range[0], s: [fromVal], h: 1 });\n kfs.push({ t: d.range[1], s: [toVal] });\n } else {\n kfs.push({ t: d.range[0], s: [fromVal], e: [toVal], i: easing.i, o: easing.o });\n kfs.push({ t: d.range[1], s: [toVal] });\n }\n\n return { a: 1, k: kfs };\n }\n\n // Multiple deltas — chain keyframes\n const sorted = [...deltas].sort((a, b) => a.range[0] - b.range[0]);\n const kfs: LottieKeyframe[] = [];\n\n for (const d of sorted) {\n const fromVal = resolveValue(d.from, property);\n const toVal = resolveValue(d.to, property);\n const easing = mapEasing(d.easing);\n\n if (\"h\" in easing) {\n kfs.push({ t: d.range[0], s: [fromVal], h: 1 });\n } else {\n kfs.push({ t: d.range[0], s: [fromVal], e: [toVal], i: easing.i, o: easing.o });\n }\n }\n\n // Final keyframe\n const last = sorted[sorted.length - 1];\n kfs.push({ t: last.range[1], s: [resolveValue(last.to, property)] });\n\n return { a: 1, k: kfs };\n}\n\n/** Map position deltas (frame.x, frame.y) to Lottie multi-dimensional animated value */\nexport function mapPositionDeltas(\n xDeltas: Delta[],\n yDeltas: Delta[],\n baseX: number,\n baseY: number,\n warnings: string[],\n): LottieAnimatedMultiValue {\n const hasXAnim = xDeltas.length > 0;\n const hasYAnim = yDeltas.length > 0;\n\n if (!hasXAnim && !hasYAnim) {\n return { a: 0, k: [baseX, baseY, 0] };\n }\n\n // Collect all unique frame points\n const frames = new Set<number>();\n for (const d of [...xDeltas, ...yDeltas]) {\n frames.add(d.range[0]);\n frames.add(d.range[1]);\n }\n const sortedFrames = [...frames].sort((a, b) => a - b);\n\n if (sortedFrames.length < 2) {\n return { a: 0, k: [baseX, baseY, 0] };\n }\n\n // Build multi-dimensional keyframes\n const kfs: LottieMultiKeyframe[] = [];\n for (let i = 0; i < sortedFrames.length; i++) {\n const f = sortedFrames[i];\n const x = resolveAtFrame(xDeltas, f, baseX);\n const y = resolveAtFrame(yDeltas, f, baseY);\n\n if (i < sortedFrames.length - 1) {\n const nextF = sortedFrames[i + 1];\n const nextX = resolveAtFrame(xDeltas, nextF, baseX);\n const nextY = resolveAtFrame(yDeltas, nextF, baseY);\n\n // Find the active delta's easing\n const activeX = xDeltas.find(d => d.range[0] <= f && d.range[1] >= f);\n const activeY = yDeltas.find(d => d.range[0] <= f && d.range[1] >= f);\n const easing = mapEasing(activeX?.easing ?? activeY?.easing);\n\n if (\"h\" in easing) {\n kfs.push({ t: f, s: [x, y, 0], h: 1 });\n } else {\n kfs.push({ t: f, s: [x, y, 0], e: [nextX, nextY, 0], i: easing.i, o: easing.o });\n }\n } else {\n kfs.push({ t: f, s: [x, y, 0] });\n }\n }\n\n // Check for lossy warnings\n for (const d of [...xDeltas, ...yDeltas]) {\n const msg = isLossyEasing(d.easing);\n if (msg) warnings.push(`position: ${msg}`);\n }\n\n return { a: 1, k: kfs };\n}\n\nfunction resolveAtFrame(deltas: Delta[], frame: number, base: number): number {\n for (const d of deltas) {\n if (frame >= d.range[0] && frame <= d.range[1]) {\n const from = typeof d.from === \"number\" ? d.from : base;\n const to = typeof d.to === \"number\" ? d.to : base;\n const progress = d.range[0] === d.range[1] ? 1 : (frame - d.range[0]) / (d.range[1] - d.range[0]);\n return from + (to - from) * progress;\n }\n }\n // Hold last completed value\n let lastCompleted: Delta | undefined;\n for (const d of deltas) {\n if (frame > d.range[1]) {\n if (!lastCompleted || d.range[1] > lastCompleted.range[1]) {\n lastCompleted = d;\n }\n }\n }\n if (lastCompleted) return typeof lastCompleted.to === \"number\" ? lastCompleted.to : base;\n return base;\n}\n\nfunction resolveValue(val: unknown, _property: AnimatableProperty): number {\n if (typeof val === \"number\") return val;\n if (typeof val === \"string\" && val.startsWith(\"#\")) {\n // Color — for Lottie, colors are handled separately\n return 0;\n }\n return 0;\n}\n\nfunction isExpression(val: unknown): boolean {\n return typeof val === \"object\" && val !== null && \"expr\" in val;\n}\n","import type { AtelierDocument, Layer, Delta, State, UnitValue } from \"@a-company/atelier-types\";\nimport type { LottieLayer, LottieTransform, LottieTextData } from \"./lottie-types.js\";\nimport { mapShapeVisual } from \"./map-shapes.js\";\nimport { mapDeltasToAnimated, mapPositionDeltas } from \"./map-keyframes.js\";\nimport { colorToLottie } from \"./map-colors.js\";\n\n/** Resolve a UnitValue to a plain number (percentages treated as 0 for Lottie) */\nfunction toNum(v: UnitValue): number {\n return typeof v === \"number\" ? v : 0;\n}\n\n/** Map all layers in a document+state to Lottie layers */\nexport function mapLayers(\n doc: AtelierDocument,\n state: State,\n warnings: string[],\n): LottieLayer[] {\n const layerIndexMap = new Map<string, number>();\n doc.layers.forEach((l, i) => layerIndexMap.set(l.id, i));\n\n return doc.layers.map((layer, index) => {\n const deltas = state.deltas.filter(d => d.layer === layer.id);\n return mapLayer(layer, index, deltas, layerIndexMap, doc, warnings);\n });\n}\n\nfunction mapLayer(\n layer: Layer,\n index: number,\n deltas: Delta[],\n layerIndexMap: Map<string, number>,\n doc: AtelierDocument,\n warnings: string[],\n): LottieLayer {\n const duration = getMaxFrame(deltas, doc);\n\n const base: LottieLayer = {\n ty: getLayerType(layer),\n nm: layer.id,\n ind: index,\n ip: 0,\n op: duration,\n st: 0,\n ks: buildTransform(layer, deltas, warnings),\n };\n\n // Parent\n if (layer.parentId) {\n const parentIdx = layerIndexMap.get(layer.parentId);\n if (parentIdx !== undefined) {\n base.parent = parentIdx;\n }\n }\n\n // Blend mode\n if (layer.blendMode) {\n base.bm = mapBlendMode(layer.blendMode);\n }\n\n // Shape layer\n if (layer.visual.type === \"shape\") {\n base.shapes = mapShapeVisual(layer.visual, toNum(layer.bounds.width), toNum(layer.bounds.height));\n }\n\n // Text layer\n if (layer.visual.type === \"text\") {\n const color = colorToLottie(layer.visual.style.color);\n base.t = {\n d: {\n k: [{\n s: {\n s: layer.visual.style.fontSize,\n f: layer.visual.style.fontFamily,\n t: layer.visual.content,\n fc: color.slice(0, 3),\n j: layer.visual.style.textAlign === \"center\" ? 1 :\n layer.visual.style.textAlign === \"right\" ? 2 : 0,\n },\n t: 0,\n }],\n },\n } as LottieTextData;\n }\n\n // Image layer\n if (layer.visual.type === \"image\") {\n base.refId = layer.visual.assetId;\n base.w = toNum(layer.bounds.width);\n base.h = toNum(layer.bounds.height);\n\n if (layer.visual.spritesheet) {\n warnings.push(`Layer \"${layer.id}\": Spritesheet animation not supported in Lottie export`);\n }\n if (layer.visual.sourceRect) {\n warnings.push(`Layer \"${layer.id}\": sourceRect cropping not supported in Lottie export`);\n }\n }\n\n // Tint warning\n if (layer.tint && layer.tint.amount > 0) {\n warnings.push(`Layer \"${layer.id}\": Tint effect not supported in Lottie export`);\n }\n\n return base;\n}\n\nfunction getLayerType(layer: Layer): number {\n switch (layer.visual.type) {\n case \"shape\": return 4;\n case \"text\": return 5;\n case \"image\": return 2;\n case \"group\": return 3; // null layer\n case \"ref\": return 0; // precomp\n default: return 4;\n }\n}\n\nfunction buildTransform(\n layer: Layer,\n deltas: Delta[],\n warnings: string[],\n): LottieTransform {\n // Group deltas by property\n const byProp = new Map<string, Delta[]>();\n for (const d of deltas) {\n if (!byProp.has(d.property)) byProp.set(d.property, []);\n byProp.get(d.property)!.push(d);\n }\n\n const xDeltas = byProp.get(\"frame.x\") ?? [];\n const yDeltas = byProp.get(\"frame.y\") ?? [];\n const opacityDeltas = byProp.get(\"opacity\") ?? [];\n const rotationDeltas = byProp.get(\"rotation\") ?? [];\n const scaleXDeltas = byProp.get(\"scale.x\") ?? [];\n const scaleYDeltas = byProp.get(\"scale.y\") ?? [];\n\n // Position\n const position = mapPositionDeltas(\n xDeltas, yDeltas,\n typeof layer.frame.x === \"number\" ? layer.frame.x : 0,\n typeof layer.frame.y === \"number\" ? layer.frame.y : 0,\n warnings,\n );\n\n // Opacity (Lottie uses 0–100)\n let opacity;\n if (opacityDeltas.length > 0) {\n const raw = mapDeltasToAnimated(opacityDeltas, \"opacity\", warnings);\n // Scale 0-1 to 0-100\n if (raw.a === 0) {\n opacity = { a: 0 as const, k: (raw.k as number) * 100 };\n } else {\n const kfs = (raw.k as Array<{ t: number; s: [number]; e?: [number]; [key: string]: unknown }>).map(kf => ({\n ...kf,\n s: [kf.s[0] * 100] as [number],\n e: kf.e ? [kf.e[0] * 100] as [number] : undefined,\n }));\n opacity = { a: 1 as const, k: kfs };\n }\n } else {\n opacity = { a: 0 as const, k: (layer.opacity ?? 1) * 100 };\n }\n\n // Rotation\n const rotation = rotationDeltas.length > 0\n ? mapDeltasToAnimated(rotationDeltas, \"rotation\", warnings)\n : { a: 0 as const, k: layer.rotation ?? 0 };\n\n // Scale (Lottie uses 0–100)\n const baseScaleX = (layer.scale?.x ?? 1) * 100;\n const baseScaleY = (layer.scale?.y ?? 1) * 100;\n let scale;\n if (scaleXDeltas.length > 0 || scaleYDeltas.length > 0) {\n // Simplified: use x deltas for now\n scale = { a: 0 as const, k: [baseScaleX, baseScaleY, 100] };\n if (scaleXDeltas.length > 0 || scaleYDeltas.length > 0) {\n warnings.push(\"Scale animation partially mapped to Lottie\");\n }\n } else {\n scale = { a: 0 as const, k: [baseScaleX, baseScaleY, 100] };\n }\n\n // Anchor point\n const ax = (layer.anchorPoint?.x ?? 0) * toNum(layer.bounds.width);\n const ay = (layer.anchorPoint?.y ?? 0) * toNum(layer.bounds.height);\n\n return {\n o: opacity,\n r: rotation,\n p: position,\n a: { a: 0, k: [ax, ay, 0] },\n s: scale,\n };\n}\n\nfunction getMaxFrame(deltas: Delta[], doc: AtelierDocument): number {\n let max = 0;\n for (const state of Object.values(doc.states)) {\n if (state.duration > max) max = state.duration;\n }\n for (const d of deltas) {\n if (d.range[1] > max) max = d.range[1];\n }\n return max || 60;\n}\n\nfunction mapBlendMode(mode: string): number {\n const map: Record<string, number> = {\n normal: 0, multiply: 1, screen: 2, overlay: 3,\n darken: 4, lighten: 5, \"color-dodge\": 6, \"color-burn\": 7,\n \"hard-light\": 8, \"soft-light\": 9, difference: 10, exclusion: 11,\n hue: 12, saturation: 13, color: 14, luminosity: 15,\n };\n return map[mode] ?? 0;\n}\n","import type { AtelierDocument, State } from \"@a-company/atelier-types\";\n\n/** Check for unsupported features and collect warnings */\nexport function collectUnsupportedWarnings(doc: AtelierDocument, state: State): string[] {\n const warnings: string[] = [];\n\n // Audio not supported\n if (state.audio) {\n warnings.push(\"Audio tracks are not supported in Lottie format and will be dropped\");\n }\n\n // Expressions not supported\n for (const delta of state.deltas) {\n if (isExpression(delta.from) || isExpression(delta.to)) {\n warnings.push(`Expression values on \"${delta.layer}.${delta.property}\" not supported in Lottie`);\n }\n }\n\n // Shadow not supported in base Lottie\n for (const layer of doc.layers) {\n if (layer.shadow) {\n warnings.push(`Shadow on layer \"${layer.id}\" is not supported in base Lottie format`);\n }\n }\n\n // Motion path not directly mapped\n for (const layer of doc.layers) {\n if (layer.motionPath) {\n warnings.push(`Motion path on layer \"${layer.id}\" is not directly mappable to Lottie`);\n }\n }\n\n // Clip paths have limited support\n for (const layer of doc.layers) {\n if (layer.clipPath) {\n warnings.push(`Clip path on layer \"${layer.id}\" mapped as Lottie mask (partial support)`);\n }\n }\n\n // States/transitions\n if (Object.keys(doc.states).length > 1) {\n warnings.push(\"Multiple states flattened to single timeline in Lottie export\");\n }\n\n return warnings;\n}\n\nfunction isExpression(val: unknown): boolean {\n return typeof val === \"object\" && val !== null && \"expr\" in val;\n}\n","import type { AtelierDocument } from \"@a-company/atelier-types\";\nimport type { LottieAnimation, LottieAsset } from \"./lottie-types.js\";\nimport { mapLayers } from \"./map-layers.js\";\nimport { collectUnsupportedWarnings } from \"./warnings.js\";\n\nexport interface ExportLottieOptions {\n /** State to export (defaults to first state) */\n state?: string;\n}\n\nexport interface ExportLottieResult {\n /** The Lottie JSON animation */\n json: LottieAnimation;\n /** Warnings about unsupported features */\n warnings: string[];\n}\n\n/**\n * Export an Atelier document to Lottie JSON format.\n * This is a lossy conversion — not all features are supported.\n */\nexport function exportToLottie(\n doc: AtelierDocument,\n opts?: ExportLottieOptions,\n): ExportLottieResult {\n const stateNames = Object.keys(doc.states);\n if (stateNames.length === 0) {\n throw new Error(\"Document has no states to export\");\n }\n\n const stateName = opts?.state ?? stateNames[0];\n const state = doc.states[stateName];\n if (!state) {\n throw new Error(`State \"${stateName}\" not found`);\n }\n\n const warnings: string[] = [];\n\n // Collect unsupported feature warnings\n warnings.push(...collectUnsupportedWarnings(doc, state));\n\n // Map layers\n const layers = mapLayers(doc, state, warnings);\n\n // Map assets\n const assets: LottieAsset[] = [];\n if (doc.assets) {\n for (const [id, asset] of Object.entries(doc.assets)) {\n if (asset.type === \"image\") {\n assets.push({\n id,\n w: 100,\n h: 100,\n p: asset.src,\n e: 0,\n });\n }\n }\n }\n\n const json: LottieAnimation = {\n v: \"5.7.4\",\n fr: doc.canvas.fps,\n ip: 0,\n op: state.duration,\n w: doc.canvas.width,\n h: doc.canvas.height,\n nm: doc.name,\n layers,\n ...(assets.length > 0 ? { assets } : {}),\n };\n\n // Deduplicate warnings\n const uniqueWarnings = [...new Set(warnings)];\n\n return { json, warnings: uniqueWarnings };\n}\n","import { readFileSync, writeFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { Command } from \"commander\";\nimport type { AtelierDocument } from \"@a-company/atelier-types\";\nimport { parseAtelier } from \"@a-company/atelier-schema\";\nimport {\n renderDocumentToPng,\n resolveExportDimensions,\n CanvasUnavailableError,\n} from \"../lib/render-image.js\";\n\n// Re-export so existing import sites (index.ts, tests) keep resolving.\nexport { resolveExportDimensions };\n\n/** Read and parse an .atelier file, exiting on failure. */\nfunction readAndParse(file: string): AtelierDocument {\n const absPath = resolve(file);\n let content: string;\n try {\n content = readFileSync(absPath, \"utf-8\");\n } catch {\n console.error(`Cannot read file: ${absPath}`);\n return process.exit(1);\n }\n\n const result = parseAtelier(content);\n if (!result.success) {\n console.error(\"Parse errors:\");\n for (const error of result.errors) {\n console.error(` - ${error.path}: ${error.message}`);\n }\n return process.exit(1);\n }\n\n return result.data;\n}\n\ninterface ExportImageOptions {\n state?: string;\n frame: string;\n width?: string;\n height?: string;\n out: string;\n}\n\n/** Parse a positive integer CLI option, returning undefined if not set. */\nfunction parseDim(raw: string | undefined, name: string): number | undefined {\n if (raw === undefined) return undefined;\n const n = parseInt(raw, 10);\n if (isNaN(n) || n <= 0) {\n console.error(`Invalid --${name}: ${raw}`);\n process.exit(1);\n }\n return n;\n}\n\n/** Register the `export-image` subcommand on the Commander program. */\nexport function exportImageCommand(program: Command): void {\n program\n .command(\"export-image <file>\")\n .description(\n \"Export a single frame as a PNG image. \" +\n \"Aspect-preserving when only one of --width/--height is set; \" +\n \"if both are set the renderer uses both verbatim (may squash).\",\n )\n .requiredOption(\"-o, --out <path>\", \"Output PNG file path\")\n .option(\"-s, --state <name>\", \"State name (defaults to first state)\")\n .option(\"-f, --frame <number>\", \"Frame number (defaults to 0)\", \"0\")\n .option(\"--width <number>\", \"Override output width (px)\")\n .option(\"--height <number>\", \"Override output height (px)\")\n .action(async (file: string, options: ExportImageOptions) => {\n const doc = readAndParse(file);\n\n const frameNumber = parseInt(options.frame, 10);\n if (isNaN(frameNumber) || frameNumber < 0) {\n console.error(`Invalid frame number: ${options.frame}`);\n process.exit(1);\n return;\n }\n\n const width = parseDim(options.width, \"width\");\n const height = parseDim(options.height, \"height\");\n\n try {\n const buffer = await renderDocumentToPng(doc, {\n state: options.state,\n frame: frameNumber,\n width,\n height,\n });\n writeFileSync(resolve(options.out), buffer);\n } catch (err) {\n if (err instanceof CanvasUnavailableError) {\n console.error(err.message);\n process.exit(1);\n return;\n }\n console.error((err as Error).message);\n process.exit(1);\n }\n });\n}\n","/**\n * Shared single-frame render-to-PNG path.\n *\n * Extracted from `atelier export-image` so the carousel batch driver and the\n * single-frame export command render through one code path. Rasterizes via\n * `@napi-rs/canvas` — a Canvas2D implementation shipped as prebuilt platform\n * binaries, so PNG export works on a plain install with no node-gyp build and\n * no system libraries (cairo/pango/etc.). Pre-loads any ImageVisual layers via\n * its `loadImage` (file path / data-URL / buffer all work server-side, the same\n * mechanism the MP4 render-pipeline uses) and renders one resolved frame scaled\n * to the requested output dimensions.\n */\n\nimport type { AtelierDocument } from \"@a-company/atelier-types\";\nimport { resolveFrame } from \"@a-company/atelier-core\";\nimport { createCanvas, loadImage } from \"@napi-rs/canvas\";\n\n/** @napi-rs/canvas surface — the subset this module touches. */\ninterface NodeCanvas {\n getContext(id: \"2d\"): unknown;\n toBuffer(format: \"image/png\"): Buffer;\n}\n\n/** A loaded image — width/height are the natural pixel dims. */\nexport interface LoadedImage {\n width: number;\n height: number;\n}\n\n/** Canvas module surface (createCanvas + loadImage). */\ninterface CanvasModule {\n createCanvas: (w: number, h: number) => NodeCanvas;\n loadImage: (src: string) => Promise<LoadedImage>;\n}\n\n/** Bounds + centered frame for fitting an image into a canvas. */\nexport interface ImageFitResult {\n bounds: { width: number; height: number };\n frame: { x: number; y: number };\n}\n\n/**\n * Fit a natural-sized image into a canvas while preserving aspect ratio,\n * centered. Adapted from studio's image-drop helper (kept local so the CLI's\n * server-side render path doesn't import the browser studio package).\n *\n * Landscape (wider than canvas) → fit to width; portrait (taller or equal) →\n * fit to height; both dims capped at the canvas extents. Degenerate natural\n * sizes fall back to canvas extents so callers always get valid bounds.\n */\nexport function fitImageToCanvas(\n canvas: { width: number; height: number },\n natural: { width: number; height: number },\n): ImageFitResult {\n const cw = canvas.width;\n const ch = canvas.height;\n const iw = natural.width;\n const ih = natural.height;\n\n if (!iw || !ih) {\n return { bounds: { width: cw, height: ch }, frame: { x: cw / 2, y: ch / 2 } };\n }\n\n const imageAspect = iw / ih;\n const canvasAspect = cw / ch;\n\n let width: number;\n let height: number;\n if (imageAspect > canvasAspect) {\n width = cw;\n height = cw / imageAspect;\n } else {\n height = ch;\n width = ch * imageAspect;\n }\n width = Math.min(width, cw);\n height = Math.min(height, ch);\n\n return { bounds: { width, height }, frame: { x: cw / 2, y: ch / 2 } };\n}\n\n/**\n * Raised when the `@napi-rs/canvas` rasterizer cannot be loaded. In practice\n * this never fires: `@napi-rs/canvas` is a hard dependency that ships prebuilt\n * platform binaries (no node-gyp, no system libraries). It can only happen if\n * the install is corrupt or running on an unsupported platform with no prebuilt\n * binary — in which case a reinstall is the fix. Kept as a typed seam so call\n * sites can still distinguish a missing-rasterizer failure from a bad document.\n */\nexport class CanvasUnavailableError extends Error {\n constructor() {\n super(\n \"The '@napi-rs/canvas' rasterizer could not be loaded.\\n\" +\n \"This package ships prebuilt platform binaries — no system libraries needed.\\n\" +\n \"Try reinstalling dependencies (e.g. `npm install`) to fetch the binary for this platform.\",\n );\n this.name = \"CanvasUnavailableError\";\n }\n}\n\n/**\n * Resolve the canvas module. `@napi-rs/canvas` is a hard, statically-imported\n * dependency, so this is a thin accessor that surfaces a typed\n * {@link CanvasUnavailableError} on the (essentially impossible) chance the\n * platform binary failed to load.\n */\nexport async function loadCanvasModule(): Promise<CanvasModule> {\n if (typeof createCanvas !== \"function\" || typeof loadImage !== \"function\") {\n throw new CanvasUnavailableError();\n }\n return {\n createCanvas: createCanvas as unknown as CanvasModule[\"createCanvas\"],\n loadImage: loadImage as unknown as CanvasModule[\"loadImage\"],\n };\n}\n\n/**\n * Compute final output dimensions from the document canvas and optional\n * width/height overrides. When only one of width/height is provided, the\n * other is derived to preserve the document's aspect ratio. When both are\n * provided, both are used verbatim (allows non-uniform scaling).\n */\nexport function resolveExportDimensions(\n docWidth: number,\n docHeight: number,\n width?: number,\n height?: number,\n): { width: number; height: number } {\n if (width !== undefined && height !== undefined) {\n return { width, height };\n }\n if (width !== undefined) {\n const h = Math.max(1, Math.round((width * docHeight) / docWidth));\n return { width, height: h };\n }\n if (height !== undefined) {\n const w = Math.max(1, Math.round((height * docWidth) / docHeight));\n return { width: w, height };\n }\n return { width: docWidth, height: docHeight };\n}\n\n/**\n * Build an {@link ImageCache} pre-populated with every ImageVisual source in\n * the document. Mirrors render-pipeline.ts: load each source with\n * `loadImage`, then wire a synchronous-from-cache `createImage` so the renderer\n * resolves images immediately. Returns the cache plus a map of src → loaded\n * image so callers can read natural dimensions.\n */\nasync function preloadImages(\n doc: AtelierDocument,\n loadImage: (src: string) => Promise<LoadedImage>,\n): Promise<{\n imageCache: import(\"@a-company/atelier-canvas\").ImageCache;\n loaded: Map<string, LoadedImage>;\n}> {\n const { ImageCache } = await import(\"@a-company/atelier-canvas\");\n\n const sources = new Set<string>();\n for (const layer of doc.layers) {\n if (layer.visual.type === \"image\") {\n const iv = layer.visual as { src?: string; assetId?: string };\n if (iv.src) sources.add(iv.src);\n else if (iv.assetId && doc.assets?.[iv.assetId]) sources.add(doc.assets[iv.assetId].src);\n }\n }\n\n if (sources.size === 0) {\n return { imageCache: new ImageCache(), loaded: new Map() };\n }\n\n const preloaded = new Map<string, LoadedImage>();\n await Promise.all(\n [...sources].map(async (src) => {\n try {\n preloaded.set(src, await loadImage(src));\n } catch {\n // Sources that fail to load render blank — same tolerance as render-pipeline.\n }\n }),\n );\n\n const imageCache = new ImageCache({\n createImage: (src, onLoad, onError) => {\n const img = preloaded.get(src);\n if (img) {\n process.nextTick(onLoad);\n return img;\n }\n process.nextTick(onError);\n return {};\n },\n });\n for (const src of preloaded.keys()) imageCache.load(src);\n await new Promise<void>((resolve) => process.nextTick(resolve));\n\n return { imageCache, loaded: preloaded };\n}\n\nexport interface RenderToPngOptions {\n /** State to resolve (defaults to the first state). */\n state?: string;\n /** Frame within the state (defaults to 0). */\n frame?: number;\n /** Output width override (px). */\n width?: number;\n /** Output height override (px). */\n height?: number;\n /**\n * Optional hook to adjust each image layer's bounds once natural dimensions\n * are known from the loaded image. Receives the layer src + natural dims and\n * the doc canvas; returns new bounds (and frame). Used by the carousel driver\n * to aspect-fit fit-to-canvas images that were composed with placeholder\n * bounds (natural dims are unknown until the rasterizer decodes the file).\n */\n refitImageBounds?: (args: {\n canvas: { width: number; height: number };\n natural: { width: number; height: number };\n }) => { bounds: { width: number; height: number }; frame: { x: number; y: number } };\n}\n\n/**\n * Render a single resolved frame of a document to a PNG buffer.\n *\n * Validates state/frame, pre-loads image layers, scales rendering to fit the\n * requested output dimensions, and returns the encoded PNG bytes. Throws\n * {@link CanvasUnavailableError} when the rasterizer is missing and a plain\n * Error for bad state/frame selection.\n */\nexport async function renderDocumentToPng(\n doc: AtelierDocument,\n opts: RenderToPngOptions = {},\n): Promise<Buffer> {\n const stateNames = Object.keys(doc.states);\n if (stateNames.length === 0) {\n throw new Error(\"Document has no states\");\n }\n const stateName = opts.state ?? stateNames[0];\n if (!doc.states[stateName]) {\n throw new Error(`State \"${stateName}\" not found. Available: ${stateNames.join(\", \")}`);\n }\n const frameNumber = opts.frame ?? 0;\n if (!Number.isInteger(frameNumber) || frameNumber < 0) {\n throw new Error(`Invalid frame number: ${frameNumber}`);\n }\n const duration = doc.states[stateName].duration;\n if (frameNumber >= duration) {\n throw new Error(\n `Frame ${frameNumber} is out of range for state \"${stateName}\" (duration ${duration})`,\n );\n }\n\n const { createCanvas, loadImage } = await loadCanvasModule();\n const { renderFrame } = await import(\"@a-company/atelier-canvas\");\n\n const { imageCache, loaded } = await preloadImages(doc, loadImage);\n\n // Re-fit image layers now that natural dims are known (carousel path).\n let renderDoc = doc;\n if (opts.refitImageBounds && loaded.size > 0) {\n renderDoc = {\n ...doc,\n layers: doc.layers.map((layer) => {\n if (layer.visual.type !== \"image\") return layer;\n const iv = layer.visual as { src?: string; assetId?: string };\n const src = iv.src ?? (iv.assetId ? doc.assets?.[iv.assetId]?.src : undefined);\n const natural = src ? loaded.get(src) : undefined;\n if (!natural || !natural.width || !natural.height) return layer;\n const fit = opts.refitImageBounds!({ canvas: doc.canvas, natural });\n return { ...layer, bounds: fit.bounds, frame: fit.frame };\n }),\n };\n }\n\n const { width: outW, height: outH } = resolveExportDimensions(\n renderDoc.canvas.width,\n renderDoc.canvas.height,\n opts.width,\n opts.height,\n );\n\n const resolved = resolveFrame(renderDoc, stateName, frameNumber);\n const cvs = createCanvas(outW, outH);\n const ctx = cvs.getContext(\"2d\") as unknown as import(\"@a-company/atelier-canvas\").RenderContext;\n\n const sx = outW / renderDoc.canvas.width;\n const sy = outH / renderDoc.canvas.height;\n if (sx !== 1 || sy !== 1) ctx.scale(sx, sy);\n\n renderFrame(ctx, resolved, renderDoc, imageCache);\n\n return cvs.toBuffer(\"image/png\");\n}\n","import { readdirSync, mkdirSync, writeFileSync, statSync } from \"node:fs\";\nimport { resolve, join, basename, extname, dirname, sep } from \"node:path\";\nimport type { Command } from \"commander\";\nimport type { AtelierDocument, StudioRecipe } from \"@a-company/atelier-types\";\nimport { loadRecipe, applyRecipeToOverlay } from \"../lib/recipe.js\";\nimport {\n renderDocumentToPng,\n fitImageToCanvas,\n CanvasUnavailableError,\n} from \"../lib/render-image.js\";\n\n/** Image extensions the carousel driver accepts (lower-cased, with leading dot). */\nconst IMAGE_EXTS = new Set([\".png\", \".jpg\", \".jpeg\", \".webp\"]);\n\n/** Default canvas size when neither --width/--height nor a recipe aspect is set. */\nconst DEFAULT_CANVAS = 1080;\n\n/**\n * Expand an --inputs pattern into a sorted list of absolute image file paths.\n *\n * Deliberately minimal (no new glob dependency): supports a directory path\n * (lists all image files within), a single file path, or a single-segment\n * `*`-glob in the final path component (e.g. `shots/*.png`, `shots/img-*`).\n * Multi-segment / recursive globs are out of scope — point --inputs at a\n * directory instead. The returned list is filtered to image extensions and\n * sorted lexicographically so output ordering is stable.\n */\nexport function expandInputs(pattern: string): string[] {\n const abs = resolve(pattern);\n\n // Directory → every image file inside it.\n let isDir = false;\n try {\n isDir = statSync(abs).isDirectory();\n } catch {\n isDir = false;\n }\n if (isDir) {\n return listImages(abs);\n }\n\n const dir = dirname(abs);\n const base = basename(abs);\n\n // Single-segment `*` glob in the final component.\n if (base.includes(\"*\")) {\n const matcher = globToRegExp(base);\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return [];\n }\n return entries\n .filter((name) => matcher.test(name) && isImageFile(name))\n .map((name) => join(dir, name))\n .sort();\n }\n\n // Literal file path — keep it only if it is an image file.\n return isImageFile(base) ? [abs] : [];\n}\n\n/** List image files directly within a directory, sorted, as absolute paths. */\nfunction listImages(dir: string): string[] {\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return [];\n }\n return entries\n .filter(isImageFile)\n .map((name) => join(dir, name))\n .sort();\n}\n\n/** True when a filename has an accepted image extension. */\nfunction isImageFile(name: string): boolean {\n return IMAGE_EXTS.has(extname(name).toLowerCase());\n}\n\n/**\n * Convert a single path-component glob (only `*` and `?` honored) into a\n * RegExp anchored to the whole name. `*` matches any run except the path\n * separator; `?` matches a single non-separator char.\n */\nfunction globToRegExp(glob: string): RegExp {\n const escaped = glob.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const pattern = escaped.replace(/\\*/g, `[^${sep === \"\\\\\" ? \"\\\\\\\\\" : sep}]*`).replace(/\\?/g, \".\");\n return new RegExp(`^${pattern}$`);\n}\n\n/**\n * Build a single-frame carousel document for one image: a canvas-sized doc with\n * a background and one fit-to-canvas ImageVisual layer, then the recipe's\n * overlay_rules applied with currentIndex/totalCount threaded so the page-number\n * overlay renders \"i/N\".\n *\n * Pure + synchronous (no node-canvas): image bounds use the canvas-extent\n * fallback because natural dimensions aren't known until node-canvas decodes\n * the file at render time. {@link renderDocumentToPng} re-fits the bounds via\n * `refitImageBounds` once the image is loaded.\n */\nexport function composeCarouselFrameDoc(args: {\n imagePath: string;\n index: number;\n total: number;\n width: number;\n height: number;\n recipe: StudioRecipe;\n background?: string;\n}): AtelierDocument {\n const { imagePath, index, total, width, height, recipe } = args;\n const canvas = { width, height };\n\n // Natural dims unknown here → fitImageToCanvas falls back to canvas extents.\n const fit = fitImageToCanvas(canvas, { width: 0, height: 0 });\n\n const assetId = \"carousel-image-asset\";\n const baseDoc: AtelierDocument = {\n version: \"1.0\",\n name: `carousel-${index}`,\n canvas: { width, height, fps: 30, background: args.background ?? \"#000000\" },\n assets: { [assetId]: { type: \"image\", src: imagePath } },\n layers: [\n {\n id: \"carousel-image\",\n visual: { type: \"image\", assetId, src: imagePath },\n frame: fit.frame,\n bounds: fit.bounds,\n anchorPoint: { x: 0.5, y: 0.5 },\n opacity: 1,\n },\n ],\n states: { default: { duration: 1, deltas: [] } },\n };\n\n // Thread the carousel position so page_number resolves \"i/N\"; handle (if any)\n // is anchored on top of the image.\n return applyRecipeToOverlay(baseDoc, recipe, { currentIndex: index, totalCount: total });\n}\n\n/** Zero-padded sortable filename prefix: width = max(2, digits in N). */\nexport function carouselFileName(index: number, total: number, imagePath: string): string {\n const padWidth = Math.max(2, String(total).length);\n const prefix = String(index).padStart(padWidth, \"0\");\n const ext = extname(imagePath);\n const stem = basename(imagePath, ext);\n return `${prefix}-${stem}.png`;\n}\n\ninterface CarouselOptions {\n inputs: string;\n outDir: string;\n width?: string;\n height?: string;\n frame: string;\n}\n\n/** Parse a positive-integer CLI option, exiting on bad input. */\nfunction parseDim(raw: string | undefined, name: string): number | undefined {\n if (raw === undefined) return undefined;\n const n = parseInt(raw, 10);\n if (isNaN(n) || n <= 0) {\n console.error(`Invalid --${name}: ${raw}`);\n process.exit(1);\n }\n return n;\n}\n\n/**\n * Register `atelier carousel <recipe> --inputs <glob> --out-dir <dir>` — batch\n * compose a folder of images into recipe-overlaid PNGs.\n *\n * For each image i of N: build a fit-to-canvas doc, apply the recipe's\n * overlay_rules with currentIndex=i / totalCount=N (handle + \"i/N\" page-number),\n * render via the shared export-image path, and write a zero-padded sortable\n * PNG into --out-dir.\n */\nexport function carouselCommand(program: Command): void {\n program\n .command(\"carousel <recipe>\")\n .description(\n \"Batch-compose a folder of images into recipe-overlaid PNGs. \" +\n \"Each image becomes a fit-to-canvas post with the recipe's handle + \" +\n \"page-number ('i/N') overlays, written to --out-dir as zero-padded PNGs.\",\n )\n .requiredOption(\"-i, --inputs <glob>\", \"Input images: a directory, file, or single-segment *-glob\")\n .requiredOption(\"-d, --out-dir <dir>\", \"Destination directory for composed PNGs\")\n .option(\"--width <number>\", \"Canvas width (px)\", String(DEFAULT_CANVAS))\n .option(\"--height <number>\", \"Canvas height (px)\", String(DEFAULT_CANVAS))\n .option(\"-f, --frame <number>\", \"Frame to render (defaults to 0)\", \"0\")\n .action(async (recipeRef: string, options: CarouselOptions) => {\n // Resolve the recipe (same chain as other recipe commands).\n let recipe: StudioRecipe;\n try {\n const loaded = loadRecipe(recipeRef);\n recipe = loaded.recipe;\n console.log(`Loaded recipe ${loaded.path}`);\n for (const w of loaded.warnings) console.log(` ⚠ ${w}`);\n } catch (err) {\n console.error(`atelier carousel: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n return;\n }\n\n // Expand inputs.\n const inputs = expandInputs(options.inputs);\n if (inputs.length === 0) {\n console.error(\n `atelier carousel: no image files matched --inputs \"${options.inputs}\" ` +\n `(accepted: ${[...IMAGE_EXTS].join(\", \")})`,\n );\n process.exit(1);\n return;\n }\n\n const width = parseDim(options.width, \"width\") ?? DEFAULT_CANVAS;\n const height = parseDim(options.height, \"height\") ?? DEFAULT_CANVAS;\n const frame = parseInt(options.frame, 10);\n if (isNaN(frame) || frame < 0) {\n console.error(`Invalid frame number: ${options.frame}`);\n process.exit(1);\n return;\n }\n\n // Ensure the destination exists / is writable.\n const outDir = resolve(options.outDir);\n try {\n mkdirSync(outDir, { recursive: true });\n } catch (err) {\n console.error(`atelier carousel: cannot create out-dir ${outDir}: ${(err as Error).message}`);\n process.exit(1);\n return;\n }\n\n const total = inputs.length;\n console.log(`Composing ${total} image${total === 1 ? \"\" : \"s\"} → ${outDir}`);\n\n try {\n for (let n = 0; n < total; n++) {\n const index = n + 1; // 1-based\n const imagePath = inputs[n];\n const doc = composeCarouselFrameDoc({\n imagePath,\n index,\n total,\n width,\n height,\n recipe,\n });\n\n const buffer = await renderDocumentToPng(doc, {\n frame,\n // Re-fit the image to the canvas using real natural dims once decoded.\n refitImageBounds: ({ canvas, natural }) => fitImageToCanvas(canvas, natural),\n });\n\n const outName = carouselFileName(index, total, imagePath);\n writeFileSync(join(outDir, outName), buffer);\n console.log(` [${index}/${total}] ${basename(imagePath)} → ${outName}`);\n }\n } catch (err) {\n if (err instanceof CanvasUnavailableError) {\n console.error(err.message);\n process.exit(1);\n return;\n }\n console.error(`atelier carousel: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n return;\n }\n\n console.log(`\\nDone. ${total} image${total === 1 ? \"\" : \"s\"} → ${outDir}`);\n });\n}\n","import { readFileSync, existsSync } from \"node:fs\";\nimport { join, resolve, isAbsolute } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { parse as parseYaml, stringify as stringifyYaml } from \"yaml\";\nimport { validateRecipe } from \"@a-company/atelier-schema\";\nimport type {\n StudioRecipe,\n SilencePolicy,\n CaptionStyle,\n CaptionGrouping,\n AtelierDocument,\n Layer,\n OverlayAnchor,\n OverlayTextStyle,\n OverlayPageNumberRule,\n} from \"@a-company/atelier-types\";\nimport type { TrimOptions } from \"../commands/trim.js\";\nimport type { TranscribeOptions } from \"../commands/transcribe.js\";\nimport type { CaptionStyle as RuntimeCaptionStyle, BuildCaptionsOptions } from \"./caption-builder.js\";\n\nexport const RECIPE_VERSION = \"1.0\";\n\nexport interface LoadedRecipe {\n recipe: StudioRecipe;\n /** Absolute path the recipe was read from */\n path: string;\n /** Warnings from validateRecipe (e.g. reserved-field usage) */\n warnings: string[];\n}\n\n/**\n * Resolve a recipe reference to an absolute path.\n *\n * Resolution order (per studio-recipe.md §6.2):\n * 1. <projectDir>/.atelier/recipes/<name>.recipe.{yaml,json}\n * 2. ~/.atelier/recipes/<name>.recipe.{yaml,json}\n * 3. The literal path passed (absolute or relative to cwd)\n *\n * Absolute paths and paths containing slashes skip resolution and load directly.\n */\nexport function resolveRecipePath(pathOrName: string, projectDir?: string): string {\n // Absolute path or contains a path separator — treat as literal\n if (isAbsolute(pathOrName) || pathOrName.includes(\"/\") || pathOrName.includes(\"\\\\\")) {\n return resolve(pathOrName);\n }\n\n const candidates: string[] = [];\n const exts = [\".recipe.yaml\", \".recipe.json\", \".yaml\", \".yml\", \".json\"];\n\n if (projectDir) {\n const projectRecipesDir = join(resolve(projectDir), \".atelier\", \"recipes\");\n for (const ext of exts) candidates.push(join(projectRecipesDir, `${pathOrName}${ext}`));\n }\n const userRecipesDir = join(homedir(), \".atelier\", \"recipes\");\n for (const ext of exts) candidates.push(join(userRecipesDir, `${pathOrName}${ext}`));\n\n for (const candidate of candidates) {\n if (existsSync(candidate)) return candidate;\n }\n\n throw new Error(\n `Recipe \"${pathOrName}\" not found. Looked in:\\n${candidates.map((c) => ` ${c}`).join(\"\\n\")}`,\n );\n}\n\n/**\n * Load and validate a recipe from a path or name.\n *\n * Sniffs YAML vs JSON by file extension; falls back to YAML parser when\n * unclear (YAML is a superset of JSON for objects).\n */\nexport function loadRecipe(pathOrName: string, projectDir?: string): LoadedRecipe {\n const path = resolveRecipePath(pathOrName, projectDir);\n const raw = readFileSync(path, \"utf-8\");\n\n let parsed: unknown;\n if (path.endsWith(\".json\")) {\n parsed = JSON.parse(raw);\n } else {\n parsed = parseYaml(raw);\n }\n\n const result = validateRecipe(parsed);\n if (!result.success) {\n const msg = result.errors.map((e) => ` ${e.path}: ${e.message}`).join(\"\\n\");\n throw new Error(`Invalid recipe at ${path}:\\n${msg}`);\n }\n\n return {\n recipe: result.data,\n path,\n warnings: result.warnings ?? [],\n };\n}\n\n/**\n * Generate a starter recipe YAML with every Phase 1 field present and\n * inline comments documenting it. Authors learn the shape by editing.\n */\nexport function scaffoldRecipeYaml(name: string): string {\n return `# Studio Recipe — ${name}\n# Phase 1 — manual authoring + apply\n# https://github.com/ascend42/a-atelier/blob/main/.paradigm/specs/studio-recipe.md\n\nversion: \"${RECIPE_VERSION}\"\nname: \"${name}\"\ndescription: \"\"\nauthor: \"\"\ntags: []\n\n# ── Silence-trim policy — consumed by \\`atelier trim\\` ───────────────────\nsilence_policy:\n # silencedetect noise threshold (default: -30dB)\n noise: \"-30dB\"\n # Minimum silence duration to register, in seconds (default: 0.35)\n min_silence: 0.35\n # Default leading padding for new cuts, in seconds (default: 0.08)\n default_padding_pre: 0.08\n # Default trailing padding for new cuts, in seconds (default: 0.12)\n default_padding_post: 0.12\n # Re-detect match tolerance for preserving user padding, in seconds (default: 0.5)\n match_tolerance: 0.5\n\n# ── Caption visual style — consumed by \\`atelier transcribe\\` ────────────\ncaption_style:\n font_family: \"Inter\"\n font_size: 84\n font_weight: \"bold\" # normal | bold | numeric (100..900)\n text_align: \"center\" # left | center | right\n color: \"#FFFFFF\"\n y_ratio: 0.85 # 0=top, 1=bottom\n width_ratio: 0.9\n fade_seconds: 0.05\n\n# ── Caption phrase grouping — consumed by \\`atelier transcribe\\` ─────────\ncaption_grouping:\n max_words: 5\n pause_gap: 0.4\n\n# ── Overlay rules (Phase 1.5) — anchored handle + page-number overlays ─\n# overlay_rules:\n# handle:\n# text: \"@username\"\n# anchor: \"bottom-left\" # top-left | top-right | bottom-left | bottom-right\n# margin: 24 # px from anchored edges\n# style:\n# font_family: \"Inter\"\n# font_size: 36\n# font_weight: \"bold\"\n# color: \"#FFFFFF\"\n# page_number:\n# format: \"{current}/{total}\" # supports {current:02d} / {total:02d} zero-pad\n# anchor: \"top-right\"\n# margin: 24\n# style:\n# font_family: \"Inter\"\n# font_size: 36\n# font_weight: \"normal\"\n# color: \"#FFFFFF\"\n\n# ── Phase 3 fields (reserved — Phase 1 ignores these) ──────────────────\n# caption_highlight: {}\n# transition_kit: {}\n# palette: {}\n# audio_policy: {}\n# aspect_targets: []\n`;\n}\n\n// ─── Recipe → CLI options translation ─────────────────────────────\n\n/**\n * Merge a recipe's silence_policy into TrimOptions.\n * CLI options take precedence (per studio-recipe.md §4.1).\n */\nexport function applyRecipeToTrimOptions(\n recipe: StudioRecipe | undefined,\n cliOptions: TrimOptions,\n): TrimOptions {\n const policy = recipe?.silence_policy;\n if (!policy) return cliOptions;\n\n // CLI options win — only fill in from recipe where CLI didn't specify\n return {\n ...cliOptions,\n noise: cliOptions.noise ?? policy.noise,\n minSilence: cliOptions.minSilence ?? policy.min_silence,\n padPre: cliOptions.padPre ?? policy.default_padding_pre,\n padPost: cliOptions.padPost ?? policy.default_padding_post,\n // matchTolerance is recipe-only at this layer (no CLI flag yet)\n matchTolerance: cliOptions.matchTolerance ?? policy.match_tolerance,\n };\n}\n\n/**\n * Translate a recipe's caption_style + caption_grouping into runtime\n * BuildCaptionsOptions for the caption builder.\n *\n * CLI doesn't currently expose per-invocation caption styling flags;\n * the recipe is the canonical source for now.\n */\nexport function applyRecipeToCaptionOptions(\n recipe: StudioRecipe | undefined,\n): BuildCaptionsOptions {\n if (!recipe) return {};\n const style = recipe.caption_style;\n const grouping = recipe.caption_grouping;\n\n const runtimeStyle: RuntimeCaptionStyle = {};\n if (style) {\n if (style.font_family !== undefined) runtimeStyle.fontFamily = style.font_family;\n if (style.font_size !== undefined) runtimeStyle.fontSize = style.font_size;\n if (style.font_weight !== undefined) runtimeStyle.fontWeight = style.font_weight;\n if (style.text_align !== undefined) runtimeStyle.textAlign = style.text_align;\n if (style.color !== undefined) runtimeStyle.color = style.color;\n if (style.y_ratio !== undefined) runtimeStyle.yRatio = style.y_ratio;\n if (style.width_ratio !== undefined) runtimeStyle.widthRatio = style.width_ratio;\n if (style.fade_seconds !== undefined) runtimeStyle.fadeSeconds = style.fade_seconds;\n }\n\n return {\n ...(Object.keys(runtimeStyle).length > 0 && { style: runtimeStyle }),\n ...(grouping?.max_words !== undefined && { maxWords: grouping.max_words }),\n ...(grouping?.pause_gap !== undefined && { pauseGap: grouping.pause_gap }),\n };\n}\n\n/**\n * Merge a recipe into TranscribeOptions. Caption-related recipe fields\n * are stashed on the transcribe options so the orchestrator can pass them\n * through to rewriteCaptionLayers.\n */\nexport function applyRecipeToTranscribeOptions(\n recipe: StudioRecipe | undefined,\n cliOptions: TranscribeOptions,\n): TranscribeOptions {\n if (!recipe) return cliOptions;\n const captionOptions = applyRecipeToCaptionOptions(recipe);\n return {\n ...cliOptions,\n captionOptions,\n };\n}\n\n/**\n * Render a recipe's effective values (recipe overlaid on code defaults).\n * Used by `atelier recipe show --with-defaults`.\n */\nexport function renderRecipeWithDefaults(recipe: StudioRecipe): StudioRecipe {\n const defaults: { silence_policy: SilencePolicy; caption_style: CaptionStyle; caption_grouping: CaptionGrouping } = {\n silence_policy: {\n noise: \"-30dB\",\n min_silence: 0.35,\n default_padding_pre: 0.08,\n default_padding_post: 0.12,\n match_tolerance: 0.5,\n },\n caption_style: {\n font_family: \"Inter\",\n font_size: 84,\n font_weight: \"bold\",\n text_align: \"center\",\n color: \"#FFFFFF\",\n y_ratio: 0.85,\n width_ratio: 0.9,\n fade_seconds: 0.05,\n },\n caption_grouping: {\n max_words: 5,\n pause_gap: 0.4,\n },\n };\n\n return {\n ...recipe,\n silence_policy: { ...defaults.silence_policy, ...recipe.silence_policy },\n caption_style: { ...defaults.caption_style, ...recipe.caption_style },\n caption_grouping: { ...defaults.caption_grouping, ...recipe.caption_grouping },\n };\n}\n\n/** Serialize a recipe back to YAML for `recipe show` output */\nexport function recipeToYaml(recipe: StudioRecipe): string {\n return stringifyYaml(recipe);\n}\n\n// ─── Overlay translator ──────────────────────────────────────────\n//\n// Translates recipe.overlay_rules → tag-isolated TextVisual layers\n// (handle + page_number). Mirrors rewriteCaptionLayers' tag-namespace\n// isolation invariant: only \"overlay\"-tagged layers are touched; all\n// user-authored / silence-trim / caption layers pass through untouched.\n// Re-applying is idempotent — the prior overlay set is dropped before\n// the fresh one is appended.\n\n/** Context for overlay translation — required for page_number rendering */\nexport interface ApplyOverlayContext {\n /** 1-based index in a carousel (substituted for {current}) */\n currentIndex?: number;\n /** Total carousel size (substituted for {total}) */\n totalCount?: number;\n}\n\n/** Default px from the anchored edges when a rule omits margin */\nconst DEFAULT_OVERLAY_MARGIN = 24;\n\n/** Default TextStyle baseline — overlay rule.style merges OVER these */\nconst DEFAULT_OVERLAY_TEXT_STYLE = {\n fontFamily: \"Inter\",\n fontSize: 24,\n fontWeight: 600 as const,\n color: \"#F5F5F7\",\n};\n\n/** Stable layer ids — re-apply replaces by id within the overlay drop+re-add */\nconst HANDLE_LAYER_ID = \"overlay-handle\";\nconst PAGE_NUMBER_LAYER_ID = \"overlay-page-number\";\n\n/**\n * Apply a Studio Recipe's overlay_rules to a document. Returns a NEW document;\n * input is never mutated. Drops every layer tagged \"overlay\" before appending\n * the freshly-derived overlay layers, so re-running is idempotent.\n *\n * page_number layers are only emitted when ctx.currentIndex + ctx.totalCount\n * are both present — single-frame application silently skips page_number\n * (with a one-shot console.warn) so apply-recipe pipelines stay safe outside\n * carousel batches.\n */\nexport function applyRecipeToOverlay(\n doc: AtelierDocument,\n recipe: StudioRecipe,\n ctx?: ApplyOverlayContext,\n): AtelierDocument {\n const preserved = doc.layers.filter((l) => !(l.tags ?? []).includes(\"overlay\"));\n const overlayLayers: Layer[] = [];\n\n const rules = recipe.overlay_rules;\n if (!rules) {\n return { ...doc, layers: preserved };\n }\n\n if (rules.handle) {\n overlayLayers.push(buildHandleLayer(rules.handle, doc.canvas));\n }\n\n if (rules.page_number) {\n if (ctx?.currentIndex != null && ctx?.totalCount != null) {\n overlayLayers.push(\n buildPageNumberLayer(rules.page_number, doc.canvas, ctx.currentIndex, ctx.totalCount),\n );\n } else {\n console.warn(\n `applyRecipeToOverlay: recipe.overlay_rules.page_number present but ` +\n `currentIndex/totalCount not provided — skipping page_number layer.`,\n );\n }\n }\n\n return {\n ...doc,\n layers: [...preserved, ...overlayLayers],\n };\n}\n\n/** Build the anchored handle TextVisual layer */\nfunction buildHandleLayer(\n rule: { text: string; anchor: OverlayAnchor; margin?: number; style?: OverlayTextStyle },\n canvas: AtelierDocument[\"canvas\"],\n): Layer {\n const margin = rule.margin ?? DEFAULT_OVERLAY_MARGIN;\n const { frame, anchorPoint } = anchorToFrame(rule.anchor, canvas, margin);\n return {\n id: HANDLE_LAYER_ID,\n tags: [\"overlay\"],\n visual: {\n type: \"text\",\n content: rule.text,\n style: mergeOverlayStyle(rule.style),\n },\n frame,\n bounds: { width: 600, height: 80 },\n anchorPoint,\n };\n}\n\n/** Build the anchored page-number TextVisual layer */\nfunction buildPageNumberLayer(\n rule: OverlayPageNumberRule,\n canvas: AtelierDocument[\"canvas\"],\n currentIndex: number,\n totalCount: number,\n): Layer {\n const margin = rule.margin ?? DEFAULT_OVERLAY_MARGIN;\n const { frame, anchorPoint } = anchorToFrame(rule.anchor, canvas, margin);\n return {\n id: PAGE_NUMBER_LAYER_ID,\n tags: [\"overlay\"],\n visual: {\n type: \"text\",\n content: renderPageNumberFormat(rule.format, currentIndex, totalCount),\n style: mergeOverlayStyle(rule.style),\n },\n frame,\n bounds: { width: 200, height: 80 },\n anchorPoint,\n };\n}\n\n/** Anchor enum → frame coords + 0/1 anchorPoint */\nfunction anchorToFrame(\n anchor: OverlayAnchor,\n canvas: AtelierDocument[\"canvas\"],\n margin: number,\n): { frame: { x: number; y: number }; anchorPoint: { x: number; y: number } } {\n switch (anchor) {\n case \"top-left\":\n return { frame: { x: margin, y: margin }, anchorPoint: { x: 0, y: 0 } };\n case \"top-right\":\n return { frame: { x: canvas.width - margin, y: margin }, anchorPoint: { x: 1, y: 0 } };\n case \"bottom-left\":\n return { frame: { x: margin, y: canvas.height - margin }, anchorPoint: { x: 0, y: 1 } };\n case \"bottom-right\":\n return {\n frame: { x: canvas.width - margin, y: canvas.height - margin },\n anchorPoint: { x: 1, y: 1 },\n };\n }\n}\n\n/** Merge a partial OverlayTextStyle over the runtime defaults (always returns a complete TextStyle) */\nfunction mergeOverlayStyle(\n style: OverlayTextStyle | undefined,\n): { fontFamily: string; fontSize: number; fontWeight: number | \"normal\" | \"bold\"; color: string } {\n return {\n fontFamily: style?.font_family ?? DEFAULT_OVERLAY_TEXT_STYLE.fontFamily,\n fontSize: style?.font_size ?? DEFAULT_OVERLAY_TEXT_STYLE.fontSize,\n fontWeight: style?.font_weight ?? DEFAULT_OVERLAY_TEXT_STYLE.fontWeight,\n color: style?.color ?? DEFAULT_OVERLAY_TEXT_STYLE.color,\n };\n}\n\n/**\n * Render a page_number format template by substituting {current} / {total}\n * placeholders (including zero-pad forms like {current:02d}, {total:02d}).\n *\n * The schema (^valid-recipe gate) already enforces that ≥1 placeholder is\n * present — this is render-time substitution only.\n */\nexport function renderPageNumberFormat(\n format: string,\n currentIndex: number,\n totalCount: number,\n): string {\n return format.replace(\n /\\{(current|total)(?::0(\\d+)d)?\\}/g,\n (_, name: string, padWidth?: string) => {\n const value = name === \"current\" ? currentIndex : totalCount;\n const str = String(value);\n if (padWidth) {\n const width = parseInt(padWidth, 10);\n return str.padStart(width, \"0\");\n }\n return str;\n },\n );\n}\n","import { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { Command } from \"commander\";\nimport type { AtelierDocument, ImageVisual } from \"@a-company/atelier-types\";\nimport { parseAtelier } from \"@a-company/atelier-schema\";\n\n/** Asset summary entry */\nexport interface AssetInfo {\n assetId: string;\n type: string;\n src: string;\n description?: string;\n usedByLayers: string[];\n usedByStates: string[];\n}\n\n/**\n * Extract asset info from a parsed AtelierDocument.\n */\nexport function getAssets(doc: AtelierDocument): AssetInfo[] {\n const assets = doc.assets ?? {};\n return Object.entries(assets).map(([assetId, asset]) => {\n const usedByLayers = doc.layers\n .filter(l => l.visual.type === \"image\" && (l.visual as ImageVisual).assetId === assetId)\n .map(l => l.id);\n\n const usedByStates = Object.entries(doc.states)\n .filter(([, state]) => state.audio?.src === assetId)\n .map(([name]) => name);\n\n return {\n assetId,\n type: asset.type,\n src: asset.src,\n description: asset.description,\n usedByLayers,\n usedByStates,\n };\n });\n}\n\n/**\n * Format asset info for terminal output.\n */\nfunction formatAssets(assets: AssetInfo[]): string {\n if (assets.length === 0) return \"No assets registered.\";\n\n const lines: string[] = [`Assets: ${assets.length}`];\n for (const a of assets) {\n const desc = a.description ? ` — ${a.description}` : \"\";\n lines.push(` - ${a.assetId} (${a.type}): ${a.src}${desc}`);\n if (a.usedByLayers.length > 0) {\n lines.push(` Layers: ${a.usedByLayers.join(\", \")}`);\n }\n if (a.usedByStates.length > 0) {\n lines.push(` States: ${a.usedByStates.join(\", \")}`);\n }\n }\n return lines.join(\"\\n\");\n}\n\n/** Read and parse an .atelier file, exiting on failure. */\nfunction readAndParse(file: string): AtelierDocument {\n const absPath = resolve(file);\n let content: string;\n try {\n content = readFileSync(absPath, \"utf-8\");\n } catch {\n console.error(`Cannot read file: ${absPath}`);\n return process.exit(1);\n }\n\n const result = parseAtelier(content);\n if (!result.success) {\n console.error(\"Parse errors:\");\n for (const error of result.errors) {\n console.error(` - ${error.path}: ${error.message}`);\n }\n return process.exit(1);\n }\n\n return result.data;\n}\n\n/**\n * Register the `assets` subcommand on the Commander program.\n */\nexport function assetsCommand(program: Command): void {\n program\n .command(\"assets <file>\")\n .description(\"List all assets in an .atelier file with usage info\")\n .action((file: string) => {\n const doc = readAndParse(file);\n const assets = getAssets(doc);\n console.log(formatAssets(assets));\n });\n}\n","import { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { Command } from \"commander\";\nimport type { AtelierDocument } from \"@a-company/atelier-types\";\nimport { parseAtelier } from \"@a-company/atelier-schema\";\nimport { findTemplateVariables } from \"@a-company/atelier-core\";\n\n/** Variable summary entry */\nexport interface VariableInfo {\n name: string;\n type: string;\n description?: string;\n default?: unknown;\n referenced: boolean;\n}\n\n/**\n * Extract variable info from a parsed AtelierDocument.\n */\nexport function getVariables(doc: AtelierDocument): { variables: VariableInfo[]; undeclared: string[] } {\n const variables = doc.variables ?? {};\n const referenced = findTemplateVariables(doc);\n\n const entries = Object.entries(variables).map(([name, variable]) => ({\n name,\n type: variable.type,\n description: variable.description,\n default: variable.default,\n referenced: referenced.includes(name),\n }));\n\n const undeclared = referenced.filter(r => !variables[r]);\n\n return { variables: entries, undeclared };\n}\n\n/**\n * Format variable info for terminal output.\n */\nfunction formatVariables(info: { variables: VariableInfo[]; undeclared: string[] }): string {\n if (info.variables.length === 0 && info.undeclared.length === 0) return \"No variables declared or referenced.\";\n\n const lines: string[] = [];\n\n if (info.variables.length > 0) {\n lines.push(`Variables: ${info.variables.length}`);\n for (const v of info.variables) {\n const desc = v.description ? ` — ${v.description}` : \"\";\n const def = v.default !== undefined ? ` [default: ${JSON.stringify(v.default)}]` : \"\";\n const ref = v.referenced ? \"\" : \" (unused)\";\n lines.push(` - {{${v.name}}} (${v.type})${def}${desc}${ref}`);\n }\n }\n\n if (info.undeclared.length > 0) {\n lines.push(`Undeclared references: ${info.undeclared.length}`);\n for (const name of info.undeclared) {\n lines.push(` - {{${name}}} (not declared in variables)`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\n/** Read and parse an .atelier file, exiting on failure. */\nfunction readAndParse(file: string): AtelierDocument {\n const absPath = resolve(file);\n let content: string;\n try {\n content = readFileSync(absPath, \"utf-8\");\n } catch {\n console.error(`Cannot read file: ${absPath}`);\n return process.exit(1);\n }\n\n const result = parseAtelier(content);\n if (!result.success) {\n console.error(\"Parse errors:\");\n for (const error of result.errors) {\n console.error(` - ${error.path}: ${error.message}`);\n }\n return process.exit(1);\n }\n\n return result.data;\n}\n\n/**\n * Register the `variables` subcommand on the Commander program.\n */\nexport function variablesCommand(program: Command): void {\n program\n .command(\"variables <file>\")\n .description(\"List all variables in an .atelier file with usage info\")\n .action((file: string) => {\n const doc = readAndParse(file);\n const info = getVariables(doc);\n console.log(formatVariables(info));\n });\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync } from \"node:fs\";\nimport { join, basename, extname, resolve } from \"node:path\";\nimport type { AtelierDocument, Layer } from \"@a-company/atelier-types\";\nimport type {\n CutEntry,\n VideoCutList,\n VideoTranscript,\n VideoProjectManifest,\n} from \"@a-company/atelier-types\";\n\nexport const VIDEO_PROJECT_VERSION = \"1.0\";\nexport const VIDEO_CUTLIST_VERSION = \"1.1\";\nexport const VIDEO_TRANSCRIPT_VERSION = \"1.1\";\n\n/**\n * Compute the effective in/out span for a cut, clamped to source bounds.\n * `start = max(0, rawStart - paddingPre)`, `end = min(duration, rawEnd + paddingPost)`.\n */\nexport function effectiveSpan(\n cut: CutEntry,\n duration: number,\n): { start: number; end: number } {\n return {\n start: Math.max(0, cut.rawStart - cut.paddingPre),\n end: Math.min(duration, cut.rawEnd + cut.paddingPost),\n };\n}\n\n/** Resolved absolute paths for all files in a VideoProject folder */\nexport interface VideoProject {\n /** Absolute path to the project folder */\n dir: string;\n /** Absolute path to source video file */\n sourcePath: string;\n /** Absolute path to project.atelier composition */\n compositionPath: string;\n /** Absolute path to transcript.json */\n transcriptPath: string;\n /** Absolute path to cuts.json */\n cutsPath: string;\n /** Absolute path to export/ directory */\n exportDir: string;\n /** Manifest metadata */\n manifest: VideoProjectManifest;\n}\n\n/**\n * Scaffold a new VideoProject folder from a source video file.\n *\n * Creates the folder at `destDir` (defaults to same directory as source,\n * named after the video file without extension). Copies the source video\n * into the folder as \"source<ext>\". Writes an empty draft project.atelier,\n * an empty cuts.json, and creates the export/ directory.\n *\n * Does NOT run metadata extraction — the caller (`atelier edit`, T-005)\n * probes duration/fps via ffprobe and fills in videoMeta before saving.\n */\nexport async function createVideoProject(\n srcPath: string,\n destDir?: string,\n): Promise<VideoProject> {\n const absSrc = resolve(srcPath);\n const ext = extname(absSrc);\n const stem = basename(absSrc, ext);\n\n const projectDir = destDir ? resolve(destDir) : join(resolve(absSrc, \"..\"), stem);\n\n if (!existsSync(projectDir)) {\n mkdirSync(projectDir, { recursive: true });\n }\n\n const sourceFilename = `source${ext}`;\n const sourcePath = join(projectDir, sourceFilename);\n\n // Copy source only if not already in place (idempotent re-init)\n if (!existsSync(sourcePath)) {\n copyFileSync(absSrc, sourcePath);\n }\n\n const compositionPath = join(projectDir, \"project.atelier\");\n const transcriptPath = join(projectDir, \"transcript.json\");\n const cutsPath = join(projectDir, \"cuts.json\");\n const exportDir = join(projectDir, \"export\");\n\n if (!existsSync(exportDir)) {\n mkdirSync(exportDir, { recursive: true });\n }\n\n const manifest: VideoProjectManifest = {\n source: sourceFilename,\n composition: \"project.atelier\",\n transcript: \"transcript.json\",\n cuts: \"cuts.json\",\n exportDir: \"export/\",\n createdAt: new Date().toISOString(),\n version: VIDEO_PROJECT_VERSION,\n };\n\n // Draft composition — caller fills in canvas dimensions and videoMeta after probing\n const draft: AtelierDocument = {\n version: \"1.0\",\n name: stem,\n canvas: { width: 1920, height: 1080, fps: 30 },\n assets: {\n src: {\n type: \"video\",\n src: sourceFilename,\n description: `Source: ${basename(absSrc)}`,\n },\n },\n layers: [\n {\n id: \"clip-0\",\n visual: {\n type: \"video\",\n assetId: \"src\",\n src: sourceFilename,\n startFrame: 0,\n sourceOffset: 0,\n playbackRate: 1.0,\n objectFit: \"contain\",\n },\n frame: { x: 0, y: 0 },\n bounds: { width: 1920, height: 1080 },\n },\n ],\n states: {\n default: {\n duration: 0,\n deltas: [],\n },\n },\n };\n\n if (!existsSync(compositionPath)) {\n writeFileSync(compositionPath, JSON.stringify(draft, null, 2), \"utf-8\");\n }\n\n const initialCuts: VideoCutList = {\n version: VIDEO_CUTLIST_VERSION,\n source: sourceFilename,\n cuts: [],\n };\n\n if (!existsSync(cutsPath)) {\n writeFileSync(cutsPath, JSON.stringify(initialCuts, null, 2), \"utf-8\");\n }\n\n return {\n dir: projectDir,\n sourcePath,\n compositionPath,\n transcriptPath,\n cutsPath,\n exportDir,\n manifest,\n };\n}\n\n/**\n * Load an existing VideoProject from a folder path.\n * Does not validate that the composition or cut list are well-formed —\n * callers that need that should use lintFile() after loading.\n */\nexport function loadVideoProject(dir: string): VideoProject {\n const projectDir = resolve(dir);\n\n // Detect source file (source.mp4, source.mov, etc.)\n const possibleExts = [\".mp4\", \".mov\", \".webm\", \".mkv\", \".avi\"];\n let sourceFilename = \"source.mp4\";\n for (const ext of possibleExts) {\n if (existsSync(join(projectDir, `source${ext}`))) {\n sourceFilename = `source${ext}`;\n break;\n }\n }\n\n const manifest: VideoProjectManifest = {\n source: sourceFilename,\n composition: \"project.atelier\",\n transcript: \"transcript.json\",\n cuts: \"cuts.json\",\n exportDir: \"export/\",\n createdAt: new Date().toISOString(),\n version: VIDEO_PROJECT_VERSION,\n };\n\n return {\n dir: projectDir,\n sourcePath: join(projectDir, sourceFilename),\n compositionPath: join(projectDir, \"project.atelier\"),\n transcriptPath: join(projectDir, \"transcript.json\"),\n cutsPath: join(projectDir, \"cuts.json\"),\n exportDir: join(projectDir, \"export\"),\n manifest,\n };\n}\n\n/**\n * Read and parse cuts.json from a VideoProject.\n *\n * Migrates legacy 1.0 cuts (flat { start, end }) to 1.1 parametric form\n * (rawStart/rawEnd + zero padding) on the fly. Writers always emit 1.1.\n */\nexport function readCutList(project: VideoProject): VideoCutList {\n if (!existsSync(project.cutsPath)) {\n return { version: VIDEO_CUTLIST_VERSION, source: project.manifest.source, cuts: [] };\n }\n const raw = JSON.parse(readFileSync(project.cutsPath, \"utf-8\")) as {\n version?: string;\n source: string;\n cuts: Array<CutEntry | { start: number; end: number; label?: string }>;\n };\n\n const cuts: CutEntry[] = raw.cuts.map((entry) => {\n if (\"rawStart\" in entry) return entry;\n // Legacy 1.0 migration — stored start/end become rawStart/rawEnd with zero padding.\n return {\n rawStart: entry.start,\n rawEnd: entry.end,\n paddingPre: 0,\n paddingPost: 0,\n ...(entry.label !== undefined && { label: entry.label }),\n };\n });\n\n return {\n version: VIDEO_CUTLIST_VERSION,\n source: raw.source,\n cuts,\n };\n}\n\n/** Write cuts.json to a VideoProject (always at the current cut list version) */\nexport function writeCutList(project: VideoProject, cuts: VideoCutList): void {\n const payload: VideoCutList = { ...cuts, version: VIDEO_CUTLIST_VERSION };\n writeFileSync(project.cutsPath, JSON.stringify(payload, null, 2), \"utf-8\");\n}\n\n/**\n * Read and parse transcript.json from a VideoProject.\n *\n * Migrates legacy 1.0 transcripts (TranscriptWord had flat `word: string`)\n * to 1.1 (`detected` + `text` + flags) on the fly. Writers always emit 1.1.\n */\nexport function readTranscript(project: VideoProject): VideoTranscript | null {\n if (!existsSync(project.transcriptPath)) return null;\n const raw = JSON.parse(readFileSync(project.transcriptPath, \"utf-8\")) as {\n version?: string;\n language?: string;\n segments: Array<{\n text: string;\n start: number;\n end: number;\n words: Array<\n // 1.1 shape\n | { detected: string; text: string; start: number; end: number; confidence?: number; userEdited?: boolean; userAdded?: boolean; hidden?: boolean }\n // 1.0 legacy shape\n | { word: string; start: number; end: number; confidence?: number }\n >;\n }>;\n };\n\n const segments = raw.segments.map((seg) => ({\n text: seg.text,\n start: seg.start,\n end: seg.end,\n words: seg.words.map((w) => {\n if (\"detected\" in w) return w;\n // 1.0 migration — `word` becomes both detected and text\n return {\n detected: w.word,\n text: w.word,\n start: w.start,\n end: w.end,\n ...(w.confidence !== undefined && { confidence: w.confidence }),\n };\n }),\n }));\n\n return {\n version: VIDEO_TRANSCRIPT_VERSION,\n ...(raw.language !== undefined && { language: raw.language }),\n segments,\n };\n}\n\n/** Write transcript.json to a VideoProject (always at the current transcript version) */\nexport function writeTranscript(project: VideoProject, transcript: VideoTranscript): void {\n const payload: VideoTranscript = { ...transcript, version: VIDEO_TRANSCRIPT_VERSION };\n writeFileSync(project.transcriptPath, JSON.stringify(payload, null, 2), \"utf-8\");\n}\n\n/** Read and parse project.atelier from a VideoProject */\nexport function readComposition(project: VideoProject): AtelierDocument {\n return JSON.parse(readFileSync(project.compositionPath, \"utf-8\")) as AtelierDocument;\n}\n\n/** Write project.atelier to a VideoProject */\nexport function writeComposition(project: VideoProject, doc: AtelierDocument): void {\n writeFileSync(project.compositionPath, JSON.stringify(doc, null, 2), \"utf-8\");\n}\n\n/**\n * Rewrite the silence-trim layers in a composition from a current cut list.\n *\n * Drops every layer tagged \"silence-trim\" then appends one VideoVisual layer\n * per cut, in temporal order, with cumulative startFrame computed from prior\n * clip durations. All other layers (user-authored, captions, overlays) are\n * preserved untouched — this is the tag-namespace isolation invariant.\n */\nexport function rewriteCutLayers(\n doc: AtelierDocument,\n cuts: CutEntry[],\n sourceFilename: string,\n sourceDuration: number,\n assetId = \"src\",\n): AtelierDocument {\n const preserved = doc.layers.filter((l) => !(l.tags ?? []).includes(\"silence-trim\"));\n const fps = doc.canvas.fps;\n\n let cumulativeFrame = 0;\n const trimLayers: Layer[] = cuts.map((cut, idx) => {\n const span = effectiveSpan(cut, sourceDuration);\n const sourceOffsetFrames = Math.floor(span.start * fps) / fps;\n const sourceEndFrames = Math.ceil(span.end * fps) / fps;\n const durationFrames = Math.max(1, Math.round((sourceEndFrames - sourceOffsetFrames) * fps));\n\n const layer: Layer = {\n id: `clip-trim-${idx}`,\n tags: [\"silence-trim\"],\n visual: {\n type: \"video\",\n assetId,\n src: sourceFilename,\n startFrame: cumulativeFrame,\n sourceOffset: sourceOffsetFrames,\n sourceEnd: sourceEndFrames,\n playbackRate: 1.0,\n objectFit: \"contain\",\n },\n frame: { x: 0, y: 0 },\n bounds: { width: doc.canvas.width, height: doc.canvas.height },\n };\n cumulativeFrame += durationFrames;\n return layer;\n });\n\n return { ...doc, layers: [...preserved, ...trimLayers] };\n}\n","import { spawn } from \"node:child_process\";\n\n/** A detected silence interval, in seconds from source start */\nexport interface SilenceInterval {\n start: number;\n end: number;\n}\n\n/** Options for the ffmpeg silencedetect filter */\nexport interface SilenceDetectOptions {\n /** Threshold below which audio counts as silence (default: \"-30dB\") */\n noise?: string;\n /** Minimum silence duration in seconds to register (default: 0.35) */\n minSilence?: number;\n}\n\n/**\n * Verify the local ffmpeg build has the silencedetect filter compiled in.\n * Fails loud and early if missing — some minimal builds disable filters.\n */\nexport async function probeSilencedetect(): Promise<void> {\n const stdout = await runCapture(\"ffmpeg\", [\"-hide_banner\", \"-filters\"]);\n if (!/\\bsilencedetect\\b/.test(stdout)) {\n throw new Error(\n \"Your ffmpeg build lacks the silencedetect filter. \" +\n \"Install a standard ffmpeg ≥ 4.0 (brew install ffmpeg / apt install ffmpeg).\",\n );\n }\n}\n\n/**\n * Probe source video duration via ffprobe, in seconds.\n * Used for boundary clamping of the trailing cut.\n */\nexport async function probeDuration(sourcePath: string): Promise<number> {\n const stdout = await runCapture(\"ffprobe\", [\n \"-v\", \"error\",\n \"-show_entries\", \"format=duration\",\n \"-of\", \"csv=p=0\",\n sourcePath,\n ]);\n const n = parseFloat(stdout.trim());\n if (!Number.isFinite(n) || n <= 0) {\n throw new Error(`ffprobe returned invalid duration for ${sourcePath}: \"${stdout}\"`);\n }\n return n;\n}\n\n/**\n * Run ffmpeg silencedetect on a source file, return list of silence intervals.\n *\n * silencedetect writes paired lines to stderr:\n * [silencedetect @ 0x...] silence_start: 1.234\n * [silencedetect @ 0x...] silence_end: 5.678 | silence_duration: 4.444\n *\n * Output to null muxer — no decoded frames, just the filter walking the audio.\n */\nexport async function runSilenceDetect(\n sourcePath: string,\n options: SilenceDetectOptions = {},\n): Promise<SilenceInterval[]> {\n const noise = options.noise ?? \"-30dB\";\n const minSilence = options.minSilence ?? 0.35;\n const filter = `silencedetect=noise=${noise}:d=${minSilence}`;\n\n const stderr = await runCaptureStderr(\"ffmpeg\", [\n \"-hide_banner\",\n \"-nostats\",\n \"-i\", sourcePath,\n \"-af\", filter,\n \"-f\", \"null\",\n \"-\",\n ]);\n\n return parseSilenceDetectStderr(stderr);\n}\n\n/**\n * Parse ffmpeg's silencedetect stderr output into intervals.\n * Pairs start/end lines in order; tolerates other filter chatter interleaved.\n * Exported for unit testing.\n */\nexport function parseSilenceDetectStderr(stderr: string): SilenceInterval[] {\n const intervals: SilenceInterval[] = [];\n const startRe = /silence_start:\\s*(-?[\\d.]+)/;\n const endRe = /silence_end:\\s*(-?[\\d.]+)/;\n\n let pendingStart: number | null = null;\n for (const line of stderr.split(/\\r?\\n/)) {\n const sm = line.match(startRe);\n if (sm) {\n pendingStart = parseFloat(sm[1]);\n continue;\n }\n const em = line.match(endRe);\n if (em && pendingStart !== null) {\n const end = parseFloat(em[1]);\n // silencedetect can emit start <0 if audio starts in silence — clamp\n intervals.push({ start: Math.max(0, pendingStart), end });\n pendingStart = null;\n }\n }\n return intervals;\n}\n\n// ─── subprocess plumbing ─────────────────────────────────────────\n\nfunction runCapture(cmd: string, args: string[]): Promise<string> {\n return new Promise((resolve, reject) => {\n const proc = spawn(cmd, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n let stdout = \"\";\n proc.stdout.on(\"data\", (b) => (stdout += b.toString()));\n proc.on(\"error\", (err) => {\n const e = err as NodeJS.ErrnoException;\n if (e.code === \"ENOENT\") {\n reject(new Error(`${cmd} not found on PATH. Install ffmpeg/ffprobe (brew install ffmpeg).`));\n } else {\n reject(err);\n }\n });\n proc.on(\"close\", (code) => {\n if (code !== 0) reject(new Error(`${cmd} exited ${code}`));\n else resolve(stdout);\n });\n });\n}\n\nfunction runCaptureStderr(cmd: string, args: string[]): Promise<string> {\n return new Promise((resolve, reject) => {\n const proc = spawn(cmd, args, { stdio: [\"ignore\", \"ignore\", \"pipe\"] });\n let stderr = \"\";\n proc.stderr.on(\"data\", (b) => (stderr += b.toString()));\n proc.on(\"error\", (err) => {\n const e = err as NodeJS.ErrnoException;\n if (e.code === \"ENOENT\") {\n reject(new Error(`${cmd} not found on PATH. Install ffmpeg (brew install ffmpeg).`));\n } else {\n reject(err);\n }\n });\n // null muxer always exits non-zero on EOF in some ffmpeg builds — accept any code\n proc.on(\"close\", () => resolve(stderr));\n });\n}\n","import type { CutEntry } from \"@a-company/atelier-types\";\nimport type { SilenceInterval } from \"./silence-detect.js\";\n\n/** A detected speech span (inverse of silence) — pre-padding */\nexport interface SpeechInterval {\n start: number;\n end: number;\n}\n\n/** Default leading/trailing padding around each detected speech segment */\nexport const DEFAULT_PADDING_PRE = 0.08;\nexport const DEFAULT_PADDING_POST = 0.12;\n\n/** Match tolerance (seconds) for preserving user padding on re-detect */\nexport const DEFAULT_MATCH_TOLERANCE = 0.5;\n\n/**\n * Invert silence intervals into the gaps between them — the speech intervals.\n * Handles leading and trailing silence (no speech before first silence /\n * after last silence respectively).\n */\nexport function invertToSpeechIntervals(\n silences: SilenceInterval[],\n duration: number,\n): SpeechInterval[] {\n if (silences.length === 0) {\n return duration > 0 ? [{ start: 0, end: duration }] : [];\n }\n\n const sorted = [...silences].sort((a, b) => a.start - b.start);\n const speech: SpeechInterval[] = [];\n\n // Leading speech (before first silence)\n if (sorted[0].start > 0) {\n speech.push({ start: 0, end: sorted[0].start });\n }\n\n // Speech between adjacent silences\n for (let i = 0; i < sorted.length - 1; i++) {\n const gapStart = sorted[i].end;\n const gapEnd = sorted[i + 1].start;\n if (gapEnd > gapStart) {\n speech.push({ start: gapStart, end: gapEnd });\n }\n }\n\n // Trailing speech (after last silence)\n const last = sorted[sorted.length - 1];\n if (last.end < duration) {\n speech.push({ start: last.end, end: duration });\n }\n\n return speech;\n}\n\n/** Build initial CutEntry[] from speech intervals using default padding */\nexport function buildInitialCuts(\n speech: SpeechInterval[],\n paddingPre = DEFAULT_PADDING_PRE,\n paddingPost = DEFAULT_PADDING_POST,\n): CutEntry[] {\n return speech.map((s) => ({\n rawStart: s.start,\n rawEnd: s.end,\n paddingPre,\n paddingPost,\n }));\n}\n\n/**\n * Resolve overlap between adjacent cuts (when paddingPost[i] + paddingPre[i+1]\n * exceeds the silence gap between them). Each cut gets equal share of the\n * silence — neither \"wins.\"\n *\n * Mutates cuts in place.\n */\nexport function resolveOverlaps(cuts: CutEntry[]): void {\n for (let i = 0; i < cuts.length - 1; i++) {\n const a = cuts[i];\n const b = cuts[i + 1];\n const aEnd = a.rawEnd + a.paddingPost;\n const bStart = b.rawStart - b.paddingPre;\n if (aEnd > bStart) {\n // Overlap — split at silence midpoint\n const mid = (a.rawEnd + b.rawStart) / 2;\n a.paddingPost = Math.max(0, mid - a.rawEnd);\n b.paddingPre = Math.max(0, b.rawStart - mid);\n }\n }\n}\n\n/**\n * Clamp first cut's paddingPre so effective start >= 0, and last cut's\n * paddingPost so effective end <= duration.\n *\n * Mutates cuts in place.\n */\nexport function clampBoundaries(cuts: CutEntry[], duration: number): void {\n if (cuts.length === 0) return;\n const first = cuts[0];\n if (first.rawStart - first.paddingPre < 0) {\n first.paddingPre = first.rawStart;\n }\n const last = cuts[cuts.length - 1];\n if (last.rawEnd + last.paddingPost > duration) {\n last.paddingPost = Math.max(0, duration - last.rawEnd);\n }\n}\n\n/**\n * Merge a freshly-detected cut list with an existing one, preserving user\n * padding overrides on any cut whose raw boundary still matches within\n * `tolerance` seconds (both start and end).\n *\n * For each fresh cut:\n * - if an existing cut matches: copy its padding and label, update raw values\n * - if no match: keep the fresh defaults\n *\n * Returns a new array; does not mutate inputs.\n */\nexport function mergeWithExisting(\n fresh: CutEntry[],\n existing: CutEntry[],\n tolerance = DEFAULT_MATCH_TOLERANCE,\n): CutEntry[] {\n return fresh.map((f) => {\n const match = existing.find(\n (e) =>\n Math.abs(e.rawStart - f.rawStart) < tolerance &&\n Math.abs(e.rawEnd - f.rawEnd) < tolerance,\n );\n if (!match) return f;\n return {\n rawStart: f.rawStart,\n rawEnd: f.rawEnd,\n paddingPre: match.paddingPre,\n paddingPost: match.paddingPost,\n ...(match.label !== undefined && { label: match.label }),\n };\n });\n}\n\n/**\n * Apply a global padding delta to every cut (positive = loosen, negative = tighten).\n * Floors padding at 0 — never goes negative.\n *\n * Mutates cuts in place.\n */\nexport function applyGlobalPadding(cuts: CutEntry[], deltaSeconds: number): void {\n for (const cut of cuts) {\n cut.paddingPre = Math.max(0, cut.paddingPre + deltaSeconds);\n cut.paddingPost = Math.max(0, cut.paddingPost + deltaSeconds);\n }\n}\n","import type { Command } from \"commander\";\nimport type { CutEntry } from \"@a-company/atelier-types\";\nimport {\n loadVideoProject,\n readCutList,\n writeCutList,\n readComposition,\n writeComposition,\n rewriteCutLayers,\n} from \"../lib/video-project.js\";\nimport {\n probeSilencedetect,\n probeDuration,\n runSilenceDetect,\n} from \"../lib/silence-detect.js\";\nimport {\n invertToSpeechIntervals,\n buildInitialCuts,\n resolveOverlaps,\n clampBoundaries,\n mergeWithExisting,\n applyGlobalPadding,\n DEFAULT_PADDING_PRE,\n DEFAULT_PADDING_POST,\n} from \"../lib/cut-model.js\";\nimport { loadRecipe, applyRecipeToTrimOptions } from \"../lib/recipe.js\";\n\nexport interface TrimOptions {\n /** silencedetect noise threshold, e.g. \"-30dB\" */\n noise?: string;\n /** Minimum silence duration to register, in seconds */\n minSilence?: number;\n /** Default leading padding for new cuts, in seconds */\n padPre?: number;\n /** Default trailing padding for new cuts, in seconds */\n padPost?: number;\n /** Re-detect match tolerance for preserving user padding, in seconds */\n matchTolerance?: number;\n /** Global tighten across all cuts, in milliseconds (positive number) */\n tightenMs?: number;\n /** Global loosen across all cuts, in milliseconds (positive number) */\n loosenMs?: number;\n /** Apply --pad-pre / --pad-post to one specific cut index only */\n cutIndex?: number;\n /** Discard existing padding; full fresh detect with default padding */\n reset?: boolean;\n /** Don't write files; return result */\n dryRun?: boolean;\n}\n\nexport interface TrimResult {\n projectDir: string;\n duration: number;\n cuts: CutEntry[];\n layerCount: number;\n}\n\n/**\n * Run the silence-trim pipeline on a VideoProject folder.\n *\n * Pipeline:\n * 1. Probe ffmpeg has silencedetect filter\n * 2. ffprobe source duration\n * 3. Run silencedetect → silence intervals\n * 4. Invert to speech intervals\n * 5. Build fresh CutEntry[] with default (or recipe) padding\n * 6. Merge with existing cuts (preserves user padding) unless --reset\n * 7. Apply --tighten / --loosen / --cut overrides\n * 8. Resolve overlaps at silence midpoints\n * 9. Clamp boundaries to [0, duration]\n * 10. Write cuts.json + rewrite silence-trim layers in project.atelier\n */\nexport async function trimProject(\n projectDir: string,\n options: TrimOptions = {},\n): Promise<TrimResult> {\n const project = loadVideoProject(projectDir);\n\n await probeSilencedetect();\n const duration = await probeDuration(project.sourcePath);\n\n const silences = await runSilenceDetect(project.sourcePath, {\n noise: options.noise,\n minSilence: options.minSilence,\n });\n const speech = invertToSpeechIntervals(silences, duration);\n\n const padPre = options.padPre ?? DEFAULT_PADDING_PRE;\n const padPost = options.padPost ?? DEFAULT_PADDING_POST;\n let cuts = buildInitialCuts(speech, padPre, padPost);\n\n // Re-run preservation — keep user padding on matching raw boundaries\n if (!options.reset) {\n const existing = readCutList(project);\n cuts = mergeWithExisting(cuts, existing.cuts, options.matchTolerance);\n }\n\n // Global tighten/loosen — applied to current padding (preserves overrides)\n if (typeof options.tightenMs === \"number\") {\n applyGlobalPadding(cuts, -options.tightenMs / 1000);\n }\n if (typeof options.loosenMs === \"number\") {\n applyGlobalPadding(cuts, options.loosenMs / 1000);\n }\n\n // Single-cut override\n if (typeof options.cutIndex === \"number\") {\n if (options.cutIndex < 0 || options.cutIndex >= cuts.length) {\n throw new Error(\n `--cut ${options.cutIndex} out of range (have ${cuts.length} cuts)`,\n );\n }\n if (options.padPre !== undefined) cuts[options.cutIndex].paddingPre = options.padPre;\n if (options.padPost !== undefined) cuts[options.cutIndex].paddingPost = options.padPost;\n }\n\n resolveOverlaps(cuts);\n clampBoundaries(cuts, duration);\n\n const result: TrimResult = {\n projectDir: project.dir,\n duration,\n cuts,\n layerCount: cuts.length,\n };\n\n if (options.dryRun) return result;\n\n writeCutList(project, {\n version: \"1.1\",\n source: project.manifest.source,\n cuts,\n });\n\n const doc = readComposition(project);\n const updated = rewriteCutLayers(doc, cuts, project.manifest.source, duration);\n writeComposition(project, updated);\n\n return result;\n}\n\n/** Format a TrimResult for human-readable terminal output */\nfunction formatResult(result: TrimResult): string {\n const lines: string[] = [];\n lines.push(`Trimmed ${result.projectDir}`);\n lines.push(` source duration: ${result.duration.toFixed(2)}s`);\n lines.push(` cuts: ${result.cuts.length}`);\n for (let i = 0; i < result.cuts.length; i++) {\n const c = result.cuts[i];\n const effStart = Math.max(0, c.rawStart - c.paddingPre);\n const effEnd = Math.min(result.duration, c.rawEnd + c.paddingPost);\n const dur = effEnd - effStart;\n lines.push(\n ` [${i}] ${effStart.toFixed(2)}s → ${effEnd.toFixed(2)}s ` +\n `(${dur.toFixed(2)}s, pad ${c.paddingPre.toFixed(2)}/${c.paddingPost.toFixed(2)})` +\n (c.label ? ` \"${c.label}\"` : \"\"),\n );\n }\n return lines.join(\"\\n\");\n}\n\n/** Register `atelier trim` on the Commander program */\nexport function trimCommand(program: Command): void {\n program\n .command(\"trim <project>\")\n .description(\n \"Detect silence and rewrite project.atelier with parametric cuts \" +\n \"(silence-trim tagged layers). Preserves user padding overrides on re-run.\",\n )\n .option(\"--noise <dB>\", \"Silence threshold (default: -30dB)\", \"-30dB\")\n .option(\n \"--min-silence <seconds>\",\n \"Minimum silence duration to register (default: 0.35)\",\n (v) => parseFloat(v),\n 0.35,\n )\n .option(\n \"--pad-pre <seconds>\",\n `Default leading padding (default: ${DEFAULT_PADDING_PRE})`,\n (v) => parseFloat(v),\n )\n .option(\n \"--pad-post <seconds>\",\n `Default trailing padding (default: ${DEFAULT_PADDING_POST})`,\n (v) => parseFloat(v),\n )\n .option(\"--tighten <ms>\", \"Reduce all current padding by N ms\", (v) => parseInt(v, 10))\n .option(\"--loosen <ms>\", \"Increase all current padding by N ms\", (v) => parseInt(v, 10))\n .option(\n \"--cut <index>\",\n \"Apply --pad-pre / --pad-post to one specific cut only\",\n (v) => parseInt(v, 10),\n )\n .option(\"--reset\", \"Discard existing padding; re-detect from scratch\")\n .option(\"--recipe <name>\", \"Apply a Studio Recipe's silence_policy as the baseline\")\n .option(\"--dry-run\", \"Print computed cuts; don't write files\")\n .option(\"--json\", \"Output result as JSON for piping\")\n .action(async (\n project: string,\n opts: {\n noise: string;\n minSilence: number;\n padPre?: number;\n padPost?: number;\n tighten?: number;\n loosen?: number;\n cut?: number;\n reset?: boolean;\n recipe?: string;\n dryRun?: boolean;\n json?: boolean;\n },\n ) => {\n try {\n let trimOpts: TrimOptions = {\n noise: opts.noise,\n minSilence: opts.minSilence,\n padPre: opts.padPre,\n padPost: opts.padPost,\n tightenMs: opts.tighten,\n loosenMs: opts.loosen,\n cutIndex: opts.cut,\n reset: opts.reset,\n dryRun: opts.dryRun,\n };\n if (opts.recipe) {\n const { recipe } = loadRecipe(opts.recipe, project);\n trimOpts = applyRecipeToTrimOptions(recipe, trimOpts);\n }\n const result = await trimProject(project, trimOpts);\n\n if (opts.json) {\n console.log(JSON.stringify(result, null, 2));\n } else {\n console.log(formatResult(result));\n if (opts.dryRun) console.log(\"(dry-run — no files written)\");\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`atelier trim: ${msg}`);\n process.exit(1);\n }\n });\n}\n","import { spawn } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport type { VideoTranscript } from \"@a-company/atelier-types\";\n\n/** Whisper model selection (size/quality tradeoff) */\nexport type WhisperModel =\n | \"tiny\" | \"tiny.en\"\n | \"base\" | \"base.en\"\n | \"small\" | \"small.en\"\n | \"medium\" | \"medium.en\"\n | \"large-v3\";\n\nexport interface WhisperOptions {\n /** Model size (default: \"base.en\") */\n model?: WhisperModel;\n /** BCP-47 language hint (omit for autodetect) */\n language?: string;\n /** Explicit path to the model file — overrides model lookup by name */\n modelPath?: string;\n}\n\n/** Backend that produced a transcript run */\nexport type WhisperBackend = \"whisper-cpp\" | \"openai-api\" | \"none\";\n\n/**\n * Probe which Whisper backend is available on this machine.\n * - \"whisper-cpp\" if `whisper-cli` is on PATH\n * - \"openai-api\" if OPENAI_API_KEY is set and --use-api requested\n * - \"none\" otherwise — caller throws with install guidance\n */\nexport async function probeWhisper(): Promise<WhisperBackend> {\n if (await commandExists(\"whisper-cli\")) return \"whisper-cpp\";\n if (process.env.OPENAI_API_KEY) return \"openai-api\";\n return \"none\";\n}\n\n/**\n * Run whisper.cpp on a source file and return the raw JSON stdout.\n * Caller passes to parseWhisperCppJson() to get a VideoTranscript.\n *\n * Note: whisper-cli writes JSON to stdout when given --output-json and \"-\"\n * as the output destination. Some builds always write to a file with the\n * `.json` suffix appended to the input path; we handle both.\n */\nexport async function runWhisperCpp(\n sourcePath: string,\n options: WhisperOptions = {},\n): Promise<string> {\n const model = options.modelPath ?? options.model ?? \"base.en\";\n\n const args = [\n sourcePath,\n \"--model\", model,\n \"--output-json\",\n \"--word-thold\", \"0.01\", // emit word-level timestamps\n \"--print-progress\", \"false\",\n ];\n if (options.language) {\n args.push(\"--language\", options.language);\n }\n\n return runCaptureStdout(\"whisper-cli\", args);\n}\n\n/**\n * Parse whisper.cpp --output-json output into a VideoTranscript.\n *\n * whisper.cpp emits a structure like:\n * {\n * \"result\": { \"language\": \"en\" },\n * \"transcription\": [\n * {\n * \"timestamps\": { \"from\": \"00:00:00,000\", \"to\": \"00:00:02,300\" },\n * \"offsets\": { \"from\": 0, \"to\": 2300 },\n * \"text\": \" Hello world\",\n * \"tokens\": [ ... ] // optional per-token detail\n * }\n * ]\n * }\n *\n * Token-level word timestamps come via the \"tokens\" array on each segment\n * when --output-json-full is used. With --output-json (lighter), only\n * segment-level boundaries are present and we synthesize word timing by\n * even split across the segment.\n *\n * Exported for unit testing.\n */\nexport function parseWhisperCppJson(jsonStr: string): VideoTranscript {\n const raw = JSON.parse(jsonStr) as {\n result?: { language?: string };\n transcription?: Array<{\n offsets?: { from: number; to: number };\n timestamps?: { from: string; to: string };\n text: string;\n tokens?: Array<{\n text: string;\n offsets?: { from: number; to: number };\n timestamps?: { from: string; to: string };\n p?: number; // probability/confidence\n }>;\n }>;\n };\n\n const segments = (raw.transcription ?? []).map((seg) => {\n const segStart = (seg.offsets?.from ?? 0) / 1000;\n const segEnd = (seg.offsets?.to ?? 0) / 1000;\n const segText = seg.text.trim();\n\n let words;\n if (seg.tokens && seg.tokens.length > 0) {\n // Token-level detail available — emit one word per non-special token\n words = seg.tokens\n .filter((t) => t.text.trim().length > 0 && !t.text.startsWith(\"[_\"))\n .map((t) => ({\n detected: t.text.trim(),\n text: t.text.trim(),\n start: (t.offsets?.from ?? segStart * 1000) / 1000,\n end: (t.offsets?.to ?? segEnd * 1000) / 1000,\n ...(t.p !== undefined && { confidence: t.p }),\n }));\n } else {\n // Fall back to even-split across segment\n const tokens = segText.split(/\\s+/).filter((t) => t.length > 0);\n const span = segEnd - segStart;\n const per = tokens.length > 0 ? span / tokens.length : 0;\n words = tokens.map((tok, i) => ({\n detected: tok,\n text: tok,\n start: segStart + i * per,\n end: segStart + (i + 1) * per,\n }));\n }\n\n return {\n text: segText,\n start: segStart,\n end: segEnd,\n words,\n };\n });\n\n return {\n version: \"1.1\",\n ...(raw.result?.language !== undefined && { language: raw.result.language }),\n segments,\n };\n}\n\n// ─── subprocess plumbing ─────────────────────────────────────────\n\n/** Check whether a binary is callable on PATH (cross-platform-ish via `which` / `where`) */\nexport async function commandExists(name: string): Promise<boolean> {\n // If user passed an absolute path, just check the file\n if (name.startsWith(\"/\") || name.match(/^[A-Z]:\\\\/)) {\n return existsSync(name);\n }\n return new Promise((resolve) => {\n const probe = spawn(process.platform === \"win32\" ? \"where\" : \"which\", [name], {\n stdio: [\"ignore\", \"ignore\", \"ignore\"],\n });\n probe.on(\"error\", () => resolve(false));\n probe.on(\"close\", (code) => resolve(code === 0));\n });\n}\n\nfunction runCaptureStdout(cmd: string, args: string[]): Promise<string> {\n return new Promise((resolve, reject) => {\n const proc = spawn(cmd, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n let stdout = \"\";\n let stderr = \"\";\n proc.stdout.on(\"data\", (b) => (stdout += b.toString()));\n proc.stderr.on(\"data\", (b) => (stderr += b.toString()));\n proc.on(\"error\", (err) => {\n const e = err as NodeJS.ErrnoException;\n if (e.code === \"ENOENT\") {\n reject(new Error(\n `${cmd} not found on PATH. Install whisper.cpp (brew install whisper-cpp) ` +\n `or run \\`pnpm add @xenova/transformers\\` for the ONNX fallback.`,\n ));\n } else {\n reject(err);\n }\n });\n proc.on(\"close\", (code) => {\n if (code !== 0) {\n reject(new Error(`${cmd} exited ${code}\\n${stderr}`));\n } else {\n resolve(stdout);\n }\n });\n });\n}\n","import type { TranscriptWord, TranscriptSegment, VideoTranscript } from \"@a-company/atelier-types\";\n\n/** Default match tolerance for re-transcribe merge (seconds) */\nexport const DEFAULT_TRANSCRIPT_MATCH_TOLERANCE = 0.3;\n\n/** Default phrase grouping parameters */\nexport const DEFAULT_PHRASE_MAX_WORDS = 5;\nexport const DEFAULT_PHRASE_PAUSE_GAP_SECONDS = 0.4;\n\n/** End-of-phrase punctuation marks */\nconst END_PUNCT = /[.!?,;:]\\s*$/;\n\n/**\n * Flatten a VideoTranscript's segments into a single ordered word array.\n * Used by edit commands that take a global word index.\n */\nexport function flattenWords(transcript: VideoTranscript): TranscriptWord[] {\n return transcript.segments.flatMap((s) => s.words);\n}\n\n/**\n * Phrase block — one rendered caption layer's worth of words.\n */\nexport interface CaptionPhrase {\n start: number;\n end: number;\n text: string;\n words: TranscriptWord[];\n}\n\n/**\n * Group transcript words into caption-sized phrases.\n *\n * Emits a new phrase when ANY of:\n * - currentPhrase.length >= maxWords\n * - currentWord.text ends in . ! ? , ; :\n * - gap between currentWord and nextWord > pauseGap seconds\n *\n * Hidden words are skipped (do not appear in captions but still in transcript).\n */\nexport function groupIntoPhrases(\n transcript: VideoTranscript,\n options: { maxWords?: number; pauseGap?: number } = {},\n): CaptionPhrase[] {\n const maxWords = options.maxWords ?? DEFAULT_PHRASE_MAX_WORDS;\n const pauseGap = options.pauseGap ?? DEFAULT_PHRASE_PAUSE_GAP_SECONDS;\n\n const phrases: CaptionPhrase[] = [];\n const visibleWords = flattenWords(transcript).filter((w) => !w.hidden);\n\n let current: TranscriptWord[] = [];\n for (let i = 0; i < visibleWords.length; i++) {\n const w = visibleWords[i];\n current.push(w);\n\n const next = visibleWords[i + 1];\n const gap = next ? next.start - w.end : 0;\n const endsOnPunct = END_PUNCT.test(w.text);\n const atMax = current.length >= maxWords;\n\n if (!next || atMax || endsOnPunct || gap > pauseGap) {\n phrases.push({\n start: current[0].start,\n end: current[current.length - 1].end,\n text: current.map((c) => c.text).join(\" \"),\n words: current,\n });\n current = [];\n }\n }\n\n return phrases;\n}\n\n/**\n * Merge a freshly-detected transcript with an existing one, preserving user\n * edits (userEdited / userAdded / hidden + text override) on any word whose\n * raw boundary still matches within `tolerance` seconds AND whose `detected`\n * string is unchanged.\n *\n * userAdded words in the existing transcript have no matching fresh entry by\n * definition — they're preserved as orphans, inserted into the appropriate\n * segment by timing.\n */\nexport function mergeTranscriptWithExisting(\n fresh: VideoTranscript,\n existing: VideoTranscript,\n tolerance = DEFAULT_TRANSCRIPT_MATCH_TOLERANCE,\n): VideoTranscript {\n const existingWords = flattenWords(existing);\n\n const merged: VideoTranscript = {\n version: \"1.1\",\n ...(fresh.language !== undefined && { language: fresh.language }),\n segments: fresh.segments.map((seg) => ({\n text: seg.text,\n start: seg.start,\n end: seg.end,\n words: seg.words.map((freshWord) => {\n const match = existingWords.find(\n (e) =>\n !e.userAdded &&\n Math.abs(e.start - freshWord.start) < tolerance &&\n e.detected === freshWord.detected,\n );\n if (!match) return freshWord;\n return {\n detected: freshWord.detected,\n text: match.text,\n start: freshWord.start,\n end: freshWord.end,\n ...(freshWord.confidence !== undefined && { confidence: freshWord.confidence }),\n ...(match.userEdited && { userEdited: true }),\n ...(match.hidden && { hidden: true }),\n };\n }),\n })),\n };\n\n // Re-insert user-added orphan words into their original segment by timing\n const orphans = existingWords.filter((w) => w.userAdded);\n for (const orphan of orphans) {\n const segIdx = merged.segments.findIndex(\n (s) => orphan.start >= s.start && orphan.start <= s.end,\n );\n const targetSeg = segIdx >= 0\n ? merged.segments[segIdx]\n : merged.segments[merged.segments.length - 1];\n if (!targetSeg) continue;\n const insertIdx = targetSeg.words.findIndex((w) => w.start > orphan.start);\n if (insertIdx === -1) targetSeg.words.push(orphan);\n else targetSeg.words.splice(insertIdx, 0, orphan);\n }\n\n return merged;\n}\n\n/**\n * Replace the text of word at the given global index. Sets userEdited=true.\n * Returns a new VideoTranscript; does not mutate input.\n */\nexport function applyTextEdit(\n transcript: VideoTranscript,\n wordIndex: number,\n newText: string,\n): VideoTranscript {\n return mapWord(transcript, wordIndex, (w) => ({\n ...w,\n text: newText,\n userEdited: true,\n }));\n}\n\n/**\n * Apply a batch find/replace across all detected words.\n * Sets userEdited=true on every word whose detected text matches `find`.\n */\nexport function applyBatchReplace(\n transcript: VideoTranscript,\n find: string,\n replace: string,\n): VideoTranscript {\n return {\n ...transcript,\n segments: transcript.segments.map((seg) => ({\n ...seg,\n words: seg.words.map((w) =>\n w.detected === find\n ? { ...w, text: replace, userEdited: true }\n : w,\n ),\n })),\n };\n}\n\n/** Hide a word (excluded from caption render, kept in transcript). Sets hidden=true. */\nexport function applyHide(transcript: VideoTranscript, wordIndex: number): VideoTranscript {\n return mapWord(transcript, wordIndex, (w) => ({ ...w, hidden: true }));\n}\n\n/**\n * Insert a user-added word after the given global index.\n * Sets userAdded=true. Duration defaults to 0.15s if not specified.\n * Word is placed in the segment containing the anchor word.\n */\nexport function applyAdd(\n transcript: VideoTranscript,\n afterIndex: number,\n text: string,\n duration = 0.15,\n): VideoTranscript {\n const flat = flattenWords(transcript);\n if (afterIndex < 0 || afterIndex >= flat.length) {\n throw new Error(`afterIndex ${afterIndex} out of range (have ${flat.length} words)`);\n }\n const anchor = flat[afterIndex];\n const newWord: TranscriptWord = {\n detected: text,\n text,\n start: anchor.end,\n end: anchor.end + duration,\n userAdded: true,\n };\n\n // Find which segment contains the anchor word and insert there\n let cursor = 0;\n return {\n ...transcript,\n segments: transcript.segments.map((seg) => {\n const segStart = cursor;\n cursor += seg.words.length;\n if (afterIndex < segStart || afterIndex >= cursor) return seg;\n const localIdx = afterIndex - segStart;\n return {\n ...seg,\n words: [...seg.words.slice(0, localIdx + 1), newWord, ...seg.words.slice(localIdx + 1)],\n };\n }),\n };\n}\n\n/**\n * Merge adjacent words at indices i and i+1 into a single word.\n * The merged word's detected/text becomes the concatenation; timing spans both.\n * Sets userEdited=true.\n */\nexport function applyMerge(transcript: VideoTranscript, firstIndex: number): VideoTranscript {\n const flat = flattenWords(transcript);\n if (firstIndex < 0 || firstIndex >= flat.length - 1) {\n throw new Error(`firstIndex ${firstIndex} out of range for merge`);\n }\n const a = flat[firstIndex];\n const b = flat[firstIndex + 1];\n const mergedWord: TranscriptWord = {\n detected: a.detected + b.detected,\n text: a.text + b.text,\n start: a.start,\n end: b.end,\n userEdited: true,\n };\n\n let cursor = 0;\n return {\n ...transcript,\n segments: transcript.segments.map((seg) => {\n const segStart = cursor;\n cursor += seg.words.length;\n // Both words must fall in the same segment to merge cleanly\n if (firstIndex < segStart || firstIndex >= cursor - 1) return seg;\n const localIdx = firstIndex - segStart;\n return {\n ...seg,\n words: [...seg.words.slice(0, localIdx), mergedWord, ...seg.words.slice(localIdx + 2)],\n };\n }),\n };\n}\n\n/**\n * Split one word at the given fractional point (0–1). Timing prorates.\n * Sets userEdited=true on both halves. `firstText` and `secondText` default\n * to slicing the original text at its character midpoint.\n */\nexport function applySplit(\n transcript: VideoTranscript,\n wordIndex: number,\n fraction: number,\n firstText?: string,\n secondText?: string,\n): VideoTranscript {\n if (fraction <= 0 || fraction >= 1) {\n throw new Error(`split fraction must be in (0, 1), got ${fraction}`);\n }\n const flat = flattenWords(transcript);\n if (wordIndex < 0 || wordIndex >= flat.length) {\n throw new Error(`wordIndex ${wordIndex} out of range`);\n }\n const w = flat[wordIndex];\n const splitTime = w.start + (w.end - w.start) * fraction;\n const cutChar = Math.max(1, Math.floor(w.text.length * fraction));\n const first: TranscriptWord = {\n detected: w.detected,\n text: firstText ?? w.text.slice(0, cutChar),\n start: w.start,\n end: splitTime,\n userEdited: true,\n };\n const second: TranscriptWord = {\n detected: w.detected,\n text: secondText ?? w.text.slice(cutChar),\n start: splitTime,\n end: w.end,\n userEdited: true,\n };\n\n let cursor = 0;\n return {\n ...transcript,\n segments: transcript.segments.map((seg) => {\n const segStart = cursor;\n cursor += seg.words.length;\n if (wordIndex < segStart || wordIndex >= cursor) return seg;\n const localIdx = wordIndex - segStart;\n return {\n ...seg,\n words: [...seg.words.slice(0, localIdx), first, second, ...seg.words.slice(localIdx + 1)],\n };\n }),\n };\n}\n\n// ─── helpers ─────────────────────────────────────────────────────\n\nfunction mapWord(\n transcript: VideoTranscript,\n wordIndex: number,\n fn: (w: TranscriptWord) => TranscriptWord,\n): VideoTranscript {\n const flat = flattenWords(transcript);\n if (wordIndex < 0 || wordIndex >= flat.length) {\n throw new Error(`wordIndex ${wordIndex} out of range (have ${flat.length} words)`);\n }\n let cursor = 0;\n return {\n ...transcript,\n segments: transcript.segments.map((seg) => {\n const segStart = cursor;\n cursor += seg.words.length;\n if (wordIndex < segStart || wordIndex >= cursor) return seg;\n const localIdx = wordIndex - segStart;\n return {\n ...seg,\n words: seg.words.map((w, i) => (i === localIdx ? fn(w) : w)),\n };\n }),\n };\n}\n\n// Re-export for convenience\nexport type { TranscriptSegment };\n","import type {\n AtelierDocument,\n Layer,\n Delta,\n VideoTranscript,\n} from \"@a-company/atelier-types\";\nimport { groupIntoPhrases, type CaptionPhrase } from \"./transcript-model.js\";\n\n/** Default style for generated caption layers — overridable by Studio Recipe */\nexport interface CaptionStyle {\n fontFamily?: string;\n fontSize?: number;\n fontWeight?: number | \"normal\" | \"bold\";\n textAlign?: \"left\" | \"center\" | \"right\";\n color?: string;\n /** Relative vertical position 0–1 (0 = top, 1 = bottom). Default 0.85 — lower-third. */\n yRatio?: number;\n /** Horizontal width as ratio of canvas width. Default 0.9. */\n widthRatio?: number;\n /** Fade duration in seconds for opacity in/out. Default 0.05 (3 frames at 60fps). */\n fadeSeconds?: number;\n}\n\nconst DEFAULT_STYLE: Required<CaptionStyle> = {\n fontFamily: \"Inter\",\n fontSize: 84,\n fontWeight: \"bold\" as const,\n textAlign: \"center\" as const,\n color: \"#FFFFFF\",\n yRatio: 0.85,\n widthRatio: 0.9,\n fadeSeconds: 0.05,\n};\n\nexport interface BuildCaptionsOptions {\n style?: CaptionStyle;\n /** Phrase grouping — max words per caption phrase */\n maxWords?: number;\n /** Phrase grouping — pause gap in seconds that forces a phrase break */\n pauseGap?: number;\n}\n\n/**\n * Build caption-tagged TextVisual layers from a transcript + canvas dimensions.\n *\n * Each phrase becomes one Layer with:\n * - tags: [\"caption\"]\n * - TextVisual with merged style\n * - opacity: 0 (default; deltas animate to 1 during the phrase)\n * - positioned at canvas.width * 0.5, canvas.height * yRatio\n *\n * Caller is responsible for appending these to a state's deltas array\n * (returned alongside the layers as Delta[] keyed to layer id).\n */\nexport function buildCaptionLayers(\n transcript: VideoTranscript,\n canvas: AtelierDocument[\"canvas\"],\n options: BuildCaptionsOptions = {},\n): { layers: Layer[]; deltas: Delta[] } {\n const style = { ...DEFAULT_STYLE, ...options.style };\n const phrases = groupIntoPhrases(transcript, {\n maxWords: options.maxWords,\n pauseGap: options.pauseGap,\n });\n\n const layers: Layer[] = [];\n const deltas: Delta[] = [];\n const fps = canvas.fps;\n\n phrases.forEach((phrase, idx) => {\n const layerId = `caption-${idx}`;\n layers.push(buildPhraseLayer(layerId, phrase, canvas, style));\n deltas.push(...buildPhraseDeltas(layerId, phrase, style.fadeSeconds, fps));\n });\n\n return { layers, deltas };\n}\n\n/**\n * Drop caption-tagged layers + their deltas from a composition,\n * append fresh ones from the current transcript.\n *\n * Mirrors rewriteCutLayers' invariant: only caption-tagged layers touched;\n * all other layers (silence-trim, overlay, user-authored) preserved.\n *\n * Deltas are written into the default state (or first state if no \"default\").\n */\nexport function rewriteCaptionLayers(\n doc: AtelierDocument,\n transcript: VideoTranscript,\n options: BuildCaptionsOptions = {},\n): AtelierDocument {\n const preserved = doc.layers.filter((l) => !(l.tags ?? []).includes(\"caption\"));\n const { layers: captionLayers, deltas: captionDeltas } = buildCaptionLayers(\n transcript,\n doc.canvas,\n options,\n );\n const captionLayerIds = new Set(captionLayers.map((l) => l.id));\n\n // Pick the target state for caption deltas: prefer \"default\", else first\n const stateNames = Object.keys(doc.states);\n const targetStateName = stateNames.includes(\"default\") ? \"default\" : stateNames[0];\n const states = { ...doc.states };\n\n if (targetStateName && states[targetStateName]) {\n const existing = states[targetStateName];\n // Drop deltas on caption layers we're rewriting; keep all others\n const preservedDeltas = existing.deltas.filter(\n (d) => !captionLayerIds.has(d.layer),\n );\n states[targetStateName] = {\n ...existing,\n deltas: [...preservedDeltas, ...captionDeltas],\n };\n }\n\n return {\n ...doc,\n layers: [...preserved, ...captionLayers],\n states,\n };\n}\n\n// ─── internals ───────────────────────────────────────────────────\n\nfunction buildPhraseLayer(\n id: string,\n phrase: CaptionPhrase,\n canvas: AtelierDocument[\"canvas\"],\n style: Required<CaptionStyle>,\n): Layer {\n return {\n id,\n tags: [\"caption\"],\n visual: {\n type: \"text\",\n content: phrase.text,\n style: {\n fontFamily: style.fontFamily,\n fontSize: style.fontSize,\n fontWeight: style.fontWeight,\n textAlign: style.textAlign,\n color: style.color,\n },\n },\n frame: {\n x: canvas.width / 2,\n y: canvas.height * style.yRatio,\n },\n bounds: {\n width: canvas.width * style.widthRatio,\n height: Math.max(120, style.fontSize * 1.6),\n },\n anchorPoint: { x: 0.5, y: 0.5 },\n opacity: 0,\n };\n}\n\nfunction buildPhraseDeltas(\n layerId: string,\n phrase: CaptionPhrase,\n fadeSeconds: number,\n fps: number,\n): Delta[] {\n const fadeFrames = Math.max(1, Math.round(fadeSeconds * fps));\n const startFrame = Math.floor(phrase.start * fps);\n const endFrame = Math.ceil(phrase.end * fps);\n const fadeOutStart = Math.max(startFrame + 1, endFrame - fadeFrames);\n\n return [\n // Fade in to visible at phrase start\n {\n layer: layerId,\n property: \"opacity\",\n range: [Math.max(0, startFrame - fadeFrames), startFrame],\n from: 0,\n to: 1,\n easing: \"ease-out\",\n },\n // Hold visible through the phrase\n // (no explicit delta needed — value persists between deltas)\n // Fade out at phrase end\n {\n layer: layerId,\n property: \"opacity\",\n range: [fadeOutStart, endFrame],\n from: 1,\n to: 0,\n easing: \"ease-in\",\n },\n ];\n}\n","import type { Command } from \"commander\";\nimport type { VideoTranscript } from \"@a-company/atelier-types\";\nimport {\n loadVideoProject,\n readTranscript,\n writeTranscript,\n readComposition,\n writeComposition,\n} from \"../lib/video-project.js\";\nimport {\n probeWhisper,\n runWhisperCpp,\n parseWhisperCppJson,\n type WhisperModel,\n} from \"../lib/whisper.js\";\nimport { mergeTranscriptWithExisting } from \"../lib/transcript-model.js\";\nimport { rewriteCaptionLayers, type BuildCaptionsOptions } from \"../lib/caption-builder.js\";\nimport { loadRecipe, applyRecipeToTranscribeOptions } from \"../lib/recipe.js\";\n\nexport interface TranscribeOptions {\n /** Whisper model selection */\n model?: WhisperModel;\n /** BCP-47 language hint (omit for autodetect) */\n language?: string;\n /** Discard existing user edits; full fresh transcript */\n reset?: boolean;\n /** Skip caption layer generation; transcript.json only */\n noCaptions?: boolean;\n /** Don't write files; return result */\n dryRun?: boolean;\n /** Caption style + grouping overrides, typically supplied by a Studio Recipe */\n captionOptions?: BuildCaptionsOptions;\n}\n\nexport interface TranscribeResult {\n projectDir: string;\n backend: string;\n transcript: VideoTranscript;\n wordCount: number;\n captionsGenerated: boolean;\n}\n\n/**\n * Run the transcription pipeline on a VideoProject folder.\n *\n * Pipeline:\n * 1. Probe Whisper backend (whisper-cpp / openai-api / none)\n * 2. Run Whisper → raw transcript JSON\n * 3. Parse into VideoTranscript shape\n * 4. Merge with existing transcript (preserves user edits) unless --reset\n * 5. Write transcript.json\n * 6. Unless --no-captions: rewriteCaptionLayers in project.atelier\n */\nexport async function transcribeProject(\n projectDir: string,\n options: TranscribeOptions = {},\n): Promise<TranscribeResult> {\n const project = loadVideoProject(projectDir);\n\n const backend = await probeWhisper();\n if (backend === \"none\") {\n throw new Error(\n \"No Whisper backend available. Install whisper.cpp (brew install whisper-cpp) \" +\n \"or set OPENAI_API_KEY and pass --use-api.\",\n );\n }\n if (backend === \"openai-api\") {\n // Reserved for the --use-api opt-in path; not implemented in this milestone\n throw new Error(\n \"OpenAI API backend is not yet implemented. Install whisper.cpp for local transcription.\",\n );\n }\n\n const rawJson = await runWhisperCpp(project.sourcePath, {\n model: options.model,\n language: options.language,\n });\n let transcript = parseWhisperCppJson(rawJson);\n\n // Re-run preservation — keep user edits on matching detected words\n if (!options.reset) {\n const existing = readTranscript(project);\n if (existing) {\n transcript = mergeTranscriptWithExisting(transcript, existing);\n }\n }\n\n const wordCount = transcript.segments.reduce((n, s) => n + s.words.length, 0);\n\n const result: TranscribeResult = {\n projectDir: project.dir,\n backend,\n transcript,\n wordCount,\n captionsGenerated: false,\n };\n\n if (options.dryRun) return result;\n\n writeTranscript(project, transcript);\n\n if (!options.noCaptions) {\n const doc = readComposition(project);\n const updated = rewriteCaptionLayers(doc, transcript, options.captionOptions);\n writeComposition(project, updated);\n result.captionsGenerated = true;\n }\n\n return result;\n}\n\nfunction formatResult(result: TranscribeResult): string {\n const lines: string[] = [];\n lines.push(`Transcribed ${result.projectDir} via ${result.backend}`);\n if (result.transcript.language) {\n lines.push(` language: ${result.transcript.language}`);\n }\n lines.push(` segments: ${result.transcript.segments.length}`);\n lines.push(` words: ${result.wordCount}`);\n if (result.captionsGenerated) {\n lines.push(` captions: written to project.atelier`);\n } else {\n lines.push(` captions: skipped`);\n }\n return lines.join(\"\\n\");\n}\n\n/** Register `atelier transcribe` on the Commander program */\nexport function transcribeCommand(program: Command): void {\n program\n .command(\"transcribe <project>\")\n .description(\n \"Transcribe source video via Whisper, write transcript.json, and \" +\n \"rewrite caption-tagged TextVisual layers in project.atelier. \" +\n \"Preserves user transcript edits on re-run.\",\n )\n .option(\"--model <name>\", \"Whisper model: tiny|base|small|medium|large-v3\", \"base.en\")\n .option(\"--language <code>\", \"BCP-47 language hint (omit for autodetect)\")\n .option(\"--reset\", \"Discard existing user edits; full fresh transcript\")\n .option(\"--no-captions\", \"Write transcript.json only; skip caption layer generation\")\n .option(\"--recipe <name>\", \"Apply a Studio Recipe's caption_style + caption_grouping\")\n .option(\"--dry-run\", \"Print transcript; don't write files\")\n .option(\"--json\", \"Output result as JSON for piping\")\n .action(async (\n project: string,\n opts: {\n model: WhisperModel;\n language?: string;\n reset?: boolean;\n captions: boolean;\n recipe?: string;\n dryRun?: boolean;\n json?: boolean;\n },\n ) => {\n try {\n let transcribeOpts: TranscribeOptions = {\n model: opts.model,\n language: opts.language,\n reset: opts.reset,\n noCaptions: !opts.captions,\n dryRun: opts.dryRun,\n };\n if (opts.recipe) {\n const { recipe } = loadRecipe(opts.recipe, project);\n transcribeOpts = applyRecipeToTranscribeOptions(recipe, transcribeOpts);\n }\n const result = await transcribeProject(project, transcribeOpts);\n if (opts.json) {\n console.log(JSON.stringify(result, null, 2));\n } else {\n console.log(formatResult(result));\n if (opts.dryRun) console.log(\"(dry-run — no files written)\");\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`atelier transcribe: ${msg}`);\n process.exit(1);\n }\n });\n}\n","import type { Command } from \"commander\";\nimport type { VideoTranscript } from \"@a-company/atelier-types\";\nimport {\n loadVideoProject,\n readTranscript,\n writeTranscript,\n readComposition,\n writeComposition,\n} from \"../lib/video-project.js\";\nimport {\n applyTextEdit,\n applyBatchReplace,\n applyHide,\n applyAdd,\n applyMerge,\n applySplit,\n flattenWords,\n} from \"../lib/transcript-model.js\";\nimport { rewriteCaptionLayers } from \"../lib/caption-builder.js\";\n\n/** Read the project's transcript; throws if none exists */\nfunction loadOrThrow(projectDir: string): { project: ReturnType<typeof loadVideoProject>; transcript: VideoTranscript } {\n const project = loadVideoProject(projectDir);\n const transcript = readTranscript(project);\n if (!transcript) {\n throw new Error(\n `No transcript.json in ${projectDir}. Run \\`atelier transcribe ${projectDir}\\` first.`,\n );\n }\n return { project, transcript };\n}\n\n/** Save the transcript and optionally regenerate caption layers */\nfunction save(\n project: ReturnType<typeof loadVideoProject>,\n transcript: VideoTranscript,\n noRegenerate: boolean,\n): void {\n writeTranscript(project, transcript);\n if (!noRegenerate) {\n const doc = readComposition(project);\n const updated = rewriteCaptionLayers(doc, transcript);\n writeComposition(project, updated);\n }\n}\n\n/** Register the `atelier transcript` family of edit subcommands */\nexport function transcriptCommand(program: Command): void {\n const transcript = program\n .command(\"transcript\")\n .description(\"Edit transcript.json — fix, add, delete, merge, split, list\");\n\n transcript\n .command(\"fix <project>\")\n .description(\"Apply text correction(s). Use --replace 'wrong=right' for batch, or --word <idx> --text '<correction>' for single edit.\")\n .option(\"--replace <pair...>\", \"Batch find/replace, format: 'detected=replacement' (repeatable)\")\n .option(\"--word <index>\", \"Single-word edit by index\", (v) => parseInt(v, 10))\n .option(\"--text <text>\", \"Correction text (paired with --word)\")\n .option(\"--no-regenerate\", \"Skip caption layer regeneration after edit\")\n .action(async (\n projectDir: string,\n opts: { replace?: string[]; word?: number; text?: string; regenerate: boolean },\n ) => {\n try {\n const { project, transcript } = loadOrThrow(projectDir);\n let updated = transcript;\n\n if (opts.replace?.length) {\n for (const pair of opts.replace) {\n const [find, repl] = pair.split(\"=\");\n if (!find || repl === undefined) {\n throw new Error(`Invalid --replace pair: \"${pair}\". Use 'detected=replacement'.`);\n }\n updated = applyBatchReplace(updated, find, repl);\n }\n }\n\n if (typeof opts.word === \"number\") {\n if (opts.text === undefined) {\n throw new Error(\"--word requires --text <correction>\");\n }\n updated = applyTextEdit(updated, opts.word, opts.text);\n }\n\n if (!opts.replace?.length && opts.word === undefined) {\n throw new Error(\"Provide --replace 'wrong=right' or --word <idx> --text '...'\");\n }\n\n save(project, updated, !opts.regenerate);\n console.log(`Updated transcript in ${project.dir}`);\n } catch (err) {\n console.error(`atelier transcript fix: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\n transcript\n .command(\"add <project>\")\n .description(\"Insert a user-added word after the anchor word.\")\n .requiredOption(\"--after-word <index>\", \"Anchor word index\", (v) => parseInt(v, 10))\n .requiredOption(\"--text <text>\", \"Text of the new word\")\n .option(\"--duration <seconds>\", \"Word duration in seconds (default: 0.15)\", (v) => parseFloat(v), 0.15)\n .option(\"--no-regenerate\", \"Skip caption layer regeneration\")\n .action(async (\n projectDir: string,\n opts: { afterWord: number; text: string; duration: number; regenerate: boolean },\n ) => {\n try {\n const { project, transcript } = loadOrThrow(projectDir);\n const updated = applyAdd(transcript, opts.afterWord, opts.text, opts.duration);\n save(project, updated, !opts.regenerate);\n console.log(`Inserted \"${opts.text}\" after word ${opts.afterWord} in ${project.dir}`);\n } catch (err) {\n console.error(`atelier transcript add: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\n transcript\n .command(\"delete <project>\")\n .description(\"Hide a word (excluded from captions, kept in transcript).\")\n .requiredOption(\"--word <index>\", \"Word index\", (v) => parseInt(v, 10))\n .option(\"--no-regenerate\", \"Skip caption layer regeneration\")\n .action(async (\n projectDir: string,\n opts: { word: number; regenerate: boolean },\n ) => {\n try {\n const { project, transcript } = loadOrThrow(projectDir);\n const updated = applyHide(transcript, opts.word);\n save(project, updated, !opts.regenerate);\n console.log(`Hidden word ${opts.word} in ${project.dir}`);\n } catch (err) {\n console.error(`atelier transcript delete: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\n transcript\n .command(\"merge <project>\")\n .description(\"Merge two adjacent words at indices i and i+1.\")\n .requiredOption(\"--word <index>\", \"First of the two words to merge\", (v) => parseInt(v, 10))\n .option(\"--no-regenerate\", \"Skip caption layer regeneration\")\n .action(async (\n projectDir: string,\n opts: { word: number; regenerate: boolean },\n ) => {\n try {\n const { project, transcript } = loadOrThrow(projectDir);\n const updated = applyMerge(transcript, opts.word);\n save(project, updated, !opts.regenerate);\n console.log(`Merged words ${opts.word} and ${opts.word + 1} in ${project.dir}`);\n } catch (err) {\n console.error(`atelier transcript merge: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\n transcript\n .command(\"split <project>\")\n .description(\"Split one word at a fractional point.\")\n .requiredOption(\"--word <index>\", \"Word to split\", (v) => parseInt(v, 10))\n .requiredOption(\"--at <fraction>\", \"Split point (0–1)\", (v) => parseFloat(v))\n .option(\"--first <text>\", \"Override text for first half\")\n .option(\"--second <text>\", \"Override text for second half\")\n .option(\"--no-regenerate\", \"Skip caption layer regeneration\")\n .action(async (\n projectDir: string,\n opts: { word: number; at: number; first?: string; second?: string; regenerate: boolean },\n ) => {\n try {\n const { project, transcript } = loadOrThrow(projectDir);\n const updated = applySplit(transcript, opts.word, opts.at, opts.first, opts.second);\n save(project, updated, !opts.regenerate);\n console.log(`Split word ${opts.word} at ${opts.at} in ${project.dir}`);\n } catch (err) {\n console.error(`atelier transcript split: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\n transcript\n .command(\"list <project>\")\n .description(\"Print all words with their indices for reference.\")\n .option(\"--json\", \"Output as JSON\")\n .action(async (projectDir: string, opts: { json?: boolean }) => {\n try {\n const { transcript } = loadOrThrow(projectDir);\n const words = flattenWords(transcript);\n if (opts.json) {\n console.log(JSON.stringify(words.map((w, i) => ({ index: i, ...w })), null, 2));\n } else {\n for (let i = 0; i < words.length; i++) {\n const w = words[i];\n const flags: string[] = [];\n if (w.userEdited) flags.push(\"edited\");\n if (w.userAdded) flags.push(\"added\");\n if (w.hidden) flags.push(\"hidden\");\n const flagStr = flags.length ? ` [${flags.join(\", \")}]` : \"\";\n const editedDisplay = w.text !== w.detected ? ` ← \"${w.detected}\"` : \"\";\n console.log(\n ` [${i.toString().padStart(4)}] ${w.start.toFixed(2).padStart(7)}s ` +\n `\"${w.text}\"${editedDisplay}${flagStr}`,\n );\n }\n }\n } catch (err) {\n console.error(`atelier transcript list: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n}\n","import type { Command } from \"commander\";\nimport {\n loadVideoProject,\n readTranscript,\n readComposition,\n writeComposition,\n} from \"../lib/video-project.js\";\nimport { rewriteCaptionLayers } from \"../lib/caption-builder.js\";\nimport { loadRecipe, applyRecipeToCaptionOptions } from \"../lib/recipe.js\";\n\n/** Register `atelier captions regenerate <project>` */\nexport function captionsCommand(program: Command): void {\n const captions = program.command(\"captions\").description(\"Manage caption layers in a VideoProject\");\n\n captions\n .command(\"regenerate <project>\")\n .description(\n \"Re-derive caption-tagged TextVisual layers from the current transcript.json. \" +\n \"Used when caption styling changes without re-running Whisper.\",\n )\n .option(\"--recipe <name>\", \"Apply a Studio Recipe's caption_style + caption_grouping\")\n .action(async (projectDir: string, opts: { recipe?: string }) => {\n try {\n const project = loadVideoProject(projectDir);\n const transcript = readTranscript(project);\n if (!transcript) {\n throw new Error(\n `No transcript.json in ${projectDir}. Run \\`atelier transcribe ${projectDir}\\` first.`,\n );\n }\n const captionOptions = opts.recipe\n ? applyRecipeToCaptionOptions(loadRecipe(opts.recipe, projectDir).recipe)\n : {};\n const doc = readComposition(project);\n const updated = rewriteCaptionLayers(doc, transcript, captionOptions);\n writeComposition(project, updated);\n const captionLayers = updated.layers.filter((l) =>\n (l.tags ?? []).includes(\"caption\"),\n );\n console.log(\n `Regenerated ${captionLayers.length} caption layer${captionLayers.length === 1 ? \"\" : \"s\"} in ${project.dir}`,\n );\n } catch (err) {\n console.error(`atelier captions regenerate: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n}\n","import { writeFileSync, existsSync, mkdirSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport type { Command } from \"commander\";\nimport {\n loadRecipe,\n scaffoldRecipeYaml,\n renderRecipeWithDefaults,\n recipeToYaml,\n} from \"../lib/recipe.js\";\n\n/** Register `atelier recipe new/validate/show` family */\nexport function recipeCommand(program: Command): void {\n const recipe = program.command(\"recipe\").description(\"Manage Studio Recipes — reusable style presets\");\n\n recipe\n .command(\"new <name>\")\n .description(\"Scaffold a starter recipe YAML with all current defaults filled in\")\n .option(\"--dir <path>\", \"Where to write the recipe (default: ./.atelier/recipes/)\")\n .action((name: string, opts: { dir?: string }) => {\n try {\n const baseDir = opts.dir ?? join(resolve(process.cwd()), \".atelier\", \"recipes\");\n if (!existsSync(baseDir)) {\n mkdirSync(baseDir, { recursive: true });\n }\n // Footgun guard: if the name already carries a recognized extension,\n // treat it as a filename so `recipe new foo.recipe.yaml` doesn't\n // produce `foo.recipe.yaml.recipe.yaml`. Otherwise append the canonical\n // extension. The bare-name stem (without ext) is what `recipe validate\n // <name>` resolves, so they stay in agreement.\n const hasKnownExt = /\\.(recipe\\.yaml|recipe\\.json|yaml|yml|json)$/i.test(name);\n const fileName = hasKnownExt ? name : `${name}.recipe.yaml`;\n const outPath = join(baseDir, fileName);\n if (existsSync(outPath)) {\n throw new Error(`Recipe already exists at ${outPath} — refusing to overwrite.`);\n }\n const recipeName = name.replace(/\\.(recipe\\.yaml|recipe\\.json|yaml|yml|json)$/i, \"\");\n const yaml = scaffoldRecipeYaml(recipeName);\n writeFileSync(outPath, yaml, \"utf-8\");\n console.log(`Created ${outPath}`);\n } catch (err) {\n console.error(`atelier recipe new: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\n recipe\n .command(\"validate <path>\")\n .description(\"Validate a recipe against the schema (^valid-recipe gate). Warns on reserved Phase 3 fields.\")\n .option(\"--json\", \"Output result as JSON\")\n .action((path: string, opts: { json?: boolean }) => {\n try {\n // Resolve a bare name against the project-local recipes dir first\n // (where `recipe new` writes), then the user library — so new/validate\n // agree on where a bare name lives.\n const loaded = loadRecipe(path, process.cwd());\n if (opts.json) {\n console.log(JSON.stringify({\n valid: true,\n path: loaded.path,\n warnings: loaded.warnings,\n }, null, 2));\n } else {\n console.log(`PASS ${loaded.path}`);\n for (const w of loaded.warnings) {\n console.log(` ⚠ ${w}`);\n }\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (opts.json) {\n console.log(JSON.stringify({ valid: false, error: msg }, null, 2));\n } else {\n console.error(`FAIL ${path}`);\n console.error(` ${msg}`);\n }\n process.exit(1);\n }\n });\n\n recipe\n .command(\"show <path>\")\n .description(\"Print a recipe; --with-defaults fills in every omitted field with its code default.\")\n .option(\"--with-defaults\", \"Overlay code defaults onto omitted fields\")\n .action((path: string, opts: { withDefaults?: boolean }) => {\n try {\n // Same project-local-first resolution as `recipe validate`.\n const loaded = loadRecipe(path, process.cwd());\n const out = opts.withDefaults ? renderRecipeWithDefaults(loaded.recipe) : loaded.recipe;\n console.log(recipeToYaml(out));\n for (const w of loaded.warnings) {\n console.error(`# ⚠ ${w}`);\n }\n } catch (err) {\n console.error(`atelier recipe show: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n}\n","import type { Command } from \"commander\";\nimport { trimProject } from \"./trim.js\";\nimport { transcribeProject } from \"./transcribe.js\";\nimport {\n loadRecipe,\n applyRecipeToTrimOptions,\n applyRecipeToTranscribeOptions,\n applyRecipeToOverlay,\n} from \"../lib/recipe.js\";\nimport {\n loadVideoProject,\n readComposition,\n writeComposition,\n} from \"../lib/video-project.js\";\n\n/**\n * Register `atelier apply-recipe <project> <recipe>` — convenience verb that\n * runs `atelier trim` and `atelier transcribe` against the same recipe in\n * one shot. Useful for fresh-project bootstrap.\n */\nexport function applyRecipeCommand(program: Command): void {\n program\n .command(\"apply-recipe <project> <recipe>\")\n .description(\n \"Apply a Studio Recipe by running atelier trim + atelier transcribe \" +\n \"with the same recipe, in that order.\",\n )\n .option(\"--reset\", \"Apply --reset to both pipelines (destructive — discards existing user padding and transcript edits)\")\n .option(\"--no-trim\", \"Skip the atelier trim step\")\n .option(\"--no-transcribe\", \"Skip the atelier transcribe step\")\n .action(async (\n project: string,\n recipeRef: string,\n opts: { reset?: boolean; trim: boolean; transcribe: boolean },\n ) => {\n try {\n const { recipe, path, warnings } = loadRecipe(recipeRef, project);\n console.log(`Loaded recipe ${path}`);\n for (const w of warnings) console.log(` ⚠ ${w}`);\n\n if (opts.trim) {\n const trimOpts = applyRecipeToTrimOptions(recipe, { reset: opts.reset });\n console.log(`\\nRunning atelier trim...`);\n const r = await trimProject(project, trimOpts);\n console.log(` ${r.cuts.length} cut${r.cuts.length === 1 ? \"\" : \"s\"} written`);\n }\n\n if (opts.transcribe) {\n const transcribeOpts = applyRecipeToTranscribeOptions(recipe, { reset: opts.reset });\n console.log(`\\nRunning atelier transcribe...`);\n const r = await transcribeProject(project, transcribeOpts);\n console.log(` ${r.wordCount} words, ${r.captionsGenerated ? \"captions written\" : \"captions skipped\"}`);\n }\n\n // Overlay translator — rewrites \"overlay\"-tagged layers in project.atelier\n // from recipe.overlay_rules. Single-frame apply: no carousel ctx, so\n // page_number is silently skipped (handle is always applied if present).\n if (recipe.overlay_rules) {\n const vp = loadVideoProject(project);\n const doc = readComposition(vp);\n const updated = applyRecipeToOverlay(doc, recipe);\n writeComposition(vp, updated);\n console.log(`\\nApplied overlay rules to ${vp.compositionPath}`);\n }\n\n console.log(`\\nDone.`);\n } catch (err) {\n console.error(`atelier apply-recipe: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n}\n"],"mappings":"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","names":["init_dist","import_zod","z","yamlParse","init_dist","import_node_fs","import_node_path","import_node_fs","import_node_path","init_dist","readAndParse","createCanvas","renderFrame","import_node_fs","import_node_path","init_dist","resolve","loadImage","createCanvas","readAndParse","import_node_fs","import_node_path","init_dist","colorToCSS","readAndParse","import_node_fs","import_node_path","r","g","b","a","isExpression","kfs","buildTransform","readAndParse","import_node_fs","import_node_path","init_dist","preloadImages","loadImage","ImageCache","resolve","createCanvas","renderFrame","readAndParse","import_node_fs","import_node_path","import_node_fs","import_node_path","import_yaml","parseYaml","stringifyYaml","parseDim","import_node_fs","import_node_path","readAndParse","import_node_fs","import_node_path","init_dist","readAndParse","import_node_fs","import_node_path","import_node_child_process","resolve","import_node_child_process","import_node_fs","resolve","formatResult","transcript","import_node_fs","import_node_path"]}
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","../src/index.ts","../src/commands/validate.ts","../../schema/src/units.ts","../../schema/src/coordinates.ts","../../schema/src/color.ts","../../schema/src/shape.ts","../../schema/src/easing.ts","../../schema/src/shadow.ts","../../schema/src/layer.ts","../../schema/src/interaction.ts","../../schema/src/delta.ts","../../schema/src/state.ts","../../schema/src/preset.ts","../../schema/src/variable.ts","../../schema/src/asset.ts","../../schema/src/document.ts","../../schema/src/timeline.ts","../../schema/src/slides.ts","../../schema/src/design.ts","../../schema/src/recipe.ts","../../schema/src/validate.ts","../../schema/src/parse.ts","../../schema/src/artifacts/slot.ts","../../schema/src/artifacts/technique.ts","../../schema/src/artifacts/design.ts","../../schema/src/artifacts/script.ts","../../schema/src/artifacts/storyboard.ts","../../schema/src/artifacts/parse.ts","../../schema/src/artifacts/validate.ts","../../schema/src/workspace.ts","../../schema/src/media-notes.ts","../src/commands/info.ts","../src/commands/still.ts","../src/commands/render.ts","../src/commands/render-pipeline.ts","../src/commands/export-svg.ts","../../svg/src/render-svg.ts","../../svg/src/svg-properties.ts","../../svg/src/svg-gradients.ts","../../svg/src/svg-shapes.ts","../../svg/src/svg-text.ts","../../svg/src/svg-filters.ts","../../svg/src/svg-clip.ts","../src/commands/export-lottie.ts","../../lottie/src/map-colors.ts","../../lottie/src/map-shapes.ts","../../lottie/src/map-easing.ts","../../lottie/src/map-keyframes.ts","../../lottie/src/map-layers.ts","../../lottie/src/warnings.ts","../../lottie/src/export-lottie.ts","../src/commands/export-image.ts","../src/lib/render-image.ts","../src/commands/carousel.ts","../src/lib/recipe.ts","../src/commands/assets.ts","../src/commands/variables.ts","../src/lib/video-project.ts","../src/lib/silence-detect.ts","../src/lib/cut-model.ts","../src/commands/trim.ts","../src/lib/whisper.ts","../src/lib/transcript-model.ts","../src/lib/caption-builder.ts","../src/commands/transcribe.ts","../src/commands/transcript.ts","../src/commands/captions.ts","../src/commands/recipe.ts","../src/commands/apply-recipe.ts","../src/commands/init.ts","../src/commands/artifacts.ts","../src/lib/artifact-project.ts","../src/lib/artifact-templates.ts","../src/lib/learning-mode.ts","../src/lib/workspace.ts","../src/lib/paradigm-augment.ts","../src/lib/compose-video-project.ts","../src/lib/transcribe-orchestrator.ts","../src/lib/timeline-ops.ts","../src/lib/compose-image-project.ts","../src/lib/compose-carousel-project.ts","../src/lib/ref-cycle.ts","../src/lib/ingest-dispatch.ts","../src/lib/doc-management.ts","../src/lib/doc-management-types.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","// @a-company/atelier-cli — CLI tool: validate, preview, render, info commands\n\n// Command registration functions (for programmatic use)\nexport { validateCommand, validateFile } from \"./commands/validate.js\";\nexport { infoCommand, getInfo } from \"./commands/info.js\";\nexport type { DocumentInfo } from \"./commands/info.js\";\nexport { stillCommand, resolveStill } from \"./commands/still.js\";\nexport { renderCommand } from \"./commands/render.js\";\nexport { exportSvgCommand } from \"./commands/export-svg.js\";\nexport { exportLottieCommand } from \"./commands/export-lottie.js\";\nexport { exportImageCommand, resolveExportDimensions } from \"./commands/export-image.js\";\nexport {\n carouselCommand,\n composeCarouselFrameDoc,\n expandInputs,\n carouselFileName,\n} from \"./commands/carousel.js\";\nexport {\n renderDocumentToPng,\n fitImageToCanvas,\n loadCanvasModule,\n CanvasUnavailableError,\n} from \"./lib/render-image.js\";\nexport { assetsCommand, getAssets } from \"./commands/assets.js\";\nexport type { AssetInfo } from \"./commands/assets.js\";\nexport { variablesCommand, getVariables } from \"./commands/variables.js\";\nexport type { VariableInfo } from \"./commands/variables.js\";\nexport {\n checkFfmpeg,\n buildFfmpegArgs,\n renderDocument,\n} from \"./commands/render-pipeline.js\";\nexport type {\n RenderFormat,\n RenderOptions,\n RenderResult,\n ProgressInfo,\n} from \"./commands/render-pipeline.js\";\n\n// VideoProject — local-first video project folder structure\nexport {\n createVideoProject,\n loadVideoProject,\n readCutList,\n writeCutList,\n readTranscript,\n writeTranscript,\n readComposition,\n writeComposition,\n rewriteCutLayers,\n effectiveSpan,\n VIDEO_PROJECT_VERSION,\n VIDEO_CUTLIST_VERSION,\n} from \"./lib/video-project.js\";\nexport type { VideoProject } from \"./lib/video-project.js\";\n\n// Silence trim — parametric cuts + atelier trim pipeline\nexport { trimCommand, trimProject } from \"./commands/trim.js\";\nexport type { TrimOptions, TrimResult } from \"./commands/trim.js\";\n\n// Transcribe — Whisper + editable transcript + caption layers\nexport { transcribeCommand, transcribeProject } from \"./commands/transcribe.js\";\nexport type { TranscribeOptions, TranscribeResult } from \"./commands/transcribe.js\";\nexport { transcriptCommand } from \"./commands/transcript.js\";\nexport { captionsCommand } from \"./commands/captions.js\";\nexport {\n buildCaptionLayers,\n rewriteCaptionLayers,\n} from \"./lib/caption-builder.js\";\nexport type { CaptionStyle, BuildCaptionsOptions } from \"./lib/caption-builder.js\";\nexport {\n flattenWords,\n groupIntoPhrases,\n mergeTranscriptWithExisting,\n applyTextEdit,\n applyBatchReplace,\n applyHide,\n applyAdd,\n applyMerge,\n applySplit,\n} from \"./lib/transcript-model.js\";\nexport {\n probeWhisper,\n runWhisperCpp,\n parseWhisperCppJson,\n} from \"./lib/whisper.js\";\nexport type { WhisperBackend, WhisperModel, WhisperOptions } from \"./lib/whisper.js\";\nexport { VIDEO_TRANSCRIPT_VERSION } from \"./lib/video-project.js\";\n\n// Studio Recipe — Phase 1\nexport { recipeCommand } from \"./commands/recipe.js\";\nexport { applyRecipeCommand } from \"./commands/apply-recipe.js\";\n\n// First-run setup — Phase 3 delegation C\nexport { initCommand, runInit, isInsideGitRepo, parseModeAnswer } from \"./commands/init.js\";\nexport type { RunInitOptions, InitResult } from \"./commands/init.js\";\nexport {\n writeLearningMode,\n readLearningMode,\n learningModePath,\n renderLearningModeYaml,\n LEARNING_MODES,\n LEARNING_MODE_VERSION,\n} from \"./lib/learning-mode.js\";\nexport type { LearningMode, LearningModeFile } from \"./lib/learning-mode.js\";\n\n// Front-of-pipeline artifacts — Phase 2 CLI surface\nexport { artifactsCommand } from \"./commands/artifacts.js\";\nexport {\n loadArtifactsFromProject,\n ARTIFACT_FILENAMES,\n} from \"./lib/artifact-project.js\";\nexport type {\n LoadedArtifactProject,\n ArtifactParseError,\n} from \"./lib/artifact-project.js\";\nexport {\n loadRecipe,\n resolveRecipePath,\n scaffoldRecipeYaml,\n applyRecipeToTrimOptions,\n applyRecipeToTranscribeOptions,\n applyRecipeToCaptionOptions,\n renderRecipeWithDefaults,\n recipeToYaml,\n RECIPE_VERSION,\n} from \"./lib/recipe.js\";\nexport type { LoadedRecipe } from \"./lib/recipe.js\";\n\n// Compose pipelines — kind:\"video\" / kind:\"image\" / kind:\"carousel\" project ingest.\nexport {\n composeVideoProject,\n type ComposeVideoProjectOptions,\n type ComposeVideoProjectResult,\n} from \"./lib/compose-video-project.js\";\nexport {\n composeImageProject,\n type ComposeImageProjectOptions,\n type ComposeImageProjectResult,\n} from \"./lib/compose-image-project.js\";\nexport {\n composeCarouselProject,\n type ComposeCarouselProjectOptions,\n type ComposeCarouselProjectResult,\n} from \"./lib/compose-carousel-project.js\";\n\n// Drag-drop ingest dispatcher — classify file + route.\n// Post-pivot (Kit-A): `routeIngest` returns `BinIngestRoute` (no-target media\n// bin add); `routeIngestWithTarget` handles canvas-area drops with composition.\nexport {\n classifyMediaFile,\n routeIngest,\n routeIngestWithTarget,\n type IngestKind,\n type IngestRoute,\n type BinIngestRoute,\n} from \"./lib/ingest-dispatch.js\";\n\n// ── doc-management (v1.0.x) ──────────────────────────────────────\n// Public verbs (createDoc / duplicateDoc / deleteDoc / listDocs) shared by\n// the studio HTTP layer and the MCP doc tools. Types live in\n// `doc-management-types.ts`; see that file for the validator surface.\nexport {\n createDoc,\n duplicateDoc,\n deleteDoc,\n listDocs,\n type CreateDocResult,\n} from \"./lib/doc-management.js\";\nexport {\n isValidDocName,\n // isManifestDoc retired with the manifest concept (v1.0.x flat-docs pivot)\n type DocKind,\n type CreateDocOptions,\n type DuplicateDocOptions,\n type DeleteDocOptions,\n type DocListEntry,\n type DocDeleteCascadeReport,\n type DocDuplicateReport,\n} from \"./lib/doc-management-types.js\";\n","import { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { Command } from \"commander\";\nimport { parseAtelier } from \"@a-company/atelier-schema\";\nimport { validateAllDeltas } from \"@a-company/atelier-core\";\n\n/**\n * Validate an .atelier file: parse YAML, check schema, check delta overlaps.\n * Returns { valid, errors } for programmatic use.\n */\nexport function validateFile(filePath: string): {\n valid: boolean;\n errors: string[];\n} {\n const absPath = resolve(filePath);\n let content: string;\n try {\n content = readFileSync(absPath, \"utf-8\");\n } catch {\n return { valid: false, errors: [`Cannot read file: ${absPath}`] };\n }\n\n const result = parseAtelier(content);\n if (!result.success) {\n return {\n valid: false,\n errors: result.errors.map(\n (e) => `${e.path}: ${e.message}`,\n ),\n };\n }\n\n // Check all states for delta overlaps\n const overlapErrors: string[] = [];\n for (const [stateName, state] of Object.entries(result.data.states)) {\n const overlaps = validateAllDeltas(state.deltas);\n for (const overlap of overlaps) {\n overlapErrors.push(`State \"${stateName}\": ${overlap.message}`);\n }\n }\n\n if (overlapErrors.length > 0) {\n return { valid: false, errors: overlapErrors };\n }\n\n return { valid: true, errors: [] };\n}\n\n/**\n * Register the `validate` subcommand on the Commander program.\n */\nexport function validateCommand(program: Command): void {\n program\n .command(\"validate <file>\")\n .description(\"Validate an .atelier YAML file\")\n .action((file: string) => {\n const { valid, errors } = validateFile(file);\n if (valid) {\n console.log(\"Valid\");\n } else {\n console.error(\"Validation errors:\");\n for (const error of errors) {\n console.error(` - ${error}`);\n }\n process.exit(1);\n }\n });\n}\n","import { z } from \"zod\";\n\n/** Pixel value — any number */\nexport const PixelSchema = z.number();\n\n/** Percentage string like \"50%\" */\nexport const PercentageSchema = z.string().regex(/^-?\\d+(\\.\\d+)?%$/, {\n message: \"Percentage must be a number followed by %, e.g. \\\"50%\\\"\",\n});\n\n/** Either pixel or percentage */\nexport const UnitValueSchema = z.union([PixelSchema, PercentageSchema]);\n","import { z } from \"zod\";\nimport { UnitValueSchema } from \"./units.js\";\n\nexport const FrameSchema = z.object({\n x: UnitValueSchema,\n y: UnitValueSchema,\n});\n\nexport const BoundsSchema = z.object({\n width: UnitValueSchema,\n height: UnitValueSchema,\n});\n\nexport const AnchorPointSchema = z.object({\n x: z.number().min(0).max(1),\n y: z.number().min(0).max(1),\n});\n","import { z } from \"zod\";\n\nexport const RGBAColorSchema = z.object({\n r: z.number().min(0).max(255),\n g: z.number().min(0).max(255),\n b: z.number().min(0).max(255),\n a: z.number().min(0).max(1),\n});\n\nexport const HSLAColorSchema = z.object({\n h: z.number().min(0).max(360),\n s: z.number().min(0).max(100),\n l: z.number().min(0).max(100),\n a: z.number().min(0).max(1),\n});\n\nexport const HexColorSchema = z.string().regex(/^#([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/, {\n message: \"Color must be a hex string: #RGB, #RGBA, #RRGGBB, or #RRGGBBAA\",\n});\n\nexport const ColorSchema = z.union([RGBAColorSchema, HSLAColorSchema, HexColorSchema]);\n","import { z } from \"zod\";\nimport { ColorSchema } from \"./color.js\";\nimport { UnitValueSchema } from \"./units.js\";\n\nexport const PathPointSchema = z.object({\n x: z.number(),\n y: z.number(),\n in: z.object({ x: z.number(), y: z.number() }).optional(),\n out: z.object({ x: z.number(), y: z.number() }).optional(),\n});\n\nexport const RectShapeSchema = z.object({\n type: z.literal(\"rect\"),\n cornerRadius: z.union([\n z.number().min(0),\n z.tuple([z.number().min(0), z.number().min(0), z.number().min(0), z.number().min(0)]),\n ]).optional(),\n});\n\nexport const EllipseShapeSchema = z.object({\n type: z.literal(\"ellipse\"),\n});\n\nexport const PathShapeSchema = z.object({\n type: z.literal(\"path\"),\n points: z.array(PathPointSchema).min(2, \"Path must have at least 2 points\"),\n closed: z.boolean().optional(),\n});\n\nexport const ShapeSchema = z.discriminatedUnion(\"type\", [\n RectShapeSchema,\n EllipseShapeSchema,\n PathShapeSchema,\n]);\n\nexport const GradientStopSchema = z.object({\n offset: z.number().min(0).max(1),\n color: ColorSchema,\n});\n\nexport const SolidFillSchema = z.object({\n type: z.literal(\"solid\"),\n color: ColorSchema,\n});\n\nexport const LinearGradientFillSchema = z.object({\n type: z.literal(\"linear-gradient\"),\n angle: z.number(),\n stops: z.array(GradientStopSchema).min(2, \"Gradient needs at least 2 stops\"),\n});\n\nexport const RadialGradientFillSchema = z.object({\n type: z.literal(\"radial-gradient\"),\n center: z.object({ x: UnitValueSchema, y: UnitValueSchema }),\n radius: UnitValueSchema,\n stops: z.array(GradientStopSchema).min(2, \"Gradient needs at least 2 stops\"),\n});\n\nexport const FillSchema = z.discriminatedUnion(\"type\", [\n SolidFillSchema,\n LinearGradientFillSchema,\n RadialGradientFillSchema,\n]);\n\nexport const StrokeSchema = z.object({\n color: ColorSchema,\n width: z.number().min(0),\n dash: z.array(z.number().min(0)).optional(),\n lineCap: z.enum([\"butt\", \"round\", \"square\"]).optional(),\n lineJoin: z.enum([\"miter\", \"round\", \"bevel\"]).optional(),\n strokeStart: z.number().min(0).max(1).optional(),\n strokeEnd: z.number().min(0).max(1).optional(),\n});\n\nexport const TextStyleSchema = z.object({\n fontFamily: z.string().min(1, \"fontFamily is required\"),\n fontSize: z.number().positive(\"fontSize must be positive\"),\n fontWeight: z.union([z.number(), z.enum([\"normal\", \"bold\"])]).optional(),\n fontStyle: z.enum([\"normal\", \"italic\"]).optional(),\n textAlign: z.enum([\"left\", \"center\", \"right\"]).optional(),\n lineHeight: z.number().positive().optional(),\n letterSpacing: z.number().optional(),\n color: ColorSchema,\n});\n","import { z } from \"zod\";\n\nexport const LinearEasingSchema = z.object({ type: z.literal(\"linear\") });\n\nexport const CubicBezierEasingSchema = z.object({\n type: z.literal(\"cubic-bezier\"),\n x1: z.number().min(0).max(1),\n y1: z.number(),\n x2: z.number().min(0).max(1),\n y2: z.number(),\n});\n\nexport const SpringEasingSchema = z.object({\n type: z.literal(\"spring\"),\n mass: z.number().positive().optional(),\n stiffness: z.number().positive().optional(),\n damping: z.number().positive().optional(),\n velocity: z.number().optional(),\n});\n\nexport const StepEasingSchema = z.object({\n type: z.literal(\"step\"),\n steps: z.number().int().positive(),\n position: z.enum([\"start\", \"end\"]).optional(),\n});\n\nexport const EasingPresetSchema = z.enum([\"linear\", \"ease-in\", \"ease-out\", \"ease-in-out\"]);\n\nexport const EasingSchema = z.union([\n LinearEasingSchema,\n CubicBezierEasingSchema,\n SpringEasingSchema,\n StepEasingSchema,\n EasingPresetSchema,\n]);\n","import { z } from \"zod\";\nimport { ColorSchema } from \"./color.js\";\n\nexport const ShadowSchema = z.object({\n color: ColorSchema,\n blur: z.number().min(0),\n offsetX: z.number().optional(),\n offsetY: z.number().optional(),\n});\n","import { z } from \"zod\";\nimport { ShapeSchema, FillSchema, StrokeSchema, TextStyleSchema, PathPointSchema } from \"./shape.js\";\nimport { FrameSchema, BoundsSchema, AnchorPointSchema } from \"./coordinates.js\";\nimport { ShadowSchema } from \"./shadow.js\";\nimport { InteractionSchema } from \"./interaction.js\";\n\nexport const BlendModeSchema = z.enum([\n \"normal\", \"multiply\", \"screen\", \"overlay\",\n \"darken\", \"lighten\", \"color-dodge\", \"color-burn\",\n \"hard-light\", \"soft-light\", \"difference\", \"exclusion\",\n \"hue\", \"saturation\", \"color\", \"luminosity\",\n]);\n\nexport const MotionPathSchema = z.object({\n points: z.array(PathPointSchema).min(2, \"Motion path must have at least 2 points\"),\n closed: z.boolean().optional(),\n autoRotate: z.boolean().optional(),\n autoRotateOffset: z.number().optional(),\n});\n\nexport const ShapeVisualSchema = z.object({\n type: z.literal(\"shape\"),\n shape: ShapeSchema,\n fill: FillSchema.optional(),\n stroke: StrokeSchema.optional(),\n});\n\nexport const TextVisualSchema = z.object({\n type: z.literal(\"text\"),\n content: z.string(),\n style: TextStyleSchema,\n});\n\nexport const SpritesheetConfigSchema = z.object({\n columns: z.number().int().positive(),\n rows: z.number().int().positive(),\n frameCount: z.number().int().positive().optional(),\n frameWidth: z.number().positive(),\n frameHeight: z.number().positive(),\n});\n\nexport const SourceRectSchema = z.object({\n x: z.number(),\n y: z.number(),\n width: z.number().positive(),\n height: z.number().positive(),\n});\n\nexport const ImageVisualSchema = z.object({\n type: z.literal(\"image\"),\n assetId: z.string().min(1, \"assetId is required\"),\n src: z.string().optional(),\n sourceRect: SourceRectSchema.optional(),\n spritesheet: SpritesheetConfigSchema.optional(),\n frameIndex: z.number().int().min(0).optional(),\n});\n\nexport const VideoVisualSchema = z.object({\n type: z.literal(\"video\"),\n assetId: z.string().min(1, \"assetId is required\"),\n src: z.string().optional(),\n startFrame: z.number().int().min(0).optional(),\n sourceOffset: z.number().min(0).optional(),\n sourceEnd: z.number().positive().optional(),\n playbackRate: z.number().positive().optional(),\n volume: z.number().min(0).max(1).optional(),\n muted: z.boolean().optional(),\n objectFit: z.enum([\"contain\", \"cover\", \"fill\"]).optional(),\n});\n\nexport const AudioVisualSchema = z.object({\n type: z.literal(\"audio\"),\n assetId: z.string().min(1, \"assetId is required\"),\n src: z.string().optional(),\n startFrame: z.number().int().min(0).optional(),\n sourceOffset: z.number().min(0).optional(),\n sourceEnd: z.number().positive().optional(),\n playbackRate: z.number().positive().optional(),\n volume: z.number().min(0).max(1).optional(),\n muted: z.boolean().optional(),\n fadeIn: z.number().min(0).optional(),\n fadeOut: z.number().min(0).optional(),\n});\n\nexport const GroupVisualSchema = z.object({\n type: z.literal(\"group\"),\n});\n\nexport const RefVisualSchema = z.object({\n type: z.literal(\"ref\"),\n src: z.string().min(1, \"src is required\"),\n state: z.string().optional(),\n frame: z.number().int().min(0).optional(),\n});\n\nexport const VisualSchema = z.discriminatedUnion(\"type\", [\n ShapeVisualSchema,\n TextVisualSchema,\n ImageVisualSchema,\n VideoVisualSchema,\n AudioVisualSchema,\n GroupVisualSchema,\n RefVisualSchema,\n]);\n\nexport const LayerSchema = z.object({\n id: z.string().min(1, \"Layer id is required\"),\n description: z.string().optional(),\n tags: z.array(z.string()).optional(),\n visual: VisualSchema,\n frame: FrameSchema,\n bounds: BoundsSchema,\n anchorPoint: AnchorPointSchema.optional(),\n parentId: z.string().optional(),\n opacity: z.number().min(0).max(1).optional(),\n rotation: z.number().optional(),\n scale: z.object({ x: z.number(), y: z.number() }).optional(),\n visible: z.boolean().optional(),\n shadow: ShadowSchema.optional(),\n blendMode: BlendModeSchema.optional(),\n motionPath: MotionPathSchema.optional(),\n clipPath: ShapeSchema.optional(),\n tint: z.object({\n color: z.string(),\n amount: z.number().min(0).max(1),\n }).optional(),\n interactions: z.array(InteractionSchema).optional(),\n});\n","import { z } from \"zod\";\n\nexport const TriggerTypeSchema = z.enum([\n \"click\", \"hover\", \"pointerdown\", \"pointerup\", \"timer\", \"signal\",\n]);\n\nexport const TriggerSchema = z.object({\n type: TriggerTypeSchema,\n delay: z.number().nonnegative(\"Timer delay must be non-negative\").optional(),\n signal: z.string().optional(),\n}).refine(\n (t) => t.type !== \"timer\" || t.delay !== undefined,\n { message: \"Timer trigger requires a delay\" },\n).refine(\n (t) => t.type !== \"signal\" || t.signal !== undefined,\n { message: \"Signal trigger requires a signal name\" },\n);\n\nexport const ActionTypeSchema = z.enum([\n \"go-to-state\", \"emit-signal\", \"set-variable\", \"toggle-visibility\",\n]);\n\nexport const ActionSchema = z.object({\n type: ActionTypeSchema,\n state: z.string().optional(),\n signal: z.string().optional(),\n variable: z.string().optional(),\n value: z.unknown().optional(),\n targetLayer: z.string().optional(),\n}).refine(\n (a) => a.type !== \"go-to-state\" || a.state !== undefined,\n { message: \"go-to-state action requires a state name\" },\n).refine(\n (a) => a.type !== \"emit-signal\" || a.signal !== undefined,\n { message: \"emit-signal action requires a signal name\" },\n).refine(\n (a) => a.type !== \"set-variable\" || (a.variable !== undefined && a.value !== undefined),\n { message: \"set-variable action requires variable and value\" },\n);\n\nexport const InteractionSchema = z.object({\n id: z.string().min(1, \"Interaction id is required\"),\n trigger: TriggerSchema,\n action: ActionSchema,\n description: z.string().optional(),\n});\n","import { z } from \"zod\";\nimport { EasingSchema } from \"./easing.js\";\n\nexport const AnimatablePropertySchema = z.enum([\n \"frame.x\",\n \"frame.y\",\n \"bounds.width\",\n \"bounds.height\",\n \"opacity\",\n \"rotation\",\n \"scale.x\",\n \"scale.y\",\n \"anchorPoint.x\",\n \"anchorPoint.y\",\n \"visual.shape.cornerRadius\",\n \"visual.fill.color\",\n \"visual.stroke.color\",\n \"visual.stroke.width\",\n \"visual.stroke.start\",\n \"visual.stroke.end\",\n \"visual.style.fontSize\",\n \"visual.style.color\",\n \"shadow.color\",\n \"shadow.blur\",\n \"shadow.offsetX\",\n \"shadow.offsetY\",\n \"motionPath.progress\",\n \"visual.fill.angle\",\n \"visual.fill.center.x\",\n \"visual.fill.center.y\",\n \"visual.fill.radius\",\n \"visual.image.sourceRect.x\",\n \"visual.image.sourceRect.y\",\n \"visual.image.sourceRect.width\",\n \"visual.image.sourceRect.height\",\n \"visual.image.frameIndex\",\n \"visible\",\n \"tint.color\",\n \"tint.amount\",\n]);\n\nexport const FrameRangeSchema = z.tuple([\n z.number().int().min(0, \"Frame start must be >= 0\"),\n z.number().int().min(0, \"Frame end must be >= 0\"),\n]).refine(([start, end]) => end >= start, {\n message: \"Frame range end must be >= start\",\n});\n\nexport const DeltaSchema = z.object({\n id: z.string().optional(),\n name: z.string().optional(),\n layer: z.string().min(1, \"Delta must reference a layer id\"),\n property: AnimatablePropertySchema,\n range: FrameRangeSchema,\n from: z.unknown(),\n to: z.unknown(),\n easing: EasingSchema.optional(),\n description: z.string().optional(),\n tags: z.array(z.string()).optional(),\n});\n","import { z } from \"zod\";\nimport { DeltaSchema } from \"./delta.js\";\nimport { EasingSchema } from \"./easing.js\";\n\nexport const StateTransitionConfigSchema = z.object({\n duration: z.number().int().positive(\"Transition duration must be a positive integer (frames)\"),\n easing: EasingSchema.optional(),\n});\n\n/**\n * State schema — v1.0 removed `audio?: AudioSchema`. Audio is now a\n * first-class `AudioVisual` layer (see `layer.ts`); pre-1.0 documents that\n * still carry `state.audio` are tolerated by the legacy-friendly parser\n * (which drops the field via `passthrough` then strips on validate) but\n * authors should migrate.\n *\n * The strict StateSchema rejects unknown keys, so callers passing a\n * pre-1.0 doc through StateSchema.parse will fail on `audio`. Use\n * `parseAtelier` to migrate-on-read.\n */\nexport const StateSchema = z.object({\n description: z.string().optional(),\n tags: z.array(z.string()).optional(),\n parent: z.string().optional(),\n duration: z.number().int().positive(\"State duration must be a positive integer (frames)\"),\n deltas: z.array(DeltaSchema),\n transitions: z.record(z.string(), StateTransitionConfigSchema).optional(),\n});\n","import { z } from \"zod\";\nimport { AnimatablePropertySchema } from \"./delta.js\";\nimport { EasingSchema } from \"./easing.js\";\n\nexport const PresetDeltaSchema = z.object({\n property: AnimatablePropertySchema,\n offset: z.tuple([z.number().int().min(0), z.number().int().min(0)]).optional(),\n from: z.unknown(),\n to: z.unknown(),\n easing: EasingSchema.optional(),\n});\n\nexport const PresetSchema = z.object({\n description: z.string().optional(),\n tags: z.array(z.string()).optional(),\n deltas: z.array(PresetDeltaSchema).min(1, \"Preset must have at least one delta\"),\n});\n","import { z } from \"zod\";\n\nexport const VariableTypeSchema = z.enum([\"string\", \"number\", \"color\", \"asset\", \"boolean\"]);\n\nexport const VariableSchema = z.object({\n type: VariableTypeSchema,\n default: z.unknown().optional(),\n description: z.string().optional(),\n});\n","import { z } from \"zod\";\n\nexport const AssetTypeSchema = z.enum([\"image\", \"svg\", \"font\", \"animation\", \"audio\", \"video\"]);\n\nexport const AssetSchema = z.object({\n type: AssetTypeSchema,\n src: z.string().min(1, \"Asset src is required\"),\n description: z.string().optional(),\n spritesheet: z.object({\n columns: z.number().int().positive(),\n rows: z.number().int().positive(),\n frameCount: z.number().int().positive().optional(),\n frameWidth: z.number().positive(),\n frameHeight: z.number().positive(),\n }).optional(),\n videoMeta: z.object({\n duration: z.number().positive(\"videoMeta.duration must be positive\"),\n fps: z.number().positive(\"videoMeta.fps must be positive\"),\n width: z.number().int().positive(),\n height: z.number().int().positive(),\n }).optional(),\n});\n","import { z } from \"zod\";\nimport { LayerSchema } from \"./layer.js\";\nimport { StateSchema } from \"./state.js\";\nimport { PresetSchema } from \"./preset.js\";\nimport { VariableSchema } from \"./variable.js\";\nimport { AssetSchema } from \"./asset.js\";\nimport { TimelineSchema } from \"./timeline.js\";\nimport { SlideRefSchema } from \"./slides.js\";\nimport { AtelierDesignSchema } from \"./design.js\";\n\nexport const CanvasSchema = z.object({\n width: z.number().int().positive(\"Canvas width must be a positive integer\"),\n height: z.number().int().positive(\"Canvas height must be a positive integer\"),\n fps: z.number().int().positive(\"FPS must be a positive integer\"),\n background: z.string().optional(),\n});\n\n/**\n * Project kind — controls which downstream exporters apply and which other\n * fields are meaningful. Absent = `\"video\"` for backcompat with pre-v1\n * documents. See `@a-company/atelier-types/document.ts` for the full\n * narrative.\n */\nexport const ProjectKindSchema = z.enum([\"video\", \"image\", \"carousel\"]);\n\nexport const AtelierDocumentSchema = z.object({\n version: z.string().min(1, \"Version is required\"),\n name: z.string().min(1, \"Animation name is required\"),\n kind: ProjectKindSchema.optional(),\n description: z.string().optional(),\n tags: z.array(z.string()).optional(),\n canvas: CanvasSchema,\n /** Per-file design / brand block (self-contained — no auto-merge). */\n design: AtelierDesignSchema.optional(),\n variables: z.record(z.string(), VariableSchema).optional(),\n assets: z.record(z.string(), AssetSchema).optional(),\n presets: z.record(z.string(), PresetSchema).optional(),\n layers: z.array(LayerSchema),\n states: z.record(z.string(), StateSchema),\n /** Derived timeline summary for video-kind projects. Optional. */\n timeline: TimelineSchema.optional(),\n /** Carousel slides — only meaningful when kind === \"carousel\". */\n slides: z.array(SlideRefSchema).optional(),\n});\n","import { z } from \"zod\";\n\n/**\n * Timeline summary schemas — see `@a-company/atelier-types/timeline.ts` for\n * semantics (derived overview of clip placements, regenerated by writers,\n * tolerated as missing/stale by readers).\n */\n\nexport const TimelineClipSchema = z.object({\n layerId: z.string().min(1, \"TimelineClip.layerId is required\"),\n startFrame: z.number().int().min(0, \"startFrame must be a non-negative integer\"),\n endFrame: z.number().int().min(0, \"endFrame must be a non-negative integer\"),\n source: z.string().min(1, \"TimelineClip.source is required\"),\n});\n\nexport const TimelineTrackSchema = z.object({\n id: z.string().min(1, \"TimelineTrack.id is required\"),\n kind: z.enum([\"video\", \"audio\"]),\n clips: z.array(TimelineClipSchema),\n});\n\nexport const TimelineSchema = z.object({\n fps: z.number().int().positive(\"Timeline.fps must be a positive integer\"),\n totalFrames: z.number().int().min(0, \"Timeline.totalFrames must be a non-negative integer\"),\n tracks: z.array(TimelineTrackSchema),\n});\n","import { z } from \"zod\";\n\n/**\n * Carousel slide schemas — see `@a-company/atelier-types/slides.ts` for\n * semantics. SlideRef.src points at any `.atelier` file in the workspace;\n * cycle detection is the resolver's responsibility (not enforced here).\n */\n\nexport const SlideTransitionSchema = z.object({\n kind: z.enum([\"cut\", \"crossfade\", \"fade\"]),\n durationFrames: z.number().int().min(0, \"transition durationFrames must be a non-negative integer\").optional(),\n color: z.string().optional(),\n});\n\nexport const SlideRefSchema = z.object({\n src: z.string().min(1, \"SlideRef.src is required\"),\n duration: z.number().int().min(0, \"SlideRef.duration must be a non-negative integer (frames)\").optional(),\n transition: SlideTransitionSchema.optional(),\n label: z.string().optional(),\n});\n","import { z } from \"zod\";\n\n/**\n * Per-file design block schemas — see `@a-company/atelier-types/design.ts`\n * for semantics. Self-contained per file; lineage is explicit via\n * `relatedTo[]` (no auto-merge at render time).\n */\n\nexport const DesignRelationSchema = z.enum([\"derived-from\", \"sibling\", \"references\"]);\n\nexport const AtelierDesignRelationSchema = z.object({\n source: z.string().min(1, \"AtelierDesignRelation.source is required\"),\n relation: DesignRelationSchema,\n note: z.string().optional(),\n});\n\nexport const DesignTypographySchema = z.object({\n headingFamily: z.string().optional(),\n bodyFamily: z.string().optional(),\n monoFamily: z.string().optional(),\n sizes: z.record(z.string(), z.number().positive()).optional(),\n weights: z.record(z.string(), z.string()).optional(),\n});\n\nexport const DesignMotionSchema = z.object({\n defaultEasing: z.string().optional(),\n defaultDuration: z.number().int().positive().optional(),\n});\n\nexport const AtelierDesignSchema = z.object({\n palette: z.record(z.string(), z.string()).optional(),\n typography: DesignTypographySchema.optional(),\n motion: DesignMotionSchema.optional(),\n relatedTo: z.array(AtelierDesignRelationSchema).optional(),\n});\n","import { z } from \"zod\";\n\n/** Silence-trim policy block */\nexport const SilencePolicySchema = z.object({\n noise: z.string().optional(),\n min_silence: z.number().nonnegative().optional(),\n default_padding_pre: z.number().nonnegative().optional(),\n default_padding_post: z.number().nonnegative().optional(),\n match_tolerance: z.number().nonnegative().optional(),\n}).strict();\n\n/** Caption visual style block */\nexport const CaptionStyleSchema = z.object({\n font_family: z.string().optional(),\n font_size: z.number().positive().optional(),\n font_weight: z.union([z.literal(\"normal\"), z.literal(\"bold\"), z.number()]).optional(),\n text_align: z.enum([\"left\", \"center\", \"right\"]).optional(),\n color: z.string().optional(),\n y_ratio: z.number().min(0).max(1).optional(),\n width_ratio: z.number().min(0).max(1).optional(),\n fade_seconds: z.number().nonnegative().optional(),\n}).strict();\n\n/** Caption phrase grouping block */\nexport const CaptionGroupingSchema = z.object({\n max_words: z.number().int().positive().optional(),\n pause_gap: z.number().nonnegative().optional(),\n}).strict();\n\n/** Anchor enum shared by every overlay sub-rule */\nexport const OverlayAnchorSchema = z.enum([\n \"top-left\",\n \"top-right\",\n \"bottom-left\",\n \"bottom-right\",\n]);\n\n/** Visual subset for overlay text — recipe snake_case convention */\nexport const OverlayTextStyleSchema = z.object({\n font_family: z.string().optional(),\n font_size: z.number().positive().optional(),\n font_weight: z.union([z.literal(\"normal\"), z.literal(\"bold\"), z.number()]).optional(),\n color: z.string().optional(),\n}).strict();\n\n/**\n * Validate a page-number format template:\n * - braces must balance\n * - every {…} group must match (current|total)(:0Nd)? (zero-pad form)\n * - at least one of {current} / {total} must appear\n *\n * Render-time substitution is a separate concern; this only validates syntax.\n */\nfunction validatePageNumberFormat(\n format: string,\n ctx: z.RefinementCtx,\n): void {\n if (format.length === 0) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: \"format must be a non-empty string\",\n });\n return;\n }\n\n const open = (format.match(/\\{/g) ?? []).length;\n const close = (format.match(/\\}/g) ?? []).length;\n if (open !== close) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `format has unbalanced braces (${open} '{' vs ${close} '}')`,\n });\n return;\n }\n\n const groupRe = /\\{([^{}]*)\\}/g;\n const groupRule = /^(current|total)(:0\\d+d)?$/;\n let m: RegExpExecArray | null;\n let sawCurrent = false;\n let sawTotal = false;\n while ((m = groupRe.exec(format)) !== null) {\n const inner = m[1];\n if (!groupRule.test(inner)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `format placeholder \"{${inner}}\" is not recognized — expected {current}, {total}, {current:0Nd}, or {total:0Nd}`,\n });\n return;\n }\n if (inner.startsWith(\"current\")) sawCurrent = true;\n if (inner.startsWith(\"total\")) sawTotal = true;\n }\n\n if (!sawCurrent && !sawTotal) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: \"format must contain at least one {current} or {total} placeholder\",\n });\n }\n}\n\n/** Handle overlay rule schema — anchored creator-handle text */\nexport const OverlayHandleRuleSchema = z.object({\n text: z.string().min(1),\n anchor: OverlayAnchorSchema,\n margin: z.number().nonnegative().optional(),\n style: OverlayTextStyleSchema.optional(),\n}).strict();\n\n/** Page-number overlay rule schema — anchored \"1/5\"-style indicator */\nexport const OverlayPageNumberRuleSchema = z.object({\n format: z.string().superRefine(validatePageNumberFormat),\n anchor: OverlayAnchorSchema,\n margin: z.number().nonnegative().optional(),\n style: OverlayTextStyleSchema.optional(),\n}).strict();\n\n/** Overlay rules block (Phase 1.5) — both sub-blocks optional */\nexport const OverlayRulesSchema = z.object({\n handle: OverlayHandleRuleSchema.optional(),\n page_number: OverlayPageNumberRuleSchema.optional(),\n}).strict();\n\n/**\n * StudioRecipe — top-level. Reserved fields (caption_highlight, transition_kit,\n * etc.) still accept any shape; Phase 3 will refine them. Validate-time warnings\n * surface reserved-fields-in-use to authors so they aren't caught off-guard\n * when those sections become load-bearing.\n *\n * overlay_rules was promoted from reserved Phase 3 to first-class Phase 1.5.\n */\nexport const StudioRecipeSchema = z.object({\n version: z.string(),\n name: z.string(),\n description: z.string().optional(),\n author: z.string().optional(),\n tags: z.array(z.string()).optional(),\n\n silence_policy: SilencePolicySchema.optional(),\n caption_style: CaptionStyleSchema.optional(),\n caption_grouping: CaptionGroupingSchema.optional(),\n\n // Phase 1.5 — first-class overlay rules\n overlay_rules: OverlayRulesSchema.optional(),\n\n // Reserved — Phase 3 (parse-opaque)\n caption_highlight: z.unknown().optional(),\n transition_kit: z.unknown().optional(),\n palette: z.unknown().optional(),\n audio_policy: z.unknown().optional(),\n aspect_targets: z.array(z.unknown()).optional(),\n}).strict();\n\n/** Names of still-reserved fields — used by validators to emit forward-compat warnings */\nexport const RESERVED_RECIPE_FIELDS = [\n \"caption_highlight\",\n \"transition_kit\",\n \"palette\",\n \"audio_policy\",\n \"aspect_targets\",\n] as const;\n","import type { AtelierDocument, Layer, Delta, VideoVisual, StudioRecipe } from \"@a-company/atelier-types\";\nimport { AtelierDocumentSchema } from \"./document.js\";\nimport { LayerSchema } from \"./layer.js\";\nimport { DeltaSchema } from \"./delta.js\";\nimport { StudioRecipeSchema, RESERVED_RECIPE_FIELDS } from \"./recipe.js\";\nimport type { z } from \"zod\";\n\n/** Validation result — either success with typed data or failure with readable errors */\nexport type ValidationResult<T> =\n | { success: true; data: T }\n | { success: false; errors: ValidationError[] };\n\nexport interface ValidationError {\n path: string;\n message: string;\n}\n\n/** Format Zod errors into flat, AI-readable error messages */\nfunction formatErrors(error: z.ZodError): ValidationError[] {\n return error.issues.map((issue) => ({\n path: issue.path.join(\".\") || \"(root)\",\n message: issue.message,\n }));\n}\n\n/** Validate a complete AtelierDocument */\nexport function validateDocument(input: unknown): ValidationResult<AtelierDocument> {\n const result = AtelierDocumentSchema.safeParse(input);\n if (result.success) {\n return { success: true, data: result.data as AtelierDocument };\n }\n return { success: false, errors: formatErrors(result.error) };\n}\n\n/** Validate a single Layer */\nexport function validateLayer(input: unknown): ValidationResult<Layer> {\n const result = LayerSchema.safeParse(input);\n if (result.success) {\n return { success: true, data: result.data as Layer };\n }\n return { success: false, errors: formatErrors(result.error) };\n}\n\n/** Validate a single Delta */\nexport function validateDelta(input: unknown): ValidationResult<Delta> {\n const result = DeltaSchema.safeParse(input);\n if (result.success) {\n return { success: true, data: result.data as Delta };\n }\n return { success: false, errors: formatErrors(result.error) };\n}\n\n/**\n * Validate a VideoVisual layer against semantic constraints (^valid-video-layer gate).\n * Checks beyond Zod: sourceEnd > sourceOffset, sourceEnd ≤ videoMeta.duration.\n */\nexport function validateVideoLayer(\n visual: VideoVisual,\n videoMetaDuration?: number,\n): ValidationResult<VideoVisual> {\n const errors: ValidationError[] = [];\n\n if (!visual.assetId) {\n errors.push({ path: \"assetId\", message: \"assetId is required\" });\n }\n\n const sourceOffset = visual.sourceOffset ?? 0;\n if (visual.sourceEnd !== undefined) {\n if (visual.sourceEnd <= sourceOffset) {\n errors.push({\n path: \"sourceEnd\",\n message: `sourceEnd (${visual.sourceEnd}) must be greater than sourceOffset (${sourceOffset})`,\n });\n }\n if (videoMetaDuration !== undefined && visual.sourceEnd > videoMetaDuration) {\n errors.push({\n path: \"sourceEnd\",\n message: `sourceEnd (${visual.sourceEnd}) exceeds asset duration (${videoMetaDuration})`,\n });\n }\n }\n\n if (videoMetaDuration !== undefined && sourceOffset >= videoMetaDuration) {\n errors.push({\n path: \"sourceOffset\",\n message: `sourceOffset (${sourceOffset}) is at or beyond asset duration (${videoMetaDuration})`,\n });\n }\n\n if (errors.length > 0) return { success: false, errors };\n return { success: true, data: visual };\n}\n\n/**\n * Validate a StudioRecipe against the schema (^valid-recipe gate).\n * Returns success with the parsed recipe and optional warnings about\n * reserved-but-unimplemented Phase 3 fields that are present.\n */\nexport function validateRecipe(\n recipe: unknown,\n): ValidationResult<StudioRecipe> & { warnings?: string[] } {\n const parsed = StudioRecipeSchema.safeParse(recipe);\n if (!parsed.success) {\n return { success: false, errors: formatErrors(parsed.error) };\n }\n\n const warnings: string[] = [];\n const data = parsed.data as StudioRecipe;\n for (const field of RESERVED_RECIPE_FIELDS) {\n if (data[field] !== undefined) {\n warnings.push(\n `${field} is reserved for Phase 3 and currently has no effect.`,\n );\n }\n }\n\n return { success: true, data, ...(warnings.length > 0 && { warnings }) };\n}\n","import { parse as yamlParse, stringify as yamlStringify } from \"yaml\";\nimport type { AtelierDocument } from \"@a-company/atelier-types\";\nimport { validateDocument } from \"./validate.js\";\nimport type { ValidationResult } from \"./validate.js\";\n\n/**\n * Parse a YAML string into a validated AtelierDocument.\n * Returns validation errors if the YAML is invalid or doesn't match the schema.\n */\nexport function parseAtelier(yamlString: string): ValidationResult<AtelierDocument> {\n let parsed: unknown;\n try {\n parsed = yamlParse(yamlString);\n } catch (err) {\n return {\n success: false,\n errors: [{ path: \"(yaml)\", message: `YAML parse error: ${(err as Error).message}` }],\n };\n }\n return validateDocument(parsed);\n}\n\n/**\n * Serialize an AtelierDocument to YAML string.\n */\nexport function serializeAtelier(doc: AtelierDocument): string {\n return yamlStringify(doc, { indent: 2 });\n}\n","import { z } from \"zod\";\n\n/**\n * Slot reference vocabulary (front-of-pipeline artifacts spec §5).\n *\n * Format: `<kind>.<open-tail>` where `<open-tail>` is one or more\n * dot-separated segments. The canonical form per spec §5 is\n * `<kind>.<category>.<specifier>` (three segments), but the spec's own\n * STORYBOARD example mixes longer (`audio.sfx.transition.whoosh`, four\n * segments) and shorter (`font.display`, two segments) forms. We reconcile\n * by enforcing the contract that's stable across both: `kind` is closed,\n * everything after the first dot is open.\n *\n * - `kind` is a CLOSED enum (the seven asset-family roots).\n * - At least one open segment must follow the kind.\n *\n * Aligns conceptually with `VariableSchema`'s `asset` type — both are the\n * inert-reference surface that the mind-map binds at compose time (TD-229).\n */\nexport const SlotKindSchema = z.enum([\n \"image\",\n \"video\",\n \"audio\",\n \"text\",\n \"font\",\n \"voice\",\n \"logo\",\n]);\n\nexport type SlotKind = z.infer<typeof SlotKindSchema>;\n\nexport const SLOT_KINDS = SlotKindSchema.options;\n\n/**\n * Parse a slot reference into its segments. Returns null if the string\n * does not satisfy `<kind>.<tail>` (where `<tail>` has at least one\n * segment); otherwise returns the kind plus the logical category and\n * specifier. When only two segments are present (e.g. `font.display`),\n * `category` carries the open segment and `specifier` is the empty string.\n */\nexport function parseSlotRef(ref: string): { kind: SlotKind; category: string; specifier: string } | null {\n if (typeof ref !== \"string\" || ref.length === 0) return null;\n const segments = ref.split(\".\");\n if (segments.length < 2) return null;\n if (segments.some((s) => s.length === 0)) return null;\n const [rawKind, category, ...rest] = segments;\n const kindParsed = SlotKindSchema.safeParse(rawKind);\n if (!kindParsed.success) return null;\n const specifier = rest.join(\".\");\n return { kind: kindParsed.data, category, specifier };\n}\n\n/**\n * Zod schema for a single slot reference string. Refuses unknown `kind`\n * segments; permits any open `category.specifier`. The specifier may\n * itself contain dots (e.g. `audio.sfx.transition.whoosh`).\n */\nexport const SlotRefSchema = z.string().superRefine((value, ctx) => {\n const parsed = parseSlotRef(value);\n if (parsed !== null) return;\n // Distinguish \"unknown kind\" from \"shape wrong\" for clearer errors.\n const segments = value.split(\".\");\n if (segments.length < 2) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `slot reference \"${value}\" must have at least two segments (<kind>.<open-tail>)`,\n });\n return;\n }\n if (segments.some((s) => s.length === 0)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `slot reference \"${value}\" has an empty segment`,\n });\n return;\n }\n const kindOk = SlotKindSchema.safeParse(segments[0]).success;\n if (!kindOk) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `slot reference \"${value}\" has unknown kind \"${segments[0]}\" — expected one of ${SLOT_KINDS.join(\", \")}`,\n });\n return;\n }\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `slot reference \"${value}\" is malformed`,\n });\n});\n","import { z } from \"zod\";\n\n/**\n * Animation Technique Library (front-of-pipeline artifacts spec §5).\n *\n * STORYBOARD beats declare `technique_library_version` and pull techniques\n * by string name from the library at that version. Iris refuses unknown\n * techniques at compose time. The schema itself only validates that\n * `techniques: string[]` — membership against the active library is\n * enforced by `validateArtifactSet` so the pure-Zod schema stays version-\n * agnostic.\n *\n * Versioning policy: integer (per artifact spec §9 Q4). Bump on additive\n * changes; older STORYBOARDs continue to declare their original version.\n * Library versions are SUPERSETS: v(n+1) contains every entry from v(n) so\n * a creator's existing storyboard never breaks when the library moves\n * forward.\n */\nexport const TechniqueLibrarySchema = z.object({\n version: z.number().int().positive(),\n techniques: z.array(z.string().min(1)),\n}).strict();\n\nexport type TechniqueLibrary = z.infer<typeof TechniqueLibrarySchema>;\n\n/**\n * Version 1 starter set — drawn from the artifact spec's STORYBOARD EXAMPLE.\n * Additions land in higher versions; v1 stays frozen.\n */\nexport const TECHNIQUE_LIBRARY_V1: TechniqueLibrary = {\n version: 1,\n techniques: [\n \"text.split-reveal\",\n \"image.ken-burns.slow\",\n \"super.weight-pulse-on-keyword\",\n \"background.color-shift-to-token\",\n ],\n};\n\n/**\n * Version 2 — additive expansion for Iris's animation craft. Twelve new\n * techniques cover text, image, transition, super (overlay text), and audio\n * categories so STORYBOARD can call for them by name and Iris's\n * compose-from-primitives skill knows how to lower each one to MCP calls.\n *\n * Every v1 entry is preserved (supersets, not replacements).\n */\nexport const TECHNIQUE_LIBRARY_V2: TechniqueLibrary = {\n version: 2,\n techniques: [\n // v1 (preserved)\n \"text.split-reveal\",\n \"image.ken-burns.slow\",\n \"super.weight-pulse-on-keyword\",\n \"background.color-shift-to-token\",\n // v2 additions — text\n \"text.kinetic-keyword-emphasis\",\n \"text.typewriter\",\n // v2 additions — image\n \"image.zoom-in-during-beat\",\n \"image.cross-dissolve\",\n \"image.parallax-pan-slow\",\n // v2 additions — transition\n \"transition.whip-pan\",\n \"transition.match-cut\",\n \"transition.morph-cut\",\n \"transition.fade-through-color\",\n // v2 additions — super (overlay text)\n \"super.scale-in-spring\",\n \"super.drop-in-staggered\",\n // v2 additions — audio\n \"audio.fade-under-vo\",\n ],\n};\n\n/** Registry of all shipped technique-library versions. Keyed by integer version. */\nexport const TECHNIQUE_LIBRARIES: Record<number, TechniqueLibrary> = {\n 1: TECHNIQUE_LIBRARY_V1,\n 2: TECHNIQUE_LIBRARY_V2,\n};\n\n/** The current/latest shipped library version. */\nexport const CURRENT_TECHNIQUE_LIBRARY_VERSION = 2;\n\n/** Resolve a library by version; returns null if the version is not shipped. */\nexport function getTechniqueLibrary(version: number): TechniqueLibrary | null {\n return TECHNIQUE_LIBRARIES[version] ?? null;\n}\n","import { z } from \"zod\";\n\n/**\n * DESIGN.md — the creative-direction artifact (front-of-pipeline spec §3).\n *\n * Authored by Quill. Captures the brand register the entire Project must\n * respect: audience, voice, visual register, constraints, brand references.\n * When a recipe is attached, DESIGN defers to the recipe and only records\n * `variances` — divergences from the recipe.\n *\n * Every sub-schema is `.strict()` to match the recipe-package convention\n * (unknown keys are a hard error, not silently dropped — see the package-\n * level `recipe-strict-no-unknown-keys` aspect).\n */\n\n/** audience block — primary/secondary/platform list */\nexport const DesignAudienceSchema = z.object({\n primary: z.string().min(1),\n secondary: z.string().optional(),\n platform: z.array(z.string().min(1)).min(1),\n}).strict();\n\n/** voice block — descriptors are an ordered 3-to-5 list */\nexport const DesignVoiceSchema = z.object({\n descriptors: z.array(z.string().min(1)).min(3).max(5),\n references: z.array(z.string().min(1)).optional(),\n}).strict();\n\n/** Palette roles — closed enum per spec §3 (\"Roles are an enum, not free text\") */\nexport const PaletteRoleSchema = z.enum([\n \"background\",\n \"surface\",\n \"primary\",\n \"accent\",\n \"text-on-dark\",\n \"text-on-light\",\n]);\n\n/** Single palette entry — token + hex + role */\nexport const PaletteEntrySchema = z.object({\n token: z.string().min(1),\n hex: z.string().regex(/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$/, {\n message: \"hex must be a #RGB / #RRGGBB / #RRGGBBAA string\",\n }),\n role: PaletteRoleSchema,\n}).strict();\n\n/** Typography usage enum — closed */\nexport const TypographyUsageSchema = z.enum([\"display\", \"body\", \"caption\", \"mono\"]);\n\n/** Typography entry */\nexport const TypographyEntrySchema = z.object({\n token: z.string().min(1),\n family: z.string().min(1),\n weights: z.array(z.number().int().min(1).max(1000)).min(1),\n usage: TypographyUsageSchema,\n}).strict();\n\n/** motion_register sub-block — tempo / easing_bias / camera_bias */\nexport const MotionRegisterSchema = z.object({\n tempo: z.enum([\"snappy\", \"steady\", \"calm\", \"languid\"]),\n easing_bias: z.enum([\"linear\", \"ease-out\", \"spring\"]),\n camera_bias: z.enum([\"static\", \"drift\", \"snap\"]).optional(),\n}).strict();\n\n/** visual_register block — palette + typography + motion_register */\nexport const VisualRegisterSchema = z.object({\n palette: z.array(PaletteEntrySchema).min(1),\n typography: z.array(TypographyEntrySchema).min(1),\n motion_register: MotionRegisterSchema,\n}).strict();\n\n/** Aspect ratios — closed enum per spec §3 */\nexport const AspectRatioSchema = z.enum([\"9:16\", \"1:1\", \"16:9\", \"4:5\"]);\n\n/** constraints block — duration, aspect ratios, do_not list */\nexport const DesignConstraintsSchema = z.object({\n max_duration_seconds: z.number().positive().optional(),\n aspect_ratios: z.array(AspectRatioSchema).min(1),\n do_not: z.array(z.string().min(1)),\n}).strict();\n\n/** Logo brand reference — closed role enum */\nexport const BrandLogoSchema = z.object({\n token: z.string().min(1),\n role: z.enum([\"primary\", \"mark\", \"wordmark\"]),\n}).strict();\n\n/** Handle brand reference — platform + value */\nexport const BrandHandleSchema = z.object({\n platform: z.string().min(1),\n value: z.string().min(1),\n}).strict();\n\n/** brand_references block — logos + handles + page-number convention */\nexport const BrandReferencesSchema = z.object({\n logos: z.array(BrandLogoSchema).min(1),\n handles: z.array(BrandHandleSchema).optional(),\n page_number_convention: z.string().optional(),\n}).strict();\n\n/**\n * Variance entry — structured form per spec §9 Q5 recommendation:\n * { field, value, reason }. Agents diff variances; free-text Markdown\n * would be opaque to them.\n */\nexport const DesignVarianceSchema = z.object({\n field: z.string().min(1),\n value: z.unknown(),\n reason: z.string().min(1),\n}).strict();\n\n/**\n * DesignArtifactSchema — the typed frontmatter shape. The Markdown body\n * is carried as a `body: string` field set by the parser (verbatim — voice\n * notes, prose, etc. live there).\n */\nexport const DesignArtifactSchema = z.object({\n audience: DesignAudienceSchema,\n voice: DesignVoiceSchema,\n visual_register: VisualRegisterSchema,\n constraints: DesignConstraintsSchema,\n brand_references: BrandReferencesSchema,\n variances: z.array(DesignVarianceSchema).optional(),\n /** Markdown body verbatim — attached by the parser, not part of frontmatter. */\n body: z.string(),\n}).strict();\n\nexport type DesignArtifact = z.infer<typeof DesignArtifactSchema>;\n","import { z } from \"zod\";\nimport { SlotRefSchema } from \"./slot.js\";\n\n/**\n * SCRIPT.md — the narrative artifact (front-of-pipeline spec §4).\n *\n * Authored by Quill, refined by Lux. Two modes — discriminated union on\n * `mode`:\n * - `narrated`: four ordered blocks (hook/story/proof/cta), each a list\n * of beats with optional `bind_to_transcript` for post-VO rebinding.\n * - `carousel` | `text-only`: flat ordered list of beats; no transcript\n * binding (no VO exists).\n *\n * Every sub-schema is `.strict()` per the package convention.\n */\n\n/** Kebab-case beat-id pattern — also accepts `b-roll.<rest>` prefix used by STORYBOARD. */\nconst BEAT_ID_RE = /^[a-z][a-z0-9-]*(\\.[a-z0-9][a-z0-9-]*)*$/;\n\n/** Beat-id schema — kebab-case, may contain dot-prefixed namespaces like `b-roll.intro`. */\nexport const ScriptBeatIdSchema = z.string().regex(BEAT_ID_RE, {\n message: \"beat id must be kebab-case (optionally dot-namespaced, e.g. `proof-1` or `b-roll.intro`)\",\n});\n\n/** Narrated-mode beat — has `bind_to_transcript` (default true). */\nexport const NarratedBeatSchema = z.object({\n id: ScriptBeatIdSchema,\n copy: z.string().min(1),\n intent: z.string().min(1),\n est_duration_s: z.number().nonnegative(),\n bind_to_transcript: z.boolean().default(true),\n}).strict();\n\nexport type NarratedBeat = z.infer<typeof NarratedBeatSchema>;\n\n/** Non-narrated beat — carousel / text-only; no transcript binding field. */\nexport const NonNarratedBeatSchema = z.object({\n id: ScriptBeatIdSchema,\n copy: z.string().min(1),\n intent: z.string().min(1),\n est_duration_s: z.number().nonnegative(),\n}).strict();\n\nexport type NonNarratedBeat = z.infer<typeof NonNarratedBeatSchema>;\n\n/** Narrated-mode body: four ordered blocks. */\nexport const NarratedBlocksSchema = z.object({\n hook: z.array(NarratedBeatSchema).min(1),\n story: z.array(NarratedBeatSchema),\n proof: z.array(NarratedBeatSchema),\n cta: z.array(NarratedBeatSchema).min(1),\n}).strict();\n\n/**\n * The script mode is a discriminated union on `mode`. Top-level fields:\n * mode, target_duration_s, language, tts_voice?\n * Plus the beat structure: narrated has four blocks; carousel/text-only\n * has a flat `beats` list.\n */\nexport const NarratedScriptSchema = z.object({\n mode: z.literal(\"narrated\"),\n target_duration_s: z.number().positive(),\n language: z.string().min(1),\n /** Optional slot reference for TTS voice (e.g. `voice.host.primary`). */\n tts_voice: SlotRefSchema.optional(),\n blocks: NarratedBlocksSchema,\n /** Markdown body verbatim — attached by the parser when the body has stray prose. */\n body: z.string(),\n}).strict();\n\nexport const CarouselScriptSchema = z.object({\n mode: z.literal(\"carousel\"),\n target_duration_s: z.number().positive(),\n language: z.string().min(1),\n tts_voice: SlotRefSchema.optional(),\n beats: z.array(NonNarratedBeatSchema).min(1),\n body: z.string(),\n}).strict();\n\nexport const TextOnlyScriptSchema = z.object({\n mode: z.literal(\"text-only\"),\n target_duration_s: z.number().positive(),\n language: z.string().min(1),\n tts_voice: SlotRefSchema.optional(),\n beats: z.array(NonNarratedBeatSchema).min(1),\n body: z.string(),\n}).strict();\n\n/** ScriptArtifactSchema — discriminated union across the three modes. */\nexport const ScriptArtifactSchema = z.discriminatedUnion(\"mode\", [\n NarratedScriptSchema,\n CarouselScriptSchema,\n TextOnlyScriptSchema,\n]);\n\nexport type ScriptArtifact = z.infer<typeof ScriptArtifactSchema>;\nexport type NarratedScript = z.infer<typeof NarratedScriptSchema>;\nexport type CarouselScript = z.infer<typeof CarouselScriptSchema>;\nexport type TextOnlyScript = z.infer<typeof TextOnlyScriptSchema>;\n\n/**\n * Flatten all beats from a script regardless of mode — useful for\n * cross-artifact validation and duration summing.\n */\nexport function allScriptBeats(script: ScriptArtifact): Array<NarratedBeat | NonNarratedBeat> {\n if (script.mode === \"narrated\") {\n return [\n ...script.blocks.hook,\n ...script.blocks.story,\n ...script.blocks.proof,\n ...script.blocks.cta,\n ];\n }\n return script.beats;\n}\n","import { z } from \"zod\";\nimport { SlotRefSchema } from \"./slot.js\";\nimport { ScriptBeatIdSchema } from \"./script.js\";\n\n/**\n * STORYBOARD.md — the composition artifact (front-of-pipeline spec §5).\n *\n * Authored by Lux. One storyboard beat per script beat (matching `id`),\n * plus optional storyboard-only beats prefixed `b-roll.*`.\n *\n * Iris walks this file beat by beat to mutate the live AtelierDocument.\n * Techniques are validated string-level here; library-version membership\n * is enforced by `validateArtifactSet` so the schema stays version-agnostic.\n *\n * Every sub-schema is `.strict()` per the package convention.\n */\n\n/** Beat time window — `start_s` < `end_s` enforced by refine. */\nexport const BeatWindowSchema = z.object({\n start_s: z.number().nonnegative(),\n end_s: z.number().nonnegative(),\n}).strict().refine((w) => w.end_s > w.start_s, {\n message: \"window.end_s must be > window.start_s\",\n});\n\n/** Camera block — closed kind enum, optional subject string. */\nexport const CameraSchema = z.object({\n kind: z.enum([\"static\", \"drift\", \"push\", \"pull\", \"whip\"]),\n subject: z.string().min(1).optional(),\n}).strict();\n\n/**\n * Mood adjectives — closed-ish vocabulary per spec §5. The spec lists\n * `tense | warm | clinical | mischievous | ...` with an explicit \"...\",\n * signalling the canonical set is open to extension. We validate\n * non-empty kebab-ish strings rather than enforce membership; the closed\n * set lives at the agent layer where it has UX consequence.\n */\nexport const MoodSchema = z.string().min(1);\n\n/** Transition-out kind — closed enum per spec §5. */\nexport const TransitionKindSchema = z.enum([\"cut\", \"dip-to-color\", \"whip\", \"crossfade\"]);\n\n/**\n * transition_out — the move out of this beat. `color` is a DESIGN\n * palette-token reference (e.g. `ember`), NOT a slot reference, so it is\n * a plain string here. Validation of token-existence is a cross-artifact\n * concern handled outside this schema.\n */\nexport const TransitionOutSchema = z.object({\n kind: TransitionKindSchema,\n duration_s: z.number().nonnegative().optional(),\n color: z.string().min(1).optional(),\n}).strict();\n\n/** A single SFX entry on a beat — a slot ref + gain. */\nexport const SfxEntrySchema = z.object({\n slot: SlotRefSchema,\n gain_db: z.number(),\n}).strict();\n\n/** Single storyboard beat. */\nexport const StoryboardBeatSchema = z.object({\n id: ScriptBeatIdSchema,\n window: BeatWindowSchema,\n mood: z.array(MoodSchema).min(1),\n camera: CameraSchema,\n slots: z.array(SlotRefSchema),\n techniques: z.array(z.string().min(1)),\n transition_out: TransitionOutSchema,\n sfx: z.array(SfxEntrySchema).optional(),\n}).strict();\n\nexport type StoryboardBeat = z.infer<typeof StoryboardBeatSchema>;\n\n/** Aspect ratios — closed enum (matches DESIGN's). Inlined to avoid a circular import. */\nexport const StoryboardAspectRatioSchema = z.enum([\"9:16\", \"1:1\", \"16:9\", \"4:5\"]);\n\n/** StoryboardArtifactSchema — frontmatter + ordered beats. */\nexport const StoryboardArtifactSchema = z.object({\n technique_library_version: z.number().int().positive(),\n aspect_ratio: StoryboardAspectRatioSchema,\n beats: z.array(StoryboardBeatSchema).min(1),\n}).strict();\n\nexport type StoryboardArtifact = z.infer<typeof StoryboardArtifactSchema>;\n\n/** True if a storyboard beat id is in the b-roll namespace (script-id exempt). */\nexport function isBRollBeatId(id: string): boolean {\n return id.startsWith(\"b-roll.\");\n}\n","import { parse as yamlParse, stringify as yamlStringify } from \"yaml\";\nimport { DesignArtifactSchema } from \"./design.js\";\nimport type { DesignArtifact } from \"./design.js\";\nimport {\n ScriptArtifactSchema,\n NarratedScriptSchema,\n NarratedBeatSchema,\n NonNarratedBeatSchema,\n type ScriptArtifact,\n} from \"./script.js\";\nimport { StoryboardArtifactSchema, type StoryboardArtifact } from \"./storyboard.js\";\n\n/**\n * Parsers for the three front-of-pipeline artifacts (DESIGN.md, SCRIPT.md,\n * STORYBOARD.md). Universal file shape:\n *\n * ---\n * <YAML frontmatter>\n * ---\n * <Markdown body>\n *\n * For DESIGN, the body is free prose (kept verbatim as `body`).\n * For SCRIPT, the body has `# Hook | # Story | # Proof | # CTA` headings\n * with YAML-list beats — or, in carousel/text-only modes, a flat\n * beat list. The parser lifts beats out of the body into typed fields.\n * For STORYBOARD, the body has `## <beat-id>` headings followed by a\n * YAML mapping block; the parser lifts beats into typed fields.\n *\n * Each parser returns a fully-validated artifact (throws z.ZodError on\n * structural failure — callers wrap as needed). Serializers emit a\n * canonical form so round-trip-then-re-parse is structurally stable.\n */\n\nconst FRONTMATTER_FENCE = \"---\";\n\n/** Internal: split a raw artifact string into its YAML frontmatter and body. */\nexport interface FrontmatterSplit {\n frontmatter: Record<string, unknown>;\n body: string;\n}\n\nexport function splitFrontmatter(raw: string): FrontmatterSplit {\n // Normalize newlines so Windows-style files don't break the split.\n const text = raw.replace(/\\r\\n/g, \"\\n\").replace(/^/, \"\");\n const lines = text.split(\"\\n\");\n\n // First non-empty line must be `---`.\n let i = 0;\n while (i < lines.length && lines[i].trim() === \"\") i++;\n if (i >= lines.length || lines[i].trim() !== FRONTMATTER_FENCE) {\n throw new Error(\"artifact missing leading `---` frontmatter fence\");\n }\n const startLine = i + 1;\n\n // Find closing fence.\n let endLine = -1;\n for (let j = startLine; j < lines.length; j++) {\n if (lines[j].trim() === FRONTMATTER_FENCE) {\n endLine = j;\n break;\n }\n }\n if (endLine === -1) {\n throw new Error(\"artifact missing closing `---` frontmatter fence\");\n }\n\n const frontmatterText = lines.slice(startLine, endLine).join(\"\\n\");\n const body = lines.slice(endLine + 1).join(\"\\n\").replace(/^\\n+/, \"\");\n\n let frontmatter: unknown;\n try {\n frontmatter = frontmatterText.trim().length === 0 ? {} : yamlParse(frontmatterText);\n } catch (err) {\n throw new Error(`frontmatter YAML parse error: ${(err as Error).message}`);\n }\n if (frontmatter === null || typeof frontmatter !== \"object\" || Array.isArray(frontmatter)) {\n throw new Error(\"frontmatter must be a YAML mapping\");\n }\n return { frontmatter: frontmatter as Record<string, unknown>, body };\n}\n\n/** Internal: render YAML for embedding inside a fenced block. */\nfunction dumpYaml(value: unknown): string {\n // `lineWidth: 0` keeps long lines unwrapped (better for round-trip stability).\n return yamlStringify(value, { indent: 2, lineWidth: 0 });\n}\n\n// ---------- DESIGN ----------\n\n/**\n * Parse a DESIGN.md raw string into a validated `DesignArtifact`. The body\n * is carried verbatim (no further structural lifting).\n */\nexport function parseDesign(raw: string): DesignArtifact {\n const { frontmatter, body } = splitFrontmatter(raw);\n return DesignArtifactSchema.parse({ ...frontmatter, body });\n}\n\n/** Serialize a DesignArtifact to a canonical Markdown-with-frontmatter string. */\nexport function serializeDesign(artifact: DesignArtifact): string {\n const { body, ...frontmatter } = artifact;\n return `---\\n${dumpYaml(frontmatter)}---\\n\\n${body}`;\n}\n\n// ---------- SCRIPT ----------\n\n/**\n * Extract beat lists from a SCRIPT body.\n *\n * The body uses `# Hook | # Story | # Proof | # CTA` H1 headings for\n * narrated mode, with each section containing a YAML-list of beat\n * mappings. For carousel / text-only modes, the body is a single YAML\n * list (no headings required) — but we also accept a `# Beats` heading\n * for symmetry.\n *\n * NOTE on idiom choice: we elected to put beats in the body (per the\n * spec's EXAMPLE) rather than in frontmatter — it diffs better when a\n * creator edits a single beat, which is the load-bearing re-entry move\n * (spec §7).\n */\nconst NARRATED_SECTION_HEADERS: Record<string, \"hook\" | \"story\" | \"proof\" | \"cta\"> = {\n hook: \"hook\",\n story: \"story\",\n proof: \"proof\",\n cta: \"cta\",\n};\n\ninterface ParsedScriptBody {\n blocks?: { hook: unknown[]; story: unknown[]; proof: unknown[]; cta: unknown[] };\n beats?: unknown[];\n remainder: string;\n}\n\nfunction parseScriptBody(body: string, mode: string): ParsedScriptBody {\n const text = body.replace(/\\r\\n/g, \"\\n\");\n const lines = text.split(\"\\n\");\n\n if (mode === \"narrated\") {\n const blocks: { hook: unknown[]; story: unknown[]; proof: unknown[]; cta: unknown[] } = {\n hook: [], story: [], proof: [], cta: [],\n };\n let currentKey: \"hook\" | \"story\" | \"proof\" | \"cta\" | null = null;\n let buffer: string[] = [];\n const remainder: string[] = [];\n\n const flush = () => {\n if (currentKey === null) return;\n const yamlText = buffer.join(\"\\n\").trim();\n if (yamlText.length === 0) return;\n const parsed = yamlParse(yamlText);\n if (!Array.isArray(parsed)) {\n throw new Error(`SCRIPT section \"${currentKey}\" must be a YAML list of beats`);\n }\n blocks[currentKey] = parsed;\n };\n\n for (const line of lines) {\n const headingMatch = /^#\\s+(.+?)\\s*$/.exec(line);\n if (headingMatch) {\n const key = headingMatch[1].toLowerCase();\n if (key in NARRATED_SECTION_HEADERS) {\n flush();\n currentKey = NARRATED_SECTION_HEADERS[key];\n buffer = [];\n continue;\n }\n }\n if (currentKey === null) {\n remainder.push(line);\n } else {\n buffer.push(line);\n }\n }\n flush();\n return { blocks, remainder: remainder.join(\"\\n\").trim() };\n }\n\n // carousel / text-only — single YAML list, optionally under `# Beats`.\n const beatLines: string[] = [];\n const remainder: string[] = [];\n let inBeats = false;\n let sawAnyHeading = false;\n for (const line of lines) {\n const headingMatch = /^#\\s+(.+?)\\s*$/.exec(line);\n if (headingMatch) {\n sawAnyHeading = true;\n inBeats = headingMatch[1].toLowerCase() === \"beats\";\n continue;\n }\n if (sawAnyHeading) {\n if (inBeats) beatLines.push(line);\n else remainder.push(line);\n } else {\n beatLines.push(line);\n }\n }\n const yamlText = beatLines.join(\"\\n\").trim();\n let beats: unknown[] = [];\n if (yamlText.length > 0) {\n const parsed = yamlParse(yamlText);\n if (!Array.isArray(parsed)) {\n throw new Error(`SCRIPT body must be a YAML list of beats (mode: ${mode})`);\n }\n beats = parsed;\n }\n return { beats, remainder: remainder.join(\"\\n\").trim() };\n}\n\n/** Parse a SCRIPT.md raw string into a validated `ScriptArtifact`. */\nexport function parseScript(raw: string): ScriptArtifact {\n const { frontmatter, body } = splitFrontmatter(raw);\n const mode = (frontmatter as { mode?: unknown }).mode;\n if (mode !== \"narrated\" && mode !== \"carousel\" && mode !== \"text-only\") {\n throw new Error(`SCRIPT frontmatter \"mode\" must be one of narrated | carousel | text-only (got ${JSON.stringify(mode)})`);\n }\n const parsed = parseScriptBody(body, mode);\n\n const assembled: Record<string, unknown> = { ...frontmatter, body: parsed.remainder };\n if (mode === \"narrated\") {\n assembled.blocks = parsed.blocks;\n } else {\n assembled.beats = parsed.beats;\n }\n return ScriptArtifactSchema.parse(assembled);\n}\n\n/** Serialize a ScriptArtifact to a canonical Markdown-with-frontmatter string. */\nexport function serializeScript(artifact: ScriptArtifact): string {\n if (artifact.mode === \"narrated\") {\n // Validate-and-strip default-bearing fields via the per-beat schema so\n // round-trip stays canonical (bind_to_transcript defaults to true).\n const { mode, target_duration_s, language, tts_voice, blocks, body } = artifact;\n const fm: Record<string, unknown> = { mode, target_duration_s, language };\n if (tts_voice !== undefined) fm.tts_voice = tts_voice;\n const sectionDump = (key: \"Hook\" | \"Story\" | \"Proof\" | \"CTA\", beats: unknown[]) =>\n `# ${key}\\n${dumpYaml(beats).trimEnd()}\\n`;\n const sections = [\n sectionDump(\"Hook\", blocks.hook),\n sectionDump(\"Story\", blocks.story),\n sectionDump(\"Proof\", blocks.proof),\n sectionDump(\"CTA\", blocks.cta),\n ].join(\"\\n\");\n const trailingBody = body.trim().length > 0 ? `\\n${body.trim()}\\n` : \"\";\n return `---\\n${dumpYaml(fm)}---\\n\\n${sections}${trailingBody}`;\n }\n const { mode, target_duration_s, language, tts_voice, beats, body } = artifact;\n const fm: Record<string, unknown> = { mode, target_duration_s, language };\n if (tts_voice !== undefined) fm.tts_voice = tts_voice;\n const trailingBody = body.trim().length > 0 ? `\\n${body.trim()}\\n` : \"\";\n return `---\\n${dumpYaml(fm)}---\\n\\n# Beats\\n${dumpYaml(beats).trimEnd()}\\n${trailingBody}`;\n}\n\n/** Re-export — useful for fixture builders that want to validate piecewise. */\nexport { NarratedBeatSchema, NonNarratedBeatSchema, NarratedScriptSchema };\n\n// ---------- STORYBOARD ----------\n\n/**\n * Extract a STORYBOARD body into a list of beats. Each beat is a `## <id>`\n * heading followed by a YAML mapping block (lines until the next `##`).\n */\nfunction parseStoryboardBody(body: string): unknown[] {\n const text = body.replace(/\\r\\n/g, \"\\n\");\n const lines = text.split(\"\\n\");\n\n type Pending = { id: string; buffer: string[] };\n const beats: Pending[] = [];\n let current: Pending | null = null;\n\n for (const line of lines) {\n const headingMatch = /^##\\s+(.+?)\\s*$/.exec(line);\n if (headingMatch) {\n if (current !== null) beats.push(current);\n current = { id: headingMatch[1].trim(), buffer: [] };\n continue;\n }\n if (current !== null) {\n current.buffer.push(line);\n }\n }\n if (current !== null) beats.push(current);\n\n return beats.map(({ id, buffer }) => {\n const yamlText = buffer.join(\"\\n\").trim();\n if (yamlText.length === 0) {\n return { id };\n }\n const parsed = yamlParse(yamlText);\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new Error(`STORYBOARD beat \"${id}\" body must be a YAML mapping`);\n }\n return { id, ...(parsed as Record<string, unknown>) };\n });\n}\n\n/** Parse a STORYBOARD.md raw string into a validated `StoryboardArtifact`. */\nexport function parseStoryboard(raw: string): StoryboardArtifact {\n const { frontmatter, body } = splitFrontmatter(raw);\n const beats = parseStoryboardBody(body);\n return StoryboardArtifactSchema.parse({ ...frontmatter, beats });\n}\n\n/** Serialize a StoryboardArtifact to a canonical Markdown-with-frontmatter string. */\nexport function serializeStoryboard(artifact: StoryboardArtifact): string {\n const { technique_library_version, aspect_ratio, beats } = artifact;\n const fm = { technique_library_version, aspect_ratio };\n const beatBlocks = beats.map((beat) => {\n const { id, ...rest } = beat;\n return `## ${id}\\n${dumpYaml(rest).trimEnd()}\\n`;\n }).join(\"\\n\");\n return `---\\n${dumpYaml(fm)}---\\n\\n${beatBlocks}`;\n}\n","import type { DesignArtifact } from \"./design.js\";\nimport { allScriptBeats, type ScriptArtifact } from \"./script.js\";\nimport { isBRollBeatId, type StoryboardArtifact } from \"./storyboard.js\";\nimport { parseSlotRef, SLOT_KINDS } from \"./slot.js\";\nimport { getTechniqueLibrary } from \"./technique.js\";\n\n/**\n * Cross-artifact validation result.\n *\n * NOTE: this return shape (`{ ok, warnings, errors }`) is intentionally\n * DIFFERENT from the recipe-package's `{ success, warnings? }` idiom.\n * The artifacts surface validates multiple independent rules whose\n * severities differ (some are warn-only pre-VO, others are hard fails)\n * — flattening to a single discriminated result loses that.\n */\nexport interface ArtifactSetValidationResult {\n ok: boolean;\n warnings: string[];\n errors: string[];\n}\n\nexport interface ArtifactSet {\n design?: DesignArtifact;\n script?: ScriptArtifact;\n storyboard?: StoryboardArtifact;\n}\n\n/**\n * Run cross-artifact validation:\n * 1. Every STORYBOARD beat id must match a SCRIPT beat id, UNLESS the\n * storyboard id is prefixed `b-roll.` (exemption per task spec).\n * 2. Every slot reference (in storyboard beats and sfx entries) parses\n * to `<kind>.<category>.<specifier>` and uses a known kind.\n * 3. Every storyboard technique exists in the active technique library\n * (resolved by `technique_library_version`).\n * 4. Sum of script beat `est_duration_s` ≤ `target_duration_s`\n * (WARN-ONLY pre-VO; the Iris pipeline will hard-fail post-transcript\n * binding — that's later code, not this validator's job).\n *\n * This function never throws — callers decide what to do with `ok`.\n */\nexport function validateArtifactSet(set: ArtifactSet): ArtifactSetValidationResult {\n const warnings: string[] = [];\n const errors: string[] = [];\n\n // --- (1) Beat-id matching between STORYBOARD and SCRIPT ---\n if (set.storyboard && set.script) {\n const scriptIds = new Set(allScriptBeats(set.script).map((b) => b.id));\n for (const beat of set.storyboard.beats) {\n if (isBRollBeatId(beat.id)) continue;\n if (!scriptIds.has(beat.id)) {\n errors.push(\n `STORYBOARD beat \"${beat.id}\" has no matching SCRIPT beat (and is not prefixed b-roll.)`,\n );\n }\n }\n }\n\n // --- (2) Slot reference validity ---\n if (set.storyboard) {\n for (const beat of set.storyboard.beats) {\n for (const slot of beat.slots) {\n const parsed = parseSlotRef(slot);\n if (parsed === null) {\n errors.push(\n `STORYBOARD beat \"${beat.id}\" slot \"${slot}\" is malformed or has an unknown kind (expected one of ${SLOT_KINDS.join(\", \")})`,\n );\n }\n }\n if (beat.sfx) {\n for (const sfx of beat.sfx) {\n const parsed = parseSlotRef(sfx.slot);\n if (parsed === null) {\n errors.push(\n `STORYBOARD beat \"${beat.id}\" sfx slot \"${sfx.slot}\" is malformed or has an unknown kind`,\n );\n }\n }\n }\n }\n }\n\n // --- (3) Technique library membership ---\n if (set.storyboard) {\n const lib = getTechniqueLibrary(set.storyboard.technique_library_version);\n if (lib === null) {\n errors.push(\n `STORYBOARD references unknown technique_library_version ${set.storyboard.technique_library_version}`,\n );\n } else {\n const known = new Set(lib.techniques);\n for (const beat of set.storyboard.beats) {\n for (const t of beat.techniques) {\n if (!known.has(t)) {\n errors.push(\n `STORYBOARD beat \"${beat.id}\" uses unknown technique \"${t}\" (technique_library_version=${lib.version})`,\n );\n }\n }\n }\n }\n }\n\n // --- (4) Duration sum (WARN-ONLY pre-VO) ---\n if (set.script) {\n const sum = allScriptBeats(set.script).reduce((acc, b) => acc + b.est_duration_s, 0);\n if (sum > set.script.target_duration_s) {\n warnings.push(\n `SCRIPT beat est_duration_s sum (${sum.toFixed(2)}s) exceeds target_duration_s (${set.script.target_duration_s}s) — warn only pre-VO; will hard-fail post-transcript binding`,\n );\n }\n }\n\n return { ok: errors.length === 0, warnings, errors };\n}\n","import { z } from \"zod\";\n\n/**\n * Workspace manifest schema — the YAML-serialized contents of\n * `workspace.atelier` at the root of an Atelier workspace.\n *\n * A workspace is a layer above Project: a single directory holding multiple\n * content Projects with shared creator-level configuration (learning mode,\n * mind-map, recipes). The manifest itself stays tiny on purpose — most\n * workspace-level state lives in `.atelier/` siblings on disk, not in this\n * file. Fields listed here are the minimum needed to:\n * - discover the workspace from any descendant cwd (`workspace.atelier`\n * is the on-disk signal)\n * - propagate creator preferences into newly-init'd projects\n * (`default_mode`)\n * - hint which subdirs are Projects (`projects?`); on-disk truth is\n * authoritative — `projects` is advisory and may be refreshed by\n * scanning for `project.atelier` files.\n *\n * `workspace.atelier` is YAML (intentionally — distinguishes it from\n * `project.atelier` which is JSON) and shares the `.atelier` extension as\n * a family marker. The two never collide in a single directory.\n */\n\n/**\n * WorkspaceManifest — top-level. Strict on unknown keys; same posture as the\n * recipe schema so authoring typos surface at validate time rather than being\n * silently dropped.\n */\nexport const WorkspaceManifestSchema = z.object({\n /** Manifest schema version. Pinned to '1.0' for the initial release. */\n version: z.literal(\"1.0\"),\n /** Display name; defaults to the workspace dir basename at create time. */\n name: z.string().min(1),\n /** ISO 8601 timestamp set by the writer. */\n created: z.string().min(1),\n /**\n * Inherited by new projects created via `atelier init` inside this\n * workspace. The creator pre-declares the autonomy posture once at the\n * workspace level; per-project overrides still allowed via per-project\n * `.atelier/learning-mode.yaml` (writes always happen at workspace level\n * unless explicitly overridden).\n */\n default_mode: z.enum([\"ambient\", \"explicit\"]),\n /**\n * Relative paths (single-segment subdir names) of known Projects inside\n * the workspace. Advisory only — on-disk `project.atelier` presence is\n * authoritative. Refresh via `listProjects(workspaceDir)`.\n */\n projects: z.array(z.string().min(1)).optional(),\n /** Free-text creator description. Optional. */\n description: z.string().optional(),\n}).strict();\n\n/** Inferred TS type for the manifest. */\nexport type WorkspaceManifest = z.infer<typeof WorkspaceManifestSchema>;\n","// MediaNotes — sidecar notes for a media file.\n//\n// Lives at `<media-file>.notes.md` next to every media file in a Project's\n// `media/` directory. Structured YAML frontmatter (constraints + tags) plus a\n// freeform Markdown body with conventional per-author headings (Lux notes,\n// Quill notes, Iris notes, Creator notes). Inert artifact — pure data, no\n// executables, no upload paths (TD-229, TD-271).\n//\n// The frontmatter is the contract Lux/Iris/Quill honor at slot-binding time:\n// - constraints.do_not[] — non-negotiable exclusions\n// - constraints.prefer_for[] — soft preference signals\n// - tags[] — free-form labels the mind-map / scorer reads\n//\n// The body is the conversation log between the creator and the agent team.\n// Each author appends to their own section (sentinel headings); idempotent\n// replacement keeps the file readable across many sessions.\n\nimport { z } from \"zod\";\n\n/**\n * Free-form string list that may not contain blank entries — used for tags,\n * do_not, and prefer_for. Empties are dropped at parse time so a forgotten\n * trailing dash in YAML doesn't break a build.\n */\nconst NonEmptyStringListSchema = z.array(z.string().min(1));\n\n/** Optional constraint sub-object on a notes frontmatter. */\nexport const MediaNotesConstraintsSchema = z.object({\n /**\n * Hard exclusions. Lux + Iris MUST refuse to bind this media to a slot if\n * any do_not rule applies (the rule is a free-text string the agent reads\n * with judgement — e.g. \"never use in a CTA beat\", \"no client logos\").\n */\n do_not: NonEmptyStringListSchema.optional(),\n /**\n * Soft preferences — weight up candidacy when these conditions hold. Same\n * free-text shape as do_not but the polarity is positive (\"good for hooks\",\n * \"prefer for warm tone\").\n */\n prefer_for: NonEmptyStringListSchema.optional(),\n}).strict();\n\nexport type MediaNotesConstraints = z.infer<typeof MediaNotesConstraintsSchema>;\n\n/**\n * Frontmatter shape — the typed half of a `<file>.notes.md`. Required fields\n * (file/added/added_by) are written by `scaffoldMediaNotes` at ingest time;\n * everything else accumulates over the life of the project.\n */\nexport const MediaNotesFrontmatterSchema = z.object({\n /** Basename of the media file this notes file sits next to. */\n file: z.string().min(1),\n /** ISO 8601 timestamp captured at ingest. */\n added: z.string().min(1),\n /** User identifier (env $USER at ingest, \"creator\" fallback). */\n added_by: z.string().min(1),\n /** Free-form labels. Empty by default; agents and creator both append. */\n tags: NonEmptyStringListSchema.default([]),\n /** Optional do_not / prefer_for object. Absent means \"no rules yet.\" */\n constraints: MediaNotesConstraintsSchema.optional(),\n}).strict();\n\nexport type MediaNotesFrontmatter = z.infer<typeof MediaNotesFrontmatterSchema>;\n\n/**\n * Whole-file shape — frontmatter plus the Markdown body. The body is parsed\n * as opaque text; the per-author section convention is enforced at write time\n * (via `appendNoteSection`) rather than at the schema level so creators can\n * freely edit the body without breaking validation.\n */\nexport const MediaNotesSchema = z.object({\n frontmatter: MediaNotesFrontmatterSchema,\n body: z.string(),\n}).strict();\n\nexport type MediaNotes = z.infer<typeof MediaNotesSchema>;\n\n/**\n * Closed enum of author identities recognized in per-section appends. Matches\n * the persona ids registered in `.paradigm/personas/index.yaml` (lux/quill/\n * iris) plus the `creator` self-author.\n */\nexport const MediaNotesAuthorSchema = z.enum([\"lux\", \"quill\", \"iris\", \"creator\"]);\nexport type MediaNotesAuthor = z.infer<typeof MediaNotesAuthorSchema>;\n\n/**\n * Stable per-author heading text — exported for the CLI/MCP writers so the\n * sentinel format never drifts. Format: `## <Name> notes (<role>?)` where the\n * \"(role)\" suffix matches the canonical persona file for the named persona.\n */\nexport const MEDIA_NOTES_HEADINGS: Record<MediaNotesAuthor, string> = {\n lux: \"## Lux notes (cinematographer)\",\n quill: \"## Quill notes (showrunner)\",\n iris: \"## Iris notes (composer)\",\n creator: \"## Creator notes\",\n};\n","import { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { Command } from \"commander\";\nimport type { AtelierDocument } from \"@a-company/atelier-types\";\nimport { parseAtelier } from \"@a-company/atelier-schema\";\n\n/**\n * Info summary for an AtelierDocument.\n */\nexport interface DocumentInfo {\n name: string;\n description?: string;\n canvas: {\n width: number;\n height: number;\n fps: number;\n background?: string;\n };\n layers: {\n count: number;\n items: { id: string; type: string }[];\n };\n states: {\n count: number;\n items: { name: string; duration: number; deltaCount: number }[];\n };\n presets: {\n count: number;\n };\n}\n\n/**\n * Extract summary info from a parsed AtelierDocument.\n */\nexport function getInfo(doc: AtelierDocument): DocumentInfo {\n return {\n name: doc.name,\n description: doc.description,\n canvas: {\n width: doc.canvas.width,\n height: doc.canvas.height,\n fps: doc.canvas.fps,\n background: doc.canvas.background,\n },\n layers: {\n count: doc.layers.length,\n items: doc.layers.map((layer) => ({\n id: layer.id,\n type: layer.visual.type,\n })),\n },\n states: {\n count: Object.keys(doc.states).length,\n items: Object.entries(doc.states).map(([name, state]) => ({\n name,\n duration: state.duration,\n deltaCount: state.deltas.length,\n })),\n },\n presets: {\n count: doc.presets ? Object.keys(doc.presets).length : 0,\n },\n };\n}\n\n/**\n * Format a DocumentInfo for terminal output.\n */\nfunction formatInfo(info: DocumentInfo): string {\n const lines: string[] = [];\n\n lines.push(`Name: ${info.name}`);\n if (info.description) {\n lines.push(`Description: ${info.description}`);\n }\n\n const bg = info.canvas.background\n ? `, background: ${info.canvas.background}`\n : \"\";\n lines.push(\n `Canvas: ${info.canvas.width}x${info.canvas.height} @ ${info.canvas.fps}fps${bg}`,\n );\n\n lines.push(`Layers: ${info.layers.count}`);\n for (const layer of info.layers.items) {\n lines.push(` - ${layer.id} (${layer.type})`);\n }\n\n lines.push(`States: ${info.states.count}`);\n for (const state of info.states.items) {\n lines.push(\n ` - ${state.name}: ${state.duration} frames, ${state.deltaCount} deltas`,\n );\n }\n\n if (info.presets.count > 0) {\n lines.push(`Presets: ${info.presets.count}`);\n }\n\n return lines.join(\"\\n\");\n}\n\n/** Read and parse an .atelier file, exiting on failure. */\nfunction readAndParse(file: string): AtelierDocument {\n const absPath = resolve(file);\n let content: string;\n try {\n content = readFileSync(absPath, \"utf-8\");\n } catch {\n console.error(`Cannot read file: ${absPath}`);\n return process.exit(1);\n }\n\n const result = parseAtelier(content);\n if (!result.success) {\n console.error(\"Parse errors:\");\n for (const error of result.errors) {\n console.error(` - ${error.path}: ${error.message}`);\n }\n return process.exit(1);\n }\n\n return result.data;\n}\n\n/**\n * Register the `info` subcommand on the Commander program.\n */\nexport function infoCommand(program: Command): void {\n program\n .command(\"info <file>\")\n .description(\"Display summary info for an .atelier file\")\n .action((file: string) => {\n const doc = readAndParse(file);\n const info = getInfo(doc);\n console.log(formatInfo(info));\n });\n}\n","import { readFileSync, writeFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { Command } from \"commander\";\nimport type { AtelierDocument } from \"@a-company/atelier-types\";\nimport { parseAtelier } from \"@a-company/atelier-schema\";\nimport { resolveFrame } from \"@a-company/atelier-core\";\nimport type { ResolvedFrame } from \"@a-company/atelier-core\";\n\n/**\n * Resolve a single frame from a document, returning the resolved frame data.\n * If stateName is not provided, uses the first state.\n * If frame is not provided, uses frame 0.\n */\nexport function resolveStill(\n doc: AtelierDocument,\n stateName?: string,\n frame?: number,\n): ResolvedFrame {\n const stateNames = Object.keys(doc.states);\n if (stateNames.length === 0) {\n throw new Error(\"Document has no states\");\n }\n\n const resolvedStateName = stateName ?? stateNames[0];\n if (!(resolvedStateName in doc.states)) {\n throw new Error(\n `State \"${resolvedStateName}\" not found. Available: ${stateNames.join(\", \")}`,\n );\n }\n\n const resolvedFrame = frame ?? 0;\n return resolveFrame(doc, resolvedStateName, resolvedFrame);\n}\n\n/** Read and parse an .atelier file, exiting on failure. */\nfunction readAndParse(file: string): AtelierDocument {\n const absPath = resolve(file);\n let content: string;\n try {\n content = readFileSync(absPath, \"utf-8\");\n } catch {\n console.error(`Cannot read file: ${absPath}`);\n return process.exit(1);\n }\n\n const result = parseAtelier(content);\n if (!result.success) {\n console.error(\"Parse errors:\");\n for (const error of result.errors) {\n console.error(` - ${error.path}: ${error.message}`);\n }\n return process.exit(1);\n }\n\n return result.data;\n}\n\n/**\n * Register the `still` subcommand on the Commander program.\n */\nexport function stillCommand(program: Command): void {\n program\n .command(\"still <file>\")\n .description(\n \"Resolve a single frame and output as JSON or render as PNG\",\n )\n .option(\"-s, --state <name>\", \"State name (defaults to first state)\")\n .option(\n \"-f, --frame <number>\",\n \"Frame number (defaults to 0)\",\n \"0\",\n )\n .option(\n \"--format <type>\",\n \"Output format: json | png (default: json)\",\n \"json\",\n )\n .option(\"-o, --output <path>\", \"Output file path (default: stdout)\")\n .action(\n async (\n file: string,\n options: { state?: string; frame: string; format: string; output?: string },\n ) => {\n const doc = readAndParse(file);\n\n const frameNumber = parseInt(options.frame, 10);\n if (isNaN(frameNumber) || frameNumber < 0) {\n console.error(\n `Invalid frame number: ${options.frame}`,\n );\n process.exit(1);\n return;\n }\n\n if (options.format !== \"json\" && options.format !== \"png\") {\n console.error(`Unknown format: \"${options.format}\". Use json or png.`);\n process.exit(1);\n return;\n }\n\n try {\n const resolved = resolveStill(\n doc,\n options.state,\n frameNumber,\n );\n\n if (options.format === \"json\") {\n const json = JSON.stringify(resolved, null, 2);\n if (options.output) {\n writeFileSync(resolve(options.output), json, \"utf-8\");\n } else {\n console.log(json);\n }\n } else {\n // PNG format — rasterize via @napi-rs/canvas (prebuilt platform\n // binaries; no node-gyp / system libraries, so PNG output works on\n // a plain install).\n const { createCanvas } = await import(\"@napi-rs/canvas\");\n const { renderFrame } = await import(\"@a-company/atelier-canvas\");\n\n const cvs = createCanvas(doc.canvas.width, doc.canvas.height);\n const ctx = cvs.getContext(\"2d\");\n renderFrame(ctx as unknown as import(\"@a-company/atelier-canvas\").RenderContext, resolved, doc);\n\n const buffer = cvs.toBuffer(\"image/png\");\n if (options.output) {\n writeFileSync(resolve(options.output), buffer);\n } else {\n process.stdout.write(buffer);\n }\n }\n } catch (err) {\n console.error(\n (err as Error).message,\n );\n process.exit(1);\n }\n },\n );\n}\n","/**\n * `atelier render <file>` — render an animation to MP4 or GIF via FFmpeg.\n *\n * Usage:\n * atelier render animation.atelier → animation.mp4\n * atelier render animation.atelier -f gif → animation.gif\n * atelier render animation.atelier -o out/video.mp4 → custom path\n * atelier render animation.atelier -s intro outro → specific states\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { resolve, basename, extname } from \"node:path\";\nimport type { Command } from \"commander\";\nimport type { AtelierDocument } from \"@a-company/atelier-types\";\nimport { parseAtelier } from \"@a-company/atelier-schema\";\nimport {\n checkFfmpeg,\n renderDocument,\n type RenderFormat,\n} from \"./render-pipeline.js\";\n\n/** Read and parse an .atelier file, exiting on failure. */\nfunction readAndParse(file: string): AtelierDocument {\n const absPath = resolve(file);\n let content: string;\n try {\n content = readFileSync(absPath, \"utf-8\");\n } catch {\n console.error(`Cannot read file: ${absPath}`);\n return process.exit(1);\n }\n\n const result = parseAtelier(content);\n if (!result.success) {\n console.error(\"Parse errors:\");\n for (const error of result.errors) {\n console.error(` - ${error.path}: ${error.message}`);\n }\n return process.exit(1);\n }\n\n return result.data;\n}\n\nfunction inferFormat(output: string | undefined): RenderFormat {\n if (!output) return \"mp4\";\n const ext = extname(output).toLowerCase();\n if (ext === \".gif\") return \"gif\";\n return \"mp4\";\n}\n\n/** Register the `render` subcommand on the Commander program. */\nexport function renderCommand(program: Command): void {\n program\n .command(\"render <file>\")\n .description(\"Render animation to MP4 or GIF via FFmpeg\")\n .option(\"-o, --output <path>\", \"Output file path\")\n .option(\"-f, --format <type>\", \"Output format: mp4 | gif\")\n .option(\n \"-s, --state <names...>\",\n \"State(s) to render (default: all in order)\",\n )\n .action(\n async (\n file: string,\n options: {\n output?: string;\n format?: string;\n state?: string[];\n },\n ) => {\n // Check FFmpeg availability\n const hasFfmpeg = await checkFfmpeg();\n if (!hasFfmpeg) {\n console.error(\"FFmpeg is not installed or not in PATH.\");\n console.error(\"Install it:\");\n console.error(\" macOS: brew install ffmpeg\");\n console.error(\" Ubuntu: sudo apt install ffmpeg\");\n console.error(\" Windows: https://ffmpeg.org/download.html\");\n process.exit(1);\n return;\n }\n\n const doc = readAndParse(file);\n\n // Resolve format\n let format: RenderFormat;\n if (options.format) {\n if (options.format !== \"mp4\" && options.format !== \"gif\") {\n console.error(\n `Unknown format: \"${options.format}\". Use mp4 or gif.`,\n );\n process.exit(1);\n return;\n }\n format = options.format;\n } else {\n format = inferFormat(options.output);\n }\n\n // Resolve output path\n const inputName = basename(file, extname(file));\n const output = options.output ?? `${inputName}.${format}`;\n\n const startTime = Date.now();\n\n try {\n const result = await renderDocument(doc, {\n output: resolve(output),\n format,\n states: options.state,\n // Absolute path of the input doc — the pipeline uses this to\n // resolve relative `media/...` paths in VideoVisual layers so\n // ffmpeg can read the source video as a second input (overlay\n // base + audio passthrough). Without this, render falls back to\n // canvas-only mode — no source pixels, no audio.\n docPath: resolve(file),\n onProgress: ({ frame, totalFrames, state, percent }) => {\n const elapsed = (Date.now() - startTime) / 1000;\n const rate = elapsed > 0 ? frame / elapsed : 0;\n const remaining =\n rate > 0 ? (totalFrames - frame) / rate : 0;\n process.stderr.write(\n `\\rRendering: frame ${frame}/${totalFrames} (${percent}%) - state \"${state}\" - ETA ${remaining.toFixed(1)}s`,\n );\n },\n });\n\n process.stderr.write(\"\\n\");\n console.log(\n `Done: ${result.totalFrames} frames \\u2192 ${result.output} (${(result.durationMs / 1000).toFixed(1)}s)`,\n );\n } catch (err) {\n process.stderr.write(\"\\n\");\n console.error((err as Error).message);\n process.exit(1);\n }\n },\n );\n}\n","/**\n * Core render pipeline — renders an Atelier document to MP4 or GIF via FFmpeg.\n * Pure pipeline with no Commander dependency; testable in isolation.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { dirname, resolve, isAbsolute } from \"node:path\";\nimport type { AtelierDocument } from \"@a-company/atelier-types\";\nimport { resolveFrame } from \"@a-company/atelier-core\";\nimport { renderFrame, ImageCache } from \"@a-company/atelier-canvas\";\n\n// ─── Types ───────────────────────────────────────────────────\n\nexport type RenderFormat = \"mp4\" | \"gif\";\n\nexport interface RenderOptions {\n output: string;\n format: RenderFormat;\n states?: string[];\n onProgress?: (info: ProgressInfo) => void;\n /**\n * Absolute path of the .atelier document being rendered. Used to resolve\n * relative `media/...` src paths in VideoVisual layers. When set, the\n * pipeline detects the first video layer, passes the source MOV/MP4 to\n * ffmpeg as a SECOND input, and uses an overlay filter to composite the\n * caption canvas ON TOP of the source video frames (instead of feeding\n * the encoder a synthetic canvas-only stream). Source audio is also\n * mapped through verbatim. This is what closes the\n * RENDER-AUDIO-ISSUE.md gap reported by the agent team on 2026-05-28.\n *\n * Omitted → legacy behaviour (canvas-only frames, no audio).\n */\n docPath?: string;\n}\n\nexport interface ProgressInfo {\n frame: number;\n totalFrames: number;\n state: string;\n percent: number;\n}\n\nexport interface RenderResult {\n output: string;\n format: RenderFormat;\n totalFrames: number;\n states: string[];\n durationMs: number;\n}\n\n// ─── FFmpeg Helpers ──────────────────────────────────────────\n\n/** Check whether FFmpeg is available on the system PATH. */\nexport async function checkFfmpeg(): Promise<boolean> {\n return new Promise((resolve) => {\n const proc = spawn(\"ffmpeg\", [\"-version\"], { stdio: \"pipe\" });\n proc.on(\"error\", () => resolve(false));\n proc.on(\"close\", (code) => resolve(code === 0));\n });\n}\n\n/**\n * Build the FFmpeg argument array for the given format. Pure function.\n *\n * When `sourceVideoPath` is provided, ffmpeg gets TWO inputs:\n * - Input 0: the source video file (decoded photographic frames + audio)\n * - Input 1: raw BGRA canvas frames on stdin (the caption overlay)\n *\n * A `[0:v][1:v]overlay` filter composites the canvas on top of the source\n * video at matching timestamps. Source audio is copied through with\n * `-map 0:a -c:a copy` (no re-encode). The MP4 output then has full source\n * video bitrate + source audio + caption overlays — what the studio's\n * MediaRecorder path produces by accident, but deterministically and\n * scriptable for agent use.\n *\n * When `sourceVideoPath` is omitted, falls back to the legacy stdin-only\n * mode (canvas frames only, no source pixels, no audio). Used by docs\n * without any VideoVisual layers (e.g. text-only animations, image\n * carousels).\n */\nexport function buildFfmpegArgs(\n width: number,\n height: number,\n fps: number,\n format: RenderFormat,\n output: string,\n sourceVideoPath?: string,\n): string[] {\n if (sourceVideoPath && format === \"mp4\") {\n // Two-input overlay mode — closes the canvas-only / no-audio gap.\n return [\n \"-y\",\n // Input 0: source video (+ audio). ffmpeg auto-decodes via libavformat.\n \"-i\", sourceVideoPath,\n // Input 1: canvas overlay frames on stdin. Same dimensions + fps as\n // the doc so the overlay aligns frame-for-frame with input 0.\n \"-f\", \"rawvideo\",\n \"-pix_fmt\", \"bgra\",\n \"-s\", `${width}x${height}`,\n \"-r\", String(fps),\n \"-i\", \"pipe:0\",\n // Filter graph:\n // 1. Scale + pad the source video to the doc's canvas dimensions\n // with `force_original_aspect_ratio=decrease` (letterbox-fit, no\n // crop) so a 4K iPhone source rendered into a 1080x1920 doc\n // respects the doc's intent instead of upscaling everything to\n // source dimensions. The pad fills gutters with black if the\n // source aspect differs from the canvas aspect.\n // 2. Overlay the caption canvas on top. Canvas frames must have\n // transparent backgrounds — renderFrame's clearRect (fixed\n // 2026-05-28) gives true transparency by default.\n \"-filter_complex\",\n `[0:v]scale=${width}:${height}:force_original_aspect_ratio=decrease,pad=${width}:${height}:(ow-iw)/2:(oh-ih)/2:color=black[bg];[bg][1:v]overlay=0:0[v]`,\n \"-map\", \"[v]\",\n // Copy the source audio track verbatim. No re-encode.\n \"-map\", \"0:a?\",\n \"-c:v\", \"libx264\",\n \"-pix_fmt\", \"yuv420p\",\n \"-preset\", \"medium\",\n \"-crf\", \"18\",\n \"-c:a\", \"copy\",\n // `-shortest` so the output ends with whichever stream finishes first\n // (typically the source video — we render exactly that many canvas\n // frames). Without it, if the canvas had a few extra frames the\n // output would have a silent tail.\n \"-shortest\",\n \"-movflags\", \"+faststart\",\n output,\n ];\n }\n\n // Legacy stdin-only path — canvas frames only, no source video, no audio.\n // Used when the doc has no VideoVisual layers, OR for GIF (which has no\n // audio container anyway).\n const input = [\n \"-y\",\n \"-f\", \"rawvideo\",\n \"-pix_fmt\", \"bgra\",\n \"-s\", `${width}x${height}`,\n \"-r\", String(fps),\n \"-i\", \"pipe:0\",\n ];\n\n if (format === \"mp4\") {\n return [\n ...input,\n \"-c:v\", \"libx264\",\n \"-pix_fmt\", \"yuv420p\",\n \"-preset\", \"medium\",\n \"-crf\", \"18\",\n \"-movflags\", \"+faststart\",\n output,\n ];\n }\n\n // GIF — single-pass palette generation (stdin-compatible)\n return [\n ...input,\n \"-vf\", \"split[s0][s1];[s0]palettegen=stats_mode=single[p];[s1][p]paletteuse=dither=sierra2_4a\",\n \"-loop\", \"0\",\n output,\n ];\n}\n\n/**\n * Locate the first VideoVisual layer's source file as an absolute filesystem\n * path. Returns null when no video layer exists, or when the layer's src is\n * relative and we don't know the doc's directory.\n *\n * Multi-clip rendering is v1.1 — for now we pick the first VideoVisual,\n * which matches the v1.0 single-clip composition path.\n */\nexport function findPrimaryVideoSource(\n doc: AtelierDocument,\n docPath: string | undefined,\n): string | null {\n for (const layer of doc.layers) {\n if (layer.visual.type !== \"video\") continue;\n const v = layer.visual as { src?: string; assetId?: string };\n const src = v.src ?? (v.assetId ? doc.assets?.[v.assetId]?.src : undefined);\n if (!src) continue;\n if (isAbsolute(src)) {\n return existsSync(src) ? src : null;\n }\n if (!docPath) return null;\n const absolute = resolve(dirname(docPath), src);\n return existsSync(absolute) ? absolute : null;\n }\n return null;\n}\n\n// ─── Image Pre-loading ───────────────────────────────────────\n\n/**\n * Scan document layers for image visuals, pre-load them with node-canvas\n * loadImage, and return a populated ImageCache ready for rendering.\n */\nasync function preloadImages(\n doc: AtelierDocument,\n loadImage: (src: string) => Promise<unknown>,\n): Promise<ImageCache> {\n const sources = new Set<string>();\n\n for (const layer of doc.layers) {\n if (layer.visual.type === \"image\") {\n const iv = layer.visual as { src?: string; assetId?: string };\n if (iv.src) {\n sources.add(iv.src);\n } else if (iv.assetId && doc.assets?.[iv.assetId]) {\n sources.add(doc.assets[iv.assetId].src);\n }\n }\n }\n\n if (sources.size === 0) {\n return new ImageCache();\n }\n\n // Load all images in parallel\n const preloaded = new Map<string, unknown>();\n await Promise.all(\n [...sources].map(async (src) => {\n try {\n preloaded.set(src, await loadImage(src));\n } catch {\n // Images that fail to load will render as blank\n }\n }),\n );\n\n // Create ImageCache pre-populated with loaded images.\n // createImage returns the pre-loaded image and fires onLoad on next tick\n // so that the ImageCache internal `image` variable is assigned first.\n const imageCache = new ImageCache({\n createImage: (src, onLoad, onError) => {\n const img = preloaded.get(src);\n if (img) {\n process.nextTick(onLoad);\n return img;\n }\n process.nextTick(onError);\n return {};\n },\n });\n\n for (const src of preloaded.keys()) {\n imageCache.load(src);\n }\n\n // Wait for nextTick callbacks to populate the cache\n await new Promise<void>((resolve) => process.nextTick(resolve));\n\n return imageCache;\n}\n\n// ─── Main Render Loop ────────────────────────────────────────\n\n/**\n * Render an Atelier document to a video/GIF file via FFmpeg.\n * Dynamically imports `canvas` (node-canvas) — fails with a helpful\n * message if the native dependency is not installed.\n */\nexport async function renderDocument(\n doc: AtelierDocument,\n opts: RenderOptions,\n): Promise<RenderResult> {\n // Dynamic import — canvas requires native compilation.\n // Use a variable to avoid TypeScript resolving the module at compile time.\n const canvasModuleName = \"canvas\";\n let createCanvas: (w: number, h: number) => unknown;\n let loadImage: (src: string) => Promise<unknown>;\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const canvasModule = await import(/* webpackIgnore: true */ canvasModuleName);\n createCanvas = canvasModule.createCanvas;\n loadImage = canvasModule.loadImage;\n } catch {\n throw new Error(\n \"The 'canvas' package is not installed.\\n\" +\n \"Install it with:\\n\" +\n \" npm install canvas\\n\" +\n \"Prerequisites vary by OS:\\n\" +\n \" macOS: brew install pkg-config cairo pango libpng jpeg giflib librsvg pixman\\n\" +\n \" Ubuntu: sudo apt install build-essential libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev\\n\" +\n \"See: https://github.com/Automattic/node-canvas#compiling\",\n );\n }\n\n const { width, height, fps } = doc.canvas;\n const { output, format, states, onProgress } = opts;\n\n // H.264 requires even dimensions\n if (format === \"mp4\" && (width % 2 !== 0 || height % 2 !== 0)) {\n throw new Error(\n `H.264 requires even dimensions. Canvas is ${width}\\u00d7${height}. ` +\n `Try ${width + (width % 2)}\\u00d7${height + (height % 2)}.`,\n );\n }\n\n // Resolve which states to render\n const allStates = Object.keys(doc.states);\n const renderStates = states ?? allStates;\n for (const s of renderStates) {\n if (!(s in doc.states)) {\n throw new Error(\n `State \"${s}\" not found. Available: ${allStates.join(\", \")}`,\n );\n }\n }\n\n // Total frame count across all states\n let totalFrames = 0;\n for (const s of renderStates) {\n totalFrames += doc.states[s].duration;\n }\n\n if (totalFrames === 0) {\n throw new Error(\"Nothing to render \\u2014 all states have duration 0\");\n }\n\n // Pre-load images before the render loop\n const imageCache = await preloadImages(doc, loadImage);\n\n // Create node-canvas\n const canvas = createCanvas(width, height) as { getContext(id: \"2d\"): unknown; toBuffer(format: \"raw\"): Buffer };\n const ctx = canvas.getContext(\"2d\");\n\n // Detect the source video (if any) so ffmpeg can overlay the canvas on\n // top of the decoded source pixels AND copy the source audio track —\n // the agent team's RENDER-AUDIO-ISSUE.md root cause.\n const sourceVideoPath = findPrimaryVideoSource(doc, opts.docPath) ?? undefined;\n // Spawn FFmpeg\n const ffmpegArgs = buildFfmpegArgs(width, height, fps, format, output, sourceVideoPath);\n const ffmpeg = spawn(\"ffmpeg\", ffmpegArgs, {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n let stderrOutput = \"\";\n ffmpeg.stderr?.on(\"data\", (chunk: Buffer) => {\n stderrOutput += chunk.toString();\n });\n\n const startTime = Date.now();\n let frameIndex = 0;\n\n for (const stateName of renderStates) {\n const duration = doc.states[stateName].duration;\n for (let f = 0; f < duration; f++) {\n const resolved = resolveFrame(doc, stateName, f);\n renderFrame(ctx as never, resolved, doc, imageCache);\n\n const raw = canvas.toBuffer(\"raw\");\n const canWrite = ffmpeg.stdin!.write(raw);\n if (!canWrite) {\n await new Promise<void>((resolve) =>\n ffmpeg.stdin!.once(\"drain\", resolve),\n );\n }\n\n frameIndex++;\n onProgress?.({\n frame: frameIndex,\n totalFrames,\n state: stateName,\n percent: Math.round((frameIndex / totalFrames) * 100),\n });\n }\n }\n\n // Close stdin and wait for FFmpeg to exit\n ffmpeg.stdin!.end();\n\n const exitCode = await new Promise<number | null>((resolve) => {\n ffmpeg.on(\"close\", resolve);\n });\n\n if (exitCode !== 0) {\n throw new Error(\n `FFmpeg exited with code ${exitCode}.\\n${stderrOutput.slice(-500)}`,\n );\n }\n\n return {\n output,\n format,\n totalFrames,\n states: renderStates,\n durationMs: Date.now() - startTime,\n };\n}\n","import { readFileSync, writeFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { Command } from \"commander\";\nimport type { AtelierDocument } from \"@a-company/atelier-types\";\nimport { parseAtelier } from \"@a-company/atelier-schema\";\nimport { renderFrameSVG } from \"@a-company/atelier-svg\";\n\n/** Read and parse an .atelier file, exiting on failure. */\nfunction readAndParse(file: string): AtelierDocument {\n const absPath = resolve(file);\n let content: string;\n try {\n content = readFileSync(absPath, \"utf-8\");\n } catch {\n console.error(`Cannot read file: ${absPath}`);\n return process.exit(1);\n }\n\n const result = parseAtelier(content);\n if (!result.success) {\n console.error(\"Parse errors:\");\n for (const error of result.errors) {\n console.error(` - ${error.path}: ${error.message}`);\n }\n return process.exit(1);\n }\n\n return result.data;\n}\n\n/** Register the `export-svg` subcommand on the Commander program. */\nexport function exportSvgCommand(program: Command): void {\n program\n .command(\"export-svg <file>\")\n .description(\"Export a frame as SVG\")\n .option(\"-s, --state <name>\", \"State name (defaults to first state)\")\n .option(\"-f, --frame <number>\", \"Frame number (defaults to 0)\", \"0\")\n .option(\"-o, --output <path>\", \"Output file path (default: stdout)\")\n .option(\"--xml-declaration\", \"Include XML declaration\")\n .action(\n (\n file: string,\n options: { state?: string; frame: string; output?: string; xmlDeclaration?: boolean },\n ) => {\n const doc = readAndParse(file);\n\n const frameNumber = parseInt(options.frame, 10);\n if (isNaN(frameNumber) || frameNumber < 0) {\n console.error(`Invalid frame number: ${options.frame}`);\n process.exit(1);\n return;\n }\n\n const stateNames = Object.keys(doc.states);\n if (stateNames.length === 0) {\n console.error(\"Document has no states\");\n process.exit(1);\n return;\n }\n\n const stateName = options.state ?? stateNames[0];\n if (!doc.states[stateName]) {\n console.error(`State \"${stateName}\" not found. Available: ${stateNames.join(\", \")}`);\n process.exit(1);\n return;\n }\n\n try {\n const svg = renderFrameSVG(doc, stateName, frameNumber, {\n xmlDeclaration: options.xmlDeclaration,\n });\n\n if (options.output) {\n writeFileSync(resolve(options.output), svg, \"utf-8\");\n } else {\n console.log(svg);\n }\n } catch (err) {\n console.error((err as Error).message);\n process.exit(1);\n }\n },\n );\n}\n","import type { AtelierDocument, ImageVisual, RefVisual } from \"@a-company/atelier-types\";\nimport { resolveFrame, type ResolvedFrame } from \"@a-company/atelier-core\";\nimport { buildEffectiveLayer, type EffectiveLayer, type DocumentResolver } from \"@a-company/atelier-canvas\";\nimport { buildTransform, buildStyleAttrs } from \"./svg-properties.js\";\nimport { renderShapeSVG } from \"./svg-shapes.js\";\nimport { renderTextSVG } from \"./svg-text.js\";\nimport { buildShadowFilter, buildTintFilter, resetFilterCounter } from \"./svg-filters.js\";\nimport { resetGradientCounter } from \"./svg-gradients.js\";\nimport { buildClipPathDef, resetClipCounter } from \"./svg-clip.js\";\nimport { escapeXml } from \"./svg-properties.js\";\n\nexport interface RenderSVGOptions {\n /** Include XML declaration (default: false) */\n xmlDeclaration?: boolean;\n /** Include viewBox attribute (default: true) */\n viewBox?: boolean;\n /** Indent size in spaces (default: 2) */\n indent?: number;\n /** Resolves ref layer src paths to loaded AtelierDocuments */\n documentResolver?: DocumentResolver;\n /** Maximum ref nesting depth (default: 4) */\n maxRefDepth?: number;\n}\n\n/**\n * Render a resolved frame as an SVG string.\n * If no resolved frame is provided, resolves the given state and frame.\n */\nexport function renderFrameSVG(\n doc: AtelierDocument,\n stateOrFrame: string | ResolvedFrame,\n frame?: number,\n opts?: RenderSVGOptions,\n): string {\n // Reset counters for deterministic IDs\n resetGradientCounter();\n resetFilterCounter();\n resetClipCounter();\n\n let resolved: ResolvedFrame;\n if (typeof stateOrFrame === \"string\") {\n resolved = resolveFrame(doc, stateOrFrame, frame ?? 0);\n } else {\n resolved = stateOrFrame;\n }\n\n const { width, height } = doc.canvas;\n const indent = opts?.indent ?? 2;\n const pad = \" \".repeat(indent);\n\n // Build effective layers\n const effLayers: EffectiveLayer[] = resolved.layers.map(rl =>\n buildEffectiveLayer(rl, width, height),\n );\n\n // Collect all defs and layer elements\n const allDefs: string[] = [];\n const layerElements: string[] = [];\n\n for (let i = 0; i < effLayers.length; i++) {\n const eff = effLayers[i];\n const layer = resolved.layers[i].layer;\n\n // Skip invisible or fully transparent\n if (!eff.visible) continue;\n if (eff.opacity <= 0) continue;\n\n // Resolve asset for images\n if (layer.visual.type === \"image\") {\n const iv = eff.visual as ImageVisual;\n if (!iv.src && iv.assetId && doc.assets?.[iv.assetId]) {\n (eff.visual as ImageVisual).src = doc.assets[iv.assetId].src;\n }\n }\n\n // Build transform\n const transform = buildTransform(eff);\n const styleAttrs = buildStyleAttrs(eff);\n\n // Build shadow filter\n const filterResult = buildShadowFilter(eff);\n if (filterResult) allDefs.push(filterResult.defs);\n\n // Build tint filter\n const tintResult = buildTintFilter(eff);\n if (tintResult) allDefs.push(tintResult.defs);\n\n // Build clip path\n let clipAttr = \"\";\n if (layer.clipPath) {\n const clipResult = buildClipPathDef(layer.clipPath, eff.width, eff.height);\n if (clipResult.defs) {\n allDefs.push(clipResult.defs);\n clipAttr = ` clip-path=\"${clipResult.clipRef}\"`;\n }\n }\n\n // Build group attributes\n const gAttrs: string[] = [];\n if (transform) gAttrs.push(`transform=\"${transform}\"`);\n if (styleAttrs) gAttrs.push(styleAttrs);\n // Apply combined or individual filters\n if (filterResult && tintResult) {\n // When both shadow and tint exist, we need a combined filter\n // For simplicity, apply shadow filter and tint separately via style\n gAttrs.push(`filter=\"${filterResult.filterRef}\"`);\n // Tint is a second filter — wrap content in nested group\n } else if (filterResult) {\n gAttrs.push(`filter=\"${filterResult.filterRef}\"`);\n } else if (tintResult) {\n gAttrs.push(`filter=\"${tintResult.filterRef}\"`);\n }\n if (clipAttr) gAttrs.push(clipAttr.trim());\n\n // Render visual content\n let content = \"\";\n let layerDefs = \"\";\n\n switch (layer.visual.type) {\n case \"shape\": {\n const result = renderShapeSVG(eff, eff.visual as import(\"@a-company/atelier-types\").ShapeVisual);\n content = result.elements;\n layerDefs = result.defs;\n break;\n }\n case \"text\":\n content = renderTextSVG(eff, eff.visual as import(\"@a-company/atelier-types\").TextVisual);\n break;\n case \"image\": {\n const iv = eff.visual as ImageVisual;\n if (iv.src) {\n if (iv.spritesheet) {\n // Spritesheet: compute source rect and use full sheet dimensions for inner image\n const { columns, rows, frameWidth, frameHeight, frameCount } = iv.spritesheet;\n const maxFrames = frameCount ?? (columns * rows);\n const idx = Math.max(0, Math.min(Math.floor(iv.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 const imgW = columns * frameWidth;\n const imgH = rows * frameHeight;\n content = `<svg viewBox=\"${sx} ${sy} ${frameWidth} ${frameHeight}\" width=\"${eff.width}\" height=\"${eff.height}\">` +\n `<image href=\"${escapeXml(iv.src)}\" width=\"${imgW}\" height=\"${imgH}\" />` +\n `</svg>`;\n } else if (iv.sourceRect) {\n // Manual sourceRect: viewBox crops from image coordinate space\n const sr = iv.sourceRect;\n content = `<svg viewBox=\"${sr.x} ${sr.y} ${sr.width} ${sr.height}\" width=\"${eff.width}\" height=\"${eff.height}\">` +\n `<image href=\"${escapeXml(iv.src)}\" width=\"${eff.width}\" height=\"${eff.height}\" />` +\n `</svg>`;\n } else {\n content = `<image href=\"${escapeXml(iv.src)}\" width=\"${eff.width}\" height=\"${eff.height}\" />`;\n }\n }\n break;\n }\n case \"group\":\n // Groups are structural — no visual content in SVG either\n break;\n case \"ref\": {\n const refVisual = eff.visual as RefVisual;\n const refContent = renderRefSVG(eff, refVisual, doc, opts);\n content = refContent;\n break;\n }\n }\n\n if (layerDefs) allDefs.push(layerDefs);\n\n if (content) {\n const gOpen = gAttrs.length > 0 ? `<g ${gAttrs.join(\" \")}>` : \"<g>\";\n layerElements.push(`${pad}${gOpen}${content}</g>`);\n }\n }\n\n // Build SVG\n const lines: string[] = [];\n\n if (opts?.xmlDeclaration) {\n lines.push('<?xml version=\"1.0\" encoding=\"UTF-8\"?>');\n }\n\n const viewBox = opts?.viewBox !== false ? ` viewBox=\"0 0 ${width} ${height}\"` : \"\";\n const bg = doc.canvas.background;\n\n lines.push(`<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\"${viewBox}>`);\n\n // Defs section\n if (allDefs.length > 0) {\n lines.push(`${pad}<defs>`);\n for (const def of allDefs) {\n lines.push(`${pad}${pad}${def}`);\n }\n lines.push(`${pad}</defs>`);\n }\n\n // Background\n if (bg && bg !== \"transparent\") {\n lines.push(`${pad}<rect width=\"${width}\" height=\"${height}\" fill=\"${bg}\" />`);\n }\n\n // Layers\n lines.push(...layerElements);\n\n lines.push(\"</svg>\");\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Render a ref layer as SVG. Uses documentResolver to inline sub-doc content,\n * or falls back to a dashed placeholder rectangle.\n */\nfunction renderRefSVG(\n eff: EffectiveLayer,\n visual: RefVisual,\n _parentDoc: AtelierDocument,\n opts?: RenderSVGOptions,\n _depth?: number,\n _visitedRefs?: Set<string>,\n): string {\n const resolver = opts?.documentResolver;\n\n if (!resolver) {\n return `<rect width=\"${eff.width}\" height=\"${eff.height}\" fill=\"none\" stroke=\"#999\" stroke-dasharray=\"4 2\" />`;\n }\n\n const depth = _depth ?? 0;\n const maxDepth = opts?.maxRefDepth ?? 4;\n if (depth >= maxDepth) {\n return `<rect width=\"${eff.width}\" height=\"${eff.height}\" fill=\"none\" stroke=\"#c33\" stroke-dasharray=\"4 2\" /><text x=\"${eff.width / 2}\" y=\"${eff.height / 2}\" text-anchor=\"middle\" dominant-baseline=\"middle\" fill=\"#c33\" font-size=\"12\">MAX DEPTH</text>`;\n }\n\n const visitedRefs = _visitedRefs ?? new Set<string>();\n if (visitedRefs.has(visual.src)) {\n return `<rect width=\"${eff.width}\" height=\"${eff.height}\" fill=\"none\" stroke=\"#c33\" stroke-dasharray=\"4 2\" /><text x=\"${eff.width / 2}\" y=\"${eff.height / 2}\" text-anchor=\"middle\" dominant-baseline=\"middle\" fill=\"#c33\" font-size=\"12\">CYCLE</text>`;\n }\n\n const subDoc = resolver(visual.src);\n if (!subDoc) {\n return `<rect width=\"${eff.width}\" height=\"${eff.height}\" fill=\"none\" stroke=\"#999\" stroke-dasharray=\"4 2\" /><text x=\"${eff.width / 2}\" y=\"${eff.height / 2}\" text-anchor=\"middle\" dominant-baseline=\"middle\" fill=\"#999\" font-size=\"12\">NOT FOUND</text>`;\n }\n\n const stateNames = Object.keys(subDoc.states);\n if (stateNames.length === 0) {\n return `<rect width=\"${eff.width}\" height=\"${eff.height}\" fill=\"none\" stroke=\"#999\" stroke-dasharray=\"4 2\" />`;\n }\n\n const stateName = visual.state ?? stateNames[0];\n const stateObj = subDoc.states[stateName];\n if (!stateObj) {\n return `<rect width=\"${eff.width}\" height=\"${eff.height}\" fill=\"none\" stroke=\"#999\" stroke-dasharray=\"4 2\" />`;\n }\n\n const maxFrame = Math.max(0, stateObj.duration - 1);\n const frame = Math.min(visual.frame ?? 0, maxFrame);\n\n visitedRefs.add(visual.src);\n\n // Render sub-doc as nested <svg> with viewBox for scaling\n const subW = subDoc.canvas.width;\n const subH = subDoc.canvas.height;\n const resolved = resolveFrame(subDoc, stateName, frame);\n\n const subParts: string[] = [];\n for (const rl of resolved.layers) {\n const subEff = buildEffectiveLayer(rl, subW, subH);\n if (!subEff.visible || subEff.opacity <= 0) continue;\n\n // Resolve asset\n if (rl.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 const transform = buildTransform(subEff);\n const styleAttrs = buildStyleAttrs(subEff);\n const gAttrs: string[] = [];\n if (transform) gAttrs.push(`transform=\"${transform}\"`);\n if (styleAttrs) gAttrs.push(styleAttrs);\n\n let childContent = \"\";\n switch (rl.layer.visual.type) {\n case \"shape\": {\n const result = renderShapeSVG(subEff, subEff.visual as import(\"@a-company/atelier-types\").ShapeVisual);\n childContent = result.elements;\n break;\n }\n case \"text\":\n childContent = renderTextSVG(subEff, subEff.visual as import(\"@a-company/atelier-types\").TextVisual);\n break;\n case \"image\": {\n const iv = subEff.visual as ImageVisual;\n if (iv.src) {\n childContent = `<image href=\"${escapeXml(iv.src)}\" width=\"${subEff.width}\" height=\"${subEff.height}\" />`;\n }\n break;\n }\n case \"ref\": {\n const refV = subEff.visual as RefVisual;\n childContent = renderRefSVG(subEff, refV, subDoc, opts, depth + 1, visitedRefs);\n break;\n }\n }\n\n if (childContent) {\n const gOpen = gAttrs.length > 0 ? `<g ${gAttrs.join(\" \")}>` : \"<g>\";\n subParts.push(`${gOpen}${childContent}</g>`);\n }\n }\n\n visitedRefs.delete(visual.src);\n\n return `<svg x=\"0\" y=\"0\" width=\"${eff.width}\" height=\"${eff.height}\" viewBox=\"0 0 ${subW} ${subH}\">${subParts.join(\"\")}</svg>`;\n}\n","import type { EffectiveLayer } from \"@a-company/atelier-canvas\";\n\n/** Build SVG transform attribute from effective layer values */\nexport function buildTransform(eff: EffectiveLayer): string {\n const parts: string[] = [];\n\n // Translate to position\n if (eff.x !== 0 || eff.y !== 0) {\n parts.push(`translate(${eff.x}, ${eff.y})`);\n }\n\n const totalRotation = eff.rotation + eff.motionPathAngle;\n if (totalRotation !== 0 || eff.scaleX !== 1 || eff.scaleY !== 1) {\n const ax = eff.anchorX * eff.width;\n const ay = eff.anchorY * eff.height;\n\n // Move to anchor, apply rotation/scale, move back\n if (ax !== 0 || ay !== 0) {\n parts.push(`translate(${ax}, ${ay})`);\n }\n if (totalRotation !== 0) {\n parts.push(`rotate(${totalRotation})`);\n }\n if (eff.scaleX !== 1 || eff.scaleY !== 1) {\n parts.push(`scale(${eff.scaleX}, ${eff.scaleY})`);\n }\n if (ax !== 0 || ay !== 0) {\n parts.push(`translate(${-ax}, ${-ay})`);\n }\n }\n\n return parts.length > 0 ? parts.join(\" \") : \"\";\n}\n\n/** Build common SVG style attributes */\nexport function buildStyleAttrs(eff: EffectiveLayer): string {\n const attrs: string[] = [];\n\n if (eff.opacity < 1) {\n attrs.push(`opacity=\"${eff.opacity}\"`);\n }\n\n if (eff.blendMode !== \"normal\") {\n attrs.push(`style=\"mix-blend-mode: ${eff.blendMode}\"`);\n }\n\n return attrs.join(\" \");\n}\n\n/** Escape XML special characters */\nexport function escapeXml(str: string): string {\n return str\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&apos;\");\n}\n","import type { Fill, LinearGradientFill, RadialGradientFill, Color, RGBAColor, HSLAColor } from \"@a-company/atelier-types\";\n\nlet gradientIdCounter = 0;\n\nexport function resetGradientCounter(): void {\n gradientIdCounter = 0;\n}\n\n/** Convert an Atelier Color to a CSS color string */\nexport function colorToCSS(color: Color): string {\n if (typeof color === \"string\") return color;\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 if (\"h\" in color) {\n const c = color as HSLAColor;\n return `hsla(${c.h}, ${c.s}%, ${c.l}%, ${c.a})`;\n }\n return \"#000000\";\n}\n\n/** Generate SVG gradient defs and return the fill reference */\nexport function buildGradientDef(fill: Fill, width: number, height: number): { defs: string; fillRef: string } {\n if (fill.type === \"solid\") {\n return { defs: \"\", fillRef: colorToCSS(fill.color) };\n }\n\n if (fill.type === \"linear-gradient\") {\n return buildLinearGradient(fill, width, height);\n }\n\n if (fill.type === \"radial-gradient\") {\n return buildRadialGradient(fill, width, height);\n }\n\n return { defs: \"\", fillRef: \"none\" };\n}\n\nfunction buildLinearGradient(fill: LinearGradientFill, _width: number, _height: number): { defs: string; fillRef: string } {\n const id = `grad-${++gradientIdCounter}`;\n const rad = (fill.angle * Math.PI) / 180;\n const cos = Math.cos(rad);\n const sin = Math.sin(rad);\n\n // Map angle to x1,y1,x2,y2 (0-1 percentages)\n const x1 = 0.5 - cos * 0.5;\n const y1 = 0.5 - sin * 0.5;\n const x2 = 0.5 + cos * 0.5;\n const y2 = 0.5 + sin * 0.5;\n\n const stops = fill.stops.map(s =>\n `<stop offset=\"${s.offset}\" stop-color=\"${colorToCSS(s.color)}\" />`\n ).join(\"\");\n\n const def = `<linearGradient id=\"${id}\" x1=\"${x1}\" y1=\"${y1}\" x2=\"${x2}\" y2=\"${y2}\">${stops}</linearGradient>`;\n return { defs: def, fillRef: `url(#${id})` };\n}\n\nfunction buildRadialGradient(fill: RadialGradientFill, width: number, height: number): { defs: string; fillRef: string } {\n const id = `grad-${++gradientIdCounter}`;\n\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\n const stops = fill.stops.map(s =>\n `<stop offset=\"${s.offset}\" stop-color=\"${colorToCSS(s.color)}\" />`\n ).join(\"\");\n\n const def = `<radialGradient id=\"${id}\" cx=\"${cx}\" cy=\"${cy}\" r=\"${r}\" gradientUnits=\"userSpaceOnUse\">${stops}</radialGradient>`;\n return { defs: def, fillRef: `url(#${id})` };\n}\n","import type { ShapeVisual, Shape, RectShape, PathShape, Stroke } from \"@a-company/atelier-types\";\nimport type { EffectiveLayer } from \"@a-company/atelier-canvas\";\nimport { buildGradientDef, colorToCSS } from \"./svg-gradients.js\";\n\n/** Render a shape visual as SVG elements */\nexport function renderShapeSVG(\n eff: EffectiveLayer,\n visual: ShapeVisual,\n): { elements: string; defs: string } {\n const { shape } = visual;\n const defs: string[] = [];\n\n let fillAttr = \"none\";\n if (visual.fill) {\n const gradResult = buildGradientDef(visual.fill, eff.width, eff.height);\n if (gradResult.defs) defs.push(gradResult.defs);\n fillAttr = gradResult.fillRef;\n }\n\n let strokeAttrs = \"\";\n if (visual.stroke) {\n strokeAttrs = buildStrokeAttrs(visual.stroke);\n }\n\n const element = buildShapeElement(shape, eff.width, eff.height, fillAttr, strokeAttrs);\n return { elements: element, defs: defs.join(\"\") };\n}\n\nfunction buildShapeElement(\n shape: Shape,\n width: number,\n height: number,\n fill: string,\n strokeAttrs: string,\n): string {\n switch (shape.type) {\n case \"rect\":\n return buildRectElement(shape, width, height, fill, strokeAttrs);\n case \"ellipse\":\n return buildEllipseElement(width, height, fill, strokeAttrs);\n case \"path\":\n return buildPathElement(shape, fill, strokeAttrs);\n }\n}\n\nfunction buildRectElement(\n shape: RectShape,\n width: number,\n height: number,\n fill: string,\n strokeAttrs: string,\n): string {\n let rx = \"\";\n if (shape.cornerRadius) {\n const r = typeof shape.cornerRadius === \"number\" ? shape.cornerRadius : shape.cornerRadius[0];\n rx = ` rx=\"${r}\" ry=\"${r}\"`;\n }\n return `<rect width=\"${width}\" height=\"${height}\" fill=\"${fill}\"${rx}${strokeAttrs ? \" \" + strokeAttrs : \"\"} />`;\n}\n\nfunction buildEllipseElement(\n width: number,\n height: number,\n fill: string,\n strokeAttrs: string,\n): string {\n const cx = width / 2;\n const cy = height / 2;\n const rx = width / 2;\n const ry = height / 2;\n return `<ellipse cx=\"${cx}\" cy=\"${cy}\" rx=\"${rx}\" ry=\"${ry}\" fill=\"${fill}\"${strokeAttrs ? \" \" + strokeAttrs : \"\"} />`;\n}\n\nfunction buildPathElement(\n shape: PathShape,\n fill: string,\n strokeAttrs: string,\n): string {\n if (shape.points.length < 2) return \"\";\n\n const d: string[] = [];\n d.push(`M ${shape.points[0].x} ${shape.points[0].y}`);\n\n for (let i = 1; i < shape.points.length; i++) {\n const prev = shape.points[i - 1];\n const curr = shape.points[i];\n\n if (prev.out && curr.in) {\n d.push(`C ${prev.x + prev.out.x} ${prev.y + prev.out.y} ${curr.x + curr.in.x} ${curr.y + curr.in.y} ${curr.x} ${curr.y}`);\n } else {\n d.push(`L ${curr.x} ${curr.y}`);\n }\n }\n\n if (shape.closed) d.push(\"Z\");\n\n return `<path d=\"${d.join(\" \")}\" fill=\"${fill}\"${strokeAttrs ? \" \" + strokeAttrs : \"\"} />`;\n}\n\nfunction buildStrokeAttrs(stroke: Stroke): string {\n const parts: string[] = [];\n parts.push(`stroke=\"${colorToCSS(stroke.color)}\"`);\n parts.push(`stroke-width=\"${stroke.width}\"`);\n\n if (stroke.lineCap) parts.push(`stroke-linecap=\"${stroke.lineCap}\"`);\n if (stroke.lineJoin) parts.push(`stroke-linejoin=\"${stroke.lineJoin}\"`);\n if (stroke.dash) parts.push(`stroke-dasharray=\"${stroke.dash.join(\" \")}\"`);\n\n return parts.join(\" \");\n}\n","import type { TextVisual } from \"@a-company/atelier-types\";\nimport type { EffectiveLayer } from \"@a-company/atelier-canvas\";\nimport { colorToCSS } from \"./svg-gradients.js\";\nimport { escapeXml } from \"./svg-properties.js\";\n\n/** Render a text visual as SVG elements */\nexport function renderTextSVG(eff: EffectiveLayer, visual: TextVisual): string {\n const { style } = visual;\n\n const attrs: string[] = [];\n\n // Font attributes\n attrs.push(`font-family=\"${escapeXml(style.fontFamily)}\"`);\n attrs.push(`font-size=\"${style.fontSize}\"`);\n\n if (style.fontWeight && style.fontWeight !== \"normal\") {\n attrs.push(`font-weight=\"${style.fontWeight}\"`);\n }\n if (style.fontStyle && style.fontStyle !== \"normal\") {\n attrs.push(`font-style=\"${style.fontStyle}\"`);\n }\n\n // Color\n attrs.push(`fill=\"${colorToCSS(style.color)}\"`);\n\n // Text alignment\n const align = style.textAlign ?? \"left\";\n let textAnchor = \"start\";\n let x = 0;\n if (align === \"center\") {\n textAnchor = \"middle\";\n x = eff.width / 2;\n } else if (align === \"right\") {\n textAnchor = \"end\";\n x = eff.width;\n }\n attrs.push(`text-anchor=\"${textAnchor}\"`);\n\n // Letter spacing\n if (style.letterSpacing) {\n attrs.push(`letter-spacing=\"${style.letterSpacing}\"`);\n }\n\n // SVG text baseline: use dominant-baseline for top alignment\n attrs.push(`dominant-baseline=\"hanging\"`);\n\n return `<text x=\"${x}\" y=\"0\" ${attrs.join(\" \")}>${escapeXml(visual.content)}</text>`;\n}\n","import type { EffectiveLayer } from \"@a-company/atelier-canvas\";\n\nlet filterIdCounter = 0;\n\nexport function resetFilterCounter(): void {\n filterIdCounter = 0;\n}\n\n/** Build SVG filter definition for shadow effects */\nexport function buildShadowFilter(eff: EffectiveLayer): { defs: string; filterRef: string } | null {\n if (!eff.shadow) return null;\n\n const id = `filter-${++filterIdCounter}`;\n const { color, blur, offsetX, offsetY } = eff.shadow;\n\n const def = [\n `<filter id=\"${id}\" x=\"-50%\" y=\"-50%\" width=\"200%\" height=\"200%\">`,\n `<feDropShadow dx=\"${offsetX}\" dy=\"${offsetY}\" stdDeviation=\"${blur / 2}\" flood-color=\"${color}\" />`,\n `</filter>`,\n ].join(\"\");\n\n return { defs: def, filterRef: `url(#${id})` };\n}\n\n/** Build SVG filter definition for tint effect (feFlood + feBlend multiply) */\nexport function buildTintFilter(eff: EffectiveLayer): { defs: string; filterRef: string } | null {\n if (!eff.tint || eff.tint.amount <= 0) return null;\n\n const id = `filter-${++filterIdCounter}`;\n const { color, amount } = eff.tint;\n\n const def = [\n `<filter id=\"${id}\" x=\"0%\" y=\"0%\" width=\"100%\" height=\"100%\">`,\n `<feFlood flood-color=\"${color}\" flood-opacity=\"${amount}\" result=\"tint\" />`,\n `<feBlend in=\"SourceGraphic\" in2=\"tint\" mode=\"multiply\" />`,\n `</filter>`,\n ].join(\"\");\n\n return { defs: def, filterRef: `url(#${id})` };\n}\n","import type { Shape } from \"@a-company/atelier-types\";\n\nlet clipIdCounter = 0;\n\nexport function resetClipCounter(): void {\n clipIdCounter = 0;\n}\n\n/** Build SVG clipPath definition */\nexport function buildClipPathDef(shape: Shape, width: number, height: number): { defs: string; clipRef: string } {\n const id = `clip-${++clipIdCounter}`;\n let pathContent = \"\";\n\n switch (shape.type) {\n case \"rect\":\n if (shape.cornerRadius) {\n const r = typeof shape.cornerRadius === \"number\" ? shape.cornerRadius : shape.cornerRadius[0];\n pathContent = `<rect width=\"${width}\" height=\"${height}\" rx=\"${r}\" ry=\"${r}\" />`;\n } else {\n pathContent = `<rect width=\"${width}\" height=\"${height}\" />`;\n }\n break;\n case \"ellipse\":\n pathContent = `<ellipse cx=\"${width / 2}\" cy=\"${height / 2}\" rx=\"${width / 2}\" ry=\"${height / 2}\" />`;\n break;\n case \"path\": {\n if (shape.points.length < 2) return { defs: \"\", clipRef: \"\" };\n const d: string[] = [];\n d.push(`M ${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 d.push(`C ${prev.x + prev.out.x} ${prev.y + prev.out.y} ${curr.x + curr.in.x} ${curr.y + curr.in.y} ${curr.x} ${curr.y}`);\n } else {\n d.push(`L ${curr.x} ${curr.y}`);\n }\n }\n if (shape.closed) d.push(\"Z\");\n pathContent = `<path d=\"${d.join(\" \")}\" />`;\n break;\n }\n }\n\n const def = `<clipPath id=\"${id}\">${pathContent}</clipPath>`;\n return { defs: def, clipRef: `url(#${id})` };\n}\n","import { readFileSync, writeFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { Command } from \"commander\";\nimport type { AtelierDocument } from \"@a-company/atelier-types\";\nimport { parseAtelier } from \"@a-company/atelier-schema\";\nimport { exportToLottie } from \"@a-company/atelier-lottie\";\n\n/** Read and parse an .atelier file, exiting on failure. */\nfunction readAndParse(file: string): AtelierDocument {\n const absPath = resolve(file);\n let content: string;\n try {\n content = readFileSync(absPath, \"utf-8\");\n } catch {\n console.error(`Cannot read file: ${absPath}`);\n return process.exit(1);\n }\n\n const result = parseAtelier(content);\n if (!result.success) {\n console.error(\"Parse errors:\");\n for (const error of result.errors) {\n console.error(` - ${error.path}: ${error.message}`);\n }\n return process.exit(1);\n }\n\n return result.data;\n}\n\n/** Register the `export-lottie` subcommand on the Commander program. */\nexport function exportLottieCommand(program: Command): void {\n program\n .command(\"export-lottie <file>\")\n .description(\"Export a document to Lottie JSON format\")\n .option(\"-s, --state <name>\", \"State name (defaults to first state)\")\n .option(\"-o, --output <path>\", \"Output file path (default: stdout)\")\n .action(\n (\n file: string,\n options: { state?: string; output?: string },\n ) => {\n const doc = readAndParse(file);\n\n try {\n const { json, warnings } = exportToLottie(doc, {\n state: options.state,\n });\n\n // Print warnings to stderr\n for (const warning of warnings) {\n console.error(`Warning: ${warning}`);\n }\n\n const output = JSON.stringify(json, null, 2);\n if (options.output) {\n writeFileSync(resolve(options.output), output, \"utf-8\");\n } else {\n console.log(output);\n }\n } catch (err) {\n console.error((err as Error).message);\n process.exit(1);\n }\n },\n );\n}\n","import type { Color, RGBAColor, HSLAColor } from \"@a-company/atelier-types\";\n\n/** Convert an Atelier color to Lottie [r, g, b, a] format (0–1 range) */\nexport function colorToLottie(color: Color): number[] {\n if (typeof color === \"string\") {\n return hexToLottie(color);\n }\n\n if (\"r\" in color) {\n const c = color as RGBAColor;\n return [c.r / 255, c.g / 255, c.b / 255, c.a];\n }\n\n if (\"h\" in color) {\n const c = color as HSLAColor;\n const rgb = hslToRgb(c.h, c.s, c.l);\n return [rgb[0] / 255, rgb[1] / 255, rgb[2] / 255, c.a];\n }\n\n return [0, 0, 0, 1];\n}\n\nfunction hexToLottie(hex: string): number[] {\n const clean = hex.replace(\"#\", \"\");\n if (clean.length === 3 || clean.length === 4) {\n const r = parseInt(clean[0] + clean[0], 16) / 255;\n const g = parseInt(clean[1] + clean[1], 16) / 255;\n const b = parseInt(clean[2] + clean[2], 16) / 255;\n const a = clean.length === 4 ? parseInt(clean[3] + clean[3], 16) / 255 : 1;\n return [r, g, b, a];\n }\n const r = parseInt(clean.slice(0, 2), 16) / 255;\n const g = parseInt(clean.slice(2, 4), 16) / 255;\n const b = parseInt(clean.slice(4, 6), 16) / 255;\n const a = clean.length === 8 ? parseInt(clean.slice(6, 8), 16) / 255 : 1;\n return [r, g, b, a];\n}\n\nfunction hslToRgb(h: number, s: number, l: number): [number, number, number] {\n s = s / 100;\n l = l / 100;\n const a = s * Math.min(l, 1 - l);\n const f = (n: number) => {\n const k = (n + h / 30) % 12;\n return l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);\n };\n return [Math.round(f(0) * 255), Math.round(f(8) * 255), Math.round(f(4) * 255)];\n}\n","import type { ShapeVisual, Fill, Stroke } from \"@a-company/atelier-types\";\nimport type { LottieShapeItem } from \"./lottie-types.js\";\nimport { colorToLottie } from \"./map-colors.js\";\n\n/** Map an Atelier shape visual to Lottie shape items */\nexport function mapShapeVisual(\n visual: ShapeVisual,\n width: number,\n height: number,\n): LottieShapeItem[] {\n const items: LottieShapeItem[] = [];\n\n // Shape geometry\n switch (visual.shape.type) {\n case \"rect\":\n items.push({\n ty: \"rc\",\n nm: \"Rectangle\",\n d: 1,\n s: { a: 0, k: [width, height] },\n p: { a: 0, k: [width / 2, height / 2] },\n r: { a: 0, k: typeof visual.shape.cornerRadius === \"number\" ? visual.shape.cornerRadius : 0 },\n });\n break;\n case \"ellipse\":\n items.push({\n ty: \"el\",\n nm: \"Ellipse\",\n d: 1,\n s: { a: 0, k: [width, height] },\n p: { a: 0, k: [width / 2, height / 2] },\n });\n break;\n case \"path\": {\n const vertices = visual.shape.points.map(p => [p.x, p.y]);\n const inTangents = visual.shape.points.map(p => p.in ? [p.in.x, p.in.y] : [0, 0]);\n const outTangents = visual.shape.points.map(p => p.out ? [p.out.x, p.out.y] : [0, 0]);\n items.push({\n ty: \"sh\",\n nm: \"Path\",\n ks: {\n a: 0,\n k: {\n c: visual.shape.closed ?? false,\n v: vertices,\n i: inTangents,\n o: outTangents,\n },\n },\n });\n break;\n }\n }\n\n // Fill\n if (visual.fill) {\n items.push(mapFill(visual.fill, width, height));\n }\n\n // Stroke\n if (visual.stroke) {\n items.push(mapStroke(visual.stroke));\n }\n\n return items;\n}\n\nfunction mapFill(fill: Fill, _width: number, _height: number): LottieShapeItem {\n if (fill.type === \"solid\") {\n const color = colorToLottie(fill.color);\n return {\n ty: \"fl\",\n nm: \"Fill\",\n c: { a: 0, k: color.slice(0, 3) },\n o: { a: 0, k: (color[3] ?? 1) * 100 },\n r: 1,\n };\n }\n\n if (fill.type === \"linear-gradient\") {\n const stops: number[] = [];\n for (const stop of fill.stops) {\n const c = colorToLottie(stop.color);\n stops.push(stop.offset, c[0], c[1], c[2]);\n }\n return {\n ty: \"gf\",\n nm: \"Gradient Fill\",\n t: 1, // linear\n s: { a: 0, k: [0, 0] },\n e: { a: 0, k: [100, 0] },\n g: { p: fill.stops.length, k: { a: 0, k: stops } },\n r: 1,\n o: { a: 0, k: 100 },\n };\n }\n\n if (fill.type === \"radial-gradient\") {\n const stops: number[] = [];\n for (const stop of fill.stops) {\n const c = colorToLottie(stop.color);\n stops.push(stop.offset, c[0], c[1], c[2]);\n }\n return {\n ty: \"gf\",\n nm: \"Gradient Fill\",\n t: 2, // radial\n s: { a: 0, k: [50, 50] },\n e: { a: 0, k: [100, 50] },\n g: { p: fill.stops.length, k: { a: 0, k: stops } },\n r: 1,\n o: { a: 0, k: 100 },\n };\n }\n\n return { ty: \"fl\", nm: \"Fill\", c: { a: 0, k: [0, 0, 0] }, o: { a: 0, k: 100 }, r: 1 };\n}\n\nfunction mapStroke(stroke: Stroke): LottieShapeItem {\n const color = colorToLottie(stroke.color);\n return {\n ty: \"st\",\n nm: \"Stroke\",\n c: { a: 0, k: color.slice(0, 3) },\n o: { a: 0, k: (color[3] ?? 1) * 100 },\n w: { a: 0, k: stroke.width },\n lc: stroke.lineCap === \"round\" ? 2 : stroke.lineCap === \"square\" ? 3 : 1,\n lj: stroke.lineJoin === \"round\" ? 2 : stroke.lineJoin === \"bevel\" ? 3 : 1,\n };\n}\n","import type { Easing } from \"@a-company/atelier-types\";\n\nexport interface LottieEasing {\n i: { x: number[]; y: number[] };\n o: { x: number[]; y: number[] };\n}\n\n/** Map an Atelier easing to Lottie bezier easing curves */\nexport function mapEasing(easing: Easing | undefined): LottieEasing | { h: 1 } {\n if (!easing) return linearEasing();\n\n if (typeof easing === \"string\") {\n switch (easing) {\n case \"ease-in\":\n return bezierEasing(0.42, 0, 1, 1);\n case \"ease-out\":\n return bezierEasing(0, 0, 0.58, 1);\n case \"ease-in-out\":\n return bezierEasing(0.42, 0, 0.58, 1);\n default:\n return linearEasing();\n }\n }\n\n switch (easing.type) {\n case \"linear\":\n return linearEasing();\n case \"cubic-bezier\":\n return bezierEasing(easing.x1, easing.y1, easing.x2, easing.y2);\n case \"spring\":\n // Approximate spring as a cubic-bezier (lossy)\n return bezierEasing(0.25, 0.1, 0.25, 1);\n case \"step\":\n // Steps map to hold keyframes\n return { h: 1 };\n default:\n return linearEasing();\n }\n}\n\nfunction linearEasing(): LottieEasing {\n return {\n i: { x: [0.833], y: [0.833] },\n o: { x: [0.167], y: [0.167] },\n };\n}\n\nfunction bezierEasing(x1: number, y1: number, x2: number, y2: number): LottieEasing {\n return {\n i: { x: [x2], y: [y2] },\n o: { x: [x1], y: [y1] },\n };\n}\n\n/** Check if an easing is lossy (spring/step → approximated) */\nexport function isLossyEasing(easing: Easing | undefined): string | null {\n if (!easing || typeof easing === \"string\") return null;\n if (easing.type === \"spring\") return \"Spring easing approximated as cubic-bezier\";\n if (easing.type === \"step\") return \"Step easing mapped to hold keyframes\";\n return null;\n}\n","import type { Delta, AnimatableProperty } from \"@a-company/atelier-types\";\nimport type { LottieAnimatedValue, LottieAnimatedMultiValue, LottieKeyframe, LottieMultiKeyframe } from \"./lottie-types.js\";\nimport { mapEasing, isLossyEasing } from \"./map-easing.js\";\n\n/** Map deltas for a single layer+property to Lottie animated value */\nexport function mapDeltasToAnimated(\n deltas: Delta[],\n property: AnimatableProperty,\n warnings: string[],\n): LottieAnimatedValue {\n if (deltas.length === 0) {\n return { a: 0, k: 0 };\n }\n\n // Check for lossy easings\n for (const d of deltas) {\n const lossyMsg = isLossyEasing(d.easing);\n if (lossyMsg) warnings.push(`${property}: ${lossyMsg}`);\n }\n\n // If expressions, warn and use static\n for (const d of deltas) {\n if (isExpression(d.from) || isExpression(d.to)) {\n warnings.push(`${property}: Expression values not supported in Lottie, using static values`);\n return { a: 0, k: typeof d.from === \"number\" ? d.from : 0 };\n }\n }\n\n // Single delta — simple animated value\n if (deltas.length === 1) {\n const d = deltas[0];\n const fromVal = resolveValue(d.from, property);\n const toVal = resolveValue(d.to, property);\n\n if (fromVal === toVal) {\n return { a: 0, k: fromVal };\n }\n\n const easing = mapEasing(d.easing);\n const kfs: LottieKeyframe[] = [];\n\n if (\"h\" in easing) {\n kfs.push({ t: d.range[0], s: [fromVal], h: 1 });\n kfs.push({ t: d.range[1], s: [toVal] });\n } else {\n kfs.push({ t: d.range[0], s: [fromVal], e: [toVal], i: easing.i, o: easing.o });\n kfs.push({ t: d.range[1], s: [toVal] });\n }\n\n return { a: 1, k: kfs };\n }\n\n // Multiple deltas — chain keyframes\n const sorted = [...deltas].sort((a, b) => a.range[0] - b.range[0]);\n const kfs: LottieKeyframe[] = [];\n\n for (const d of sorted) {\n const fromVal = resolveValue(d.from, property);\n const toVal = resolveValue(d.to, property);\n const easing = mapEasing(d.easing);\n\n if (\"h\" in easing) {\n kfs.push({ t: d.range[0], s: [fromVal], h: 1 });\n } else {\n kfs.push({ t: d.range[0], s: [fromVal], e: [toVal], i: easing.i, o: easing.o });\n }\n }\n\n // Final keyframe\n const last = sorted[sorted.length - 1];\n kfs.push({ t: last.range[1], s: [resolveValue(last.to, property)] });\n\n return { a: 1, k: kfs };\n}\n\n/** Map position deltas (frame.x, frame.y) to Lottie multi-dimensional animated value */\nexport function mapPositionDeltas(\n xDeltas: Delta[],\n yDeltas: Delta[],\n baseX: number,\n baseY: number,\n warnings: string[],\n): LottieAnimatedMultiValue {\n const hasXAnim = xDeltas.length > 0;\n const hasYAnim = yDeltas.length > 0;\n\n if (!hasXAnim && !hasYAnim) {\n return { a: 0, k: [baseX, baseY, 0] };\n }\n\n // Collect all unique frame points\n const frames = new Set<number>();\n for (const d of [...xDeltas, ...yDeltas]) {\n frames.add(d.range[0]);\n frames.add(d.range[1]);\n }\n const sortedFrames = [...frames].sort((a, b) => a - b);\n\n if (sortedFrames.length < 2) {\n return { a: 0, k: [baseX, baseY, 0] };\n }\n\n // Build multi-dimensional keyframes\n const kfs: LottieMultiKeyframe[] = [];\n for (let i = 0; i < sortedFrames.length; i++) {\n const f = sortedFrames[i];\n const x = resolveAtFrame(xDeltas, f, baseX);\n const y = resolveAtFrame(yDeltas, f, baseY);\n\n if (i < sortedFrames.length - 1) {\n const nextF = sortedFrames[i + 1];\n const nextX = resolveAtFrame(xDeltas, nextF, baseX);\n const nextY = resolveAtFrame(yDeltas, nextF, baseY);\n\n // Find the active delta's easing\n const activeX = xDeltas.find(d => d.range[0] <= f && d.range[1] >= f);\n const activeY = yDeltas.find(d => d.range[0] <= f && d.range[1] >= f);\n const easing = mapEasing(activeX?.easing ?? activeY?.easing);\n\n if (\"h\" in easing) {\n kfs.push({ t: f, s: [x, y, 0], h: 1 });\n } else {\n kfs.push({ t: f, s: [x, y, 0], e: [nextX, nextY, 0], i: easing.i, o: easing.o });\n }\n } else {\n kfs.push({ t: f, s: [x, y, 0] });\n }\n }\n\n // Check for lossy warnings\n for (const d of [...xDeltas, ...yDeltas]) {\n const msg = isLossyEasing(d.easing);\n if (msg) warnings.push(`position: ${msg}`);\n }\n\n return { a: 1, k: kfs };\n}\n\nfunction resolveAtFrame(deltas: Delta[], frame: number, base: number): number {\n for (const d of deltas) {\n if (frame >= d.range[0] && frame <= d.range[1]) {\n const from = typeof d.from === \"number\" ? d.from : base;\n const to = typeof d.to === \"number\" ? d.to : base;\n const progress = d.range[0] === d.range[1] ? 1 : (frame - d.range[0]) / (d.range[1] - d.range[0]);\n return from + (to - from) * progress;\n }\n }\n // Hold last completed value\n let lastCompleted: Delta | undefined;\n for (const d of deltas) {\n if (frame > d.range[1]) {\n if (!lastCompleted || d.range[1] > lastCompleted.range[1]) {\n lastCompleted = d;\n }\n }\n }\n if (lastCompleted) return typeof lastCompleted.to === \"number\" ? lastCompleted.to : base;\n return base;\n}\n\nfunction resolveValue(val: unknown, _property: AnimatableProperty): number {\n if (typeof val === \"number\") return val;\n if (typeof val === \"string\" && val.startsWith(\"#\")) {\n // Color — for Lottie, colors are handled separately\n return 0;\n }\n return 0;\n}\n\nfunction isExpression(val: unknown): boolean {\n return typeof val === \"object\" && val !== null && \"expr\" in val;\n}\n","import type { AtelierDocument, Layer, Delta, State, UnitValue } from \"@a-company/atelier-types\";\nimport type { LottieLayer, LottieTransform, LottieTextData } from \"./lottie-types.js\";\nimport { mapShapeVisual } from \"./map-shapes.js\";\nimport { mapDeltasToAnimated, mapPositionDeltas } from \"./map-keyframes.js\";\nimport { colorToLottie } from \"./map-colors.js\";\n\n/** Resolve a UnitValue to a plain number (percentages treated as 0 for Lottie) */\nfunction toNum(v: UnitValue): number {\n return typeof v === \"number\" ? v : 0;\n}\n\n/** Map all layers in a document+state to Lottie layers */\nexport function mapLayers(\n doc: AtelierDocument,\n state: State,\n warnings: string[],\n): LottieLayer[] {\n const layerIndexMap = new Map<string, number>();\n doc.layers.forEach((l, i) => layerIndexMap.set(l.id, i));\n\n return doc.layers.map((layer, index) => {\n const deltas = state.deltas.filter(d => d.layer === layer.id);\n return mapLayer(layer, index, deltas, layerIndexMap, doc, warnings);\n });\n}\n\nfunction mapLayer(\n layer: Layer,\n index: number,\n deltas: Delta[],\n layerIndexMap: Map<string, number>,\n doc: AtelierDocument,\n warnings: string[],\n): LottieLayer {\n const duration = getMaxFrame(deltas, doc);\n\n const base: LottieLayer = {\n ty: getLayerType(layer),\n nm: layer.id,\n ind: index,\n ip: 0,\n op: duration,\n st: 0,\n ks: buildTransform(layer, deltas, warnings),\n };\n\n // Parent\n if (layer.parentId) {\n const parentIdx = layerIndexMap.get(layer.parentId);\n if (parentIdx !== undefined) {\n base.parent = parentIdx;\n }\n }\n\n // Blend mode\n if (layer.blendMode) {\n base.bm = mapBlendMode(layer.blendMode);\n }\n\n // Shape layer\n if (layer.visual.type === \"shape\") {\n base.shapes = mapShapeVisual(layer.visual, toNum(layer.bounds.width), toNum(layer.bounds.height));\n }\n\n // Text layer\n if (layer.visual.type === \"text\") {\n const color = colorToLottie(layer.visual.style.color);\n base.t = {\n d: {\n k: [{\n s: {\n s: layer.visual.style.fontSize,\n f: layer.visual.style.fontFamily,\n t: layer.visual.content,\n fc: color.slice(0, 3),\n j: layer.visual.style.textAlign === \"center\" ? 1 :\n layer.visual.style.textAlign === \"right\" ? 2 : 0,\n },\n t: 0,\n }],\n },\n } as LottieTextData;\n }\n\n // Image layer\n if (layer.visual.type === \"image\") {\n base.refId = layer.visual.assetId;\n base.w = toNum(layer.bounds.width);\n base.h = toNum(layer.bounds.height);\n\n if (layer.visual.spritesheet) {\n warnings.push(`Layer \"${layer.id}\": Spritesheet animation not supported in Lottie export`);\n }\n if (layer.visual.sourceRect) {\n warnings.push(`Layer \"${layer.id}\": sourceRect cropping not supported in Lottie export`);\n }\n }\n\n // Tint warning\n if (layer.tint && layer.tint.amount > 0) {\n warnings.push(`Layer \"${layer.id}\": Tint effect not supported in Lottie export`);\n }\n\n return base;\n}\n\nfunction getLayerType(layer: Layer): number {\n switch (layer.visual.type) {\n case \"shape\": return 4;\n case \"text\": return 5;\n case \"image\": return 2;\n case \"group\": return 3; // null layer\n case \"ref\": return 0; // precomp\n default: return 4;\n }\n}\n\nfunction buildTransform(\n layer: Layer,\n deltas: Delta[],\n warnings: string[],\n): LottieTransform {\n // Group deltas by property\n const byProp = new Map<string, Delta[]>();\n for (const d of deltas) {\n if (!byProp.has(d.property)) byProp.set(d.property, []);\n byProp.get(d.property)!.push(d);\n }\n\n const xDeltas = byProp.get(\"frame.x\") ?? [];\n const yDeltas = byProp.get(\"frame.y\") ?? [];\n const opacityDeltas = byProp.get(\"opacity\") ?? [];\n const rotationDeltas = byProp.get(\"rotation\") ?? [];\n const scaleXDeltas = byProp.get(\"scale.x\") ?? [];\n const scaleYDeltas = byProp.get(\"scale.y\") ?? [];\n\n // Position\n const position = mapPositionDeltas(\n xDeltas, yDeltas,\n typeof layer.frame.x === \"number\" ? layer.frame.x : 0,\n typeof layer.frame.y === \"number\" ? layer.frame.y : 0,\n warnings,\n );\n\n // Opacity (Lottie uses 0–100)\n let opacity;\n if (opacityDeltas.length > 0) {\n const raw = mapDeltasToAnimated(opacityDeltas, \"opacity\", warnings);\n // Scale 0-1 to 0-100\n if (raw.a === 0) {\n opacity = { a: 0 as const, k: (raw.k as number) * 100 };\n } else {\n const kfs = (raw.k as Array<{ t: number; s: [number]; e?: [number]; [key: string]: unknown }>).map(kf => ({\n ...kf,\n s: [kf.s[0] * 100] as [number],\n e: kf.e ? [kf.e[0] * 100] as [number] : undefined,\n }));\n opacity = { a: 1 as const, k: kfs };\n }\n } else {\n opacity = { a: 0 as const, k: (layer.opacity ?? 1) * 100 };\n }\n\n // Rotation\n const rotation = rotationDeltas.length > 0\n ? mapDeltasToAnimated(rotationDeltas, \"rotation\", warnings)\n : { a: 0 as const, k: layer.rotation ?? 0 };\n\n // Scale (Lottie uses 0–100)\n const baseScaleX = (layer.scale?.x ?? 1) * 100;\n const baseScaleY = (layer.scale?.y ?? 1) * 100;\n let scale;\n if (scaleXDeltas.length > 0 || scaleYDeltas.length > 0) {\n // Simplified: use x deltas for now\n scale = { a: 0 as const, k: [baseScaleX, baseScaleY, 100] };\n if (scaleXDeltas.length > 0 || scaleYDeltas.length > 0) {\n warnings.push(\"Scale animation partially mapped to Lottie\");\n }\n } else {\n scale = { a: 0 as const, k: [baseScaleX, baseScaleY, 100] };\n }\n\n // Anchor point\n const ax = (layer.anchorPoint?.x ?? 0) * toNum(layer.bounds.width);\n const ay = (layer.anchorPoint?.y ?? 0) * toNum(layer.bounds.height);\n\n return {\n o: opacity,\n r: rotation,\n p: position,\n a: { a: 0, k: [ax, ay, 0] },\n s: scale,\n };\n}\n\nfunction getMaxFrame(deltas: Delta[], doc: AtelierDocument): number {\n let max = 0;\n for (const state of Object.values(doc.states)) {\n if (state.duration > max) max = state.duration;\n }\n for (const d of deltas) {\n if (d.range[1] > max) max = d.range[1];\n }\n return max || 60;\n}\n\nfunction mapBlendMode(mode: string): number {\n const map: Record<string, number> = {\n normal: 0, multiply: 1, screen: 2, overlay: 3,\n darken: 4, lighten: 5, \"color-dodge\": 6, \"color-burn\": 7,\n \"hard-light\": 8, \"soft-light\": 9, difference: 10, exclusion: 11,\n hue: 12, saturation: 13, color: 14, luminosity: 15,\n };\n return map[mode] ?? 0;\n}\n","import type { AtelierDocument, State } from \"@a-company/atelier-types\";\n\n/** Check for unsupported features and collect warnings */\nexport function collectUnsupportedWarnings(doc: AtelierDocument, state: State): string[] {\n const warnings: string[] = [];\n\n // Audio layers not supported in Lottie (v1.0: audio is layer-scoped, not state-scoped)\n const audioLayers = doc.layers.filter((l) => l.visual.type === \"audio\");\n if (audioLayers.length > 0) {\n warnings.push(\n `${audioLayers.length} audio layer(s) are not supported in Lottie format and will be dropped`,\n );\n }\n\n // Expressions not supported\n for (const delta of state.deltas) {\n if (isExpression(delta.from) || isExpression(delta.to)) {\n warnings.push(`Expression values on \"${delta.layer}.${delta.property}\" not supported in Lottie`);\n }\n }\n\n // Shadow not supported in base Lottie\n for (const layer of doc.layers) {\n if (layer.shadow) {\n warnings.push(`Shadow on layer \"${layer.id}\" is not supported in base Lottie format`);\n }\n }\n\n // Motion path not directly mapped\n for (const layer of doc.layers) {\n if (layer.motionPath) {\n warnings.push(`Motion path on layer \"${layer.id}\" is not directly mappable to Lottie`);\n }\n }\n\n // Clip paths have limited support\n for (const layer of doc.layers) {\n if (layer.clipPath) {\n warnings.push(`Clip path on layer \"${layer.id}\" mapped as Lottie mask (partial support)`);\n }\n }\n\n // States/transitions\n if (Object.keys(doc.states).length > 1) {\n warnings.push(\"Multiple states flattened to single timeline in Lottie export\");\n }\n\n return warnings;\n}\n\nfunction isExpression(val: unknown): boolean {\n return typeof val === \"object\" && val !== null && \"expr\" in val;\n}\n","import type { AtelierDocument } from \"@a-company/atelier-types\";\nimport type { LottieAnimation, LottieAsset } from \"./lottie-types.js\";\nimport { mapLayers } from \"./map-layers.js\";\nimport { collectUnsupportedWarnings } from \"./warnings.js\";\n\nexport interface ExportLottieOptions {\n /** State to export (defaults to first state) */\n state?: string;\n}\n\nexport interface ExportLottieResult {\n /** The Lottie JSON animation */\n json: LottieAnimation;\n /** Warnings about unsupported features */\n warnings: string[];\n}\n\n/**\n * Export an Atelier document to Lottie JSON format.\n * This is a lossy conversion — not all features are supported.\n */\nexport function exportToLottie(\n doc: AtelierDocument,\n opts?: ExportLottieOptions,\n): ExportLottieResult {\n const stateNames = Object.keys(doc.states);\n if (stateNames.length === 0) {\n throw new Error(\"Document has no states to export\");\n }\n\n const stateName = opts?.state ?? stateNames[0];\n const state = doc.states[stateName];\n if (!state) {\n throw new Error(`State \"${stateName}\" not found`);\n }\n\n const warnings: string[] = [];\n\n // Collect unsupported feature warnings\n warnings.push(...collectUnsupportedWarnings(doc, state));\n\n // Map layers\n const layers = mapLayers(doc, state, warnings);\n\n // Map assets\n const assets: LottieAsset[] = [];\n if (doc.assets) {\n for (const [id, asset] of Object.entries(doc.assets)) {\n if (asset.type === \"image\") {\n assets.push({\n id,\n w: 100,\n h: 100,\n p: asset.src,\n e: 0,\n });\n }\n }\n }\n\n const json: LottieAnimation = {\n v: \"5.7.4\",\n fr: doc.canvas.fps,\n ip: 0,\n op: state.duration,\n w: doc.canvas.width,\n h: doc.canvas.height,\n nm: doc.name,\n layers,\n ...(assets.length > 0 ? { assets } : {}),\n };\n\n // Deduplicate warnings\n const uniqueWarnings = [...new Set(warnings)];\n\n return { json, warnings: uniqueWarnings };\n}\n","import { readFileSync, writeFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { Command } from \"commander\";\nimport type { AtelierDocument } from \"@a-company/atelier-types\";\nimport { parseAtelier } from \"@a-company/atelier-schema\";\nimport {\n renderDocumentToPng,\n resolveExportDimensions,\n CanvasUnavailableError,\n} from \"../lib/render-image.js\";\n\n// Re-export so existing import sites (index.ts, tests) keep resolving.\nexport { resolveExportDimensions };\n\n/** Read and parse an .atelier file, exiting on failure. */\nfunction readAndParse(file: string): AtelierDocument {\n const absPath = resolve(file);\n let content: string;\n try {\n content = readFileSync(absPath, \"utf-8\");\n } catch {\n console.error(`Cannot read file: ${absPath}`);\n return process.exit(1);\n }\n\n const result = parseAtelier(content);\n if (!result.success) {\n console.error(\"Parse errors:\");\n for (const error of result.errors) {\n console.error(` - ${error.path}: ${error.message}`);\n }\n return process.exit(1);\n }\n\n return result.data;\n}\n\ninterface ExportImageOptions {\n state?: string;\n frame: string;\n width?: string;\n height?: string;\n out: string;\n}\n\n/** Parse a positive integer CLI option, returning undefined if not set. */\nfunction parseDim(raw: string | undefined, name: string): number | undefined {\n if (raw === undefined) return undefined;\n const n = parseInt(raw, 10);\n if (isNaN(n) || n <= 0) {\n console.error(`Invalid --${name}: ${raw}`);\n process.exit(1);\n }\n return n;\n}\n\n/** Register the `export-image` subcommand on the Commander program. */\nexport function exportImageCommand(program: Command): void {\n program\n .command(\"export-image <file>\")\n .description(\n \"Export a single frame as a PNG image. \" +\n \"Aspect-preserving when only one of --width/--height is set; \" +\n \"if both are set the renderer uses both verbatim (may squash).\",\n )\n .requiredOption(\"-o, --out <path>\", \"Output PNG file path\")\n .option(\"-s, --state <name>\", \"State name (defaults to first state)\")\n .option(\"-f, --frame <number>\", \"Frame number (defaults to 0)\", \"0\")\n .option(\"--width <number>\", \"Override output width (px)\")\n .option(\"--height <number>\", \"Override output height (px)\")\n .action(async (file: string, options: ExportImageOptions) => {\n const doc = readAndParse(file);\n\n const frameNumber = parseInt(options.frame, 10);\n if (isNaN(frameNumber) || frameNumber < 0) {\n console.error(`Invalid frame number: ${options.frame}`);\n process.exit(1);\n return;\n }\n\n const width = parseDim(options.width, \"width\");\n const height = parseDim(options.height, \"height\");\n\n try {\n const buffer = await renderDocumentToPng(doc, {\n state: options.state,\n frame: frameNumber,\n width,\n height,\n });\n writeFileSync(resolve(options.out), buffer);\n } catch (err) {\n if (err instanceof CanvasUnavailableError) {\n console.error(err.message);\n process.exit(1);\n return;\n }\n console.error((err as Error).message);\n process.exit(1);\n }\n });\n}\n","/**\n * Shared single-frame render-to-PNG path.\n *\n * Extracted from `atelier export-image` so the carousel batch driver and the\n * single-frame export command render through one code path. Rasterizes via\n * `@napi-rs/canvas` — a Canvas2D implementation shipped as prebuilt platform\n * binaries, so PNG export works on a plain install with no node-gyp build and\n * no system libraries (cairo/pango/etc.). Pre-loads any ImageVisual layers via\n * its `loadImage` (file path / data-URL / buffer all work server-side, the same\n * mechanism the MP4 render-pipeline uses) and renders one resolved frame scaled\n * to the requested output dimensions.\n */\n\nimport type { AtelierDocument } from \"@a-company/atelier-types\";\nimport { resolveFrame } from \"@a-company/atelier-core\";\nimport { createCanvas, loadImage } from \"@napi-rs/canvas\";\n\n/** @napi-rs/canvas surface — the subset this module touches. */\ninterface NodeCanvas {\n getContext(id: \"2d\"): unknown;\n toBuffer(format: \"image/png\"): Buffer;\n}\n\n/** A loaded image — width/height are the natural pixel dims. */\nexport interface LoadedImage {\n width: number;\n height: number;\n}\n\n/** Canvas module surface (createCanvas + loadImage). */\ninterface CanvasModule {\n createCanvas: (w: number, h: number) => NodeCanvas;\n loadImage: (src: string) => Promise<LoadedImage>;\n}\n\n/** Bounds + centered frame for fitting an image into a canvas. */\nexport interface ImageFitResult {\n bounds: { width: number; height: number };\n frame: { x: number; y: number };\n}\n\n/**\n * Fit a natural-sized image into a canvas while preserving aspect ratio,\n * centered. Adapted from studio's image-drop helper (kept local so the CLI's\n * server-side render path doesn't import the browser studio package).\n *\n * Landscape (wider than canvas) → fit to width; portrait (taller or equal) →\n * fit to height; both dims capped at the canvas extents. Degenerate natural\n * sizes fall back to canvas extents so callers always get valid bounds.\n */\nexport function fitImageToCanvas(\n canvas: { width: number; height: number },\n natural: { width: number; height: number },\n): ImageFitResult {\n const cw = canvas.width;\n const ch = canvas.height;\n const iw = natural.width;\n const ih = natural.height;\n\n if (!iw || !ih) {\n return { bounds: { width: cw, height: ch }, frame: { x: cw / 2, y: ch / 2 } };\n }\n\n const imageAspect = iw / ih;\n const canvasAspect = cw / ch;\n\n let width: number;\n let height: number;\n if (imageAspect > canvasAspect) {\n width = cw;\n height = cw / imageAspect;\n } else {\n height = ch;\n width = ch * imageAspect;\n }\n width = Math.min(width, cw);\n height = Math.min(height, ch);\n\n return { bounds: { width, height }, frame: { x: cw / 2, y: ch / 2 } };\n}\n\n/**\n * Raised when the `@napi-rs/canvas` rasterizer cannot be loaded. In practice\n * this never fires: `@napi-rs/canvas` is a hard dependency that ships prebuilt\n * platform binaries (no node-gyp, no system libraries). It can only happen if\n * the install is corrupt or running on an unsupported platform with no prebuilt\n * binary — in which case a reinstall is the fix. Kept as a typed seam so call\n * sites can still distinguish a missing-rasterizer failure from a bad document.\n */\nexport class CanvasUnavailableError extends Error {\n constructor() {\n super(\n \"The '@napi-rs/canvas' rasterizer could not be loaded.\\n\" +\n \"This package ships prebuilt platform binaries — no system libraries needed.\\n\" +\n \"Try reinstalling dependencies (e.g. `npm install`) to fetch the binary for this platform.\",\n );\n this.name = \"CanvasUnavailableError\";\n }\n}\n\n/**\n * Resolve the canvas module. `@napi-rs/canvas` is a hard, statically-imported\n * dependency, so this is a thin accessor that surfaces a typed\n * {@link CanvasUnavailableError} on the (essentially impossible) chance the\n * platform binary failed to load.\n */\nexport async function loadCanvasModule(): Promise<CanvasModule> {\n if (typeof createCanvas !== \"function\" || typeof loadImage !== \"function\") {\n throw new CanvasUnavailableError();\n }\n return {\n createCanvas: createCanvas as unknown as CanvasModule[\"createCanvas\"],\n loadImage: loadImage as unknown as CanvasModule[\"loadImage\"],\n };\n}\n\n/**\n * Compute final output dimensions from the document canvas and optional\n * width/height overrides. When only one of width/height is provided, the\n * other is derived to preserve the document's aspect ratio. When both are\n * provided, both are used verbatim (allows non-uniform scaling).\n */\nexport function resolveExportDimensions(\n docWidth: number,\n docHeight: number,\n width?: number,\n height?: number,\n): { width: number; height: number } {\n if (width !== undefined && height !== undefined) {\n return { width, height };\n }\n if (width !== undefined) {\n const h = Math.max(1, Math.round((width * docHeight) / docWidth));\n return { width, height: h };\n }\n if (height !== undefined) {\n const w = Math.max(1, Math.round((height * docWidth) / docHeight));\n return { width: w, height };\n }\n return { width: docWidth, height: docHeight };\n}\n\n/**\n * Build an {@link ImageCache} pre-populated with every ImageVisual source in\n * the document. Mirrors render-pipeline.ts: load each source with\n * `loadImage`, then wire a synchronous-from-cache `createImage` so the renderer\n * resolves images immediately. Returns the cache plus a map of src → loaded\n * image so callers can read natural dimensions.\n */\nasync function preloadImages(\n doc: AtelierDocument,\n loadImage: (src: string) => Promise<LoadedImage>,\n): Promise<{\n imageCache: import(\"@a-company/atelier-canvas\").ImageCache;\n loaded: Map<string, LoadedImage>;\n}> {\n const { ImageCache } = await import(\"@a-company/atelier-canvas\");\n\n const sources = new Set<string>();\n for (const layer of doc.layers) {\n if (layer.visual.type === \"image\") {\n const iv = layer.visual as { src?: string; assetId?: string };\n if (iv.src) sources.add(iv.src);\n else if (iv.assetId && doc.assets?.[iv.assetId]) sources.add(doc.assets[iv.assetId].src);\n }\n }\n\n if (sources.size === 0) {\n return { imageCache: new ImageCache(), loaded: new Map() };\n }\n\n const preloaded = new Map<string, LoadedImage>();\n await Promise.all(\n [...sources].map(async (src) => {\n try {\n preloaded.set(src, await loadImage(src));\n } catch {\n // Sources that fail to load render blank — same tolerance as render-pipeline.\n }\n }),\n );\n\n const imageCache = new ImageCache({\n createImage: (src, onLoad, onError) => {\n const img = preloaded.get(src);\n if (img) {\n process.nextTick(onLoad);\n return img;\n }\n process.nextTick(onError);\n return {};\n },\n });\n for (const src of preloaded.keys()) imageCache.load(src);\n await new Promise<void>((resolve) => process.nextTick(resolve));\n\n return { imageCache, loaded: preloaded };\n}\n\nexport interface RenderToPngOptions {\n /** State to resolve (defaults to the first state). */\n state?: string;\n /** Frame within the state (defaults to 0). */\n frame?: number;\n /** Output width override (px). */\n width?: number;\n /** Output height override (px). */\n height?: number;\n /**\n * Optional hook to adjust each image layer's bounds once natural dimensions\n * are known from the loaded image. Receives the layer src + natural dims and\n * the doc canvas; returns new bounds (and frame). Used by the carousel driver\n * to aspect-fit fit-to-canvas images that were composed with placeholder\n * bounds (natural dims are unknown until the rasterizer decodes the file).\n */\n refitImageBounds?: (args: {\n canvas: { width: number; height: number };\n natural: { width: number; height: number };\n }) => { bounds: { width: number; height: number }; frame: { x: number; y: number } };\n}\n\n/**\n * Render a single resolved frame of a document to a PNG buffer.\n *\n * Validates state/frame, pre-loads image layers, scales rendering to fit the\n * requested output dimensions, and returns the encoded PNG bytes. Throws\n * {@link CanvasUnavailableError} when the rasterizer is missing and a plain\n * Error for bad state/frame selection.\n */\nexport async function renderDocumentToPng(\n doc: AtelierDocument,\n opts: RenderToPngOptions = {},\n): Promise<Buffer> {\n const stateNames = Object.keys(doc.states);\n if (stateNames.length === 0) {\n throw new Error(\"Document has no states\");\n }\n const stateName = opts.state ?? stateNames[0];\n if (!doc.states[stateName]) {\n throw new Error(`State \"${stateName}\" not found. Available: ${stateNames.join(\", \")}`);\n }\n const frameNumber = opts.frame ?? 0;\n if (!Number.isInteger(frameNumber) || frameNumber < 0) {\n throw new Error(`Invalid frame number: ${frameNumber}`);\n }\n const duration = doc.states[stateName].duration;\n if (frameNumber >= duration) {\n throw new Error(\n `Frame ${frameNumber} is out of range for state \"${stateName}\" (duration ${duration})`,\n );\n }\n\n const { createCanvas, loadImage } = await loadCanvasModule();\n const { renderFrame } = await import(\"@a-company/atelier-canvas\");\n\n const { imageCache, loaded } = await preloadImages(doc, loadImage);\n\n // Re-fit image layers now that natural dims are known (carousel path).\n let renderDoc = doc;\n if (opts.refitImageBounds && loaded.size > 0) {\n renderDoc = {\n ...doc,\n layers: doc.layers.map((layer) => {\n if (layer.visual.type !== \"image\") return layer;\n const iv = layer.visual as { src?: string; assetId?: string };\n const src = iv.src ?? (iv.assetId ? doc.assets?.[iv.assetId]?.src : undefined);\n const natural = src ? loaded.get(src) : undefined;\n if (!natural || !natural.width || !natural.height) return layer;\n const fit = opts.refitImageBounds!({ canvas: doc.canvas, natural });\n return { ...layer, bounds: fit.bounds, frame: fit.frame };\n }),\n };\n }\n\n const { width: outW, height: outH } = resolveExportDimensions(\n renderDoc.canvas.width,\n renderDoc.canvas.height,\n opts.width,\n opts.height,\n );\n\n const resolved = resolveFrame(renderDoc, stateName, frameNumber);\n const cvs = createCanvas(outW, outH);\n const ctx = cvs.getContext(\"2d\") as unknown as import(\"@a-company/atelier-canvas\").RenderContext;\n\n const sx = outW / renderDoc.canvas.width;\n const sy = outH / renderDoc.canvas.height;\n if (sx !== 1 || sy !== 1) ctx.scale(sx, sy);\n\n renderFrame(ctx, resolved, renderDoc, imageCache);\n\n return cvs.toBuffer(\"image/png\");\n}\n","import { readdirSync, mkdirSync, writeFileSync, statSync } from \"node:fs\";\nimport { resolve, join, basename, extname, dirname, sep } from \"node:path\";\nimport type { Command } from \"commander\";\nimport type { AtelierDocument, StudioRecipe } from \"@a-company/atelier-types\";\nimport { loadRecipe, applyRecipeToOverlay } from \"../lib/recipe.js\";\nimport {\n renderDocumentToPng,\n fitImageToCanvas,\n CanvasUnavailableError,\n} from \"../lib/render-image.js\";\n\n/** Image extensions the carousel driver accepts (lower-cased, with leading dot). */\nconst IMAGE_EXTS = new Set([\".png\", \".jpg\", \".jpeg\", \".webp\"]);\n\n/** Default canvas size when neither --width/--height nor a recipe aspect is set. */\nconst DEFAULT_CANVAS = 1080;\n\n/**\n * Expand an --inputs pattern into a sorted list of absolute image file paths.\n *\n * Deliberately minimal (no new glob dependency): supports a directory path\n * (lists all image files within), a single file path, or a single-segment\n * `*`-glob in the final path component (e.g. `shots/*.png`, `shots/img-*`).\n * Multi-segment / recursive globs are out of scope — point --inputs at a\n * directory instead. The returned list is filtered to image extensions and\n * sorted lexicographically so output ordering is stable.\n */\nexport function expandInputs(pattern: string): string[] {\n const abs = resolve(pattern);\n\n // Directory → every image file inside it.\n let isDir = false;\n try {\n isDir = statSync(abs).isDirectory();\n } catch {\n isDir = false;\n }\n if (isDir) {\n return listImages(abs);\n }\n\n const dir = dirname(abs);\n const base = basename(abs);\n\n // Single-segment `*` glob in the final component.\n if (base.includes(\"*\")) {\n const matcher = globToRegExp(base);\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return [];\n }\n return entries\n .filter((name) => matcher.test(name) && isImageFile(name))\n .map((name) => join(dir, name))\n .sort();\n }\n\n // Literal file path — keep it only if it is an image file.\n return isImageFile(base) ? [abs] : [];\n}\n\n/** List image files directly within a directory, sorted, as absolute paths. */\nfunction listImages(dir: string): string[] {\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return [];\n }\n return entries\n .filter(isImageFile)\n .map((name) => join(dir, name))\n .sort();\n}\n\n/** True when a filename has an accepted image extension. */\nfunction isImageFile(name: string): boolean {\n return IMAGE_EXTS.has(extname(name).toLowerCase());\n}\n\n/**\n * Convert a single path-component glob (only `*` and `?` honored) into a\n * RegExp anchored to the whole name. `*` matches any run except the path\n * separator; `?` matches a single non-separator char.\n */\nfunction globToRegExp(glob: string): RegExp {\n const escaped = glob.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const pattern = escaped.replace(/\\*/g, `[^${sep === \"\\\\\" ? \"\\\\\\\\\" : sep}]*`).replace(/\\?/g, \".\");\n return new RegExp(`^${pattern}$`);\n}\n\n/**\n * Build a single-frame carousel document for one image: a canvas-sized doc with\n * a background and one fit-to-canvas ImageVisual layer, then the recipe's\n * overlay_rules applied with currentIndex/totalCount threaded so the page-number\n * overlay renders \"i/N\".\n *\n * Pure + synchronous (no node-canvas): image bounds use the canvas-extent\n * fallback because natural dimensions aren't known until node-canvas decodes\n * the file at render time. {@link renderDocumentToPng} re-fits the bounds via\n * `refitImageBounds` once the image is loaded.\n */\nexport function composeCarouselFrameDoc(args: {\n imagePath: string;\n index: number;\n total: number;\n width: number;\n height: number;\n recipe: StudioRecipe;\n background?: string;\n}): AtelierDocument {\n const { imagePath, index, total, width, height, recipe } = args;\n const canvas = { width, height };\n\n // Natural dims unknown here → fitImageToCanvas falls back to canvas extents.\n const fit = fitImageToCanvas(canvas, { width: 0, height: 0 });\n\n const assetId = \"carousel-image-asset\";\n const baseDoc: AtelierDocument = {\n version: \"1.0\",\n name: `carousel-${index}`,\n canvas: { width, height, fps: 30, background: args.background ?? \"#000000\" },\n assets: { [assetId]: { type: \"image\", src: imagePath } },\n layers: [\n {\n id: \"carousel-image\",\n visual: { type: \"image\", assetId, src: imagePath },\n frame: fit.frame,\n bounds: fit.bounds,\n anchorPoint: { x: 0.5, y: 0.5 },\n opacity: 1,\n },\n ],\n states: { default: { duration: 1, deltas: [] } },\n };\n\n // Thread the carousel position so page_number resolves \"i/N\"; handle (if any)\n // is anchored on top of the image.\n return applyRecipeToOverlay(baseDoc, recipe, { currentIndex: index, totalCount: total });\n}\n\n/** Zero-padded sortable filename prefix: width = max(2, digits in N). */\nexport function carouselFileName(index: number, total: number, imagePath: string): string {\n const padWidth = Math.max(2, String(total).length);\n const prefix = String(index).padStart(padWidth, \"0\");\n const ext = extname(imagePath);\n const stem = basename(imagePath, ext);\n return `${prefix}-${stem}.png`;\n}\n\ninterface CarouselOptions {\n inputs: string;\n outDir: string;\n width?: string;\n height?: string;\n frame: string;\n}\n\n/** Parse a positive-integer CLI option, exiting on bad input. */\nfunction parseDim(raw: string | undefined, name: string): number | undefined {\n if (raw === undefined) return undefined;\n const n = parseInt(raw, 10);\n if (isNaN(n) || n <= 0) {\n console.error(`Invalid --${name}: ${raw}`);\n process.exit(1);\n }\n return n;\n}\n\n/**\n * Register `atelier carousel <recipe> --inputs <glob> --out-dir <dir>` — batch\n * compose a folder of images into recipe-overlaid PNGs.\n *\n * For each image i of N: build a fit-to-canvas doc, apply the recipe's\n * overlay_rules with currentIndex=i / totalCount=N (handle + \"i/N\" page-number),\n * render via the shared export-image path, and write a zero-padded sortable\n * PNG into --out-dir.\n */\nexport function carouselCommand(program: Command): void {\n program\n .command(\"carousel <recipe>\")\n .description(\n \"Batch-compose a folder of images into recipe-overlaid PNGs. \" +\n \"Each image becomes a fit-to-canvas post with the recipe's handle + \" +\n \"page-number ('i/N') overlays, written to --out-dir as zero-padded PNGs.\",\n )\n .requiredOption(\"-i, --inputs <glob>\", \"Input images: a directory, file, or single-segment *-glob\")\n .requiredOption(\"-d, --out-dir <dir>\", \"Destination directory for composed PNGs\")\n .option(\"--width <number>\", \"Canvas width (px)\", String(DEFAULT_CANVAS))\n .option(\"--height <number>\", \"Canvas height (px)\", String(DEFAULT_CANVAS))\n .option(\"-f, --frame <number>\", \"Frame to render (defaults to 0)\", \"0\")\n .action(async (recipeRef: string, options: CarouselOptions) => {\n // Resolve the recipe (same chain as other recipe commands).\n let recipe: StudioRecipe;\n try {\n const loaded = loadRecipe(recipeRef);\n recipe = loaded.recipe;\n console.log(`Loaded recipe ${loaded.path}`);\n for (const w of loaded.warnings) console.log(` ⚠ ${w}`);\n } catch (err) {\n console.error(`atelier carousel: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n return;\n }\n\n // Expand inputs.\n const inputs = expandInputs(options.inputs);\n if (inputs.length === 0) {\n console.error(\n `atelier carousel: no image files matched --inputs \"${options.inputs}\" ` +\n `(accepted: ${[...IMAGE_EXTS].join(\", \")})`,\n );\n process.exit(1);\n return;\n }\n\n const width = parseDim(options.width, \"width\") ?? DEFAULT_CANVAS;\n const height = parseDim(options.height, \"height\") ?? DEFAULT_CANVAS;\n const frame = parseInt(options.frame, 10);\n if (isNaN(frame) || frame < 0) {\n console.error(`Invalid frame number: ${options.frame}`);\n process.exit(1);\n return;\n }\n\n // Ensure the destination exists / is writable.\n const outDir = resolve(options.outDir);\n try {\n mkdirSync(outDir, { recursive: true });\n } catch (err) {\n console.error(`atelier carousel: cannot create out-dir ${outDir}: ${(err as Error).message}`);\n process.exit(1);\n return;\n }\n\n const total = inputs.length;\n console.log(`Composing ${total} image${total === 1 ? \"\" : \"s\"} → ${outDir}`);\n\n try {\n for (let n = 0; n < total; n++) {\n const index = n + 1; // 1-based\n const imagePath = inputs[n];\n const doc = composeCarouselFrameDoc({\n imagePath,\n index,\n total,\n width,\n height,\n recipe,\n });\n\n const buffer = await renderDocumentToPng(doc, {\n frame,\n // Re-fit the image to the canvas using real natural dims once decoded.\n refitImageBounds: ({ canvas, natural }) => fitImageToCanvas(canvas, natural),\n });\n\n const outName = carouselFileName(index, total, imagePath);\n writeFileSync(join(outDir, outName), buffer);\n console.log(` [${index}/${total}] ${basename(imagePath)} → ${outName}`);\n }\n } catch (err) {\n if (err instanceof CanvasUnavailableError) {\n console.error(err.message);\n process.exit(1);\n return;\n }\n console.error(`atelier carousel: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n return;\n }\n\n console.log(`\\nDone. ${total} image${total === 1 ? \"\" : \"s\"} → ${outDir}`);\n });\n}\n","import { readFileSync, existsSync } from \"node:fs\";\nimport { join, resolve, isAbsolute } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { parse as parseYaml, stringify as stringifyYaml } from \"yaml\";\nimport { validateRecipe } from \"@a-company/atelier-schema\";\nimport type {\n StudioRecipe,\n SilencePolicy,\n CaptionStyle,\n CaptionGrouping,\n AtelierDocument,\n Layer,\n OverlayAnchor,\n OverlayTextStyle,\n OverlayPageNumberRule,\n} from \"@a-company/atelier-types\";\nimport type { TrimOptions } from \"../commands/trim.js\";\nimport type { TranscribeOptions } from \"../commands/transcribe.js\";\nimport type { CaptionStyle as RuntimeCaptionStyle, BuildCaptionsOptions } from \"./caption-builder.js\";\n\nexport const RECIPE_VERSION = \"1.0\";\n\nexport interface LoadedRecipe {\n recipe: StudioRecipe;\n /** Absolute path the recipe was read from */\n path: string;\n /** Warnings from validateRecipe (e.g. reserved-field usage) */\n warnings: string[];\n}\n\n/**\n * Resolve a recipe reference to an absolute path.\n *\n * Resolution order (per studio-recipe.md §6.2):\n * 1. <projectDir>/.atelier/recipes/<name>.recipe.{yaml,json}\n * 2. ~/.atelier/recipes/<name>.recipe.{yaml,json}\n * 3. The literal path passed (absolute or relative to cwd)\n *\n * Absolute paths and paths containing slashes skip resolution and load directly.\n */\nexport function resolveRecipePath(pathOrName: string, projectDir?: string): string {\n // Absolute path or contains a path separator — treat as literal\n if (isAbsolute(pathOrName) || pathOrName.includes(\"/\") || pathOrName.includes(\"\\\\\")) {\n return resolve(pathOrName);\n }\n\n const candidates: string[] = [];\n const exts = [\".recipe.yaml\", \".recipe.json\", \".yaml\", \".yml\", \".json\"];\n\n if (projectDir) {\n const projectRecipesDir = join(resolve(projectDir), \".atelier\", \"recipes\");\n for (const ext of exts) candidates.push(join(projectRecipesDir, `${pathOrName}${ext}`));\n }\n const userRecipesDir = join(homedir(), \".atelier\", \"recipes\");\n for (const ext of exts) candidates.push(join(userRecipesDir, `${pathOrName}${ext}`));\n\n for (const candidate of candidates) {\n if (existsSync(candidate)) return candidate;\n }\n\n throw new Error(\n `Recipe \"${pathOrName}\" not found. Looked in:\\n${candidates.map((c) => ` ${c}`).join(\"\\n\")}`,\n );\n}\n\n/**\n * Load and validate a recipe from a path or name.\n *\n * Sniffs YAML vs JSON by file extension; falls back to YAML parser when\n * unclear (YAML is a superset of JSON for objects).\n */\nexport function loadRecipe(pathOrName: string, projectDir?: string): LoadedRecipe {\n const path = resolveRecipePath(pathOrName, projectDir);\n const raw = readFileSync(path, \"utf-8\");\n\n let parsed: unknown;\n if (path.endsWith(\".json\")) {\n parsed = JSON.parse(raw);\n } else {\n parsed = parseYaml(raw);\n }\n\n const result = validateRecipe(parsed);\n if (!result.success) {\n const msg = result.errors.map((e) => ` ${e.path}: ${e.message}`).join(\"\\n\");\n throw new Error(`Invalid recipe at ${path}:\\n${msg}`);\n }\n\n return {\n recipe: result.data,\n path,\n warnings: result.warnings ?? [],\n };\n}\n\n/**\n * Generate a starter recipe YAML with every Phase 1 field present and\n * inline comments documenting it. Authors learn the shape by editing.\n */\nexport function scaffoldRecipeYaml(name: string): string {\n return `# Studio Recipe — ${name}\n# Phase 1 — manual authoring + apply\n# https://github.com/ascend42/a-atelier/blob/main/.paradigm/specs/studio-recipe.md\n\nversion: \"${RECIPE_VERSION}\"\nname: \"${name}\"\ndescription: \"\"\nauthor: \"\"\ntags: []\n\n# ── Silence-trim policy — consumed by \\`atelier trim\\` ───────────────────\nsilence_policy:\n # silencedetect noise threshold (default: -30dB)\n noise: \"-30dB\"\n # Minimum silence duration to register, in seconds (default: 0.35)\n min_silence: 0.35\n # Default leading padding for new cuts, in seconds (default: 0.08)\n default_padding_pre: 0.08\n # Default trailing padding for new cuts, in seconds (default: 0.12)\n default_padding_post: 0.12\n # Re-detect match tolerance for preserving user padding, in seconds (default: 0.5)\n match_tolerance: 0.5\n\n# ── Caption visual style — consumed by \\`atelier transcribe\\` ────────────\ncaption_style:\n font_family: \"Inter\"\n font_size: 84\n font_weight: \"bold\" # normal | bold | numeric (100..900)\n text_align: \"center\" # left | center | right\n color: \"#FFFFFF\"\n y_ratio: 0.85 # 0=top, 1=bottom\n width_ratio: 0.9\n fade_seconds: 0.05\n\n# ── Caption phrase grouping — consumed by \\`atelier transcribe\\` ─────────\ncaption_grouping:\n max_words: 5\n pause_gap: 0.4\n\n# ── Overlay rules (Phase 1.5) — anchored handle + page-number overlays ─\n# overlay_rules:\n# handle:\n# text: \"@username\"\n# anchor: \"bottom-left\" # top-left | top-right | bottom-left | bottom-right\n# margin: 24 # px from anchored edges\n# style:\n# font_family: \"Inter\"\n# font_size: 36\n# font_weight: \"bold\"\n# color: \"#FFFFFF\"\n# page_number:\n# format: \"{current}/{total}\" # supports {current:02d} / {total:02d} zero-pad\n# anchor: \"top-right\"\n# margin: 24\n# style:\n# font_family: \"Inter\"\n# font_size: 36\n# font_weight: \"normal\"\n# color: \"#FFFFFF\"\n\n# ── Phase 3 fields (reserved — Phase 1 ignores these) ──────────────────\n# caption_highlight: {}\n# transition_kit: {}\n# palette: {}\n# audio_policy: {}\n# aspect_targets: []\n`;\n}\n\n// ─── Recipe → CLI options translation ─────────────────────────────\n\n/**\n * Merge a recipe's silence_policy into TrimOptions.\n * CLI options take precedence (per studio-recipe.md §4.1).\n */\nexport function applyRecipeToTrimOptions(\n recipe: StudioRecipe | undefined,\n cliOptions: TrimOptions,\n): TrimOptions {\n const policy = recipe?.silence_policy;\n if (!policy) return cliOptions;\n\n // CLI options win — only fill in from recipe where CLI didn't specify\n return {\n ...cliOptions,\n noise: cliOptions.noise ?? policy.noise,\n minSilence: cliOptions.minSilence ?? policy.min_silence,\n padPre: cliOptions.padPre ?? policy.default_padding_pre,\n padPost: cliOptions.padPost ?? policy.default_padding_post,\n // matchTolerance is recipe-only at this layer (no CLI flag yet)\n matchTolerance: cliOptions.matchTolerance ?? policy.match_tolerance,\n };\n}\n\n/**\n * Translate a recipe's caption_style + caption_grouping into runtime\n * BuildCaptionsOptions for the caption builder.\n *\n * CLI doesn't currently expose per-invocation caption styling flags;\n * the recipe is the canonical source for now.\n */\nexport function applyRecipeToCaptionOptions(\n recipe: StudioRecipe | undefined,\n): BuildCaptionsOptions {\n if (!recipe) return {};\n const style = recipe.caption_style;\n const grouping = recipe.caption_grouping;\n\n const runtimeStyle: RuntimeCaptionStyle = {};\n if (style) {\n if (style.font_family !== undefined) runtimeStyle.fontFamily = style.font_family;\n if (style.font_size !== undefined) runtimeStyle.fontSize = style.font_size;\n if (style.font_weight !== undefined) runtimeStyle.fontWeight = style.font_weight;\n if (style.text_align !== undefined) runtimeStyle.textAlign = style.text_align;\n if (style.color !== undefined) runtimeStyle.color = style.color;\n if (style.y_ratio !== undefined) runtimeStyle.yRatio = style.y_ratio;\n if (style.width_ratio !== undefined) runtimeStyle.widthRatio = style.width_ratio;\n if (style.fade_seconds !== undefined) runtimeStyle.fadeSeconds = style.fade_seconds;\n }\n\n return {\n ...(Object.keys(runtimeStyle).length > 0 && { style: runtimeStyle }),\n ...(grouping?.max_words !== undefined && { maxWords: grouping.max_words }),\n ...(grouping?.pause_gap !== undefined && { pauseGap: grouping.pause_gap }),\n };\n}\n\n/**\n * Merge a recipe into TranscribeOptions. Caption-related recipe fields\n * are stashed on the transcribe options so the orchestrator can pass them\n * through to rewriteCaptionLayers.\n */\nexport function applyRecipeToTranscribeOptions(\n recipe: StudioRecipe | undefined,\n cliOptions: TranscribeOptions,\n): TranscribeOptions {\n if (!recipe) return cliOptions;\n const captionOptions = applyRecipeToCaptionOptions(recipe);\n return {\n ...cliOptions,\n captionOptions,\n };\n}\n\n/**\n * Render a recipe's effective values (recipe overlaid on code defaults).\n * Used by `atelier recipe show --with-defaults`.\n */\nexport function renderRecipeWithDefaults(recipe: StudioRecipe): StudioRecipe {\n const defaults: { silence_policy: SilencePolicy; caption_style: CaptionStyle; caption_grouping: CaptionGrouping } = {\n silence_policy: {\n noise: \"-30dB\",\n min_silence: 0.35,\n default_padding_pre: 0.08,\n default_padding_post: 0.12,\n match_tolerance: 0.5,\n },\n caption_style: {\n font_family: \"Inter\",\n font_size: 84,\n font_weight: \"bold\",\n text_align: \"center\",\n color: \"#FFFFFF\",\n y_ratio: 0.85,\n width_ratio: 0.9,\n fade_seconds: 0.05,\n },\n caption_grouping: {\n max_words: 5,\n pause_gap: 0.4,\n },\n };\n\n return {\n ...recipe,\n silence_policy: { ...defaults.silence_policy, ...recipe.silence_policy },\n caption_style: { ...defaults.caption_style, ...recipe.caption_style },\n caption_grouping: { ...defaults.caption_grouping, ...recipe.caption_grouping },\n };\n}\n\n/** Serialize a recipe back to YAML for `recipe show` output */\nexport function recipeToYaml(recipe: StudioRecipe): string {\n return stringifyYaml(recipe);\n}\n\n// ─── Overlay translator ──────────────────────────────────────────\n//\n// Translates recipe.overlay_rules → tag-isolated TextVisual layers\n// (handle + page_number). Mirrors rewriteCaptionLayers' tag-namespace\n// isolation invariant: only \"overlay\"-tagged layers are touched; all\n// user-authored / silence-trim / caption layers pass through untouched.\n// Re-applying is idempotent — the prior overlay set is dropped before\n// the fresh one is appended.\n\n/** Context for overlay translation — required for page_number rendering */\nexport interface ApplyOverlayContext {\n /** 1-based index in a carousel (substituted for {current}) */\n currentIndex?: number;\n /** Total carousel size (substituted for {total}) */\n totalCount?: number;\n}\n\n/** Default px from the anchored edges when a rule omits margin */\nconst DEFAULT_OVERLAY_MARGIN = 24;\n\n/** Default TextStyle baseline — overlay rule.style merges OVER these */\nconst DEFAULT_OVERLAY_TEXT_STYLE = {\n fontFamily: \"Inter\",\n fontSize: 24,\n fontWeight: 600 as const,\n color: \"#F5F5F7\",\n};\n\n/** Stable layer ids — re-apply replaces by id within the overlay drop+re-add */\nconst HANDLE_LAYER_ID = \"overlay-handle\";\nconst PAGE_NUMBER_LAYER_ID = \"overlay-page-number\";\n\n/**\n * Apply a Studio Recipe's overlay_rules to a document. Returns a NEW document;\n * input is never mutated. Drops every layer tagged \"overlay\" before appending\n * the freshly-derived overlay layers, so re-running is idempotent.\n *\n * page_number layers are only emitted when ctx.currentIndex + ctx.totalCount\n * are both present — single-frame application silently skips page_number\n * (with a one-shot console.warn) so apply-recipe pipelines stay safe outside\n * carousel batches.\n */\nexport function applyRecipeToOverlay(\n doc: AtelierDocument,\n recipe: StudioRecipe,\n ctx?: ApplyOverlayContext,\n): AtelierDocument {\n const preserved = doc.layers.filter((l) => !(l.tags ?? []).includes(\"overlay\"));\n const overlayLayers: Layer[] = [];\n\n const rules = recipe.overlay_rules;\n if (!rules) {\n return { ...doc, layers: preserved };\n }\n\n if (rules.handle) {\n overlayLayers.push(buildHandleLayer(rules.handle, doc.canvas));\n }\n\n if (rules.page_number) {\n if (ctx?.currentIndex != null && ctx?.totalCount != null) {\n overlayLayers.push(\n buildPageNumberLayer(rules.page_number, doc.canvas, ctx.currentIndex, ctx.totalCount),\n );\n } else {\n console.warn(\n `applyRecipeToOverlay: recipe.overlay_rules.page_number present but ` +\n `currentIndex/totalCount not provided — skipping page_number layer.`,\n );\n }\n }\n\n return {\n ...doc,\n layers: [...preserved, ...overlayLayers],\n };\n}\n\n/** Build the anchored handle TextVisual layer */\nfunction buildHandleLayer(\n rule: { text: string; anchor: OverlayAnchor; margin?: number; style?: OverlayTextStyle },\n canvas: AtelierDocument[\"canvas\"],\n): Layer {\n const margin = rule.margin ?? DEFAULT_OVERLAY_MARGIN;\n const { frame, anchorPoint } = anchorToFrame(rule.anchor, canvas, margin);\n return {\n id: HANDLE_LAYER_ID,\n tags: [\"overlay\"],\n visual: {\n type: \"text\",\n content: rule.text,\n style: mergeOverlayStyle(rule.style),\n },\n frame,\n bounds: { width: 600, height: 80 },\n anchorPoint,\n };\n}\n\n/** Build the anchored page-number TextVisual layer */\nfunction buildPageNumberLayer(\n rule: OverlayPageNumberRule,\n canvas: AtelierDocument[\"canvas\"],\n currentIndex: number,\n totalCount: number,\n): Layer {\n const margin = rule.margin ?? DEFAULT_OVERLAY_MARGIN;\n const { frame, anchorPoint } = anchorToFrame(rule.anchor, canvas, margin);\n return {\n id: PAGE_NUMBER_LAYER_ID,\n tags: [\"overlay\"],\n visual: {\n type: \"text\",\n content: renderPageNumberFormat(rule.format, currentIndex, totalCount),\n style: mergeOverlayStyle(rule.style),\n },\n frame,\n bounds: { width: 200, height: 80 },\n anchorPoint,\n };\n}\n\n/** Anchor enum → frame coords + 0/1 anchorPoint */\nfunction anchorToFrame(\n anchor: OverlayAnchor,\n canvas: AtelierDocument[\"canvas\"],\n margin: number,\n): { frame: { x: number; y: number }; anchorPoint: { x: number; y: number } } {\n switch (anchor) {\n case \"top-left\":\n return { frame: { x: margin, y: margin }, anchorPoint: { x: 0, y: 0 } };\n case \"top-right\":\n return { frame: { x: canvas.width - margin, y: margin }, anchorPoint: { x: 1, y: 0 } };\n case \"bottom-left\":\n return { frame: { x: margin, y: canvas.height - margin }, anchorPoint: { x: 0, y: 1 } };\n case \"bottom-right\":\n return {\n frame: { x: canvas.width - margin, y: canvas.height - margin },\n anchorPoint: { x: 1, y: 1 },\n };\n }\n}\n\n/** Merge a partial OverlayTextStyle over the runtime defaults (always returns a complete TextStyle) */\nfunction mergeOverlayStyle(\n style: OverlayTextStyle | undefined,\n): { fontFamily: string; fontSize: number; fontWeight: number | \"normal\" | \"bold\"; color: string } {\n return {\n fontFamily: style?.font_family ?? DEFAULT_OVERLAY_TEXT_STYLE.fontFamily,\n fontSize: style?.font_size ?? DEFAULT_OVERLAY_TEXT_STYLE.fontSize,\n fontWeight: style?.font_weight ?? DEFAULT_OVERLAY_TEXT_STYLE.fontWeight,\n color: style?.color ?? DEFAULT_OVERLAY_TEXT_STYLE.color,\n };\n}\n\n/**\n * Render a page_number format template by substituting {current} / {total}\n * placeholders (including zero-pad forms like {current:02d}, {total:02d}).\n *\n * The schema (^valid-recipe gate) already enforces that ≥1 placeholder is\n * present — this is render-time substitution only.\n */\nexport function renderPageNumberFormat(\n format: string,\n currentIndex: number,\n totalCount: number,\n): string {\n return format.replace(\n /\\{(current|total)(?::0(\\d+)d)?\\}/g,\n (_, name: string, padWidth?: string) => {\n const value = name === \"current\" ? currentIndex : totalCount;\n const str = String(value);\n if (padWidth) {\n const width = parseInt(padWidth, 10);\n return str.padStart(width, \"0\");\n }\n return str;\n },\n );\n}\n","import { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { Command } from \"commander\";\nimport type { AtelierDocument, ImageVisual, VideoVisual, AudioVisual } from \"@a-company/atelier-types\";\nimport { parseAtelier } from \"@a-company/atelier-schema\";\n\n/**\n * Asset summary entry. v1.0: `usedByStates` removed — audio is no longer\n * state-scoped (it's an AudioVisual layer), so layer-only tracking is now\n * the complete picture.\n */\nexport interface AssetInfo {\n assetId: string;\n type: string;\n src: string;\n description?: string;\n usedByLayers: string[];\n}\n\n/**\n * Extract asset info from a parsed AtelierDocument.\n *\n * Walks all layers (image, video, audio) for assetId references. Pre-v1\n * docs that still carry `state.audio` are silently migrated by the parser\n * (audio field stripped); no per-state asset usage is tracked.\n */\nexport function getAssets(doc: AtelierDocument): AssetInfo[] {\n const assets = doc.assets ?? {};\n return Object.entries(assets).map(([assetId, asset]) => {\n const usedByLayers = doc.layers\n .filter(l => {\n const v = l.visual as ImageVisual | VideoVisual | AudioVisual | { type: string; assetId?: string };\n return (\n (v.type === \"image\" || v.type === \"video\" || v.type === \"audio\") &&\n \"assetId\" in v && v.assetId === assetId\n );\n })\n .map(l => l.id);\n\n return {\n assetId,\n type: asset.type,\n src: asset.src,\n description: asset.description,\n usedByLayers,\n };\n });\n}\n\n/**\n * Format asset info for terminal output.\n */\nfunction formatAssets(assets: AssetInfo[]): string {\n if (assets.length === 0) return \"No assets registered.\";\n\n const lines: string[] = [`Assets: ${assets.length}`];\n for (const a of assets) {\n const desc = a.description ? ` — ${a.description}` : \"\";\n lines.push(` - ${a.assetId} (${a.type}): ${a.src}${desc}`);\n if (a.usedByLayers.length > 0) {\n lines.push(` Layers: ${a.usedByLayers.join(\", \")}`);\n }\n }\n return lines.join(\"\\n\");\n}\n\n/** Read and parse an .atelier file, exiting on failure. */\nfunction readAndParse(file: string): AtelierDocument {\n const absPath = resolve(file);\n let content: string;\n try {\n content = readFileSync(absPath, \"utf-8\");\n } catch {\n console.error(`Cannot read file: ${absPath}`);\n return process.exit(1);\n }\n\n const result = parseAtelier(content);\n if (!result.success) {\n console.error(\"Parse errors:\");\n for (const error of result.errors) {\n console.error(` - ${error.path}: ${error.message}`);\n }\n return process.exit(1);\n }\n\n return result.data;\n}\n\n/**\n * Register the `assets` subcommand on the Commander program.\n */\nexport function assetsCommand(program: Command): void {\n program\n .command(\"assets <file>\")\n .description(\"List all assets in an .atelier file with usage info\")\n .action((file: string) => {\n const doc = readAndParse(file);\n const assets = getAssets(doc);\n console.log(formatAssets(assets));\n });\n}\n","import { readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { Command } from \"commander\";\nimport type { AtelierDocument } from \"@a-company/atelier-types\";\nimport { parseAtelier } from \"@a-company/atelier-schema\";\nimport { findTemplateVariables } from \"@a-company/atelier-core\";\n\n/** Variable summary entry */\nexport interface VariableInfo {\n name: string;\n type: string;\n description?: string;\n default?: unknown;\n referenced: boolean;\n}\n\n/**\n * Extract variable info from a parsed AtelierDocument.\n */\nexport function getVariables(doc: AtelierDocument): { variables: VariableInfo[]; undeclared: string[] } {\n const variables = doc.variables ?? {};\n const referenced = findTemplateVariables(doc);\n\n const entries = Object.entries(variables).map(([name, variable]) => ({\n name,\n type: variable.type,\n description: variable.description,\n default: variable.default,\n referenced: referenced.includes(name),\n }));\n\n const undeclared = referenced.filter(r => !variables[r]);\n\n return { variables: entries, undeclared };\n}\n\n/**\n * Format variable info for terminal output.\n */\nfunction formatVariables(info: { variables: VariableInfo[]; undeclared: string[] }): string {\n if (info.variables.length === 0 && info.undeclared.length === 0) return \"No variables declared or referenced.\";\n\n const lines: string[] = [];\n\n if (info.variables.length > 0) {\n lines.push(`Variables: ${info.variables.length}`);\n for (const v of info.variables) {\n const desc = v.description ? ` — ${v.description}` : \"\";\n const def = v.default !== undefined ? ` [default: ${JSON.stringify(v.default)}]` : \"\";\n const ref = v.referenced ? \"\" : \" (unused)\";\n lines.push(` - {{${v.name}}} (${v.type})${def}${desc}${ref}`);\n }\n }\n\n if (info.undeclared.length > 0) {\n lines.push(`Undeclared references: ${info.undeclared.length}`);\n for (const name of info.undeclared) {\n lines.push(` - {{${name}}} (not declared in variables)`);\n }\n }\n\n return lines.join(\"\\n\");\n}\n\n/** Read and parse an .atelier file, exiting on failure. */\nfunction readAndParse(file: string): AtelierDocument {\n const absPath = resolve(file);\n let content: string;\n try {\n content = readFileSync(absPath, \"utf-8\");\n } catch {\n console.error(`Cannot read file: ${absPath}`);\n return process.exit(1);\n }\n\n const result = parseAtelier(content);\n if (!result.success) {\n console.error(\"Parse errors:\");\n for (const error of result.errors) {\n console.error(` - ${error.path}: ${error.message}`);\n }\n return process.exit(1);\n }\n\n return result.data;\n}\n\n/**\n * Register the `variables` subcommand on the Commander program.\n */\nexport function variablesCommand(program: Command): void {\n program\n .command(\"variables <file>\")\n .description(\"List all variables in an .atelier file with usage info\")\n .action((file: string) => {\n const doc = readAndParse(file);\n const info = getVariables(doc);\n console.log(formatVariables(info));\n });\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync } from \"node:fs\";\nimport { join, basename, extname, resolve } from \"node:path\";\nimport type { AtelierDocument, Layer } from \"@a-company/atelier-types\";\nimport type {\n CutEntry,\n VideoCutList,\n VideoTranscript,\n VideoProjectManifest,\n} from \"@a-company/atelier-types\";\n\nexport const VIDEO_PROJECT_VERSION = \"1.0\";\nexport const VIDEO_CUTLIST_VERSION = \"1.1\";\nexport const VIDEO_TRANSCRIPT_VERSION = \"1.1\";\n\n/**\n * Compute the effective in/out span for a cut, clamped to source bounds.\n * `start = max(0, rawStart - paddingPre)`, `end = min(duration, rawEnd + paddingPost)`.\n */\nexport function effectiveSpan(\n cut: CutEntry,\n duration: number,\n): { start: number; end: number } {\n return {\n start: Math.max(0, cut.rawStart - cut.paddingPre),\n end: Math.min(duration, cut.rawEnd + cut.paddingPost),\n };\n}\n\n/** Resolved absolute paths for all files in a VideoProject folder */\nexport interface VideoProject {\n /** Absolute path to the project folder */\n dir: string;\n /** Absolute path to source video file */\n sourcePath: string;\n /** Absolute path to project.atelier composition */\n compositionPath: string;\n /** Absolute path to transcript.json */\n transcriptPath: string;\n /** Absolute path to cuts.json */\n cutsPath: string;\n /** Absolute path to export/ directory */\n exportDir: string;\n /** Manifest metadata */\n manifest: VideoProjectManifest;\n}\n\n/**\n * Scaffold a new VideoProject folder from a source video file.\n *\n * Creates the folder at `destDir` (defaults to same directory as source,\n * named after the video file without extension). Copies the source video\n * into the folder as \"source<ext>\". Writes an empty draft project.atelier,\n * an empty cuts.json, and creates the export/ directory.\n *\n * Does NOT run metadata extraction — the caller (`atelier edit`, T-005)\n * probes duration/fps via ffprobe and fills in videoMeta before saving.\n */\nexport async function createVideoProject(\n srcPath: string,\n destDir?: string,\n): Promise<VideoProject> {\n const absSrc = resolve(srcPath);\n const ext = extname(absSrc);\n const stem = basename(absSrc, ext);\n\n const projectDir = destDir ? resolve(destDir) : join(resolve(absSrc, \"..\"), stem);\n\n if (!existsSync(projectDir)) {\n mkdirSync(projectDir, { recursive: true });\n }\n\n const sourceFilename = `source${ext}`;\n const sourcePath = join(projectDir, sourceFilename);\n\n // Copy source only if not already in place (idempotent re-init)\n if (!existsSync(sourcePath)) {\n copyFileSync(absSrc, sourcePath);\n }\n\n const compositionPath = join(projectDir, \"project.atelier\");\n const transcriptPath = join(projectDir, \"transcript.json\");\n const cutsPath = join(projectDir, \"cuts.json\");\n const exportDir = join(projectDir, \"export\");\n\n if (!existsSync(exportDir)) {\n mkdirSync(exportDir, { recursive: true });\n }\n\n const manifest: VideoProjectManifest = {\n source: sourceFilename,\n composition: \"project.atelier\",\n transcript: \"transcript.json\",\n cuts: \"cuts.json\",\n exportDir: \"export/\",\n createdAt: new Date().toISOString(),\n version: VIDEO_PROJECT_VERSION,\n };\n\n // Draft composition — caller fills in canvas dimensions and videoMeta after probing\n const draft: AtelierDocument = {\n version: \"1.0\",\n name: stem,\n canvas: { width: 1920, height: 1080, fps: 30 },\n assets: {\n src: {\n type: \"video\",\n src: sourceFilename,\n description: `Source: ${basename(absSrc)}`,\n },\n },\n layers: [\n {\n id: \"clip-0\",\n visual: {\n type: \"video\",\n assetId: \"src\",\n src: sourceFilename,\n startFrame: 0,\n sourceOffset: 0,\n playbackRate: 1.0,\n objectFit: \"contain\",\n },\n frame: { x: 0, y: 0 },\n bounds: { width: 1920, height: 1080 },\n },\n ],\n states: {\n default: {\n duration: 0,\n deltas: [],\n },\n },\n };\n\n if (!existsSync(compositionPath)) {\n writeFileSync(compositionPath, JSON.stringify(draft, null, 2), \"utf-8\");\n }\n\n const initialCuts: VideoCutList = {\n version: VIDEO_CUTLIST_VERSION,\n source: sourceFilename,\n cuts: [],\n };\n\n if (!existsSync(cutsPath)) {\n writeFileSync(cutsPath, JSON.stringify(initialCuts, null, 2), \"utf-8\");\n }\n\n return {\n dir: projectDir,\n sourcePath,\n compositionPath,\n transcriptPath,\n cutsPath,\n exportDir,\n manifest,\n };\n}\n\n/**\n * Load an existing VideoProject from a folder path.\n * Does not validate that the composition or cut list are well-formed —\n * callers that need that should use lintFile() after loading.\n */\nexport function loadVideoProject(dir: string): VideoProject {\n const projectDir = resolve(dir);\n\n // Detect source file (source.mp4, source.mov, etc.)\n const possibleExts = [\".mp4\", \".mov\", \".webm\", \".mkv\", \".avi\"];\n let sourceFilename = \"source.mp4\";\n for (const ext of possibleExts) {\n if (existsSync(join(projectDir, `source${ext}`))) {\n sourceFilename = `source${ext}`;\n break;\n }\n }\n\n const manifest: VideoProjectManifest = {\n source: sourceFilename,\n composition: \"project.atelier\",\n transcript: \"transcript.json\",\n cuts: \"cuts.json\",\n exportDir: \"export/\",\n createdAt: new Date().toISOString(),\n version: VIDEO_PROJECT_VERSION,\n };\n\n return {\n dir: projectDir,\n sourcePath: join(projectDir, sourceFilename),\n compositionPath: join(projectDir, \"project.atelier\"),\n transcriptPath: join(projectDir, \"transcript.json\"),\n cutsPath: join(projectDir, \"cuts.json\"),\n exportDir: join(projectDir, \"export\"),\n manifest,\n };\n}\n\n/**\n * Read and parse cuts.json from a VideoProject.\n *\n * Migrates legacy 1.0 cuts (flat { start, end }) to 1.1 parametric form\n * (rawStart/rawEnd + zero padding) on the fly. Writers always emit 1.1.\n */\nexport function readCutList(project: VideoProject): VideoCutList {\n if (!existsSync(project.cutsPath)) {\n return { version: VIDEO_CUTLIST_VERSION, source: project.manifest.source, cuts: [] };\n }\n const raw = JSON.parse(readFileSync(project.cutsPath, \"utf-8\")) as {\n version?: string;\n source: string;\n cuts: Array<CutEntry | { start: number; end: number; label?: string }>;\n };\n\n const cuts: CutEntry[] = raw.cuts.map((entry) => {\n if (\"rawStart\" in entry) return entry;\n // Legacy 1.0 migration — stored start/end become rawStart/rawEnd with zero padding.\n return {\n rawStart: entry.start,\n rawEnd: entry.end,\n paddingPre: 0,\n paddingPost: 0,\n ...(entry.label !== undefined && { label: entry.label }),\n };\n });\n\n return {\n version: VIDEO_CUTLIST_VERSION,\n source: raw.source,\n cuts,\n };\n}\n\n/** Write cuts.json to a VideoProject (always at the current cut list version) */\nexport function writeCutList(project: VideoProject, cuts: VideoCutList): void {\n const payload: VideoCutList = { ...cuts, version: VIDEO_CUTLIST_VERSION };\n writeFileSync(project.cutsPath, JSON.stringify(payload, null, 2), \"utf-8\");\n}\n\n/**\n * Read and parse transcript.json from a VideoProject.\n *\n * Migrates legacy 1.0 transcripts (TranscriptWord had flat `word: string`)\n * to 1.1 (`detected` + `text` + flags) on the fly. Writers always emit 1.1.\n */\nexport function readTranscript(project: VideoProject): VideoTranscript | null {\n if (!existsSync(project.transcriptPath)) return null;\n const raw = JSON.parse(readFileSync(project.transcriptPath, \"utf-8\")) as {\n version?: string;\n language?: string;\n segments: Array<{\n text: string;\n start: number;\n end: number;\n words: Array<\n // 1.1 shape\n | { detected: string; text: string; start: number; end: number; confidence?: number; userEdited?: boolean; userAdded?: boolean; hidden?: boolean }\n // 1.0 legacy shape\n | { word: string; start: number; end: number; confidence?: number }\n >;\n }>;\n };\n\n const segments = raw.segments.map((seg) => ({\n text: seg.text,\n start: seg.start,\n end: seg.end,\n words: seg.words.map((w) => {\n if (\"detected\" in w) return w;\n // 1.0 migration — `word` becomes both detected and text\n return {\n detected: w.word,\n text: w.word,\n start: w.start,\n end: w.end,\n ...(w.confidence !== undefined && { confidence: w.confidence }),\n };\n }),\n }));\n\n return {\n version: VIDEO_TRANSCRIPT_VERSION,\n ...(raw.language !== undefined && { language: raw.language }),\n segments,\n };\n}\n\n/** Write transcript.json to a VideoProject (always at the current transcript version) */\nexport function writeTranscript(project: VideoProject, transcript: VideoTranscript): void {\n const payload: VideoTranscript = { ...transcript, version: VIDEO_TRANSCRIPT_VERSION };\n writeFileSync(project.transcriptPath, JSON.stringify(payload, null, 2), \"utf-8\");\n}\n\n/** Read and parse project.atelier from a VideoProject */\nexport function readComposition(project: VideoProject): AtelierDocument {\n return JSON.parse(readFileSync(project.compositionPath, \"utf-8\")) as AtelierDocument;\n}\n\n/** Write project.atelier to a VideoProject */\nexport function writeComposition(project: VideoProject, doc: AtelierDocument): void {\n writeFileSync(project.compositionPath, JSON.stringify(doc, null, 2), \"utf-8\");\n}\n\n/**\n * Rewrite the silence-trim layers in a composition from a current cut list.\n *\n * Drops every layer tagged \"silence-trim\" then appends one VideoVisual layer\n * per cut, in temporal order, with cumulative startFrame computed from prior\n * clip durations. All other layers (user-authored, captions, overlays) are\n * preserved untouched — this is the tag-namespace isolation invariant.\n */\nexport function rewriteCutLayers(\n doc: AtelierDocument,\n cuts: CutEntry[],\n sourceFilename: string,\n sourceDuration: number,\n assetId = \"src\",\n): AtelierDocument {\n const preserved = doc.layers.filter((l) => !(l.tags ?? []).includes(\"silence-trim\"));\n const fps = doc.canvas.fps;\n\n let cumulativeFrame = 0;\n const trimLayers: Layer[] = cuts.map((cut, idx) => {\n const span = effectiveSpan(cut, sourceDuration);\n const sourceOffsetFrames = Math.floor(span.start * fps) / fps;\n const sourceEndFrames = Math.ceil(span.end * fps) / fps;\n const durationFrames = Math.max(1, Math.round((sourceEndFrames - sourceOffsetFrames) * fps));\n\n const layer: Layer = {\n id: `clip-trim-${idx}`,\n tags: [\"silence-trim\"],\n visual: {\n type: \"video\",\n assetId,\n src: sourceFilename,\n startFrame: cumulativeFrame,\n sourceOffset: sourceOffsetFrames,\n sourceEnd: sourceEndFrames,\n playbackRate: 1.0,\n objectFit: \"contain\",\n },\n frame: { x: 0, y: 0 },\n bounds: { width: doc.canvas.width, height: doc.canvas.height },\n };\n cumulativeFrame += durationFrames;\n return layer;\n });\n\n return { ...doc, layers: [...preserved, ...trimLayers] };\n}\n","import { spawn } from \"node:child_process\";\n\n/** A detected silence interval, in seconds from source start */\nexport interface SilenceInterval {\n start: number;\n end: number;\n}\n\n/** Options for the ffmpeg silencedetect filter */\nexport interface SilenceDetectOptions {\n /** Threshold below which audio counts as silence (default: \"-30dB\") */\n noise?: string;\n /** Minimum silence duration in seconds to register (default: 0.35) */\n minSilence?: number;\n}\n\n/**\n * Verify the local ffmpeg build has the silencedetect filter compiled in.\n * Fails loud and early if missing — some minimal builds disable filters.\n */\nexport async function probeSilencedetect(): Promise<void> {\n const stdout = await runCapture(\"ffmpeg\", [\"-hide_banner\", \"-filters\"]);\n if (!/\\bsilencedetect\\b/.test(stdout)) {\n throw new Error(\n \"Your ffmpeg build lacks the silencedetect filter. \" +\n \"Install a standard ffmpeg ≥ 4.0 (brew install ffmpeg / apt install ffmpeg).\",\n );\n }\n}\n\n/**\n * Probe source video duration via ffprobe, in seconds.\n * Used for boundary clamping of the trailing cut.\n */\nexport async function probeDuration(sourcePath: string): Promise<number> {\n const stdout = await runCapture(\"ffprobe\", [\n \"-v\", \"error\",\n \"-show_entries\", \"format=duration\",\n \"-of\", \"csv=p=0\",\n sourcePath,\n ]);\n const n = parseFloat(stdout.trim());\n if (!Number.isFinite(n) || n <= 0) {\n throw new Error(`ffprobe returned invalid duration for ${sourcePath}: \"${stdout}\"`);\n }\n return n;\n}\n\n/**\n * Run ffmpeg silencedetect on a source file, return list of silence intervals.\n *\n * silencedetect writes paired lines to stderr:\n * [silencedetect @ 0x...] silence_start: 1.234\n * [silencedetect @ 0x...] silence_end: 5.678 | silence_duration: 4.444\n *\n * Output to null muxer — no decoded frames, just the filter walking the audio.\n */\nexport async function runSilenceDetect(\n sourcePath: string,\n options: SilenceDetectOptions = {},\n): Promise<SilenceInterval[]> {\n const noise = options.noise ?? \"-30dB\";\n const minSilence = options.minSilence ?? 0.35;\n const filter = `silencedetect=noise=${noise}:d=${minSilence}`;\n\n const stderr = await runCaptureStderr(\"ffmpeg\", [\n \"-hide_banner\",\n \"-nostats\",\n \"-i\", sourcePath,\n \"-af\", filter,\n \"-f\", \"null\",\n \"-\",\n ]);\n\n return parseSilenceDetectStderr(stderr);\n}\n\n/**\n * Parse ffmpeg's silencedetect stderr output into intervals.\n * Pairs start/end lines in order; tolerates other filter chatter interleaved.\n * Exported for unit testing.\n */\nexport function parseSilenceDetectStderr(stderr: string): SilenceInterval[] {\n const intervals: SilenceInterval[] = [];\n const startRe = /silence_start:\\s*(-?[\\d.]+)/;\n const endRe = /silence_end:\\s*(-?[\\d.]+)/;\n\n let pendingStart: number | null = null;\n for (const line of stderr.split(/\\r?\\n/)) {\n const sm = line.match(startRe);\n if (sm) {\n pendingStart = parseFloat(sm[1]);\n continue;\n }\n const em = line.match(endRe);\n if (em && pendingStart !== null) {\n const end = parseFloat(em[1]);\n // silencedetect can emit start <0 if audio starts in silence — clamp\n intervals.push({ start: Math.max(0, pendingStart), end });\n pendingStart = null;\n }\n }\n return intervals;\n}\n\n// ─── subprocess plumbing ─────────────────────────────────────────\n\nfunction runCapture(cmd: string, args: string[]): Promise<string> {\n return new Promise((resolve, reject) => {\n const proc = spawn(cmd, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n let stdout = \"\";\n proc.stdout.on(\"data\", (b) => (stdout += b.toString()));\n proc.on(\"error\", (err) => {\n const e = err as NodeJS.ErrnoException;\n if (e.code === \"ENOENT\") {\n reject(new Error(`${cmd} not found on PATH. Install ffmpeg/ffprobe (brew install ffmpeg).`));\n } else {\n reject(err);\n }\n });\n proc.on(\"close\", (code) => {\n if (code !== 0) reject(new Error(`${cmd} exited ${code}`));\n else resolve(stdout);\n });\n });\n}\n\nfunction runCaptureStderr(cmd: string, args: string[]): Promise<string> {\n return new Promise((resolve, reject) => {\n const proc = spawn(cmd, args, { stdio: [\"ignore\", \"ignore\", \"pipe\"] });\n let stderr = \"\";\n proc.stderr.on(\"data\", (b) => (stderr += b.toString()));\n proc.on(\"error\", (err) => {\n const e = err as NodeJS.ErrnoException;\n if (e.code === \"ENOENT\") {\n reject(new Error(`${cmd} not found on PATH. Install ffmpeg (brew install ffmpeg).`));\n } else {\n reject(err);\n }\n });\n // null muxer always exits non-zero on EOF in some ffmpeg builds — accept any code\n proc.on(\"close\", () => resolve(stderr));\n });\n}\n","import type { CutEntry } from \"@a-company/atelier-types\";\nimport type { SilenceInterval } from \"./silence-detect.js\";\n\n/** A detected speech span (inverse of silence) — pre-padding */\nexport interface SpeechInterval {\n start: number;\n end: number;\n}\n\n/** Default leading/trailing padding around each detected speech segment */\nexport const DEFAULT_PADDING_PRE = 0.08;\nexport const DEFAULT_PADDING_POST = 0.12;\n\n/** Match tolerance (seconds) for preserving user padding on re-detect */\nexport const DEFAULT_MATCH_TOLERANCE = 0.5;\n\n/**\n * Invert silence intervals into the gaps between them — the speech intervals.\n * Handles leading and trailing silence (no speech before first silence /\n * after last silence respectively).\n */\nexport function invertToSpeechIntervals(\n silences: SilenceInterval[],\n duration: number,\n): SpeechInterval[] {\n if (silences.length === 0) {\n return duration > 0 ? [{ start: 0, end: duration }] : [];\n }\n\n const sorted = [...silences].sort((a, b) => a.start - b.start);\n const speech: SpeechInterval[] = [];\n\n // Leading speech (before first silence)\n if (sorted[0].start > 0) {\n speech.push({ start: 0, end: sorted[0].start });\n }\n\n // Speech between adjacent silences\n for (let i = 0; i < sorted.length - 1; i++) {\n const gapStart = sorted[i].end;\n const gapEnd = sorted[i + 1].start;\n if (gapEnd > gapStart) {\n speech.push({ start: gapStart, end: gapEnd });\n }\n }\n\n // Trailing speech (after last silence)\n const last = sorted[sorted.length - 1];\n if (last.end < duration) {\n speech.push({ start: last.end, end: duration });\n }\n\n return speech;\n}\n\n/** Build initial CutEntry[] from speech intervals using default padding */\nexport function buildInitialCuts(\n speech: SpeechInterval[],\n paddingPre = DEFAULT_PADDING_PRE,\n paddingPost = DEFAULT_PADDING_POST,\n): CutEntry[] {\n return speech.map((s) => ({\n rawStart: s.start,\n rawEnd: s.end,\n paddingPre,\n paddingPost,\n }));\n}\n\n/**\n * Resolve overlap between adjacent cuts (when paddingPost[i] + paddingPre[i+1]\n * exceeds the silence gap between them). Each cut gets equal share of the\n * silence — neither \"wins.\"\n *\n * Mutates cuts in place.\n */\nexport function resolveOverlaps(cuts: CutEntry[]): void {\n for (let i = 0; i < cuts.length - 1; i++) {\n const a = cuts[i];\n const b = cuts[i + 1];\n const aEnd = a.rawEnd + a.paddingPost;\n const bStart = b.rawStart - b.paddingPre;\n if (aEnd > bStart) {\n // Overlap — split at silence midpoint\n const mid = (a.rawEnd + b.rawStart) / 2;\n a.paddingPost = Math.max(0, mid - a.rawEnd);\n b.paddingPre = Math.max(0, b.rawStart - mid);\n }\n }\n}\n\n/**\n * Clamp first cut's paddingPre so effective start >= 0, and last cut's\n * paddingPost so effective end <= duration.\n *\n * Mutates cuts in place.\n */\nexport function clampBoundaries(cuts: CutEntry[], duration: number): void {\n if (cuts.length === 0) return;\n const first = cuts[0];\n if (first.rawStart - first.paddingPre < 0) {\n first.paddingPre = first.rawStart;\n }\n const last = cuts[cuts.length - 1];\n if (last.rawEnd + last.paddingPost > duration) {\n last.paddingPost = Math.max(0, duration - last.rawEnd);\n }\n}\n\n/**\n * Merge a freshly-detected cut list with an existing one, preserving user\n * padding overrides on any cut whose raw boundary still matches within\n * `tolerance` seconds (both start and end).\n *\n * For each fresh cut:\n * - if an existing cut matches: copy its padding and label, update raw values\n * - if no match: keep the fresh defaults\n *\n * Returns a new array; does not mutate inputs.\n */\nexport function mergeWithExisting(\n fresh: CutEntry[],\n existing: CutEntry[],\n tolerance = DEFAULT_MATCH_TOLERANCE,\n): CutEntry[] {\n return fresh.map((f) => {\n const match = existing.find(\n (e) =>\n Math.abs(e.rawStart - f.rawStart) < tolerance &&\n Math.abs(e.rawEnd - f.rawEnd) < tolerance,\n );\n if (!match) return f;\n return {\n rawStart: f.rawStart,\n rawEnd: f.rawEnd,\n paddingPre: match.paddingPre,\n paddingPost: match.paddingPost,\n ...(match.label !== undefined && { label: match.label }),\n };\n });\n}\n\n/**\n * Apply a global padding delta to every cut (positive = loosen, negative = tighten).\n * Floors padding at 0 — never goes negative.\n *\n * Mutates cuts in place.\n */\nexport function applyGlobalPadding(cuts: CutEntry[], deltaSeconds: number): void {\n for (const cut of cuts) {\n cut.paddingPre = Math.max(0, cut.paddingPre + deltaSeconds);\n cut.paddingPost = Math.max(0, cut.paddingPost + deltaSeconds);\n }\n}\n","import type { Command } from \"commander\";\nimport type { CutEntry } from \"@a-company/atelier-types\";\nimport {\n loadVideoProject,\n readCutList,\n writeCutList,\n readComposition,\n writeComposition,\n rewriteCutLayers,\n} from \"../lib/video-project.js\";\nimport {\n probeSilencedetect,\n probeDuration,\n runSilenceDetect,\n} from \"../lib/silence-detect.js\";\nimport {\n invertToSpeechIntervals,\n buildInitialCuts,\n resolveOverlaps,\n clampBoundaries,\n mergeWithExisting,\n applyGlobalPadding,\n DEFAULT_PADDING_PRE,\n DEFAULT_PADDING_POST,\n} from \"../lib/cut-model.js\";\nimport { loadRecipe, applyRecipeToTrimOptions } from \"../lib/recipe.js\";\n\nexport interface TrimOptions {\n /** silencedetect noise threshold, e.g. \"-30dB\" */\n noise?: string;\n /** Minimum silence duration to register, in seconds */\n minSilence?: number;\n /** Default leading padding for new cuts, in seconds */\n padPre?: number;\n /** Default trailing padding for new cuts, in seconds */\n padPost?: number;\n /** Re-detect match tolerance for preserving user padding, in seconds */\n matchTolerance?: number;\n /** Global tighten across all cuts, in milliseconds (positive number) */\n tightenMs?: number;\n /** Global loosen across all cuts, in milliseconds (positive number) */\n loosenMs?: number;\n /** Apply --pad-pre / --pad-post to one specific cut index only */\n cutIndex?: number;\n /** Discard existing padding; full fresh detect with default padding */\n reset?: boolean;\n /** Don't write files; return result */\n dryRun?: boolean;\n}\n\nexport interface TrimResult {\n projectDir: string;\n duration: number;\n cuts: CutEntry[];\n layerCount: number;\n}\n\n/**\n * Run the silence-trim pipeline on a VideoProject folder.\n *\n * Pipeline:\n * 1. Probe ffmpeg has silencedetect filter\n * 2. ffprobe source duration\n * 3. Run silencedetect → silence intervals\n * 4. Invert to speech intervals\n * 5. Build fresh CutEntry[] with default (or recipe) padding\n * 6. Merge with existing cuts (preserves user padding) unless --reset\n * 7. Apply --tighten / --loosen / --cut overrides\n * 8. Resolve overlaps at silence midpoints\n * 9. Clamp boundaries to [0, duration]\n * 10. Write cuts.json + rewrite silence-trim layers in project.atelier\n */\nexport async function trimProject(\n projectDir: string,\n options: TrimOptions = {},\n): Promise<TrimResult> {\n const project = loadVideoProject(projectDir);\n\n await probeSilencedetect();\n const duration = await probeDuration(project.sourcePath);\n\n const silences = await runSilenceDetect(project.sourcePath, {\n noise: options.noise,\n minSilence: options.minSilence,\n });\n const speech = invertToSpeechIntervals(silences, duration);\n\n const padPre = options.padPre ?? DEFAULT_PADDING_PRE;\n const padPost = options.padPost ?? DEFAULT_PADDING_POST;\n let cuts = buildInitialCuts(speech, padPre, padPost);\n\n // Re-run preservation — keep user padding on matching raw boundaries\n if (!options.reset) {\n const existing = readCutList(project);\n cuts = mergeWithExisting(cuts, existing.cuts, options.matchTolerance);\n }\n\n // Global tighten/loosen — applied to current padding (preserves overrides)\n if (typeof options.tightenMs === \"number\") {\n applyGlobalPadding(cuts, -options.tightenMs / 1000);\n }\n if (typeof options.loosenMs === \"number\") {\n applyGlobalPadding(cuts, options.loosenMs / 1000);\n }\n\n // Single-cut override\n if (typeof options.cutIndex === \"number\") {\n if (options.cutIndex < 0 || options.cutIndex >= cuts.length) {\n throw new Error(\n `--cut ${options.cutIndex} out of range (have ${cuts.length} cuts)`,\n );\n }\n if (options.padPre !== undefined) cuts[options.cutIndex].paddingPre = options.padPre;\n if (options.padPost !== undefined) cuts[options.cutIndex].paddingPost = options.padPost;\n }\n\n resolveOverlaps(cuts);\n clampBoundaries(cuts, duration);\n\n const result: TrimResult = {\n projectDir: project.dir,\n duration,\n cuts,\n layerCount: cuts.length,\n };\n\n if (options.dryRun) return result;\n\n writeCutList(project, {\n version: \"1.1\",\n source: project.manifest.source,\n cuts,\n });\n\n const doc = readComposition(project);\n const updated = rewriteCutLayers(doc, cuts, project.manifest.source, duration);\n writeComposition(project, updated);\n\n return result;\n}\n\n/** Format a TrimResult for human-readable terminal output */\nfunction formatResult(result: TrimResult): string {\n const lines: string[] = [];\n lines.push(`Trimmed ${result.projectDir}`);\n lines.push(` source duration: ${result.duration.toFixed(2)}s`);\n lines.push(` cuts: ${result.cuts.length}`);\n for (let i = 0; i < result.cuts.length; i++) {\n const c = result.cuts[i];\n const effStart = Math.max(0, c.rawStart - c.paddingPre);\n const effEnd = Math.min(result.duration, c.rawEnd + c.paddingPost);\n const dur = effEnd - effStart;\n lines.push(\n ` [${i}] ${effStart.toFixed(2)}s → ${effEnd.toFixed(2)}s ` +\n `(${dur.toFixed(2)}s, pad ${c.paddingPre.toFixed(2)}/${c.paddingPost.toFixed(2)})` +\n (c.label ? ` \"${c.label}\"` : \"\"),\n );\n }\n return lines.join(\"\\n\");\n}\n\n/** Register `atelier trim` on the Commander program */\nexport function trimCommand(program: Command): void {\n program\n .command(\"trim <project>\")\n .description(\n \"Detect silence and rewrite project.atelier with parametric cuts \" +\n \"(silence-trim tagged layers). Preserves user padding overrides on re-run.\",\n )\n .option(\"--noise <dB>\", \"Silence threshold (default: -30dB)\", \"-30dB\")\n .option(\n \"--min-silence <seconds>\",\n \"Minimum silence duration to register (default: 0.35)\",\n (v) => parseFloat(v),\n 0.35,\n )\n .option(\n \"--pad-pre <seconds>\",\n `Default leading padding (default: ${DEFAULT_PADDING_PRE})`,\n (v) => parseFloat(v),\n )\n .option(\n \"--pad-post <seconds>\",\n `Default trailing padding (default: ${DEFAULT_PADDING_POST})`,\n (v) => parseFloat(v),\n )\n .option(\"--tighten <ms>\", \"Reduce all current padding by N ms\", (v) => parseInt(v, 10))\n .option(\"--loosen <ms>\", \"Increase all current padding by N ms\", (v) => parseInt(v, 10))\n .option(\n \"--cut <index>\",\n \"Apply --pad-pre / --pad-post to one specific cut only\",\n (v) => parseInt(v, 10),\n )\n .option(\"--reset\", \"Discard existing padding; re-detect from scratch\")\n .option(\"--recipe <name>\", \"Apply a Studio Recipe's silence_policy as the baseline\")\n .option(\"--dry-run\", \"Print computed cuts; don't write files\")\n .option(\"--json\", \"Output result as JSON for piping\")\n .action(async (\n project: string,\n opts: {\n noise: string;\n minSilence: number;\n padPre?: number;\n padPost?: number;\n tighten?: number;\n loosen?: number;\n cut?: number;\n reset?: boolean;\n recipe?: string;\n dryRun?: boolean;\n json?: boolean;\n },\n ) => {\n try {\n let trimOpts: TrimOptions = {\n noise: opts.noise,\n minSilence: opts.minSilence,\n padPre: opts.padPre,\n padPost: opts.padPost,\n tightenMs: opts.tighten,\n loosenMs: opts.loosen,\n cutIndex: opts.cut,\n reset: opts.reset,\n dryRun: opts.dryRun,\n };\n if (opts.recipe) {\n const { recipe } = loadRecipe(opts.recipe, project);\n trimOpts = applyRecipeToTrimOptions(recipe, trimOpts);\n }\n const result = await trimProject(project, trimOpts);\n\n if (opts.json) {\n console.log(JSON.stringify(result, null, 2));\n } else {\n console.log(formatResult(result));\n if (opts.dryRun) console.log(\"(dry-run — no files written)\");\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`atelier trim: ${msg}`);\n process.exit(1);\n }\n });\n}\n","import { spawn } from \"node:child_process\";\nimport { createWriteStream, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from \"node:fs\";\nimport { join, isAbsolute } from \"node:path\";\nimport { homedir, tmpdir } from \"node:os\";\nimport { pipeline } from \"node:stream/promises\";\nimport { Readable } from \"node:stream\";\nimport type { VideoTranscript } from \"@a-company/atelier-types\";\n\n/**\n * Whisper model selection (size/quality tradeoff).\n *\n * The set mirrors whisper.cpp's published model registry on HuggingFace\n * (https://huggingface.co/ggerganov/whisper.cpp). Quantized variants and\n * the v3-turbo line are first-class — creators get the best local-quality\n * tradeoff without needing to know the model-zoo URL by heart.\n *\n * Note: the type is intentionally open to `string` via the fallback union so a\n * creator can pass a future model name (e.g. a new quant variant) without us\n * having to ship a code change. `resolveWhisperModelPath` will attempt to\n * resolve any string against the cache + HuggingFace registry.\n */\nexport type WhisperModel =\n | \"tiny\" | \"tiny.en\"\n | \"base\" | \"base.en\"\n | \"small\" | \"small.en\"\n | \"medium\" | \"medium.en\"\n | \"large-v3\"\n | \"large-v3-turbo\"\n | \"large-v3-turbo-q5_0\"\n | \"large-v3-q5_0\"\n | (string & {});\n\nexport interface WhisperOptions {\n /** Model size (default: \"base.en\") */\n model?: WhisperModel;\n /** BCP-47 language hint (omit for autodetect) */\n language?: string;\n /** Explicit path to the model file — overrides model lookup by name */\n modelPath?: string;\n /**\n * Optional progress callback for model download (bytes received, total bytes\n * when known). Surfaced as a one-time download on first use of any model name.\n */\n onProgress?: (received: number, total: number | null) => void;\n}\n\n/** Backend that produced a transcript run */\nexport type WhisperBackend = \"whisper-cpp\" | \"openai-api\" | \"none\";\n\n/**\n * Probe which Whisper backend is available on this machine.\n * - \"whisper-cpp\" if `whisper-cli` is on PATH\n * - \"openai-api\" if OPENAI_API_KEY is set and --use-api requested\n * - \"none\" otherwise — caller throws with install guidance\n */\nexport async function probeWhisper(): Promise<WhisperBackend> {\n if (await commandExists(\"whisper-cli\")) return \"whisper-cpp\";\n if (process.env.OPENAI_API_KEY) return \"openai-api\";\n return \"none\";\n}\n\n/**\n * Standard whisper.cpp cache directory — `~/.cache/whisper.cpp/models/`.\n * Matches the convention upstream tools (`whisper.cpp/models/download-ggml-model.sh`)\n * use, so a model cached by one tool is found by another.\n */\nexport function whisperCacheDir(): string {\n return join(homedir(), \".cache\", \"whisper.cpp\", \"models\");\n}\n\n/** Build the canonical cache path for a model name. */\nexport function modelCachePath(model: string): string {\n return join(whisperCacheDir(), `ggml-${model}.bin`);\n}\n\n/**\n * Resolve a `WhisperModel` value to an absolute file path on disk.\n *\n * Lookup order:\n * 1. Absolute path → return as-is if it exists.\n * 2. Model name → `~/.cache/whisper.cpp/models/ggml-<name>.bin` → return if present.\n * 3. Missing → invoke `downloadWhisperModel` (HuggingFace public registry),\n * stream into cache, return resolved path.\n *\n * Local-only at runtime after the first download. No online service is\n * called per-invocation. Per TD-2026-05-27-723 (local-only by default).\n */\nexport async function resolveWhisperModelPath(\n model: string,\n onProgress?: (received: number, total: number | null) => void,\n): Promise<string> {\n // (1) Absolute path passthrough — supports a creator overriding the default\n // cache location (e.g. shared models on a mounted drive).\n if (isAbsolute(model)) {\n if (!existsSync(model)) {\n throw new Error(`whisper model file not found at absolute path: ${model}`);\n }\n return model;\n }\n\n // (2) Try the standard cache path.\n const cached = modelCachePath(model);\n if (existsSync(cached)) return cached;\n\n // (3) Download from HuggingFace, surface progress, then return cache path.\n await downloadWhisperModel(model, onProgress);\n if (!existsSync(cached)) {\n throw new Error(\n `whisper model download appeared to succeed but the file is missing at ${cached}`,\n );\n }\n return cached;\n}\n\n/**\n * Download a whisper.cpp model from the HuggingFace public registry into the\n * local cache. Uses `fetch` + streaming pipeline (no new top-level dep).\n *\n * Source: https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-<model>.bin\n * — the canonical public mirror upstream whisper.cpp tooling targets. No auth\n * required, no telemetry — a single one-shot fetch on first use.\n */\nexport async function downloadWhisperModel(\n model: string,\n onProgress?: (received: number, total: number | null) => void,\n): Promise<string> {\n const dir = whisperCacheDir();\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n const dest = modelCachePath(model);\n const url = `https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-${model}.bin`;\n\n // Use a .part file then rename, so an aborted download doesn't masquerade\n // as a complete cached model on the next run.\n const partPath = `${dest}.part`;\n\n const res = await fetch(url);\n if (!res.ok) {\n throw new Error(\n `failed to download whisper model \"${model}\" (HTTP ${res.status} ${res.statusText}) ` +\n `from ${url} — check the model name`,\n );\n }\n if (!res.body) {\n throw new Error(`whisper model download produced no body (model: ${model})`);\n }\n\n const totalHeader = res.headers.get(\"content-length\");\n const total = totalHeader ? parseInt(totalHeader, 10) : null;\n\n let received = 0;\n // Wrap the WHATWG ReadableStream into a Node Readable for pipeline. Tap the\n // stream for progress accounting via a small Transform-ish pass-through.\n const nodeStream = Readable.fromWeb(res.body as unknown as import(\"node:stream/web\").ReadableStream);\n nodeStream.on(\"data\", (chunk: Buffer) => {\n received += chunk.length;\n if (onProgress) onProgress(received, total);\n });\n\n await pipeline(nodeStream, createWriteStream(partPath));\n\n // Atomic-ish rename so a partial file never has the canonical name.\n // Cross-platform rename through fs (synchronous since the file just closed).\n const { renameSync, unlinkSync } = await import(\"node:fs\");\n if (existsSync(dest)) {\n try { unlinkSync(dest); } catch { /* swallow */ }\n }\n renameSync(partPath, dest);\n\n return dest;\n}\n\n/**\n * Run whisper.cpp on a source file and return the raw JSON stdout.\n * Caller passes to parseWhisperCppJson() to get a VideoTranscript.\n *\n * Note: whisper-cli writes JSON to stdout when given --output-json and \"-\"\n * as the output destination. Some builds always write to a file with the\n * `.json` suffix appended to the input path; we handle both.\n *\n * Model resolution: if `options.modelPath` is set, it's passed through. Else\n * `options.model` (default `base.en`) is resolved via `resolveWhisperModelPath`,\n * downloading from the HuggingFace public registry on first use. Once the\n * model is cached, no network call is made.\n */\nexport async function runWhisperCpp(\n sourcePath: string,\n options: WhisperOptions = {},\n): Promise<string> {\n const modelName = options.modelPath ?? options.model ?? \"base.en\";\n // resolveWhisperModelPath handles: absolute path passthrough, cache lookup,\n // and one-time download. Always pass the RESULT to whisper-cli as an\n // explicit path so we don't depend on whisper-cli's own model resolution.\n const resolvedModelPath = await resolveWhisperModelPath(modelName, options.onProgress);\n\n // whisper.cpp can only ingest WAV. For any other container (mp4/mov/webm/m4a/...)\n // we pre-extract to a 16kHz mono PCM WAV via ffmpeg (the format whisper expects).\n // Pure local; no online dependency. ffmpeg is a separate install (brew install ffmpeg).\n const { audioPath, tempDir } = await extractAudioForWhisper(sourcePath);\n\n try {\n const args = [\n audioPath,\n \"--model\", resolvedModelPath,\n \"--output-json-full\", // word-level timestamps in the JSON output\n \"--word-thold\", \"0.01\",\n \"--print-progress\", \"false\",\n ];\n if (options.language) {\n args.push(\"--language\", options.language);\n }\n\n const stdout = await runCaptureStdout(\"whisper-cli\", args);\n\n // whisper-cli historically writes the JSON to a file (<input>.json),\n // not stdout. If the sidecar file exists, prefer it; otherwise fall back\n // to stdout (some forks/options emit there).\n const jsonSidecar = `${audioPath}.json`;\n if (existsSync(jsonSidecar)) {\n return readFileSync(jsonSidecar, \"utf-8\");\n }\n return stdout;\n } finally {\n if (tempDir) {\n try { rmSync(tempDir, { recursive: true, force: true }); } catch { /* swallow */ }\n }\n }\n}\n\n/**\n * Ensure we hand whisper-cli a WAV file. If the source is already WAV, pass through.\n * Otherwise extract via ffmpeg into a tempdir we control + cleanup ourselves.\n * Local-only — ffmpeg is the system tool, no network.\n */\nasync function extractAudioForWhisper(\n sourcePath: string,\n): Promise<{ audioPath: string; tempDir: string | null }> {\n const lower = sourcePath.toLowerCase();\n if (lower.endsWith(\".wav\")) return { audioPath: sourcePath, tempDir: null };\n\n if (!(await commandExists(\"ffmpeg\"))) {\n throw new Error(\n `ffmpeg not found on PATH. Whisper.cpp only ingests WAV; non-WAV sources ` +\n `(${sourcePath}) need ffmpeg to extract audio. Install with: brew install ffmpeg.`,\n );\n }\n\n const tempDir = mkdtempSync(join(tmpdir(), \"atelier-whisper-\"));\n const audioPath = join(tempDir, \"audio.wav\");\n\n // 16kHz mono PCM s16le — exactly what whisper.cpp expects internally.\n // `-y` overwrites if a stale file somehow exists; `-loglevel error` keeps stderr quiet.\n await runOk(\"ffmpeg\", [\n \"-loglevel\", \"error\",\n \"-y\",\n \"-i\", sourcePath,\n \"-ar\", \"16000\",\n \"-ac\", \"1\",\n \"-c:a\", \"pcm_s16le\",\n audioPath,\n ]);\n\n return { audioPath, tempDir };\n}\n\n/** Run a command, throw on non-zero exit. Stdout/stderr discarded. */\nasync function runOk(cmd: string, args: string[]): Promise<void> {\n return new Promise((resolve, reject) => {\n const p = spawn(cmd, args, { stdio: [\"ignore\", \"ignore\", \"pipe\"] });\n let stderr = \"\";\n p.stderr?.on(\"data\", (chunk) => { stderr += chunk.toString(); });\n p.on(\"error\", reject);\n p.on(\"close\", (code) => {\n if (code === 0) resolve();\n else reject(new Error(`${cmd} exited ${code}: ${stderr.trim() || \"(no stderr)\"}`));\n });\n });\n}\n\n/**\n * Parse whisper.cpp --output-json output into a VideoTranscript.\n *\n * whisper.cpp emits a structure like:\n * {\n * \"result\": { \"language\": \"en\" },\n * \"transcription\": [\n * {\n * \"timestamps\": { \"from\": \"00:00:00,000\", \"to\": \"00:00:02,300\" },\n * \"offsets\": { \"from\": 0, \"to\": 2300 },\n * \"text\": \" Hello world\",\n * \"tokens\": [ ... ] // optional per-token detail\n * }\n * ]\n * }\n *\n * Token-level word timestamps come via the \"tokens\" array on each segment\n * when --output-json-full is used. With --output-json (lighter), only\n * segment-level boundaries are present and we synthesize word timing by\n * even split across the segment.\n *\n * Exported for unit testing.\n */\nexport function parseWhisperCppJson(jsonStr: string): VideoTranscript {\n const raw = JSON.parse(jsonStr) as {\n result?: { language?: string };\n transcription?: Array<{\n offsets?: { from: number; to: number };\n timestamps?: { from: string; to: string };\n text: string;\n tokens?: Array<{\n text: string;\n offsets?: { from: number; to: number };\n timestamps?: { from: string; to: string };\n p?: number; // probability/confidence\n }>;\n }>;\n };\n\n const segments = (raw.transcription ?? []).map((seg) => {\n const segStart = (seg.offsets?.from ?? 0) / 1000;\n const segEnd = (seg.offsets?.to ?? 0) / 1000;\n const segText = seg.text.trim();\n\n let words;\n if (seg.tokens && seg.tokens.length > 0) {\n // Token-level detail available — emit one word per non-special token\n words = seg.tokens\n .filter((t) => t.text.trim().length > 0 && !t.text.startsWith(\"[_\"))\n .map((t) => ({\n detected: t.text.trim(),\n text: t.text.trim(),\n start: (t.offsets?.from ?? segStart * 1000) / 1000,\n end: (t.offsets?.to ?? segEnd * 1000) / 1000,\n ...(t.p !== undefined && { confidence: t.p }),\n }));\n } else {\n // Fall back to even-split across segment\n const tokens = segText.split(/\\s+/).filter((t) => t.length > 0);\n const span = segEnd - segStart;\n const per = tokens.length > 0 ? span / tokens.length : 0;\n words = tokens.map((tok, i) => ({\n detected: tok,\n text: tok,\n start: segStart + i * per,\n end: segStart + (i + 1) * per,\n }));\n }\n\n return {\n text: segText,\n start: segStart,\n end: segEnd,\n words,\n };\n });\n\n return {\n version: \"1.1\",\n ...(raw.result?.language !== undefined && { language: raw.result.language }),\n segments,\n };\n}\n\n// ─── subprocess plumbing ─────────────────────────────────────────\n\n/** Check whether a binary is callable on PATH (cross-platform-ish via `which` / `where`) */\nexport async function commandExists(name: string): Promise<boolean> {\n // If user passed an absolute path, just check the file\n if (name.startsWith(\"/\") || name.match(/^[A-Z]:\\\\/)) {\n return existsSync(name);\n }\n return new Promise((resolve) => {\n const probe = spawn(process.platform === \"win32\" ? \"where\" : \"which\", [name], {\n stdio: [\"ignore\", \"ignore\", \"ignore\"],\n });\n probe.on(\"error\", () => resolve(false));\n probe.on(\"close\", (code) => resolve(code === 0));\n });\n}\n\nfunction runCaptureStdout(cmd: string, args: string[]): Promise<string> {\n return new Promise((resolve, reject) => {\n const proc = spawn(cmd, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n let stdout = \"\";\n let stderr = \"\";\n proc.stdout.on(\"data\", (b) => (stdout += b.toString()));\n proc.stderr.on(\"data\", (b) => (stderr += b.toString()));\n proc.on(\"error\", (err) => {\n const e = err as NodeJS.ErrnoException;\n if (e.code === \"ENOENT\") {\n reject(new Error(\n `${cmd} not found on PATH. Install whisper.cpp (brew install whisper-cpp) ` +\n `or run \\`pnpm add @xenova/transformers\\` for the ONNX fallback.`,\n ));\n } else {\n reject(err);\n }\n });\n proc.on(\"close\", (code) => {\n if (code !== 0) {\n reject(new Error(`${cmd} exited ${code}\\n${stderr}`));\n } else {\n resolve(stdout);\n }\n });\n });\n}\n\n","import { createHash } from \"node:crypto\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport type {\n CutEntry,\n TranscriptDerivedFrom,\n TranscriptWord,\n TranscriptSegment,\n VideoTranscript,\n} from \"@a-company/atelier-types\";\n\n/** Default match tolerance for re-transcribe merge (seconds) */\nexport const DEFAULT_TRANSCRIPT_MATCH_TOLERANCE = 0.3;\n\n/** Default phrase grouping parameters */\nexport const DEFAULT_PHRASE_MAX_WORDS = 5;\nexport const DEFAULT_PHRASE_PAUSE_GAP_SECONDS = 0.4;\n\n/** End-of-phrase punctuation marks */\nconst END_PUNCT = /[.!?,;:]\\s*$/;\n\n/**\n * Flatten a VideoTranscript's segments into a single ordered word array.\n * Used by edit commands that take a global word index.\n */\nexport function flattenWords(transcript: VideoTranscript): TranscriptWord[] {\n return transcript.segments.flatMap((s) => s.words);\n}\n\n/**\n * Phrase block — one rendered caption layer's worth of words.\n */\nexport interface CaptionPhrase {\n start: number;\n end: number;\n text: string;\n words: TranscriptWord[];\n}\n\n/**\n * Group transcript words into caption-sized phrases.\n *\n * Emits a new phrase when ANY of:\n * - currentPhrase.length >= maxWords\n * - currentWord.text ends in . ! ? , ; :\n * - gap between currentWord and nextWord > pauseGap seconds\n *\n * Hidden words are skipped (do not appear in captions but still in transcript).\n */\nexport function groupIntoPhrases(\n transcript: VideoTranscript,\n options: { maxWords?: number; pauseGap?: number } = {},\n): CaptionPhrase[] {\n const maxWords = options.maxWords ?? DEFAULT_PHRASE_MAX_WORDS;\n const pauseGap = options.pauseGap ?? DEFAULT_PHRASE_PAUSE_GAP_SECONDS;\n\n const phrases: CaptionPhrase[] = [];\n const visibleWords = flattenWords(transcript).filter((w) => !w.hidden);\n\n let current: TranscriptWord[] = [];\n for (let i = 0; i < visibleWords.length; i++) {\n const w = visibleWords[i];\n current.push(w);\n\n const next = visibleWords[i + 1];\n const gap = next ? next.start - w.end : 0;\n const endsOnPunct = END_PUNCT.test(w.text);\n const atMax = current.length >= maxWords;\n\n if (!next || atMax || endsOnPunct || gap > pauseGap) {\n phrases.push({\n start: current[0].start,\n end: current[current.length - 1].end,\n text: current.map((c) => c.text).join(\" \"),\n words: current,\n });\n current = [];\n }\n }\n\n return phrases;\n}\n\n/**\n * Merge a freshly-detected transcript with an existing one, preserving user\n * edits (userEdited / userAdded / hidden + text override) on any word whose\n * raw boundary still matches within `tolerance` seconds AND whose `detected`\n * string is unchanged.\n *\n * userAdded words in the existing transcript have no matching fresh entry by\n * definition — they're preserved as orphans, inserted into the appropriate\n * segment by timing.\n */\nexport function mergeTranscriptWithExisting(\n fresh: VideoTranscript,\n existing: VideoTranscript,\n tolerance = DEFAULT_TRANSCRIPT_MATCH_TOLERANCE,\n): VideoTranscript {\n const existingWords = flattenWords(existing);\n\n const merged: VideoTranscript = {\n version: \"1.1\",\n ...(fresh.language !== undefined && { language: fresh.language }),\n segments: fresh.segments.map((seg) => ({\n text: seg.text,\n start: seg.start,\n end: seg.end,\n words: seg.words.map((freshWord) => {\n const match = existingWords.find(\n (e) =>\n !e.userAdded &&\n Math.abs(e.start - freshWord.start) < tolerance &&\n e.detected === freshWord.detected,\n );\n if (!match) return freshWord;\n return {\n detected: freshWord.detected,\n text: match.text,\n start: freshWord.start,\n end: freshWord.end,\n ...(freshWord.confidence !== undefined && { confidence: freshWord.confidence }),\n ...(match.userEdited && { userEdited: true }),\n ...(match.hidden && { hidden: true }),\n };\n }),\n })),\n };\n\n // Re-insert user-added orphan words into their original segment by timing\n const orphans = existingWords.filter((w) => w.userAdded);\n for (const orphan of orphans) {\n const segIdx = merged.segments.findIndex(\n (s) => orphan.start >= s.start && orphan.start <= s.end,\n );\n const targetSeg = segIdx >= 0\n ? merged.segments[segIdx]\n : merged.segments[merged.segments.length - 1];\n if (!targetSeg) continue;\n const insertIdx = targetSeg.words.findIndex((w) => w.start > orphan.start);\n if (insertIdx === -1) targetSeg.words.push(orphan);\n else targetSeg.words.splice(insertIdx, 0, orphan);\n }\n\n return merged;\n}\n\n/**\n * Replace the text of word at the given global index. Sets userEdited=true.\n * Returns a new VideoTranscript; does not mutate input.\n */\nexport function applyTextEdit(\n transcript: VideoTranscript,\n wordIndex: number,\n newText: string,\n): VideoTranscript {\n return mapWord(transcript, wordIndex, (w) => ({\n ...w,\n text: newText,\n userEdited: true,\n }));\n}\n\n/**\n * Apply a batch find/replace across all detected words.\n * Sets userEdited=true on every word whose detected text matches `find`.\n */\nexport function applyBatchReplace(\n transcript: VideoTranscript,\n find: string,\n replace: string,\n): VideoTranscript {\n return {\n ...transcript,\n segments: transcript.segments.map((seg) => ({\n ...seg,\n words: seg.words.map((w) =>\n w.detected === find\n ? { ...w, text: replace, userEdited: true }\n : w,\n ),\n })),\n };\n}\n\n/** Hide a word (excluded from caption render, kept in transcript). Sets hidden=true. */\nexport function applyHide(transcript: VideoTranscript, wordIndex: number): VideoTranscript {\n return mapWord(transcript, wordIndex, (w) => ({ ...w, hidden: true }));\n}\n\n/**\n * Insert a user-added word after the given global index.\n * Sets userAdded=true. Duration defaults to 0.15s if not specified.\n * Word is placed in the segment containing the anchor word.\n */\nexport function applyAdd(\n transcript: VideoTranscript,\n afterIndex: number,\n text: string,\n duration = 0.15,\n): VideoTranscript {\n const flat = flattenWords(transcript);\n if (afterIndex < 0 || afterIndex >= flat.length) {\n throw new Error(`afterIndex ${afterIndex} out of range (have ${flat.length} words)`);\n }\n const anchor = flat[afterIndex];\n const newWord: TranscriptWord = {\n detected: text,\n text,\n start: anchor.end,\n end: anchor.end + duration,\n userAdded: true,\n };\n\n // Find which segment contains the anchor word and insert there\n let cursor = 0;\n return {\n ...transcript,\n segments: transcript.segments.map((seg) => {\n const segStart = cursor;\n cursor += seg.words.length;\n if (afterIndex < segStart || afterIndex >= cursor) return seg;\n const localIdx = afterIndex - segStart;\n return {\n ...seg,\n words: [...seg.words.slice(0, localIdx + 1), newWord, ...seg.words.slice(localIdx + 1)],\n };\n }),\n };\n}\n\n/**\n * Merge adjacent words at indices i and i+1 into a single word.\n * The merged word's detected/text becomes the concatenation; timing spans both.\n * Sets userEdited=true.\n */\nexport function applyMerge(transcript: VideoTranscript, firstIndex: number): VideoTranscript {\n const flat = flattenWords(transcript);\n if (firstIndex < 0 || firstIndex >= flat.length - 1) {\n throw new Error(`firstIndex ${firstIndex} out of range for merge`);\n }\n const a = flat[firstIndex];\n const b = flat[firstIndex + 1];\n const mergedWord: TranscriptWord = {\n detected: a.detected + b.detected,\n text: a.text + b.text,\n start: a.start,\n end: b.end,\n userEdited: true,\n };\n\n let cursor = 0;\n return {\n ...transcript,\n segments: transcript.segments.map((seg) => {\n const segStart = cursor;\n cursor += seg.words.length;\n // Both words must fall in the same segment to merge cleanly\n if (firstIndex < segStart || firstIndex >= cursor - 1) return seg;\n const localIdx = firstIndex - segStart;\n return {\n ...seg,\n words: [...seg.words.slice(0, localIdx), mergedWord, ...seg.words.slice(localIdx + 2)],\n };\n }),\n };\n}\n\n/**\n * Split one word at the given fractional point (0–1). Timing prorates.\n * Sets userEdited=true on both halves. `firstText` and `secondText` default\n * to slicing the original text at its character midpoint.\n */\nexport function applySplit(\n transcript: VideoTranscript,\n wordIndex: number,\n fraction: number,\n firstText?: string,\n secondText?: string,\n): VideoTranscript {\n if (fraction <= 0 || fraction >= 1) {\n throw new Error(`split fraction must be in (0, 1), got ${fraction}`);\n }\n const flat = flattenWords(transcript);\n if (wordIndex < 0 || wordIndex >= flat.length) {\n throw new Error(`wordIndex ${wordIndex} out of range`);\n }\n const w = flat[wordIndex];\n const splitTime = w.start + (w.end - w.start) * fraction;\n const cutChar = Math.max(1, Math.floor(w.text.length * fraction));\n const first: TranscriptWord = {\n detected: w.detected,\n text: firstText ?? w.text.slice(0, cutChar),\n start: w.start,\n end: splitTime,\n userEdited: true,\n };\n const second: TranscriptWord = {\n detected: w.detected,\n text: secondText ?? w.text.slice(cutChar),\n start: splitTime,\n end: w.end,\n userEdited: true,\n };\n\n let cursor = 0;\n return {\n ...transcript,\n segments: transcript.segments.map((seg) => {\n const segStart = cursor;\n cursor += seg.words.length;\n if (wordIndex < segStart || wordIndex >= cursor) return seg;\n const localIdx = wordIndex - segStart;\n return {\n ...seg,\n words: [...seg.words.slice(0, localIdx), first, second, ...seg.words.slice(localIdx + 1)],\n };\n }),\n };\n}\n\n// ─── helpers ─────────────────────────────────────────────────────\n\nfunction mapWord(\n transcript: VideoTranscript,\n wordIndex: number,\n fn: (w: TranscriptWord) => TranscriptWord,\n): VideoTranscript {\n const flat = flattenWords(transcript);\n if (wordIndex < 0 || wordIndex >= flat.length) {\n throw new Error(`wordIndex ${wordIndex} out of range (have ${flat.length} words)`);\n }\n let cursor = 0;\n return {\n ...transcript,\n segments: transcript.segments.map((seg) => {\n const segStart = cursor;\n cursor += seg.words.length;\n if (wordIndex < segStart || wordIndex >= cursor) return seg;\n const localIdx = wordIndex - segStart;\n return {\n ...seg,\n words: seg.words.map((w, i) => (i === localIdx ? fn(w) : w)),\n };\n }),\n };\n}\n\n// ─── Effective transcript (post-cut derivation) ─────────────────\n//\n// `applyCuts` is the pure derivation: given a raw transcript + the project's\n// cuts.json, produce the transcript that reflects the post-cut timeline.\n// Words whose `start` falls inside a cut range are dropped; surviving words\n// (and surviving segments) have their timestamps shifted backwards by the\n// cumulative cut duration that preceded them, so timestamps line up with the\n// trimmed clip the creator will export.\n//\n// This is the substrate Iris (and the agent team) reads when they need to\n// answer \"what does the talk-track look like AFTER cuts?\" without redoing\n// the math themselves. It is intentionally read-only: callers never persist\n// the effective transcript — the raw transcript.json stays authoritative on\n// disk so re-cutting later is non-destructive.\n\n/**\n * Pure derivation: apply cuts to a raw transcript and return the effective\n * (post-cut) transcript. Inputs are not mutated.\n *\n * - A word is DROPPED if its `start` falls inside any cut interval\n * `[cut.start_s, cut.end_s)` — using effective spans on each cut\n * (the same `rawStart - paddingPre` ... `rawEnd + paddingPost` math\n * the trim pipeline uses for layer rewrites, but unclamped here since\n * we don't know `duration` at this layer; clamping happens at write time).\n *\n * - A surviving word has its `start`/`end` shifted by the cumulative cut\n * duration that PRECEDES it in source-time. Effective time t' = t - sum\n * of cut durations whose end <= t.\n *\n * - Segments adjust the same way; segments whose entire word list drops\n * out are dropped.\n *\n * - A `derived_from` provenance stamp is attached so consumers (agents,\n * MCP tools) can distinguish \"raw\" from \"effective\" without re-deriving.\n *\n * NOTE: This function treats the cuts argument as KEEP intervals would be\n * the inverse — but per the existing CutEntry semantics in this codebase,\n * a CutEntry represents a KEPT speech span, not silence to remove. We must\n * therefore invert: words inside any cut are KEPT (timestamps shift relative\n * to the previous kept span's end); words OUTSIDE every cut are dropped.\n * This matches `rewriteCutLayers` which lays out kept spans sequentially.\n */\nexport function applyCuts(\n transcript: VideoTranscript,\n cuts: CutEntry[],\n options: { transcriptName?: string; cutsHash?: string; derivedAt?: string } = {},\n): VideoTranscript {\n // Sort cuts by rawStart for deterministic processing. Cuts shouldn't overlap\n // (the trim pipeline guarantees this via resolveOverlaps) but we sort anyway\n // to be safe against hand-edited cut lists.\n const sortedCuts = [...cuts].sort((a, b) => a.rawStart - b.rawStart);\n\n // Effective span per cut: [start, end] in source time. Padding extends the\n // kept region in both directions; we don't clamp negative starts here since\n // upstream code (clampBoundaries in cut-model) already does that for the\n // on-disk cuts.json.\n const keepRanges = sortedCuts.map((c) => ({\n start: Math.max(0, c.rawStart - c.paddingPre),\n end: c.rawEnd + c.paddingPost,\n }));\n\n // Map a source-time `t` to its effective time on the trimmed timeline.\n // Returns `null` if `t` is not inside any kept range (i.e. the word is cut).\n function mapTime(t: number): number | null {\n let cumulativeOffset = 0;\n for (const r of keepRanges) {\n if (t < r.start) {\n // Falls in a dropped (silent) gap before this kept range.\n return null;\n }\n if (t <= r.end) {\n // Inside this kept range — shift by the offset of this range's start.\n return t - r.start + cumulativeOffset;\n }\n cumulativeOffset += r.end - r.start;\n }\n // Past the final kept range — the word is in trailing silence and is cut.\n return null;\n }\n\n const newSegments: TranscriptSegment[] = [];\n for (const seg of transcript.segments) {\n const newWords: TranscriptWord[] = [];\n for (const w of seg.words) {\n const newStart = mapTime(w.start);\n const newEnd = mapTime(w.end);\n // Drop the word if either endpoint is in a cut gap. Strict policy:\n // a partial word straddling a boundary is dropped rather than clamped,\n // matching how transcribe.ts's caption-builder treats hidden words\n // (presence is binary; no partial render).\n if (newStart === null || newEnd === null) continue;\n newWords.push({\n ...w,\n start: newStart,\n end: newEnd,\n });\n }\n if (newWords.length === 0) continue; // segment fully cut → drop\n newSegments.push({\n text: seg.text,\n // Segment-level start/end follow the first and last surviving word so\n // the trimmed segment timing matches its words exactly.\n start: newWords[0].start,\n end: newWords[newWords.length - 1].end,\n words: newWords,\n });\n }\n\n const totalCutDurationS = keepRanges.reduce((sum, r) => sum + (r.end - r.start), 0);\n\n const derived_from: TranscriptDerivedFrom = {\n transcriptName: options.transcriptName ?? \"transcript.json\",\n cutsHash: options.cutsHash ?? hashCuts(sortedCuts),\n derivedAt: options.derivedAt ?? new Date().toISOString(),\n cutsApplied: sortedCuts.length,\n // totalCutDurationS reports the SUM of kept-range durations — i.e. the\n // duration of the effective timeline (the post-cut clip). Naming is\n // load-bearing: agents downstream use this as the trimmed-clip length.\n totalCutDurationS,\n };\n\n return {\n version: transcript.version,\n ...(transcript.language !== undefined && { language: transcript.language }),\n segments: newSegments,\n derived_from,\n };\n}\n\n/**\n * Stable hash of a sorted cut list — used as `derived_from.cutsHash` so\n * downstream callers can detect when cuts changed without comparing the full\n * list. Hashes the canonical JSON of cut tuples (rawStart, rawEnd, paddings).\n */\nexport function hashCuts(cuts: CutEntry[]): string {\n const canonical = cuts.map((c) => [c.rawStart, c.rawEnd, c.paddingPre, c.paddingPost]);\n return createHash(\"sha256\").update(JSON.stringify(canonical)).digest(\"hex\").slice(0, 16);\n}\n\n/**\n * Convenience loader: read `<projectDir>/transcript.json` + `<projectDir>/cuts.json`\n * and return the effective transcript. Returns `null` when no transcript exists.\n * When cuts.json is missing or has zero cuts, returns the raw transcript verbatim\n * (no derivation stamp) — the spec lets agents distinguish via `derived_from`\n * presence.\n *\n * Hand-rolled JSON read here (not via `loadVideoProject`/`readTranscript`) to\n * avoid a circular import — this lib is consumed by transcribe.ts which\n * already imports from video-project.ts. Inline reads keep the dependency\n * arrow one-directional.\n */\nexport function loadEffectiveTranscript(\n projectDir: string,\n transcriptName: string = \"transcript.json\",\n): VideoTranscript | null {\n const absDir = resolve(projectDir);\n const transcriptPath = join(absDir, transcriptName);\n if (!existsSync(transcriptPath)) return null;\n\n const rawTranscript = JSON.parse(readFileSync(transcriptPath, \"utf-8\")) as VideoTranscript;\n\n const cutsPath = join(absDir, \"cuts.json\");\n if (!existsSync(cutsPath)) {\n // No cuts → return raw verbatim; no derivation stamp.\n return rawTranscript;\n }\n\n const rawCuts = JSON.parse(readFileSync(cutsPath, \"utf-8\")) as {\n version?: string;\n source?: string;\n cuts: CutEntry[];\n };\n\n if (!rawCuts.cuts || rawCuts.cuts.length === 0) {\n return rawTranscript;\n }\n\n return applyCuts(rawTranscript, rawCuts.cuts, { transcriptName });\n}\n\n// Re-export for convenience\nexport type { TranscriptSegment };\n","import type {\n AtelierDocument,\n Layer,\n Delta,\n VideoTranscript,\n} from \"@a-company/atelier-types\";\nimport { groupIntoPhrases, type CaptionPhrase } from \"./transcript-model.js\";\n\n/** Default style for generated caption layers — overridable by Studio Recipe */\nexport interface CaptionStyle {\n fontFamily?: string;\n fontSize?: number;\n fontWeight?: number | \"normal\" | \"bold\";\n textAlign?: \"left\" | \"center\" | \"right\";\n color?: string;\n /** Relative vertical position 0–1 (0 = top, 1 = bottom). Default 0.85 — lower-third. */\n yRatio?: number;\n /** Horizontal width as ratio of canvas width. Default 0.9. */\n widthRatio?: number;\n /** Fade duration in seconds for opacity in/out. Default 0.05 (3 frames at 60fps). */\n fadeSeconds?: number;\n}\n\nconst DEFAULT_STYLE: Required<CaptionStyle> = {\n fontFamily: \"Inter\",\n fontSize: 84,\n fontWeight: \"bold\" as const,\n textAlign: \"center\" as const,\n color: \"#FFFFFF\",\n yRatio: 0.85,\n widthRatio: 0.9,\n fadeSeconds: 0.05,\n};\n\nexport interface BuildCaptionsOptions {\n style?: CaptionStyle;\n /** Phrase grouping — max words per caption phrase */\n maxWords?: number;\n /** Phrase grouping — pause gap in seconds that forces a phrase break */\n pauseGap?: number;\n}\n\n/**\n * Build caption-tagged TextVisual layers from a transcript + canvas dimensions.\n *\n * Each phrase becomes one Layer with:\n * - tags: [\"caption\"]\n * - TextVisual with merged style\n * - opacity: 0 (default; deltas animate to 1 during the phrase)\n * - positioned at canvas.width * 0.5, canvas.height * yRatio\n *\n * Caller is responsible for appending these to a state's deltas array\n * (returned alongside the layers as Delta[] keyed to layer id).\n */\nexport function buildCaptionLayers(\n transcript: VideoTranscript,\n canvas: AtelierDocument[\"canvas\"],\n options: BuildCaptionsOptions = {},\n): { layers: Layer[]; deltas: Delta[] } {\n const style = { ...DEFAULT_STYLE, ...options.style };\n const phrases = groupIntoPhrases(transcript, {\n maxWords: options.maxWords,\n pauseGap: options.pauseGap,\n });\n\n const layers: Layer[] = [];\n const deltas: Delta[] = [];\n const fps = canvas.fps;\n\n phrases.forEach((phrase, idx) => {\n const layerId = `caption-${idx}`;\n layers.push(buildPhraseLayer(layerId, phrase, canvas, style));\n deltas.push(...buildPhraseDeltas(layerId, phrase, style.fadeSeconds, fps));\n });\n\n return { layers, deltas };\n}\n\n/**\n * Drop caption-tagged layers + their deltas from a composition,\n * append fresh ones from the current transcript.\n *\n * Mirrors rewriteCutLayers' invariant: only caption-tagged layers touched;\n * all other layers (silence-trim, overlay, user-authored) preserved.\n *\n * Deltas are written into the default state (or first state if no \"default\").\n */\nexport function rewriteCaptionLayers(\n doc: AtelierDocument,\n transcript: VideoTranscript,\n options: BuildCaptionsOptions = {},\n): AtelierDocument {\n const preserved = doc.layers.filter((l) => !(l.tags ?? []).includes(\"caption\"));\n const { layers: captionLayers, deltas: captionDeltas } = buildCaptionLayers(\n transcript,\n doc.canvas,\n options,\n );\n const captionLayerIds = new Set(captionLayers.map((l) => l.id));\n\n // Pick the target state for caption deltas: prefer \"default\", else first\n const stateNames = Object.keys(doc.states);\n const targetStateName = stateNames.includes(\"default\") ? \"default\" : stateNames[0];\n const states = { ...doc.states };\n\n if (targetStateName && states[targetStateName]) {\n const existing = states[targetStateName];\n // Drop deltas on caption layers we're rewriting; keep all others\n const preservedDeltas = existing.deltas.filter(\n (d) => !captionLayerIds.has(d.layer),\n );\n states[targetStateName] = {\n ...existing,\n deltas: [...preservedDeltas, ...captionDeltas],\n };\n }\n\n return {\n ...doc,\n layers: [...preserved, ...captionLayers],\n states,\n };\n}\n\n// ─── internals ───────────────────────────────────────────────────\n\nfunction buildPhraseLayer(\n id: string,\n phrase: CaptionPhrase,\n canvas: AtelierDocument[\"canvas\"],\n style: Required<CaptionStyle>,\n): Layer {\n return {\n id,\n tags: [\"caption\"],\n visual: {\n type: \"text\",\n content: phrase.text,\n style: {\n fontFamily: style.fontFamily,\n fontSize: style.fontSize,\n fontWeight: style.fontWeight,\n textAlign: style.textAlign,\n color: style.color,\n },\n },\n frame: {\n x: canvas.width / 2,\n y: canvas.height * style.yRatio,\n },\n bounds: {\n width: canvas.width * style.widthRatio,\n height: Math.max(120, style.fontSize * 1.6),\n },\n anchorPoint: { x: 0.5, y: 0.5 },\n opacity: 0,\n };\n}\n\nfunction buildPhraseDeltas(\n layerId: string,\n phrase: CaptionPhrase,\n fadeSeconds: number,\n fps: number,\n): Delta[] {\n const fadeFrames = Math.max(1, Math.round(fadeSeconds * fps));\n const startFrame = Math.floor(phrase.start * fps);\n const endFrame = Math.ceil(phrase.end * fps);\n // Cap fade-in length at HALF the phrase duration so very short phrases\n // (one-word interjections) don't have a fade-in window that runs past the\n // fade-out — would otherwise produce a backwards animation.\n const phraseFrames = Math.max(1, endFrame - startFrame);\n const fadeInFrames = Math.min(fadeFrames, Math.floor(phraseFrames / 2));\n const fadeOutStart = Math.max(startFrame + fadeInFrames, endFrame - fadeFrames);\n\n return [\n // Fade in AT phrase start (the [start, start+fadeF] window lives INSIDE\n // the phrase). Previously this was [start - fadeF, start], which placed\n // the fade-in BEFORE the phrase began — for touching transcript phrases\n // (end_N === start_{N+1}, the common case), cap-N's fade-out [end-2, end]\n // and cap-(N+1)'s fade-in [start-2, start] overlapped on the same frame\n // window, producing a visible 2-frame cross-fade where both captions\n // rendered at ~0.68 opacity simultaneously (the \"captions stacking\"\n // false-bug report L-2026-05-28-ascend-151000-001). Moving the fade-in\n // INSIDE the phrase means cap-N fades out [88, 90] → cap-(N+1) fades in\n // [90, 92], no overlap, captions transition discretely.\n {\n layer: layerId,\n property: \"opacity\",\n range: [startFrame, startFrame + fadeInFrames],\n from: 0,\n to: 1,\n easing: \"ease-out\",\n },\n // Hold visible through the phrase\n // (no explicit delta needed — value persists between deltas)\n // Fade out at phrase end\n {\n layer: layerId,\n property: \"opacity\",\n range: [fadeOutStart, endFrame],\n from: 1,\n to: 0,\n easing: \"ease-in\",\n },\n ];\n}\n","import type { Command } from \"commander\";\nimport type { VideoTranscript } from \"@a-company/atelier-types\";\nimport {\n loadVideoProject,\n readTranscript,\n writeTranscript,\n readComposition,\n writeComposition,\n} from \"../lib/video-project.js\";\nimport {\n probeWhisper,\n runWhisperCpp,\n parseWhisperCppJson,\n type WhisperModel,\n} from \"../lib/whisper.js\";\nimport { mergeTranscriptWithExisting } from \"../lib/transcript-model.js\";\nimport { rewriteCaptionLayers, type BuildCaptionsOptions } from \"../lib/caption-builder.js\";\nimport { loadRecipe, applyRecipeToTranscribeOptions } from \"../lib/recipe.js\";\n\nexport interface TranscribeOptions {\n /** Whisper model selection */\n model?: WhisperModel;\n /** Explicit path to the model file — overrides model short-name lookup */\n modelPath?: string;\n /**\n * Override the source video path. By default `transcribeProject` uses the\n * legacy VideoProject scanner (`<projectDir>/source.<ext>`). Passing\n * `sourcePath` lets the multi-media ingest target an arbitrary media file\n * (e.g. `<projectDir>/media/<basename>`) without copying it to root.\n */\n sourcePath?: string;\n /** BCP-47 language hint (omit for autodetect) */\n language?: string;\n /** Discard existing user edits; full fresh transcript */\n reset?: boolean;\n /** Skip caption layer generation; transcript.json only */\n noCaptions?: boolean;\n /** Don't write files; return result */\n dryRun?: boolean;\n /** Caption style + grouping overrides, typically supplied by a Studio Recipe */\n captionOptions?: BuildCaptionsOptions;\n}\n\nexport interface TranscribeResult {\n projectDir: string;\n backend: string;\n transcript: VideoTranscript;\n wordCount: number;\n captionsGenerated: boolean;\n}\n\n/**\n * Run the transcription pipeline on a VideoProject folder.\n *\n * Pipeline:\n * 1. Probe Whisper backend (whisper-cpp / openai-api / none)\n * 2. Run Whisper → raw transcript JSON\n * 3. Parse into VideoTranscript shape\n * 4. Merge with existing transcript (preserves user edits) unless --reset\n * 5. Write transcript.json\n * 6. Unless --no-captions: rewriteCaptionLayers in project.atelier\n */\nexport async function transcribeProject(\n projectDir: string,\n options: TranscribeOptions = {},\n): Promise<TranscribeResult> {\n const project = loadVideoProject(projectDir);\n\n const backend = await probeWhisper();\n if (backend === \"none\") {\n throw new Error(\n \"No Whisper backend available. Install whisper.cpp (brew install whisper-cpp) \" +\n \"or set OPENAI_API_KEY and pass --use-api.\",\n );\n }\n if (backend === \"openai-api\") {\n // Reserved for the --use-api opt-in path; not implemented in this milestone\n throw new Error(\n \"OpenAI API backend is not yet implemented. Install whisper.cpp for local transcription.\",\n );\n }\n\n const sourcePath = options.sourcePath ?? project.sourcePath;\n const rawJson = await runWhisperCpp(sourcePath, {\n model: options.model,\n modelPath: options.modelPath,\n language: options.language,\n });\n let transcript = parseWhisperCppJson(rawJson);\n\n // Re-run preservation — keep user edits on matching detected words\n if (!options.reset) {\n const existing = readTranscript(project);\n if (existing) {\n transcript = mergeTranscriptWithExisting(transcript, existing);\n }\n }\n\n const wordCount = transcript.segments.reduce((n, s) => n + s.words.length, 0);\n\n const result: TranscribeResult = {\n projectDir: project.dir,\n backend,\n transcript,\n wordCount,\n captionsGenerated: false,\n };\n\n if (options.dryRun) return result;\n\n writeTranscript(project, transcript);\n\n if (!options.noCaptions) {\n const doc = readComposition(project);\n const updated = rewriteCaptionLayers(doc, transcript, options.captionOptions);\n writeComposition(project, updated);\n result.captionsGenerated = true;\n }\n\n return result;\n}\n\nfunction formatResult(result: TranscribeResult): string {\n const lines: string[] = [];\n lines.push(`Transcribed ${result.projectDir} via ${result.backend}`);\n if (result.transcript.language) {\n lines.push(` language: ${result.transcript.language}`);\n }\n lines.push(` segments: ${result.transcript.segments.length}`);\n lines.push(` words: ${result.wordCount}`);\n if (result.captionsGenerated) {\n lines.push(` captions: written to project.atelier`);\n } else {\n lines.push(` captions: skipped`);\n }\n return lines.join(\"\\n\");\n}\n\n/** Register `atelier transcribe` on the Commander program */\nexport function transcribeCommand(program: Command): void {\n program\n .command(\"transcribe <project>\")\n .description(\n \"Transcribe source video via Whisper, write transcript.json, and \" +\n \"rewrite caption-tagged TextVisual layers in project.atelier. \" +\n \"Preserves user transcript edits on re-run.\",\n )\n .option(\"--model <name>\", \"Whisper model: tiny|base|small|medium|large-v3\", \"base.en\")\n .option(\"--model-path <path>\", \"Explicit path to a .bin model file (overrides --model)\")\n .option(\"--language <code>\", \"BCP-47 language hint (omit for autodetect)\")\n .option(\"--reset\", \"Discard existing user edits; full fresh transcript\")\n .option(\"--no-captions\", \"Write transcript.json only; skip caption layer generation\")\n .option(\"--recipe <name>\", \"Apply a Studio Recipe's caption_style + caption_grouping\")\n .option(\"--dry-run\", \"Print transcript; don't write files\")\n .option(\"--json\", \"Output result as JSON for piping\")\n .action(async (\n project: string,\n opts: {\n model: WhisperModel;\n modelPath?: string;\n language?: string;\n reset?: boolean;\n captions: boolean;\n recipe?: string;\n dryRun?: boolean;\n json?: boolean;\n },\n ) => {\n try {\n let transcribeOpts: TranscribeOptions = {\n model: opts.model,\n modelPath: opts.modelPath,\n language: opts.language,\n reset: opts.reset,\n noCaptions: !opts.captions,\n dryRun: opts.dryRun,\n };\n if (opts.recipe) {\n const { recipe } = loadRecipe(opts.recipe, project);\n transcribeOpts = applyRecipeToTranscribeOptions(recipe, transcribeOpts);\n }\n const result = await transcribeProject(project, transcribeOpts);\n if (opts.json) {\n console.log(JSON.stringify(result, null, 2));\n } else {\n console.log(formatResult(result));\n if (opts.dryRun) console.log(\"(dry-run — no files written)\");\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`atelier transcribe: ${msg}`);\n process.exit(1);\n }\n });\n}\n","import type { Command } from \"commander\";\nimport type { VideoTranscript } from \"@a-company/atelier-types\";\nimport {\n loadVideoProject,\n readTranscript,\n writeTranscript,\n readComposition,\n writeComposition,\n} from \"../lib/video-project.js\";\nimport {\n applyTextEdit,\n applyBatchReplace,\n applyHide,\n applyAdd,\n applyMerge,\n applySplit,\n flattenWords,\n} from \"../lib/transcript-model.js\";\nimport { rewriteCaptionLayers } from \"../lib/caption-builder.js\";\n\n/** Read the project's transcript; throws if none exists */\nfunction loadOrThrow(projectDir: string): { project: ReturnType<typeof loadVideoProject>; transcript: VideoTranscript } {\n const project = loadVideoProject(projectDir);\n const transcript = readTranscript(project);\n if (!transcript) {\n throw new Error(\n `No transcript.json in ${projectDir}. Run \\`atelier transcribe ${projectDir}\\` first.`,\n );\n }\n return { project, transcript };\n}\n\n/** Save the transcript and optionally regenerate caption layers */\nfunction save(\n project: ReturnType<typeof loadVideoProject>,\n transcript: VideoTranscript,\n noRegenerate: boolean,\n): void {\n writeTranscript(project, transcript);\n if (!noRegenerate) {\n const doc = readComposition(project);\n const updated = rewriteCaptionLayers(doc, transcript);\n writeComposition(project, updated);\n }\n}\n\n/** Register the `atelier transcript` family of edit subcommands */\nexport function transcriptCommand(program: Command): void {\n const transcript = program\n .command(\"transcript\")\n .description(\"Edit transcript.json — fix, add, delete, merge, split, list\");\n\n transcript\n .command(\"fix <project>\")\n .description(\"Apply text correction(s). Use --replace 'wrong=right' for batch, or --word <idx> --text '<correction>' for single edit.\")\n .option(\"--replace <pair...>\", \"Batch find/replace, format: 'detected=replacement' (repeatable)\")\n .option(\"--word <index>\", \"Single-word edit by index\", (v) => parseInt(v, 10))\n .option(\"--text <text>\", \"Correction text (paired with --word)\")\n .option(\"--no-regenerate\", \"Skip caption layer regeneration after edit\")\n .action(async (\n projectDir: string,\n opts: { replace?: string[]; word?: number; text?: string; regenerate: boolean },\n ) => {\n try {\n const { project, transcript } = loadOrThrow(projectDir);\n let updated = transcript;\n\n if (opts.replace?.length) {\n for (const pair of opts.replace) {\n const [find, repl] = pair.split(\"=\");\n if (!find || repl === undefined) {\n throw new Error(`Invalid --replace pair: \"${pair}\". Use 'detected=replacement'.`);\n }\n updated = applyBatchReplace(updated, find, repl);\n }\n }\n\n if (typeof opts.word === \"number\") {\n if (opts.text === undefined) {\n throw new Error(\"--word requires --text <correction>\");\n }\n updated = applyTextEdit(updated, opts.word, opts.text);\n }\n\n if (!opts.replace?.length && opts.word === undefined) {\n throw new Error(\"Provide --replace 'wrong=right' or --word <idx> --text '...'\");\n }\n\n save(project, updated, !opts.regenerate);\n console.log(`Updated transcript in ${project.dir}`);\n } catch (err) {\n console.error(`atelier transcript fix: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\n transcript\n .command(\"add <project>\")\n .description(\"Insert a user-added word after the anchor word.\")\n .requiredOption(\"--after-word <index>\", \"Anchor word index\", (v) => parseInt(v, 10))\n .requiredOption(\"--text <text>\", \"Text of the new word\")\n .option(\"--duration <seconds>\", \"Word duration in seconds (default: 0.15)\", (v) => parseFloat(v), 0.15)\n .option(\"--no-regenerate\", \"Skip caption layer regeneration\")\n .action(async (\n projectDir: string,\n opts: { afterWord: number; text: string; duration: number; regenerate: boolean },\n ) => {\n try {\n const { project, transcript } = loadOrThrow(projectDir);\n const updated = applyAdd(transcript, opts.afterWord, opts.text, opts.duration);\n save(project, updated, !opts.regenerate);\n console.log(`Inserted \"${opts.text}\" after word ${opts.afterWord} in ${project.dir}`);\n } catch (err) {\n console.error(`atelier transcript add: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\n transcript\n .command(\"delete <project>\")\n .description(\"Hide a word (excluded from captions, kept in transcript).\")\n .requiredOption(\"--word <index>\", \"Word index\", (v) => parseInt(v, 10))\n .option(\"--no-regenerate\", \"Skip caption layer regeneration\")\n .action(async (\n projectDir: string,\n opts: { word: number; regenerate: boolean },\n ) => {\n try {\n const { project, transcript } = loadOrThrow(projectDir);\n const updated = applyHide(transcript, opts.word);\n save(project, updated, !opts.regenerate);\n console.log(`Hidden word ${opts.word} in ${project.dir}`);\n } catch (err) {\n console.error(`atelier transcript delete: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\n transcript\n .command(\"merge <project>\")\n .description(\"Merge two adjacent words at indices i and i+1.\")\n .requiredOption(\"--word <index>\", \"First of the two words to merge\", (v) => parseInt(v, 10))\n .option(\"--no-regenerate\", \"Skip caption layer regeneration\")\n .action(async (\n projectDir: string,\n opts: { word: number; regenerate: boolean },\n ) => {\n try {\n const { project, transcript } = loadOrThrow(projectDir);\n const updated = applyMerge(transcript, opts.word);\n save(project, updated, !opts.regenerate);\n console.log(`Merged words ${opts.word} and ${opts.word + 1} in ${project.dir}`);\n } catch (err) {\n console.error(`atelier transcript merge: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\n transcript\n .command(\"split <project>\")\n .description(\"Split one word at a fractional point.\")\n .requiredOption(\"--word <index>\", \"Word to split\", (v) => parseInt(v, 10))\n .requiredOption(\"--at <fraction>\", \"Split point (0–1)\", (v) => parseFloat(v))\n .option(\"--first <text>\", \"Override text for first half\")\n .option(\"--second <text>\", \"Override text for second half\")\n .option(\"--no-regenerate\", \"Skip caption layer regeneration\")\n .action(async (\n projectDir: string,\n opts: { word: number; at: number; first?: string; second?: string; regenerate: boolean },\n ) => {\n try {\n const { project, transcript } = loadOrThrow(projectDir);\n const updated = applySplit(transcript, opts.word, opts.at, opts.first, opts.second);\n save(project, updated, !opts.regenerate);\n console.log(`Split word ${opts.word} at ${opts.at} in ${project.dir}`);\n } catch (err) {\n console.error(`atelier transcript split: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\n transcript\n .command(\"list <project>\")\n .description(\"Print all words with their indices for reference.\")\n .option(\"--json\", \"Output as JSON\")\n .action(async (projectDir: string, opts: { json?: boolean }) => {\n try {\n const { transcript } = loadOrThrow(projectDir);\n const words = flattenWords(transcript);\n if (opts.json) {\n console.log(JSON.stringify(words.map((w, i) => ({ index: i, ...w })), null, 2));\n } else {\n for (let i = 0; i < words.length; i++) {\n const w = words[i];\n const flags: string[] = [];\n if (w.userEdited) flags.push(\"edited\");\n if (w.userAdded) flags.push(\"added\");\n if (w.hidden) flags.push(\"hidden\");\n const flagStr = flags.length ? ` [${flags.join(\", \")}]` : \"\";\n const editedDisplay = w.text !== w.detected ? ` ← \"${w.detected}\"` : \"\";\n console.log(\n ` [${i.toString().padStart(4)}] ${w.start.toFixed(2).padStart(7)}s ` +\n `\"${w.text}\"${editedDisplay}${flagStr}`,\n );\n }\n }\n } catch (err) {\n console.error(`atelier transcript list: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n}\n","import type { Command } from \"commander\";\nimport {\n loadVideoProject,\n readTranscript,\n readComposition,\n writeComposition,\n} from \"../lib/video-project.js\";\nimport { rewriteCaptionLayers } from \"../lib/caption-builder.js\";\nimport { loadRecipe, applyRecipeToCaptionOptions } from \"../lib/recipe.js\";\n\n/** Register `atelier captions regenerate <project>` */\nexport function captionsCommand(program: Command): void {\n const captions = program.command(\"captions\").description(\"Manage caption layers in a VideoProject\");\n\n captions\n .command(\"regenerate <project>\")\n .description(\n \"Re-derive caption-tagged TextVisual layers from the current transcript.json. \" +\n \"Used when caption styling changes without re-running Whisper.\",\n )\n .option(\"--recipe <name>\", \"Apply a Studio Recipe's caption_style + caption_grouping\")\n .action(async (projectDir: string, opts: { recipe?: string }) => {\n try {\n const project = loadVideoProject(projectDir);\n const transcript = readTranscript(project);\n if (!transcript) {\n throw new Error(\n `No transcript.json in ${projectDir}. Run \\`atelier transcribe ${projectDir}\\` first.`,\n );\n }\n const captionOptions = opts.recipe\n ? applyRecipeToCaptionOptions(loadRecipe(opts.recipe, projectDir).recipe)\n : {};\n const doc = readComposition(project);\n const updated = rewriteCaptionLayers(doc, transcript, captionOptions);\n writeComposition(project, updated);\n const captionLayers = updated.layers.filter((l) =>\n (l.tags ?? []).includes(\"caption\"),\n );\n console.log(\n `Regenerated ${captionLayers.length} caption layer${captionLayers.length === 1 ? \"\" : \"s\"} in ${project.dir}`,\n );\n } catch (err) {\n console.error(`atelier captions regenerate: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n}\n","import { writeFileSync, existsSync, mkdirSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport type { Command } from \"commander\";\nimport {\n loadRecipe,\n scaffoldRecipeYaml,\n renderRecipeWithDefaults,\n recipeToYaml,\n} from \"../lib/recipe.js\";\n\n/** Register `atelier recipe new/validate/show` family */\nexport function recipeCommand(program: Command): void {\n const recipe = program.command(\"recipe\").description(\"Manage Studio Recipes — reusable style presets\");\n\n recipe\n .command(\"new <name>\")\n .description(\"Scaffold a starter recipe YAML with all current defaults filled in\")\n .option(\"--dir <path>\", \"Where to write the recipe (default: ./.atelier/recipes/)\")\n .action((name: string, opts: { dir?: string }) => {\n try {\n const baseDir = opts.dir ?? join(resolve(process.cwd()), \".atelier\", \"recipes\");\n if (!existsSync(baseDir)) {\n mkdirSync(baseDir, { recursive: true });\n }\n // Footgun guard: if the name already carries a recognized extension,\n // treat it as a filename so `recipe new foo.recipe.yaml` doesn't\n // produce `foo.recipe.yaml.recipe.yaml`. Otherwise append the canonical\n // extension. The bare-name stem (without ext) is what `recipe validate\n // <name>` resolves, so they stay in agreement.\n const hasKnownExt = /\\.(recipe\\.yaml|recipe\\.json|yaml|yml|json)$/i.test(name);\n const fileName = hasKnownExt ? name : `${name}.recipe.yaml`;\n const outPath = join(baseDir, fileName);\n if (existsSync(outPath)) {\n throw new Error(`Recipe already exists at ${outPath} — refusing to overwrite.`);\n }\n const recipeName = name.replace(/\\.(recipe\\.yaml|recipe\\.json|yaml|yml|json)$/i, \"\");\n const yaml = scaffoldRecipeYaml(recipeName);\n writeFileSync(outPath, yaml, \"utf-8\");\n console.log(`Created ${outPath}`);\n } catch (err) {\n console.error(`atelier recipe new: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n\n recipe\n .command(\"validate <path>\")\n .description(\"Validate a recipe against the schema (^valid-recipe gate). Warns on reserved Phase 3 fields.\")\n .option(\"--json\", \"Output result as JSON\")\n .action((path: string, opts: { json?: boolean }) => {\n try {\n // Resolve a bare name against the project-local recipes dir first\n // (where `recipe new` writes), then the user library — so new/validate\n // agree on where a bare name lives.\n const loaded = loadRecipe(path, process.cwd());\n if (opts.json) {\n console.log(JSON.stringify({\n valid: true,\n path: loaded.path,\n warnings: loaded.warnings,\n }, null, 2));\n } else {\n console.log(`PASS ${loaded.path}`);\n for (const w of loaded.warnings) {\n console.log(` ⚠ ${w}`);\n }\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (opts.json) {\n console.log(JSON.stringify({ valid: false, error: msg }, null, 2));\n } else {\n console.error(`FAIL ${path}`);\n console.error(` ${msg}`);\n }\n process.exit(1);\n }\n });\n\n recipe\n .command(\"show <path>\")\n .description(\"Print a recipe; --with-defaults fills in every omitted field with its code default.\")\n .option(\"--with-defaults\", \"Overlay code defaults onto omitted fields\")\n .action((path: string, opts: { withDefaults?: boolean }) => {\n try {\n // Same project-local-first resolution as `recipe validate`.\n const loaded = loadRecipe(path, process.cwd());\n const out = opts.withDefaults ? renderRecipeWithDefaults(loaded.recipe) : loaded.recipe;\n console.log(recipeToYaml(out));\n for (const w of loaded.warnings) {\n console.error(`# ⚠ ${w}`);\n }\n } catch (err) {\n console.error(`atelier recipe show: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n}\n","import type { Command } from \"commander\";\nimport { trimProject } from \"./trim.js\";\nimport { transcribeProject } from \"./transcribe.js\";\nimport {\n loadRecipe,\n applyRecipeToTrimOptions,\n applyRecipeToTranscribeOptions,\n applyRecipeToOverlay,\n} from \"../lib/recipe.js\";\nimport {\n loadVideoProject,\n readComposition,\n writeComposition,\n} from \"../lib/video-project.js\";\n\n/**\n * Register `atelier apply-recipe <project> <recipe>` — convenience verb that\n * runs `atelier trim` and `atelier transcribe` against the same recipe in\n * one shot. Useful for fresh-project bootstrap.\n */\nexport function applyRecipeCommand(program: Command): void {\n program\n .command(\"apply-recipe <project> <recipe>\")\n .description(\n \"Apply a Studio Recipe by running atelier trim + atelier transcribe \" +\n \"with the same recipe, in that order.\",\n )\n .option(\"--reset\", \"Apply --reset to both pipelines (destructive — discards existing user padding and transcript edits)\")\n .option(\"--no-trim\", \"Skip the atelier trim step\")\n .option(\"--no-transcribe\", \"Skip the atelier transcribe step\")\n .action(async (\n project: string,\n recipeRef: string,\n opts: { reset?: boolean; trim: boolean; transcribe: boolean },\n ) => {\n try {\n const { recipe, path, warnings } = loadRecipe(recipeRef, project);\n console.log(`Loaded recipe ${path}`);\n for (const w of warnings) console.log(` ⚠ ${w}`);\n\n if (opts.trim) {\n const trimOpts = applyRecipeToTrimOptions(recipe, { reset: opts.reset });\n console.log(`\\nRunning atelier trim...`);\n const r = await trimProject(project, trimOpts);\n console.log(` ${r.cuts.length} cut${r.cuts.length === 1 ? \"\" : \"s\"} written`);\n }\n\n if (opts.transcribe) {\n const transcribeOpts = applyRecipeToTranscribeOptions(recipe, { reset: opts.reset });\n console.log(`\\nRunning atelier transcribe...`);\n const r = await transcribeProject(project, transcribeOpts);\n console.log(` ${r.wordCount} words, ${r.captionsGenerated ? \"captions written\" : \"captions skipped\"}`);\n }\n\n // Overlay translator — rewrites \"overlay\"-tagged layers in project.atelier\n // from recipe.overlay_rules. Single-frame apply: no carousel ctx, so\n // page_number is silently skipped (handle is always applied if present).\n if (recipe.overlay_rules) {\n const vp = loadVideoProject(project);\n const doc = readComposition(vp);\n const updated = applyRecipeToOverlay(doc, recipe);\n writeComposition(vp, updated);\n console.log(`\\nApplied overlay rules to ${vp.compositionPath}`);\n }\n\n console.log(`\\nDone.`);\n } catch (err) {\n console.error(`atelier apply-recipe: ${err instanceof Error ? err.message : err}`);\n process.exit(1);\n }\n });\n}\n","import type { Command } from \"commander\";\nimport { execFileSync } from \"node:child_process\";\nimport {\n existsSync,\n mkdirSync,\n readdirSync,\n statSync,\n writeFileSync,\n} from \"node:fs\";\nimport { basename, join, resolve } from \"node:path\";\nimport { createInterface, type Interface as ReadlineInterface } from \"node:readline\";\nimport { scaffoldArtifacts } from \"./artifacts.js\";\nimport { ARTIFACT_SLOTS } from \"../lib/artifact-templates.js\";\nimport {\n LEARNING_MODES,\n writeLearningMode,\n type LearningMode,\n} from \"../lib/learning-mode.js\";\nimport {\n findWorkspace,\n hasProjectMarker,\n isValidProjectName,\n listProjects,\n readWorkspace,\n writeWorkspace,\n} from \"../lib/workspace.js\";\n\n/**\n * `atelier init <project-dir>` — first-run setup for a creator Project.\n *\n * Sequence (Phase 3, delegation C; see\n * docs/keynote/paradigm/phase-3-reconciliation-notes.md §3):\n *\n * 1. Create `<project-dir>` if absent (recursive). Refuse if the path\n * exists as a file rather than a directory. Existing non-empty dir is\n * a \"complete the init\" workflow — proceed with a note in output.\n * 2. Determine learning mode. `--mode <ambient|explicit>` skips the\n * prompt; otherwise readline-prompts the creator (single question, two\n * options, max 3 attempts). Bad `--mode` value exits 1 before any fs\n * writes (TD-2026-05-26-646: autonomy posture is pre-declared).\n * 3. Write `<project-dir>/.atelier/learning-mode.yaml` with the choice\n * and an ISO timestamp (delegated to #learning-mode-io).\n * 4. Create `<project-dir>/.atelier/mind-map/` with a README explaining\n * what to put there. README is preserved on re-init (TD-2026-05-26-271:\n * nothing in the mind-map leaves the machine).\n * 5. Unless `--no-scaffold`, drop DESIGN/SCRIPT/STORYBOARD via the\n * existing #cli-artifacts `scaffoldArtifacts` helper. Re-init swallows\n * the refuse-to-overwrite throw as a soft skip (the operation is\n * explicitly idempotent — see init-idempotent-re-run-safe aspect).\n * 6. Unless `--no-git`, run `git init` in `<project-dir>` if it isn't\n * already inside a git repo. Writes a minimal `.gitignore` only when\n * one doesn't exist.\n *\n * Output: human-formatted step-by-step summary by default; `--json` emits\n * the documented machine-readable shape. Exit 0 on success, 1 on any\n * irrecoverable error (file-not-dir, can't determine mode, fs failure).\n *\n * The Commander wiring is a thin shell around the pure `runInit(opts)`\n * helper so test code drives the same code path without process.exit\n * shenanigans (matches the artifacts.ts split).\n */\nexport function initCommand(program: Command): void {\n program\n .command(\"init <project-dir>\")\n .description(\n \"First-run setup for a creator Project: pick a learning mode, scaffold artifacts, \" +\n \"create the local mind-map, optionally git-init.\",\n )\n .option(\n \"--mode <ambient|explicit>\",\n \"Skip the interactive prompt and set learning mode directly.\",\n )\n .option(\"--no-git\", \"Skip `git init` (and skip writing .gitignore).\")\n .option(\n \"--no-scaffold\",\n \"Skip writing DESIGN.md / SCRIPT.md / STORYBOARD.md templates.\",\n )\n .option(\n \"--force\",\n \"Force-overwrite existing artifact files when scaffolding \" +\n \"(delegates to `atelier artifacts scaffold --force`).\",\n )\n .option(\"--json\", \"Emit a machine-readable JSON report instead of human-formatted output.\")\n .option(\n \"--no-workspace-relative\",\n \"When inside a workspace, resolve <project-dir> relative to cwd instead of \" +\n \"the workspace root. Useful for power-user paths that escape the workspace.\",\n )\n .action(async (\n projectDir: string,\n opts: {\n mode?: string;\n git?: boolean; // commander --no-git → opts.git === false\n scaffold?: boolean; // commander --no-scaffold → opts.scaffold === false\n workspaceRelative?: boolean; // commander --no-workspace-relative → false\n force?: boolean;\n json?: boolean;\n },\n ) => {\n try {\n // Workspace-aware resolution: when cwd is inside a workspace AND the\n // provided <project-dir> is RELATIVE, resolve against the workspace\n // root rather than cwd. Power-users can pass --no-workspace-relative\n // to opt out (the boolean defaults true via Commander's --no-* flag).\n let resolvedProjectDir = projectDir;\n let inheritedMode: string | undefined;\n let workspaceCtx: { workspaceDir: string; manifest: { name: string; default_mode: LearningMode; projects?: string[] } } | null = null;\n const cwd = process.cwd();\n const workspaceRelative = opts.workspaceRelative !== false;\n const ws = findWorkspace(cwd);\n if (ws) {\n workspaceCtx = ws;\n if (\n workspaceRelative &&\n !projectDir.startsWith(\"/\") &&\n !projectDir.startsWith(\".\")\n ) {\n resolvedProjectDir = join(ws.workspaceDir, projectDir);\n }\n // Inherit default_mode from the workspace UNLESS the creator\n // explicitly passed --mode. Skip the interactive prompt inside a\n // workspace — the autonomy posture is pre-declared at the\n // workspace level (TD-2026-05-26-646).\n if (opts.mode === undefined) {\n inheritedMode = ws.manifest.default_mode;\n }\n }\n\n const result = await runInit({\n projectDir: resolvedProjectDir,\n mode: opts.mode ?? inheritedMode,\n modeInheritedFromWorkspace: opts.mode === undefined && inheritedMode !== undefined,\n git: opts.git !== false,\n scaffold: opts.scaffold !== false,\n force: opts.force === true,\n json: opts.json === true,\n });\n\n // Refresh the workspace manifest's advisory `projects` list to\n // include the newly-initialized project, if cwd was inside a\n // workspace and the new project landed inside it.\n if (workspaceCtx) {\n try {\n const projects = listProjects(workspaceCtx.workspaceDir);\n writeWorkspace(workspaceCtx.workspaceDir, {\n ...workspaceCtx.manifest,\n version: \"1.0\",\n created: (workspaceCtx as { manifest: { created?: string } }).manifest.created ?? new Date().toISOString(),\n projects,\n });\n } catch {\n // Non-fatal — manifest is advisory.\n }\n }\n\n if (opts.json) {\n console.log(JSON.stringify(result, null, 2));\n } else {\n for (const line of formatHumanReport(result)) {\n console.log(line);\n }\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (opts.json) {\n console.log(JSON.stringify({ ok: false, error: msg }, null, 2));\n } else {\n console.error(`atelier init: ${msg}`);\n }\n process.exit(1);\n }\n });\n}\n\n/** Input to `runInit`. Booleans are positive-form (caller pre-resolves --no-X). */\nexport interface RunInitOptions {\n projectDir: string;\n /** Pre-supplied mode (skips the prompt). */\n mode?: string;\n /**\n * When the mode was inherited from a workspace (not explicitly chosen\n * by the creator for this project), the per-project learning-mode.yaml\n * gets a comment noting the inheritance. The wrapper sets this when\n * `--mode` was absent and a workspace's `default_mode` was used.\n */\n modeInheritedFromWorkspace?: boolean;\n /** Run `git init` when not already in a repo. Default true. */\n git: boolean;\n /** Drop DESIGN/SCRIPT/STORYBOARD via `scaffoldArtifacts`. Default true. */\n scaffold: boolean;\n /** Forward to `scaffoldArtifacts({ force })`. Default false. */\n force: boolean;\n /** Suppress interactive prompt fallback (used by --json path). Default false. */\n json: boolean;\n /**\n * Drop a minimal `project.atelier` manifest at the project root so this\n * directory is recognized as a Project by legacy discovery callers. The\n * CLI surface (`atelier init`) leaves this true so existing tooling that\n * keys on the manifest's presence keeps working; the workspace `+ new`\n * surface (via `createProjectInWorkspace`) passes `false` so the project\n * stays \"lazy\" until its first composed `.atelier` doc lands. Default\n * true (preserves pre-pivot behavior on the CLI path).\n */\n writeManifest?: boolean;\n /**\n * Test/programmatic override: a function that resolves to the chosen mode\n * when no `--mode` was passed. Production wiring uses the readline prompt.\n * Returning `undefined` or throwing aborts with the standard \"couldn't\n * determine learning mode\" error.\n */\n promptForMode?: () => Promise<LearningMode>;\n}\n\n/** Result returned by `runInit` and serialized for `--json`. */\nexport interface InitResult {\n /** True on the success path. */\n ok: true;\n projectDir: string;\n mode: LearningMode;\n learningModeFile: string;\n /** Absolute paths of artifacts written by the scaffold step (may be empty). */\n scaffolded: string[];\n /** Absolute paths of artifacts skipped (already existed, no --force). */\n scaffoldSkipped: string[];\n mindMapDir: string;\n /** True when `git init` ran in this invocation. */\n gitInitialized: boolean;\n /** True when the dir was already inside an existing git repo. */\n alreadyInRepo: boolean;\n /** Notes accumulated during the run (re-init warnings, etc.). */\n notes: string[];\n}\n\nconst MIND_MAP_README = `# Your Mind-Map\n\nThis directory holds the creator's local asset library — clips, memes,\ngraphics, audio — that the agent team queries when binding typed slots\nin recipes and storyboards (TD-2026-05-26-229).\n\nOrganize however you want; the \\`atelier_mind_map_query\\` tool walks\nrecursively and matches by file extension against slot \\`kind\\` values\n(image, video, audio, font, etc.).\n\nNothing in this directory leaves your machine (TD-2026-05-26-271).\n`;\n\nconst DEFAULT_GITIGNORE = `node_modules/\n*.log\n.atelier/cache/\n`;\n\n/**\n * Pure helper for `atelier init`. Throws on any irrecoverable error; the\n * Commander wrapper catches and maps to `process.exit(1)`. Re-runnable on an\n * already-initialized dir (scaffold's refuse-to-overwrite is downgraded to a\n * soft skip in this surface — see init-idempotent-re-run-safe aspect).\n */\nexport async function runInit(opts: RunInitOptions): Promise<InitResult> {\n const absDir = resolve(opts.projectDir);\n const notes: string[] = [];\n\n // 1. Validate --mode BEFORE any fs writes. A typo'd mode must not leave a\n // half-created dir behind.\n let mode: LearningMode | undefined;\n if (opts.mode !== undefined) {\n const normalized = opts.mode.toLowerCase();\n if (normalized !== \"ambient\" && normalized !== \"explicit\") {\n throw new Error(\n `invalid --mode \"${opts.mode}\" — expected one of ${LEARNING_MODES.join(\", \")}`,\n );\n }\n mode = normalized;\n }\n\n // 2. Dir handling. File-collision is fatal; non-empty existing dir is fine\n // (the explicit \"complete the init\" workflow).\n if (existsSync(absDir)) {\n const st = statSync(absDir);\n if (!st.isDirectory()) {\n throw new Error(`project path is not a directory: ${absDir}`);\n }\n if (readdirSync(absDir).length > 0) {\n notes.push(`project dir exists and is non-empty; completing init in place`);\n }\n } else {\n mkdirSync(absDir, { recursive: true });\n }\n\n // 3. Resolve the mode if --mode wasn't supplied. Prompt is the production\n // fallback; tests inject `promptForMode`. --json without --mode is an\n // error — scripted use must pre-declare.\n if (mode === undefined) {\n if (opts.promptForMode) {\n mode = await opts.promptForMode();\n } else if (opts.json) {\n throw new Error(\n `--mode is required with --json (scripted use must pre-declare; ` +\n `pass --mode ambient or --mode explicit)`,\n );\n } else {\n mode = await promptForLearningMode();\n }\n }\n\n // 4. Write learning-mode.yaml. Always overwrites — re-init refreshes the\n // chosen_at timestamp. When the wrapper inherited the mode from a\n // workspace, record that as a note so the human report makes the\n // inheritance visible (the YAML file itself stays unchanged in shape).\n const learningModeFile = writeLearningMode(absDir, mode);\n if (opts.modeInheritedFromWorkspace) {\n notes.push(`learning mode inherited from workspace (${mode})`);\n }\n\n // 5. Create the mind-map dir + README. README is preserved on re-init so\n // creator notes/edits survive.\n const mindMapDir = join(absDir, \".atelier\", \"mind-map\");\n if (!existsSync(mindMapDir)) {\n mkdirSync(mindMapDir, { recursive: true });\n }\n const readmePath = join(mindMapDir, \"README.md\");\n if (!existsSync(readmePath)) {\n writeFileSync(readmePath, MIND_MAP_README, \"utf-8\");\n }\n\n // 5b. Drop a minimal `project.atelier` so this directory is recognized as\n // a Project by legacy workspace-discovery callers and so workspace-\n // level operations like `setActiveProject` can point at it. Minimal\n // scaffold; Iris populates layers/states/etc. during the live loop.\n // The shape matches the canonical empty doc produced by the\n // `atelier_create` MCP tool (states: {}, empty layers) — `parseAtelier`\n // accepts both YAML and JSON, and JSON keeps the on-disk format\n // trivially editable without a YAML dep.\n //\n // Gated by `writeManifest` (default true). The workspace `+ new`\n // flow passes `false` to keep freshly-created Projects \"lazy\" — they\n // become real Projects on disk once the creator composes their first\n // `.atelier` doc. Discovery (`hasProjectMarker` in workspace.ts)\n // now accepts the `.atelier/` metadata subdir as an equivalent\n // marker so manifestless Projects still surface in the sidebar.\n // Idempotent: re-init never clobbers a creator's edits.\n if (opts.writeManifest !== false) {\n const projectAtelierPath = join(absDir, \"project.atelier\");\n if (!existsSync(projectAtelierPath)) {\n const minimalDoc = {\n version: \"1.0\",\n // Default to a vertical short-form canvas (TikTok/Reels/Shorts) at\n // 30fps; Iris/Lux rewrite canvas dimensions when DESIGN.md specifies\n // a different aspect ratio.\n name: basename(absDir),\n canvas: { width: 1080, height: 1920, fps: 30 },\n layers: [],\n states: {},\n };\n writeFileSync(\n projectAtelierPath,\n JSON.stringify(minimalDoc, null, 2) + \"\\n\",\n \"utf-8\",\n );\n }\n }\n\n // 6. Scaffold the three artifacts (unless opted out). Re-init: swallow the\n // refuse-to-overwrite throw — this surface is intentionally idempotent.\n // A genuine non-overwrite error (e.g. file-not-dir) is re-raised; we\n // detect the overwrite case by message prefix written by scaffoldArtifacts.\n const scaffolded: string[] = [];\n const scaffoldSkipped: string[] = [];\n if (opts.scaffold) {\n try {\n const sr = scaffoldArtifacts(absDir, {\n slots: [...ARTIFACT_SLOTS],\n force: opts.force,\n });\n scaffolded.push(...sr.created);\n scaffoldSkipped.push(...sr.skipped);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (msg.startsWith(\"refusing to overwrite\")) {\n notes.push(\n `artifacts already present; skipped scaffold (use --force to replace)`,\n );\n } else {\n throw err;\n }\n }\n }\n\n // 7. Git step. Skipped when --no-git OR when already inside a repo.\n let gitInitialized = false;\n let alreadyInRepo = false;\n if (opts.git) {\n if (isInsideGitRepo(absDir)) {\n alreadyInRepo = true;\n notes.push(`already inside a git repo; skipped \\`git init\\``);\n } else {\n try {\n execFileSync(\"git\", [\"init\"], { cwd: absDir, stdio: \"ignore\" });\n gitInitialized = true;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n notes.push(`\\`git init\\` failed (${msg}); continuing without git`);\n }\n // Write a minimal .gitignore only if one doesn't exist — never clobber\n // a creator-authored ignore file.\n const gitignorePath = join(absDir, \".gitignore\");\n if (!existsSync(gitignorePath)) {\n writeFileSync(gitignorePath, DEFAULT_GITIGNORE, \"utf-8\");\n }\n }\n }\n\n return {\n ok: true,\n projectDir: absDir,\n mode,\n learningModeFile,\n scaffolded,\n scaffoldSkipped,\n mindMapDir,\n gitInitialized,\n alreadyInRepo,\n notes,\n };\n}\n\n/**\n * Returns true when `dir` (or any ancestor) sits inside an existing git work\n * tree. Implemented via `git rev-parse --show-toplevel`; non-zero exit and\n * any spawn failure (missing git, etc.) are treated as \"not in a repo\" so the\n * caller falls through to `git init`.\n */\nexport function isInsideGitRepo(dir: string): boolean {\n try {\n execFileSync(\"git\", [\"-C\", dir, \"rev-parse\", \"--show-toplevel\"], {\n stdio: \"ignore\",\n });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Production interactive mode prompt. One readable question, two options,\n * 3 attempts before giving up. Accepts `1`, `2`, `ambient`, `explicit`\n * (case-insensitive). The phrasing cites TD-2026-05-26-646's autonomy stance\n * via the two option blurbs.\n */\nasync function promptForLearningMode(): Promise<LearningMode> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n process.stdout.write(\n `How should your agent team learn?\\n` +\n `\\n` +\n ` 1) Ambient — the team captures corrections silently and surfaces patterns when confident.\\n` +\n ` 2) Explicit — nothing locks in without your \"yes\"; high-control, more friction.\\n` +\n `\\n`,\n );\n for (let attempt = 0; attempt < 3; attempt++) {\n const answer = (await question(rl, `Choose [1/2]: `)).trim().toLowerCase();\n const parsed = parseModeAnswer(answer);\n if (parsed) return parsed;\n process.stdout.write(`Please enter 1, 2, ambient, or explicit.\\n`);\n }\n throw new Error(\n `couldn't determine learning mode after 3 attempts ` +\n `(hint: re-run with --mode ambient or --mode explicit for scripted use)`,\n );\n } finally {\n rl.close();\n }\n}\n\n/** Parse a single answer to the mode prompt. Returns undefined on no match. */\nexport function parseModeAnswer(answer: string): LearningMode | undefined {\n const a = answer.trim().toLowerCase();\n if (a === \"1\" || a === \"ambient\") return \"ambient\";\n if (a === \"2\" || a === \"explicit\") return \"explicit\";\n return undefined;\n}\n\n/** Promisified readline.question. */\nfunction question(rl: ReadlineInterface, prompt: string): Promise<string> {\n return new Promise((resolvePromise) => {\n rl.question(prompt, (answer) => resolvePromise(answer));\n });\n}\n\n/**\n * Human-formatted step-by-step output, mirroring the artifacts.ts style.\n * Each line is a discrete bullet — easy to scan, no chalk colors.\n */\nfunction formatHumanReport(r: InitResult): string[] {\n const lines: string[] = [];\n lines.push(`Project: ${r.projectDir}`);\n lines.push(` Learning mode: ${r.mode}`);\n lines.push(` Wrote ${r.learningModeFile}`);\n lines.push(` Mind-map: ${r.mindMapDir}`);\n if (r.scaffolded.length > 0) {\n lines.push(` Scaffolded artifacts:`);\n for (const f of r.scaffolded) lines.push(` - ${f}`);\n }\n if (r.scaffoldSkipped.length > 0) {\n lines.push(` Skipped (already exists):`);\n for (const f of r.scaffoldSkipped) lines.push(` - ${f}`);\n }\n if (r.gitInitialized) {\n lines.push(` Initialized git repo`);\n } else if (r.alreadyInRepo) {\n lines.push(` Git: already inside a repo (skipped)`);\n }\n for (const note of r.notes) {\n lines.push(` Note: ${note}`);\n }\n lines.push(\"\");\n lines.push(`Result: PASS`);\n return lines;\n}\n\n// ─── Workspace-scoped project creation (no subprocess) ──────────────────\n//\n// Extracted from initCommand's action body so the studio HTTP endpoint can\n// invoke the same flow in-process — no `atelier init` subprocess fork. The\n// CLI wrapper still goes through `runInit` directly; this helper layers\n// the workspace-relative resolution + manifest refresh on top so both\n// callers stay in lock-step.\n\n/** Options for `createProjectInWorkspace`. */\nexport interface CreateProjectInWorkspaceOptions {\n /** Single-segment project name (gated by `isValidProjectName`). */\n name: string;\n /**\n * Override the workspace's `default_mode` for this one project. When\n * omitted the workspace's mode is inherited and the inheritance is\n * recorded as a note (mirrors the CLI's behavior).\n */\n mode?: LearningMode;\n /** Forward to `runInit` — drops DESIGN/SCRIPT/STORYBOARD. Defaults true. */\n scaffold?: boolean;\n /** Forward to `runInit` — overwrite existing artifacts. Defaults false. */\n force?: boolean;\n /**\n * Run `git init` inside the new project dir. Defaults false for the\n * endpoint surface — a UI-driven create-project click shouldn't quietly\n * fork git in the background; the CLI sets this true via `runInit`.\n */\n git?: boolean;\n}\n\n/**\n * Create a new Project subdirectory inside an existing workspace. Pure\n * function — takes the workspace dir explicitly, never reads cwd. Used by\n * the `/api/workspace/projects` HTTP handler so the endpoint never spawns\n * an `atelier init` subprocess.\n *\n * Pipeline:\n * 1. Validate `name` against `isValidProjectName` (rejects path\n * separators, leading dot, control chars) before any fs work.\n * 2. Refuse if the target dir already looks like a Project — i.e. has\n * a `project.atelier` manifest OR an `.atelier/` metadata subdir\n * (`hasProjectMarker`). A bare empty dir is OK.\n * 3. Delegate to `runInit` with `writeManifest: false` so the new\n * Project stays \"lazy\" — no auto-scaffolded `project.atelier`. The\n * directory + `.atelier/` metadata subdir is enough for discovery\n * to pick it up; the first composed `.atelier` doc lands when the\n * creator drops in media or asks an agent to compose one.\n * 4. Refresh the workspace manifest's advisory `projects[]` list so the\n * manifest reflects on-disk truth (same logic as the CLI wrapper).\n *\n * Throws on: invalid name, workspace not found, target-is-existing-project,\n * any `runInit` failure. Manifest-refresh failures are non-fatal (manifest\n * is advisory; we never block a successful project creation on a manifest\n * write error).\n */\nexport async function createProjectInWorkspace(\n workspaceDir: string,\n opts: CreateProjectInWorkspaceOptions,\n): Promise<InitResult> {\n if (!isValidProjectName(opts.name)) {\n throw new Error(\n `invalid project name \"${opts.name}\" — must be a single-segment basename ` +\n `(no path separators, no leading dot, no control chars)`,\n );\n }\n const absWs = resolve(workspaceDir);\n // Re-read the manifest so we know the inherited default_mode + can\n // refresh `projects[]` after creation. A missing/bad manifest throws.\n const manifest = readWorkspace(absWs);\n const projectDir = join(absWs, opts.name);\n\n // Refuse to clobber an existing project at this name. Use the same\n // marker as discovery (either `project.atelier` OR `.atelier/`) so a\n // freshly-created lazy project is also recognized as \"already exists\".\n // An empty/missing dir is fine — that's the standard \"create new\" case.\n if (hasProjectMarker(projectDir)) {\n throw new Error(\n `project \"${opts.name}\" already exists at ${projectDir}`,\n );\n }\n\n const mode = opts.mode ?? manifest.default_mode;\n const result = await runInit({\n projectDir,\n mode,\n modeInheritedFromWorkspace: opts.mode === undefined,\n git: opts.git === true,\n scaffold: opts.scaffold !== false,\n force: opts.force === true,\n // The endpoint surface ALWAYS needs a deterministic mode (no\n // interactive prompt available behind an HTTP request), so we pass\n // --mode-equivalent and json:false (no need to error on missing mode\n // — we always supply one).\n json: false,\n // Workspace `+ new` is the \"lazy project\" path — no auto-scaffolded\n // `project.atelier`. The creator's first compose action drops a real\n // `.atelier` doc; until then the project lives off its `.atelier/`\n // metadata subdir alone.\n writeManifest: false,\n });\n\n // Refresh the manifest's advisory projects[] list. Non-fatal.\n try {\n const projects = listProjects(absWs);\n writeWorkspace(absWs, {\n ...manifest,\n projects,\n });\n } catch {\n // Advisory only; surface in logs rather than fail the create.\n }\n\n return result;\n}\n","import type { Command } from \"commander\";\nimport { validateArtifactSet } from \"@a-company/atelier-schema\";\nimport { relative, resolve, join } from \"node:path\";\nimport { existsSync, mkdirSync, statSync, writeFileSync } from \"node:fs\";\nimport {\n loadArtifactsFromProject,\n ARTIFACT_FILENAMES,\n type LoadedArtifactProject,\n} from \"../lib/artifact-project.js\";\nimport {\n ARTIFACT_TEMPLATES,\n ARTIFACT_SLOTS,\n type ArtifactSlot,\n} from \"../lib/artifact-templates.js\";\n\n/**\n * `atelier artifacts <verb>` — first user-facing surface over the\n * Phase 2 front-of-pipeline artifact schemas (DESIGN/SCRIPT/STORYBOARD).\n *\n * Phase 2 lands the schemas + the `^valid-artifact-set` gate; this CLI\n * verb is the on-disk runner. `atelier artifacts validate <project-dir>`\n * walks a Project directory for the three artifact files, parses each\n * one that's present, runs the cross-artifact validator on whatever\n * parsed, and prints a human or JSON report.\n *\n * Exit-code matrix:\n * - parse error on any file → 1\n * - validator returns ok: false → 1\n * - warnings only, no errors → 0 (per spec §9: pre-VO duration overrun\n * is warn-only and must NOT fail the gate)\n * - missing artifacts → 0 (per validator: only what's present is checked)\n *\n * Output style mirrors `recipe.ts` — `console.log` for results, status\n * lines prefixed with PASS/FAIL/WARN, `--json` toggles a machine\n * readable payload. (CLI stdout IS the deliverable here; the generic\n * Paradigm-logger rule does not apply to CLI verbs — see `recipe.ts`,\n * `validate.ts`, etc., which all follow this same convention.)\n */\nexport function artifactsCommand(program: Command): void {\n const artifacts = program\n .command(\"artifacts\")\n .description(\"Manage Atelier front-of-pipeline artifacts (DESIGN/SCRIPT/STORYBOARD)\");\n\n artifacts\n .command(\"validate <project-dir>\")\n .description(\n \"Validate the DESIGN.md / SCRIPT.md / STORYBOARD.md triplet inside a Project directory. \" +\n \"Runs the ^valid-artifact-set gate; absent artifacts are noted, not failed.\",\n )\n .option(\"--json\", \"Emit a machine-readable JSON report instead of human-formatted output\")\n .action((projectDir: string, opts: { json?: boolean }) => {\n let loaded: LoadedArtifactProject;\n try {\n loaded = loadArtifactsFromProject(projectDir);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (opts.json) {\n console.log(JSON.stringify({ ok: false, error: msg }, null, 2));\n } else {\n console.error(`atelier artifacts validate: ${msg}`);\n }\n process.exit(1);\n }\n\n // Run the cross-artifact validator on whatever parsed cleanly.\n // The validator is no-throw (returns {ok, warnings, errors}).\n const validation = validateArtifactSet({\n design: loaded.design,\n script: loaded.script,\n storyboard: loaded.storyboard,\n });\n\n const hasParseErrors = loaded.parseErrors.length > 0;\n const ok = !hasParseErrors && validation.ok;\n\n if (opts.json) {\n emitJson(loaded, validation, ok);\n } else {\n emitHuman(loaded, validation, ok);\n }\n\n if (!ok) {\n process.exit(1);\n }\n });\n\n artifacts\n .command(\"scaffold <project-dir>\")\n .description(\n \"Write canonical empty DESIGN.md / SCRIPT.md / STORYBOARD.md templates into a Project directory. \" +\n \"Creates the directory if absent; refuses to overwrite existing files without --force.\",\n )\n .option(\n \"--only <slots>\",\n \"Restrict to a subset (design | script | storyboard); comma-separated or repeated flag.\",\n collectOnly,\n [] as string[],\n )\n .option(\"--force\", \"Overwrite existing artifact files in <project-dir>\")\n .option(\"--json\", \"Emit a machine-readable JSON report instead of human-formatted output\")\n .action((projectDir: string, opts: { only?: string[]; force?: boolean; json?: boolean }) => {\n try {\n const slots = resolveSlots(opts.only ?? []);\n const result = scaffoldArtifacts(projectDir, {\n slots,\n force: opts.force === true,\n });\n if (opts.json) {\n console.log(JSON.stringify({\n ok: true,\n projectDir: result.projectDir,\n created: result.created,\n skipped: result.skipped,\n }, null, 2));\n } else {\n console.log(`Project: ${result.projectDir}`);\n for (const c of result.created) console.log(` Created ${c}`);\n for (const s of result.skipped) console.log(` Skipped ${s} (already exists; use --force to overwrite)`);\n console.log(\"\");\n console.log(\n `Result: ${result.created.length} written, ${result.skipped.length} skipped.`,\n );\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (opts.json) {\n console.log(JSON.stringify({ ok: false, error: msg }, null, 2));\n } else {\n console.error(`atelier artifacts scaffold: ${msg}`);\n }\n process.exit(1);\n }\n });\n}\n\n/**\n * Commander value collector for `--only`. Supports both `--only design`\n * repeated and `--only design,script` comma-split (and any mix). The\n * previous value is the accumulator commander threads through repeated\n * invocations; default is set to `[]` on the option.\n */\nfunction collectOnly(value: string, previous: string[]): string[] {\n const parts = value.split(\",\").map((s) => s.trim()).filter((s) => s.length > 0);\n return [...previous, ...parts];\n}\n\n/**\n * Translate the raw `--only` string list into typed `ArtifactSlot`s and\n * dedupe. Empty input expands to ALL slots (the default behavior). Unknown\n * tokens throw with the legal set listed.\n */\nfunction resolveSlots(raw: string[]): ArtifactSlot[] {\n if (raw.length === 0) return [...ARTIFACT_SLOTS];\n const seen = new Set<ArtifactSlot>();\n for (const token of raw) {\n if (!isArtifactSlot(token)) {\n throw new Error(\n `unknown --only value \"${token}\" — expected one of ${ARTIFACT_SLOTS.join(\", \")}`,\n );\n }\n seen.add(token);\n }\n // Preserve canonical write order (design → script → storyboard).\n return ARTIFACT_SLOTS.filter((s) => seen.has(s));\n}\n\nfunction isArtifactSlot(value: string): value is ArtifactSlot {\n return (ARTIFACT_SLOTS as readonly string[]).includes(value);\n}\n\n/** Result surface for the pure `scaffoldArtifacts` helper. */\nexport interface ScaffoldResult {\n /** Absolute path to the project dir that was written into. */\n projectDir: string;\n /** Absolute paths of files that were created/overwritten. */\n created: string[];\n /** Absolute paths of files that already existed and were skipped. */\n skipped: string[];\n}\n\n/**\n * Pure scaffold helper — creates the project dir if absent, writes the\n * selected artifact templates, refuses to overwrite without `force`.\n *\n * If `force` is false and ANY targeted file already exists, the whole\n * operation aborts with a single error listing every would-clobber path\n * (so the creator sees the full conflict in one pass rather than failing\n * one file at a time). Throws on a non-directory project path.\n */\nexport function scaffoldArtifacts(\n projectDir: string,\n opts: { slots: ArtifactSlot[]; force: boolean },\n): ScaffoldResult {\n const absDir = resolve(projectDir);\n\n // If the path exists, it must be a directory — refusing-to-write into a\n // file is the same failure mode `loadArtifactsFromProject` enforces on\n // the read side, kept symmetric here.\n if (existsSync(absDir)) {\n const stat = statSync(absDir);\n if (!stat.isDirectory()) {\n throw new Error(`project path is not a directory: ${absDir}`);\n }\n } else {\n // Scaffold is a create-then-fill verb — create missing dirs recursively.\n mkdirSync(absDir, { recursive: true });\n }\n\n const targets = opts.slots.map((slot) => ({\n slot,\n filename: ARTIFACT_TEMPLATES[slot].filename,\n abs: join(absDir, ARTIFACT_TEMPLATES[slot].filename),\n content: ARTIFACT_TEMPLATES[slot].content,\n }));\n\n // Pre-flight overwrite check: collect EVERY existing target so the error\n // lists the full set, not just the first hit.\n if (!opts.force) {\n const clobbered = targets.filter((t) => existsSync(t.abs)).map((t) => t.abs);\n if (clobbered.length > 0) {\n throw new Error(\n `refusing to overwrite existing file${clobbered.length === 1 ? \"\" : \"s\"} ` +\n `(use --force to replace): ${clobbered.join(\", \")}`,\n );\n }\n }\n\n const created: string[] = [];\n const skipped: string[] = [];\n for (const t of targets) {\n if (!opts.force && existsSync(t.abs)) {\n // Defensive — should never hit since the pre-flight aborts, but\n // keeps the helper safe if called programmatically with a partial\n // state.\n skipped.push(t.abs);\n continue;\n }\n writeFileSync(t.abs, t.content, \"utf-8\");\n created.push(t.abs);\n }\n\n return { projectDir: absDir, created, skipped };\n}\n\ninterface ArtifactValidationSummary {\n ok: boolean;\n projectDir: string;\n present: { design: boolean; script: boolean; storyboard: boolean };\n missing: string[];\n parseErrors: Array<{ artifact: string; file: string; message: string }>;\n validation: { ok: boolean; warnings: string[]; errors: string[] };\n}\n\nfunction emitJson(\n loaded: LoadedArtifactProject,\n validation: { ok: boolean; warnings: string[]; errors: string[] },\n ok: boolean,\n): void {\n const payload: ArtifactValidationSummary = {\n ok,\n projectDir: loaded.projectDir,\n present: {\n design: loaded.design !== undefined,\n script: loaded.script !== undefined,\n storyboard: loaded.storyboard !== undefined,\n },\n missing: loaded.missing,\n parseErrors: loaded.parseErrors.map((e) => ({\n artifact: e.artifact,\n file: e.file,\n message: e.message,\n })),\n validation: {\n ok: validation.ok,\n warnings: validation.warnings,\n errors: validation.errors,\n },\n };\n console.log(JSON.stringify(payload, null, 2));\n}\n\nfunction emitHuman(\n loaded: LoadedArtifactProject,\n validation: { ok: boolean; warnings: string[]; errors: string[] },\n ok: boolean,\n): void {\n console.log(`Project: ${loaded.projectDir}`);\n\n // Header — show found / not-present status per artifact slot.\n for (const slot of [\"design\", \"script\", \"storyboard\"] as const) {\n const filename = ARTIFACT_FILENAMES[slot];\n const present = loaded[slot] !== undefined;\n const parseErr = loaded.parseErrors.find((e) => e.artifact === slot);\n if (parseErr) {\n console.log(` ${filename} FAIL (parse error)`);\n console.log(` ${formatRelativeFile(parseErr.file, loaded.projectDir)}: ${parseErr.message}`);\n } else if (present) {\n console.log(` ${filename} OK`);\n } else {\n console.log(` ${filename} [not present]`);\n }\n }\n\n // Cross-artifact validation block.\n console.log(\"\");\n console.log(\"Cross-artifact validation:\");\n if (validation.errors.length === 0 && validation.warnings.length === 0) {\n console.log(\" PASS (no warnings, no errors)\");\n } else {\n if (validation.errors.length > 0) {\n console.log(` Errors (${validation.errors.length}):`);\n for (const e of validation.errors) {\n console.log(` - ${e}`);\n }\n }\n if (validation.warnings.length > 0) {\n console.log(` Warnings (${validation.warnings.length}):`);\n for (const w of validation.warnings) {\n console.log(` - ${w}`);\n }\n }\n }\n\n // Footer — one-line verdict.\n console.log(\"\");\n console.log(ok ? \"Result: PASS\" : \"Result: FAIL\");\n}\n\n/**\n * Render an absolute artifact path relative to the project dir for tidy\n * human output, falling back to the absolute path if `relative` fails.\n */\nfunction formatRelativeFile(file: string, projectDir: string): string {\n try {\n const rel = relative(projectDir, file);\n return rel.length > 0 ? rel : file;\n } catch {\n return file;\n }\n}\n","import { readFileSync, existsSync, statSync } from \"node:fs\";\nimport { resolve, join } from \"node:path\";\nimport {\n parseDesign,\n parseScript,\n parseStoryboard,\n type DesignArtifact,\n type ScriptArtifact,\n type StoryboardArtifact,\n} from \"@a-company/atelier-schema\";\n\n/**\n * Pure filesystem walker for the front-of-pipeline artifact triplet\n * (DESIGN.md, SCRIPT.md, STORYBOARD.md). Sibling-of-`project.atelier`\n * convention per `docs/keynote/paradigm/atelier-front-of-pipeline-artifacts.md`\n * §2 — the three files sit at the root of a Project directory.\n *\n * This module is intentionally pure: no stdout/exit-code coupling. The\n * `atelier artifacts validate` CLI command (and any future agent / MCP\n * caller) consumes the structured result and decides what to print.\n *\n * Any subset of the three files may exist — the absent ones are\n * surfaced via `missing` (informational, NOT an error). The validator\n * (`validateArtifactSet`) runs on whatever was successfully parsed.\n */\n\n/** Filenames the walker looks for at the project-dir root. */\nexport const ARTIFACT_FILENAMES = {\n design: \"DESIGN.md\",\n script: \"SCRIPT.md\",\n storyboard: \"STORYBOARD.md\",\n} as const;\n\n/** Per-file parse failure surface. */\nexport interface ArtifactParseError {\n /** Which artifact triplet slot failed. */\n artifact: \"design\" | \"script\" | \"storyboard\";\n /** Absolute path to the file that failed. */\n file: string;\n /** Human-readable failure message. */\n message: string;\n}\n\n/** Structured result of loading the three artifacts from a project dir. */\nexport interface LoadedArtifactProject {\n /** Absolute project-dir path that was inspected. */\n projectDir: string;\n /** Successfully-parsed DESIGN.md, if present and valid. */\n design?: DesignArtifact;\n /** Successfully-parsed SCRIPT.md, if present and valid. */\n script?: ScriptArtifact;\n /** Successfully-parsed STORYBOARD.md, if present and valid. */\n storyboard?: StoryboardArtifact;\n /**\n * Names of artifact files that were not found on disk.\n * NOT an error — absent artifacts are valid per the spec\n * (only what is present is validated).\n */\n missing: string[];\n /** Per-file parse errors. Empty when all present artifacts parsed cleanly. */\n parseErrors: ArtifactParseError[];\n}\n\n/**\n * Walk a project directory for DESIGN.md / SCRIPT.md / STORYBOARD.md and\n * parse whichever are present.\n *\n * Throws (synchronously) only when `projectDir` itself does not exist or\n * is not a directory — per-file parse failures are captured into the\n * returned `parseErrors` array so callers can show every problem at\n * once instead of one at a time.\n */\nexport function loadArtifactsFromProject(projectDir: string): LoadedArtifactProject {\n const absDir = resolve(projectDir);\n if (!existsSync(absDir)) {\n throw new Error(`project directory does not exist: ${absDir}`);\n }\n const stat = statSync(absDir);\n if (!stat.isDirectory()) {\n throw new Error(`project path is not a directory: ${absDir}`);\n }\n\n const result: LoadedArtifactProject = {\n projectDir: absDir,\n missing: [],\n parseErrors: [],\n };\n\n for (const slot of [\"design\", \"script\", \"storyboard\"] as const) {\n const filename = ARTIFACT_FILENAMES[slot];\n const filePath = join(absDir, filename);\n if (!existsSync(filePath)) {\n result.missing.push(filename);\n continue;\n }\n let raw: string;\n try {\n raw = readFileSync(filePath, \"utf-8\");\n } catch (err) {\n result.parseErrors.push({\n artifact: slot,\n file: filePath,\n message: `read failed: ${err instanceof Error ? err.message : String(err)}`,\n });\n continue;\n }\n try {\n if (slot === \"design\") {\n result.design = parseDesign(raw);\n } else if (slot === \"script\") {\n result.script = parseScript(raw);\n } else {\n result.storyboard = parseStoryboard(raw);\n }\n } catch (err) {\n result.parseErrors.push({\n artifact: slot,\n file: filePath,\n message: formatParseError(err),\n });\n }\n }\n\n return result;\n}\n\n/**\n * Render a parse-time exception into a readable single-line message.\n * Flattens ZodError issues into `field: message` form so multi-issue\n * failures stay legible without a JSON dump.\n */\nfunction formatParseError(err: unknown): string {\n if (err instanceof Error) {\n // ZodError carries `issues`; we surface them inline for clarity.\n const issues = (err as Error & { issues?: Array<{ path: (string | number)[]; message: string }> }).issues;\n if (Array.isArray(issues) && issues.length > 0) {\n const formatted = issues\n .map((issue) => {\n const path = issue.path.length > 0 ? issue.path.join(\".\") : \"<root>\";\n return `${path}: ${issue.message}`;\n })\n .join(\"; \");\n return formatted;\n }\n return err.message;\n }\n return String(err);\n}\n","/**\n * Canonical empty templates for the three front-of-pipeline artifacts —\n * DESIGN.md / SCRIPT.md / STORYBOARD.md (spec §3 / §4 / §5).\n *\n * `atelier artifacts scaffold <project-dir>` writes these strings verbatim\n * into a creator's Project directory as the starting point for authoring.\n *\n * INVARIANT (`artifact-templates-self-validate`): the templates here MUST\n * parse cleanly via `parseDesign` / `parseScript` / `parseStoryboard` AND\n * `validateArtifactSet` over the triple must return `ok: true` (warnings\n * permitted, errors forbidden). The schema package enforces several\n * non-empty mins (voice.descriptors >= 3, palette/typography/logos/hook/cta\n * non-empty, storyboard.beats non-empty, mood non-empty, window.end_s >\n * start_s) so the \"placeholder\" fields here are populated with sensible\n * defaults the creator is expected to overwrite — not empty arrays.\n *\n * Cross-artifact contract: SCRIPT ships with a `hook-1` and `cta-1` beat\n * (the two non-optional narrated blocks); STORYBOARD ships matching\n * `## hook-1` and `## cta-1` beats so a freshly-scaffolded triple\n * cross-validates clean out of the box.\n *\n * Templates are TS string constants (not external .md files) so they ship\n * with the consuming code, tree-shake, and are version-locked to the\n * schemas they target.\n */\n\n/**\n * DESIGN.md — the brand register. Authored by Quill.\n *\n * Placeholders use real-looking values (e.g. `paper`/`ink`/`accent` palette\n * tokens, the same names the recipe schema would issue) rather than empty\n * arrays, so the file parses on first save. Markdown comments and `TODO:`\n * markers point the creator at the fields they're expected to revisit.\n */\nexport const DESIGN_TEMPLATE = `---\n# DESIGN.md — brand register for this Project.\n# Authored by Quill. Replace the TODO values with the real ones, then run\n# \\`atelier artifacts validate <project-dir>\\` to check the result.\n\naudience:\n # TODO: who is this for? Be specific — a single sentence's worth of person.\n primary: TODO describe primary audience\n # TODO: optional second-order audience (lurkers, decision-makers, etc.)\n # secondary: TODO secondary audience\n # TODO: where this ships. Closed-ish list; common values: linkedin, x, instagram, youtube-shorts, tiktok.\n platform: [TODO-platform]\n\nvoice:\n # TODO: 3 to 5 voice descriptors. Examples: dry, precise, mischievous, warm, clinical, contrarian.\n descriptors: [TODO-descriptor-1, TODO-descriptor-2, TODO-descriptor-3]\n # TODO: optional reference voices (writers, speakers, channels). Anchors the tone.\n # references: [TODO reference]\n\nvisual_register:\n palette:\n # TODO: replace these defaults with your real brand tokens.\n # Roles are closed: background | surface | primary | accent | text-on-dark | text-on-light.\n # Hex must be #RGB / #RRGGBB / #RRGGBBAA.\n - { token: paper, hex: \"#F4EFE6\", role: background }\n - { token: ink, hex: \"#1A1A1A\", role: text-on-light }\n - { token: accent, hex: \"#C9533C\", role: accent }\n typography:\n # TODO: real font tokens. Usage is closed: display | body | caption | mono.\n - { token: display, family: \"TODO-display-family\", weights: [400, 700], usage: display }\n - { token: body, family: \"TODO-body-family\", weights: [400, 500], usage: body }\n motion_register:\n # tempo: snappy | steady | calm | languid\n # easing_bias: linear | ease-out | spring\n # camera_bias: static | drift | snap (optional)\n tempo: steady\n easing_bias: ease-out\n camera_bias: static\n\nconstraints:\n # TODO: optional max duration in seconds (omit if no hard cap).\n # max_duration_seconds: 75\n # TODO: closed aspect-ratio set: 9:16 | 1:1 | 16:9 | 4:5.\n aspect_ratios: [\"9:16\"]\n # TODO: prose rules Iris must honor at composition time. Free-text strings.\n do_not:\n - TODO add a do-not rule\n\nbrand_references:\n logos:\n # TODO: at least one logo token. Role is closed: primary | mark | wordmark.\n - { token: mark-primary, role: mark }\n # TODO: optional social handles.\n # handles:\n # - { platform: x, value: \"@your-handle\" }\n # TODO: optional page-number convention used in carousel exports.\n # page_number_convention: \"n / N, bottom-right, body font, 60% opacity\"\n\n# variances: # only populate when this Project diverges from an attached recipe.\n# - { field: visual_register.palette.accent, value: \"#FF0000\", reason: \"campaign override\" }\n---\n\n# Voice notes\n\n<!--\n Free-form prose. Quill's notes on rhythm, signature phrases, taboos.\n This body is preserved verbatim — Iris does not parse it; it's for humans\n and for the next pass of Quill.\n-->\n\n> TODO: a paragraph or two on how the voice should *feel*. Punchy openings?\n> One idea per beat? Reserve the accent color for the most important word?\n`;\n\n/**\n * SCRIPT.md — the narrative. Authored by Quill, refined by Lux.\n *\n * Default mode is `narrated` (the four-block hook/story/proof/cta lineage\n * from Hyperframes) per the task spec. We ship one beat in every block so\n * the file parses on first save and a creator can see the shape; the hook\n * and cta blocks are schema-required to be non-empty, while story and proof\n * are technically optional but populated with TODO beats for completeness.\n *\n * `tts_voice` is intentionally omitted (it's optional and must be a valid\n * slot ref like \\`voice.host.primary\\` if present — leave the choice to the\n * creator).\n */\nexport const SCRIPT_TEMPLATE = `---\n# SCRIPT.md — narrative for this Project.\n# Authored by Quill, refined by Lux. Replace TODO copy with the real line.\n# Beat ids are kebab-case and must match the STORYBOARD beat ids.\n\nmode: narrated # narrated | carousel | text-only\ntarget_duration_s: 60 # creator intent; transcript binding can rebind beat windows post-VO\nlanguage: en-US\n# tts_voice: voice.host.primary # optional slot ref; resolved at TTS time\n---\n\n# Hook\n# At least one beat. The hook earns the rest of the watch.\n- id: hook-1\n copy: \"TODO: contrarian opener — one sentence that earns the next beat.\"\n intent: \"TODO why this opener works (one sentence).\"\n est_duration_s: 3.0\n bind_to_transcript: true\n\n# Story\n# Optional block; remove if not used. Establish receipts.\n- id: story-1\n copy: \"TODO: who you are / why this is credible — one sentence.\"\n intent: \"TODO establish receipts; promise specificity.\"\n est_duration_s: 3.0\n bind_to_transcript: true\n\n# Proof\n# Optional block; remove if not used. Concrete claims.\n- id: proof-1\n copy: \"TODO: the most defensible claim, stated plainly.\"\n intent: \"TODO lead with what you can defend hardest.\"\n est_duration_s: 3.0\n bind_to_transcript: true\n\n# CTA\n# At least one beat. Single token call-to-action; no verb pileup.\n- id: cta-1\n copy: \"TODO: link in bio. / one short ask.\"\n intent: \"TODO the single action you want from the viewer.\"\n est_duration_s: 2.0\n bind_to_transcript: true\n`;\n\n/**\n * STORYBOARD.md — the compositional spec, one beat per SCRIPT beat (plus\n * any storyboard-only \\`b-roll.*\\` beats). Authored by Lux. Direct input to\n * Iris.\n *\n * Cross-artifact invariant: every \\`## <id>\\` here must match a SCRIPT beat\n * id (or be prefixed \\`b-roll.\\`). We ship matching \\`hook-1\\` and \\`cta-1\\`\n * beats so a freshly-scaffolded triple cross-validates clean. Story and\n * proof beats are commented-out — the creator un-comments after Quill\n * writes the matching SCRIPT beats.\n *\n * Slot references use the typed \\`<kind>.<category>.<specifier>\\` form (per\n * TD-2026-05-26-229). Techniques are drawn from \\`TECHNIQUE_LIBRARY_V1\\`\n * so the file validates against the current shipped library.\n */\nexport const STORYBOARD_TEMPLATE = `---\n# STORYBOARD.md — composition beats for this Project.\n# Authored by Lux. One beat per SCRIPT beat (plus optional b-roll.* beats).\n# Iris walks this file beat by beat to mutate the live AtelierDocument.\n\ntechnique_library_version: 1 # bump on additive technique-library changes\naspect_ratio: \"9:16\" # closed: 9:16 | 1:1 | 16:9 | 4:5\n---\n\n## hook-1\n# TODO: matches SCRIPT beat \\`hook-1\\`. Tune mood / camera / slots to match the line.\nwindow: { start_s: 0.0, end_s: 3.0 }\nmood: [clinical] # adjectives; closed-ish vocab — tense, warm, clinical, mischievous, ...\ncamera: { kind: static } # kind: static | drift | push | pull | whip\nslots:\n # Typed slot refs: <kind>.<category>.<specifier>.\n # Kind is closed (image | video | audio | text | font | voice | logo).\n # \\`text.super.from-script\\` pulls copy from the matching SCRIPT beat.\n - text.super.from-script\ntechniques:\n # Must exist in the active technique library (technique_library_version).\n # v1 set: text.split-reveal, image.ken-burns.slow, super.weight-pulse-on-keyword, background.color-shift-to-token.\n - text.split-reveal\ntransition_out: { kind: cut } # kind: cut | dip-to-color | whip | crossfade\n# sfx:\n# - { slot: audio.sfx.transition.whoosh, gain_db: -6 }\n\n## cta-1\n# TODO: matches SCRIPT beat \\`cta-1\\`. Final beat — keep it clean.\nwindow: { start_s: 3.0, end_s: 5.0 }\nmood: [grounded]\ncamera: { kind: static }\nslots:\n - text.super.from-script\ntechniques:\n - text.split-reveal\ntransition_out: { kind: cut }\n\n# TODO: add \\`## story-1\\`, \\`## proof-1\\`, etc. once Quill writes the matching SCRIPT beats.\n# Storyboard-only b-roll beats are also allowed; prefix the id with \\`b-roll.\\`:\n#\n# ## b-roll.intro-monitor\n# window: { start_s: 1.0, end_s: 2.0 }\n# mood: [clinical]\n# camera: { kind: drift }\n# slots:\n# - image.b-roll.code-monitor\n# techniques:\n# - image.ken-burns.slow\n# transition_out: { kind: crossfade, duration_s: 0.2 }\n`;\n\n/**\n * Template registry, keyed by the slot name used by the CLI / `--only`\n * flag. Files written to disk as `<NAME>.md` (DESIGN.md / SCRIPT.md /\n * STORYBOARD.md per sibling-of-`project.atelier` convention).\n */\nexport const ARTIFACT_TEMPLATES = {\n design: { filename: \"DESIGN.md\", content: DESIGN_TEMPLATE },\n script: { filename: \"SCRIPT.md\", content: SCRIPT_TEMPLATE },\n storyboard: { filename: \"STORYBOARD.md\", content: STORYBOARD_TEMPLATE },\n} as const;\n\nexport type ArtifactSlot = keyof typeof ARTIFACT_TEMPLATES;\n\n/** All artifact slot names, in canonical write order. */\nexport const ARTIFACT_SLOTS: readonly ArtifactSlot[] = [\"design\", \"script\", \"storyboard\"];\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\n\n// Forward import — workspace.ts also imports from this module, but only the\n// writer (writeLearningMode), so there is no circular import at the value\n// level. The `findWorkspace` helper is used purely for read-side resolution.\nimport { findWorkspace } from \"./workspace.js\";\n\n/**\n * Atelier creator learning-mode I/O.\n *\n * `learning-mode.yaml` lives at `<project-dir>/.atelier/learning-mode.yaml`\n * and stores the creator's pre-declared autonomy posture (TD-2026-05-26-646):\n *\n * - `ambient` — the agent team captures corrections silently and surfaces\n * patterns when confident.\n * - `explicit` — nothing locks in without the creator's \"yes\"; high control,\n * more friction.\n *\n * Set once by `atelier init`. The system never auto-prompts to switch — the\n * creator changes mode by editing the file. Iris (and friends) read it at\n * session start to calibrate their behavior.\n *\n * Format is hand-rolled YAML (a handful of scalars) so we don't pull in a YAML\n * dep here; the schema is intentionally tiny and stable.\n */\n\nexport type LearningMode = \"ambient\" | \"explicit\";\n\nexport const LEARNING_MODES: readonly LearningMode[] = [\"ambient\", \"explicit\"] as const;\n\nexport const LEARNING_MODE_VERSION = \"1.0\" as const;\n\n/** Shape persisted to disk. */\nexport interface LearningModeFile {\n /** Schema version of this file. */\n version: string;\n /** Creator's chosen autonomy posture. */\n mode: LearningMode;\n /** ISO 8601 timestamp set by the writer. */\n chosen_at: string;\n}\n\n/** Absolute path to the learning-mode file for a project. */\nexport function learningModePath(projectDir: string): string {\n return join(resolve(projectDir), \".atelier\", \"learning-mode.yaml\");\n}\n\n/**\n * Write `<projectDir>/.atelier/learning-mode.yaml` with the given mode and a\n * fresh ISO timestamp. Creates `<projectDir>/.atelier/` if absent. Overwrites\n * any existing file — re-running init is supported and refreshes the\n * timestamp (mode is whatever the caller passes; the caller decides whether\n * to re-prompt). Returns the absolute path written.\n */\nexport function writeLearningMode(\n projectDir: string,\n mode: LearningMode,\n opts: { chosenAt?: Date } = {},\n): string {\n const absPath = learningModePath(projectDir);\n const dir = dirname(absPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n const ts = (opts.chosenAt ?? new Date()).toISOString();\n const content = renderLearningModeYaml({ version: LEARNING_MODE_VERSION, mode, chosen_at: ts });\n writeFileSync(absPath, content, \"utf-8\");\n return absPath;\n}\n\n/**\n * Read `<projectDir>/.atelier/learning-mode.yaml` if present. Returns `undefined`\n * when the file does not exist. Throws a clear Error when the file exists but\n * is malformed (missing required fields, unknown mode). Hand-rolled scalar\n * parser — no YAML dep — matched to whatever `writeLearningMode` emits.\n */\nexport function readLearningMode(projectDir: string): LearningModeFile | undefined {\n const absPath = learningModePath(projectDir);\n if (!existsSync(absPath)) return undefined;\n const raw = readFileSync(absPath, \"utf-8\");\n const fields: Record<string, string> = {};\n for (const line of raw.split(/\\r?\\n/)) {\n const trimmed = line.trim();\n if (trimmed.length === 0 || trimmed.startsWith(\"#\")) continue;\n const m = trimmed.match(/^([A-Za-z_][\\w-]*)\\s*:\\s*(.+?)\\s*$/);\n if (!m) continue;\n fields[m[1]] = stripYamlScalar(m[2]);\n }\n const mode = fields.mode;\n if (mode !== \"ambient\" && mode !== \"explicit\") {\n throw new Error(\n `learning-mode.yaml at ${absPath} has invalid mode \"${mode ?? \"<missing>\"}\" ` +\n `— expected one of ${LEARNING_MODES.join(\", \")}`,\n );\n }\n const chosen_at = fields.chosen_at;\n if (!chosen_at) {\n throw new Error(`learning-mode.yaml at ${absPath} is missing chosen_at`);\n }\n return {\n version: fields.version ?? LEARNING_MODE_VERSION,\n mode,\n chosen_at,\n };\n}\n\n/**\n * Render the canonical YAML body — a fixed-shape doc with header comments\n * pointing the creator at where to change the mode (TD-2026-05-26-646). Kept\n * here so the writer and any future reader/round-tripper agree on the shape.\n */\nexport function renderLearningModeYaml(file: LearningModeFile): string {\n return (\n `# Atelier creator learning mode.\\n` +\n `# Set by \\`atelier init\\`. Change anytime by editing this file — the system\\n` +\n `# never auto-prompts to switch (TD-2026-05-26-646).\\n` +\n `version: '${file.version}'\\n` +\n `mode: ${file.mode}\\n` +\n `chosen_at: '${file.chosen_at}'\\n`\n );\n}\n\n/**\n * Resolve the effective learning mode for `cwd`, with explicit provenance.\n *\n * Resolution order:\n * 1. If `cwd` is inside a workspace (per `findWorkspace`), read the\n * WORKSPACE-LEVEL `.atelier/learning-mode.yaml`. Source = \"workspace\".\n * 2. Otherwise, walk up from `cwd` looking for a project root (a dir\n * containing `.atelier/learning-mode.yaml`). The first hit wins.\n * Source = \"project\".\n * 3. Otherwise default to ambient. Source = \"default\" — note that the\n * default is for READ paths only (callers needing to record the\n * mode somewhere should call `writeLearningMode` at the appropriate\n * level — workspace if in a workspace, else project).\n *\n * Pure read; never writes. Returns `mode` + a `source` discriminator so\n * callers (e.g. agent calibration) can show the creator where the value\n * came from.\n */\nexport function resolveLearningMode(\n cwd: string,\n): { mode: LearningMode; source: \"workspace\" | \"project\" | \"default\" } {\n const start = resolve(cwd);\n\n // Workspace-level wins when present — the manifest is the explicit\n // creator-wide declaration.\n const ws = findWorkspace(start);\n if (ws) {\n const wsMode = readLearningMode(ws.workspaceDir);\n if (wsMode) {\n return { mode: wsMode.mode, source: \"workspace\" };\n }\n // Workspace exists but has no learning-mode.yaml — fall through to\n // the manifest's default_mode (createWorkspace always writes the\n // YAML, so this branch is rare and indicates a hand-edited dir).\n return { mode: ws.manifest.default_mode, source: \"workspace\" };\n }\n\n // Project-level walk: look for `<dir>/.atelier/learning-mode.yaml`.\n let dir = start;\n while (true) {\n if (existsSync(learningModePath(dir))) {\n const proj = readLearningMode(dir);\n if (proj) {\n return { mode: proj.mode, source: \"project\" };\n }\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n\n return { mode: \"ambient\", source: \"default\" };\n}\n\n/** Strip wrapping single/double quotes from a YAML scalar value. */\nfunction stripYamlScalar(value: string): string {\n if (value.length >= 2) {\n const first = value[0];\n const last = value[value.length - 1];\n if ((first === '\"' && last === '\"') || (first === \"'\" && last === \"'\")) {\n return value.slice(1, -1);\n }\n }\n return value;\n}\n","import {\n existsSync,\n mkdirSync,\n readFileSync,\n readdirSync,\n rmSync,\n statSync,\n writeFileSync,\n} from \"node:fs\";\nimport { basename, dirname, join, resolve } from \"node:path\";\nimport {\n WorkspaceManifestSchema,\n type WorkspaceManifest,\n} from \"@a-company/atelier-schema\";\nimport {\n writeLearningMode,\n type LearningMode,\n} from \"./learning-mode.js\";\nimport {\n augmentParadigmWithAtelier,\n detectParadigm,\n type ParadigmAugmentationResult,\n} from \"./paradigm-augment.js\";\n\n/**\n * Workspace IO — discovery, read, write, and creation for the\n * `workspace.atelier` manifest that sits at the root of an Atelier\n * workspace.\n *\n * A workspace is a layer above Project: a single directory holding multiple\n * content Projects with shared creator-level configuration. `workspace.atelier`\n * is the on-disk DISCOVERY SIGNAL — walking up from any cwd inside the\n * workspace, the first directory containing this file is the workspace root.\n *\n * The manifest itself is intentionally YAML (distinct from `project.atelier`\n * which is JSON) but shares the `.atelier` extension as a family marker.\n * The two never collide in a single directory.\n *\n * Hand-rolled YAML — no new dep — matched to whatever this writer emits,\n * mirroring the convention `learning-mode.ts` already follows.\n */\n\n/** On-disk filename of the workspace manifest. */\nexport const WORKSPACE_MANIFEST_FILENAME = \"workspace.atelier\";\n\n/**\n * On-disk filename of the workspace's \"active project\" pointer.\n * Lives at `<workspaceDir>/.atelier/active.yaml`. Both the studio UI and\n * the agent team read + write this file as the single source of truth for\n * which Project a workspace-scoped operation should target (no in-memory\n * state to drift from — see #active-project-file-source-of-truth).\n */\nexport const ACTIVE_PROJECT_FILENAME = \"active.yaml\";\n\n/** Schema version emitted by `setActiveProject`. */\nexport const ACTIVE_PROJECT_VERSION = \"1.0\" as const;\n\n/** Absolute path to the active-project pointer for a workspace. */\nexport function activeProjectPath(workspaceDir: string): string {\n return join(resolve(workspaceDir), \".atelier\", ACTIVE_PROJECT_FILENAME);\n}\n\n/** Shape persisted to disk inside `<workspaceDir>/.atelier/active.yaml`. */\nexport interface ActiveProjectFile {\n /** Project subdir basename (validated against `isValidProjectName`). */\n name: string;\n /** ISO 8601 timestamp the pointer was last written. */\n set_at: string;\n}\n\n/**\n * True iff `name` is a safe single-segment project name. We deliberately\n * forbid anything that could escape the workspace dir at any later join:\n *\n * - empty / whitespace-only\n * - path separators (`/`, `\\`)\n * - any `.` segment (covers `..`, `.hidden`, `.` itself)\n * - control chars / leading or trailing whitespace\n *\n * Used to gate `setActiveProject` and the create-project endpoint BEFORE\n * any filesystem work — `isSafePath` is the right guard for relative-to-cwd\n * file paths, but a single project NAME has different rules (no slashes,\n * not a dotfile).\n */\nexport function isValidProjectName(name: unknown): name is string {\n if (typeof name !== \"string\") return false;\n if (name.length === 0) return false;\n if (name !== name.trim()) return false;\n // No path separators in either direction.\n if (name.includes(\"/\") || name.includes(\"\\\\\")) return false;\n // No `.` segment / no leading dot (dotdirs like `.atelier` are reserved).\n if (name.startsWith(\".\")) return false;\n // No NUL / control chars.\n // eslint-disable-next-line no-control-regex\n if (/[\u0000-\u001f]/.test(name)) return false;\n return true;\n}\n\n/** Schema version emitted by `createWorkspace`. */\nexport const WORKSPACE_MANIFEST_VERSION = \"1.0\" as const;\n\nconst MIND_MAP_README = `# Workspace Mind-Map\n\nShared across every Project in this workspace — clips, memes, graphics,\naudio — that the agent team queries when binding typed slots in recipes\nand storyboards (TD-2026-05-26-229).\n\nPer-project mind-maps still live at \\`<project>/.atelier/mind-map/\\` and\nare queried alongside this workspace-level pool. Organize however you\nwant; the \\`atelier_mind_map_query\\` tool walks recursively and matches\nby file extension against slot \\`kind\\` values.\n\nNothing in this directory leaves your machine (TD-2026-05-26-271).\n`;\n\n/** Options for `createWorkspace`. */\nexport interface CreateWorkspaceOptions {\n /** Display name. Defaults to `basename(workspaceDir)` when omitted. */\n name?: string;\n /** Inherited by new projects created inside the workspace via `atelier init`. */\n mode: LearningMode;\n /** Optional free-text description. */\n description?: string;\n /**\n * Allow overwriting an existing `workspace.atelier`. Default false —\n * refuse-to-overwrite mirrors `recipe new` and `artifacts scaffold`.\n */\n force?: boolean;\n /** Test override: deterministic created-at timestamp. */\n createdAt?: Date;\n /**\n * Skip the Paradigm + Atelier symbiosis pass even when the target\n * directory is Paradigm-initialized. Default false. Exposed as\n * `--no-paradigm-integration` on the CLI surface for creators who want\n * a pure-Atelier workspace inside an existing Paradigm dir.\n */\n skipParadigmIntegration?: boolean;\n}\n\n/**\n * Result returned by `createWorkspace`. The bare manifest is the primary\n * payload (matches every pre-augmentation caller). When the target dir\n * is Paradigm-initialized AND augmentation isn't skipped, the four-step\n * weave is run and its report is attached as `paradigmAugmentation`.\n */\nexport interface CreateWorkspaceResult {\n manifest: WorkspaceManifest;\n paradigmAugmentation?: ParadigmAugmentationResult;\n}\n\n/**\n * Walk up from `cwd` looking for `workspace.atelier`. Stops at the\n * filesystem root. Returns null when no manifest is found. Uses\n * `readWorkspace` to parse the discovered file — a manifest that fails\n * Zod validation propagates as a thrown Error rather than silently\n * pretending no workspace exists (matches the recipe-loader posture).\n */\nexport function findWorkspace(\n cwd: string,\n): { workspaceDir: string; manifest: WorkspaceManifest } | null {\n let dir = resolve(cwd);\n // dirname(root) === root on every platform — use that as the loop guard\n // so we never infinite-loop on a missing manifest.\n while (true) {\n const candidate = join(dir, WORKSPACE_MANIFEST_FILENAME);\n if (existsSync(candidate)) {\n const manifest = readWorkspace(dir);\n return { workspaceDir: dir, manifest };\n }\n const parent = dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\n/**\n * Read + parse + Zod-validate `<workspaceDir>/workspace.atelier`. Throws\n * if the file is missing, syntactically broken, or fails validation —\n * the workspace is a load-bearing surface and a malformed manifest must\n * surface, not silently degrade.\n */\nexport function readWorkspace(workspaceDir: string): WorkspaceManifest {\n const absPath = join(resolve(workspaceDir), WORKSPACE_MANIFEST_FILENAME);\n if (!existsSync(absPath)) {\n throw new Error(`workspace manifest not found: ${absPath}`);\n }\n const raw = readFileSync(absPath, \"utf-8\");\n const parsed = parseWorkspaceYaml(raw);\n const result = WorkspaceManifestSchema.safeParse(parsed);\n if (!result.success) {\n const issues = result.error.issues\n .map((issue) => {\n const path = issue.path.length > 0 ? issue.path.join(\".\") : \"<root>\";\n return `${path}: ${issue.message}`;\n })\n .join(\"; \");\n throw new Error(`workspace.atelier at ${absPath} failed validation — ${issues}`);\n }\n return result.data;\n}\n\n/**\n * Serialize `manifest` and write it to `<workspaceDir>/workspace.atelier`.\n * Creates the directory if absent. Overwrites unconditionally — callers\n * that need refuse-to-overwrite semantics gate on `existsSync` themselves\n * (see `createWorkspace`).\n */\nexport function writeWorkspace(\n workspaceDir: string,\n manifest: WorkspaceManifest,\n): string {\n const absDir = resolve(workspaceDir);\n if (!existsSync(absDir)) {\n mkdirSync(absDir, { recursive: true });\n }\n const absPath = join(absDir, WORKSPACE_MANIFEST_FILENAME);\n writeFileSync(absPath, renderWorkspaceYaml(manifest), \"utf-8\");\n return absPath;\n}\n\n/**\n * Create a fresh workspace at `workspaceDir`:\n * - mkdir-recursive the dir if absent\n * - create `.atelier/` subdir\n * - write workspace-level `learning-mode.yaml` (creator's mode, shared\n * across all projects unless a per-project file overrides)\n * - create `.atelier/mind-map/` with a creator-facing README\n * - create `.atelier/recipes/` (empty placeholder for shared recipes;\n * resolution wiring is a follow-up)\n * - write the manifest itself\n *\n * Refuses-to-overwrite an existing `workspace.atelier` unless\n * `{ force: true }` is passed.\n */\nexport function createWorkspace(\n workspaceDir: string,\n opts: CreateWorkspaceOptions,\n): CreateWorkspaceResult {\n const absDir = resolve(workspaceDir);\n const manifestPath = join(absDir, WORKSPACE_MANIFEST_FILENAME);\n if (existsSync(manifestPath) && !opts.force) {\n throw new Error(\n `refusing to overwrite existing workspace at ${manifestPath} ` +\n `(pass force:true to replace)`,\n );\n }\n\n if (!existsSync(absDir)) {\n mkdirSync(absDir, { recursive: true });\n } else {\n const st = statSync(absDir);\n if (!st.isDirectory()) {\n throw new Error(`workspace path is not a directory: ${absDir}`);\n }\n }\n\n // .atelier/ tree.\n const atelierDir = join(absDir, \".atelier\");\n if (!existsSync(atelierDir)) mkdirSync(atelierDir, { recursive: true });\n\n // Workspace-level learning mode (creator-wide).\n writeLearningMode(absDir, opts.mode);\n\n // Mind-map dir + README (preserve creator edits via existsSync-guard,\n // matching `atelier init`'s mind-map handling).\n const mindMapDir = join(atelierDir, \"mind-map\");\n if (!existsSync(mindMapDir)) mkdirSync(mindMapDir, { recursive: true });\n const readmePath = join(mindMapDir, \"README.md\");\n if (!existsSync(readmePath)) writeFileSync(readmePath, MIND_MAP_README, \"utf-8\");\n\n // Recipes dir — empty placeholder. Resolution wiring to follow.\n const recipesDir = join(atelierDir, \"recipes\");\n if (!existsSync(recipesDir)) mkdirSync(recipesDir, { recursive: true });\n\n const manifest: WorkspaceManifest = {\n version: WORKSPACE_MANIFEST_VERSION,\n name: opts.name ?? basename(absDir),\n created: (opts.createdAt ?? new Date()).toISOString(),\n default_mode: opts.mode,\n ...(opts.description !== undefined ? { description: opts.description } : {}),\n };\n writeWorkspace(absDir, manifest);\n\n // ─── Paradigm + Atelier symbiosis ──────────────────────────────────\n // When the target dir is a Paradigm-initialized directory (`paradigm\n // shift` left both CLAUDE.md and .paradigm/config.yaml), weave the\n // Atelier-specific augmentations into the existing scaffold:\n // - CLAUDE.md: sentinel-bracketed Atelier section (idempotent)\n // - .claude/agents/: drop quill/lux/iris wrappers (canonical sync)\n // - .mcp.json: merge `atelier` server entry (preserves paradigm + others)\n // - .paradigm/personas/index.yaml: merge the three persona entries\n //\n // The augmentation is opt-OUT via `skipParadigmIntegration`; the default\n // posture is \"weave when present\" so a fresh `paradigm shift` →\n // `atelier studio --new` produces a fully-symbiotic workspace without\n // an extra flag. See lib/paradigm-augment.ts for the per-step contract;\n // every step is idempotent and never touches files outside its scope.\n let paradigmAugmentation: ParadigmAugmentationResult | undefined;\n if (!opts.skipParadigmIntegration) {\n const det = detectParadigm(absDir);\n if (det.present) {\n paradigmAugmentation = augmentParadigmWithAtelier(absDir, {\n workspaceName: manifest.name,\n learningMode: opts.mode,\n atelierMcpCwd: absDir,\n });\n }\n }\n\n return { manifest, paradigmAugmentation };\n}\n\n/**\n * True when `dir` looks like an Atelier Project. A Project is identified\n * by EITHER:\n * - a top-level `project.atelier` manifest (legacy / explicitly-composed\n * Projects, including any seeded by `atelier init` and the compose-*\n * helpers), OR\n * - an `.atelier/` metadata subdir (post-pivot \"lazy\" Projects created\n * by the workspace `+ new` flow, which no longer auto-scaffold the\n * manifest — the directory becomes a real Project once its first\n * `.atelier` doc is composed).\n *\n * Both markers are tested so that the workspace sidebar lists freshly-\n * created (manifestless) Projects alongside fully-scaffolded ones without\n * forcing a `project.atelier` write at create-time.\n *\n * Cheap predicate (two existsSync calls). Symlinks are not followed —\n * `.atelier/` must be a real directory.\n */\nexport function hasProjectMarker(dir: string): boolean {\n const abs = resolve(dir);\n if (existsSync(join(abs, \"project.atelier\"))) return true;\n const dotAtelier = join(abs, \".atelier\");\n if (!existsSync(dotAtelier)) return false;\n try {\n return statSync(dotAtelier).isDirectory();\n } catch {\n return false;\n }\n}\n\n/**\n * Scan `workspaceDir/*` for subdirs that look like Atelier Projects (see\n * `hasProjectMarker` — either a top-level `project.atelier` OR a child\n * `.atelier/` metadata subdir counts). Returns the basenames sorted\n * lexicographically. One level deep only — nested projects-within-projects\n * are out of scope. Used to refresh the manifest's advisory `projects`\n * field, or by `studio` to enumerate openable projects at runtime.\n */\nexport function listProjects(workspaceDir: string): string[] {\n const absDir = resolve(workspaceDir);\n let entries: string[];\n try {\n entries = readdirSync(absDir);\n } catch {\n return [];\n }\n const out: string[] = [];\n for (const entry of entries) {\n if (entry.startsWith(\".\")) continue; // skip dotdirs like .atelier\n const full = join(absDir, entry);\n let st;\n try {\n st = statSync(full);\n } catch {\n continue;\n }\n if (!st.isDirectory()) continue;\n if (hasProjectMarker(full)) {\n out.push(entry);\n }\n }\n return out.sort();\n}\n\n/**\n * True when `dir` is an Atelier Project — either a `project.atelier`\n * manifest at the root OR an `.atelier/` metadata subdir is present.\n * Cheap predicate used by the studio context detector to tell project-\n * mode from workspace-mode. Delegates to `hasProjectMarker` so the\n * discovery rule stays single-sourced.\n */\nexport function isProjectDir(dir: string): boolean {\n return hasProjectMarker(dir);\n}\n\n/**\n * Render the canonical YAML body — hand-rolled scalar + list form so the\n * writer and the inline reader stay in agreement without pulling in a\n * YAML dep. Header comment cites the manifest's purpose.\n */\nexport function renderWorkspaceYaml(manifest: WorkspaceManifest): string {\n const lines: string[] = [];\n lines.push(`# Atelier workspace manifest.`);\n lines.push(`# Created by \\`atelier studio --new\\`. Edit fields directly when needed.`);\n lines.push(`# A workspace holds multiple Projects with shared learning mode + mind-map.`);\n lines.push(`version: '${manifest.version}'`);\n lines.push(`name: ${yamlScalar(manifest.name)}`);\n lines.push(`created: '${manifest.created}'`);\n lines.push(`default_mode: ${manifest.default_mode}`);\n if (manifest.description !== undefined) {\n lines.push(`description: ${yamlScalar(manifest.description)}`);\n }\n if (manifest.projects && manifest.projects.length > 0) {\n lines.push(`projects:`);\n for (const p of manifest.projects) {\n lines.push(` - ${yamlScalar(p)}`);\n }\n } else if (manifest.projects !== undefined) {\n // Preserve an explicitly-empty list for round-trip clarity.\n lines.push(`projects: []`);\n }\n return lines.join(\"\\n\") + \"\\n\";\n}\n\n/**\n * Parse the hand-rolled YAML emitted by `renderWorkspaceYaml`. Supports\n * the field subset the manifest actually uses: scalars + a one-level\n * sequence of strings for `projects`. Comment lines and blanks tolerated.\n * Returns a plain object; Zod handles shape validation downstream.\n */\nfunction parseWorkspaceYaml(raw: string): Record<string, unknown> {\n const fields: Record<string, unknown> = {};\n let listKey: string | null = null;\n let listItems: string[] = [];\n\n const flushList = (): void => {\n if (listKey !== null) {\n fields[listKey] = listItems;\n listKey = null;\n listItems = [];\n }\n };\n\n for (const rawLine of raw.split(/\\r?\\n/)) {\n // Comment-stripped, but preserve indentation to detect list items.\n const lineNoComment = stripTrailingComment(rawLine);\n if (lineNoComment.trim().length === 0) continue;\n\n // List item: \" - value\" while a list key is open.\n if (listKey !== null) {\n const itemMatch = lineNoComment.match(/^\\s+-\\s+(.+?)\\s*$/);\n if (itemMatch) {\n listItems.push(stripYamlScalar(itemMatch[1]));\n continue;\n }\n // Anything else closes the list.\n flushList();\n }\n\n const kv = lineNoComment.match(/^([A-Za-z_][\\w-]*)\\s*:\\s*(.*)$/);\n if (!kv) continue;\n const key = kv[1];\n const value = kv[2].trim();\n if (value.length === 0) {\n // Empty value → open list (only one we support).\n listKey = key;\n listItems = [];\n continue;\n }\n if (value === \"[]\") {\n fields[key] = [];\n continue;\n }\n fields[key] = stripYamlScalar(value);\n }\n flushList();\n return fields;\n}\n\n/** Strip wrapping single/double quotes from a YAML scalar value. */\nfunction stripYamlScalar(value: string): string {\n if (value.length >= 2) {\n const first = value[0];\n const last = value[value.length - 1];\n if ((first === '\"' && last === '\"') || (first === \"'\" && last === \"'\")) {\n return value.slice(1, -1);\n }\n }\n return value;\n}\n\n/**\n * Quote a scalar value when it contains characters that would confuse the\n * hand-rolled reader (colons, leading whitespace, brackets, or starting\n * with a quote char). Plain alphanumeric/path-like strings stay unquoted.\n */\nfunction yamlScalar(value: string): string {\n if (value.length === 0) return \"''\";\n if (/^[\\w./\\- ]+$/.test(value) && !/^\\s/.test(value) && !/\\s$/.test(value)) {\n return value;\n }\n // Use single quotes and escape any single-quote in the value via doubling.\n return `'${value.replace(/'/g, \"''\")}'`;\n}\n\n/** Strip an unquoted `# ...` trailing comment from a line. */\nfunction stripTrailingComment(line: string): string {\n let inSingle = false;\n let inDouble = false;\n for (let i = 0; i < line.length; i++) {\n const c = line[i];\n if (c === \"'\" && !inDouble) inSingle = !inSingle;\n else if (c === '\"' && !inSingle) inDouble = !inDouble;\n else if (c === \"#\" && !inSingle && !inDouble) return line.slice(0, i);\n }\n return line;\n}\n\n// ─── Active-project pointer ────────────────────────────────────────────\n//\n// `<workspaceDir>/.atelier/active.yaml` is the single source of truth for\n// \"which Project should a workspace-scoped operation target\". Both the\n// studio UI (when the creator clicks a project) and the agent team (when\n// told to focus on a project, OR at session start to recover state) read\n// and write the same file — no in-memory state to drift from. The shape\n// is intentionally tiny + stable; hand-rolled YAML matched to the writer,\n// mirroring the learning-mode.ts convention.\n\n/**\n * Read `<workspaceDir>/.atelier/active.yaml` if present.\n *\n * Returns `null` when the file is missing OR malformed (missing required\n * fields, invalid project name). The on-disk file is purely a hint — a\n * stale or corrupted pointer should NOT throw, because the studio + agent\n * team treat \"no active project\" and \"broken active project\" identically\n * (both fall through to \"ask the creator\"). Callers wanting to surface\n * stale-ness to the UI should additionally verify the named project still\n * exists on disk (see the `/api/workspace/active` endpoint, which marks\n * `stale: true` for the still-present-but-invalid case).\n */\nexport function readActiveProject(workspaceDir: string): ActiveProjectFile | null {\n const absPath = activeProjectPath(workspaceDir);\n if (!existsSync(absPath)) return null;\n let raw: string;\n try {\n raw = readFileSync(absPath, \"utf-8\");\n } catch {\n return null;\n }\n const fields: Record<string, string> = {};\n for (const line of raw.split(/\\r?\\n/)) {\n // Re-use the same trailing-comment + scalar-quote handling as the\n // workspace-manifest reader so a hand-edited file with inline\n // comments doesn't trip us up.\n const lineNoComment = stripTrailingComment(line);\n const trimmed = lineNoComment.trim();\n if (trimmed.length === 0) continue;\n const m = trimmed.match(/^([A-Za-z_][\\w-]*)\\s*:\\s*(.+?)\\s*$/);\n if (!m) continue;\n fields[m[1]] = stripYamlScalar(m[2]);\n }\n const name = fields.name;\n const set_at = fields.set_at;\n if (!name || !set_at) return null;\n // Note: name is NOT re-validated against isValidProjectName here —\n // hand-edited workspaces may legitimately contain a name that we no\n // longer write but that still names a directory on disk. The HTTP\n // endpoint marks such entries `stale: true` via existsSync, which is\n // the canonical stale-detection path. setActiveProject DOES validate\n // because that's a fresh write.\n return { name, set_at };\n}\n\n/**\n * Write `<workspaceDir>/.atelier/active.yaml` pointing at `name`.\n *\n * Validates that `<workspaceDir>/<name>` looks like a Project before\n * writing — same marker as discovery (`hasProjectMarker`: either a\n * `project.atelier` manifest OR an `.atelier/` metadata subdir). Refusing\n * to set the pointer at a non-existent project keeps the file's\n * invariant (\"if present, points at a real project at write time\") and\n * prevents a UI mistake from leaving the workspace in a mysterious\n * \"stale on first load\" state. A typo'd name AND a renamed project\n * both surface as the same error.\n *\n * Post-pivot: a freshly-created lazy Project (workspace `+ new`) has no\n * manifest yet, only an `.atelier/` subdir; `hasProjectMarker` keeps\n * `activate: true` working for that surface without forcing a stub\n * manifest at create time.\n *\n * `name` is also gated through `isValidProjectName` BEFORE any join — a\n * crafted name like `../escape` or `foo/bar` is rejected before it can\n * resolve to anything on disk.\n *\n * Hand-rolled YAML, matched-pair with `readActiveProject` above.\n */\nexport function setActiveProject(\n workspaceDir: string,\n name: string,\n opts: { setAt?: Date } = {},\n): ActiveProjectFile {\n if (!isValidProjectName(name)) {\n throw new Error(\n `invalid project name \"${name}\" — must be a single-segment basename ` +\n `(no path separators, no leading dot, no control chars)`,\n );\n }\n const absWs = resolve(workspaceDir);\n const projectDir = join(absWs, name);\n if (!hasProjectMarker(projectDir)) {\n throw new Error(\n `cannot set active project \"${name}\": ${projectDir} does not exist`,\n );\n }\n const absPath = activeProjectPath(absWs);\n const dir = dirname(absPath);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n const set_at = (opts.setAt ?? new Date()).toISOString();\n const file: ActiveProjectFile = { name, set_at };\n writeFileSync(absPath, renderActiveProjectYaml(file), \"utf-8\");\n return file;\n}\n\n/**\n * Delete `<workspaceDir>/.atelier/active.yaml` if present. No-op when the\n * file is already absent. Returns true iff a file was actually removed.\n *\n * The DELETE /api/workspace/active endpoint funnels through here; the\n * file being missing is the natural \"no active project\" state.\n */\nexport function clearActiveProject(workspaceDir: string): boolean {\n const absPath = activeProjectPath(workspaceDir);\n if (!existsSync(absPath)) return false;\n try {\n rmSync(absPath, { force: true });\n return true;\n } catch {\n return false;\n }\n}\n\n/** Render the canonical YAML body for `active.yaml`. Header comment cites\n * the two-writer reality (UI + agents) so a creator opening the file by\n * hand understands what they're looking at. */\nexport function renderActiveProjectYaml(file: ActiveProjectFile): string {\n return (\n `# Atelier workspace active project.\\n` +\n `# Set by the studio UI when a creator clicks a project, or by an agent\\n` +\n `# when explicitly told to focus on a project. The agent team reads this\\n` +\n `# per-operation to know which project to act on.\\n` +\n `version: '${ACTIVE_PROJECT_VERSION}'\\n` +\n `name: ${yamlScalar(file.name)}\\n` +\n `set_at: '${file.set_at}'\\n`\n );\n}\n","/**\n * Paradigm + Atelier workspace symbiosis.\n *\n * When `atelier studio --new` (or `createWorkspace`) runs inside a directory\n * that has been Paradigm-initialized (`paradigm shift`), Atelier WEAVES\n * augmentations into the existing scaffold WITHOUT replacing anything\n * Paradigm owns. Four idempotent operations:\n *\n * 1. CLAUDE.md — sentinel-bracketed Atelier section appended/replaced.\n * Anything outside the sentinels is preserved byte-for-byte. Re-running\n * replaces the section between sentinels; a hand-edited Paradigm\n * preamble is never touched.\n * 2. .claude/agents/ — drop atelier-{quill,lux,iris}.md from the snapshot\n * in `atelier-agent-templates.ts`. Canonical sync — overwrites OK\n * (TD-2026-05-26-210 \"shared personas\"). The directory is created if\n * absent; Paradigm's shift command doesn't ship an agents/ subdir.\n * 3. .mcp.json — add `atelier` server entry alongside any existing\n * `paradigm` (and other) servers. Re-running updates `cwd` (workspace\n * may have moved) but preserves every other server entry and the\n * `permissions` block verbatim.\n * 4. .paradigm/personas/index.yaml — add Quill / Lux / Iris persona index\n * entries. The personas index has a richer multi-key shape than\n * learning-mode.yaml (top-level `version`, `generated`, `personas`,\n * `chains`, `gate_coverage`, `route_coverage`, `uncovered_routes`),\n * so we use block-replacement on raw text: locate the `personas:`\n * key, splice in a freshly-rendered map, leave every other top-level\n * key untouched. Pre-existing persona entries are preserved.\n *\n * Constraints (TD-2026-05-26-275 / TD-540 / TD-271 / TD-229):\n * - Never modify files outside the augmentation contract.\n * - All augmentations idempotent (replay-safe).\n * - No new npm dependencies (hand-rolled YAML + JSON.parse/stringify).\n * - Workspace augmentations are LOCAL-ONLY artifacts; nothing leaves the\n * creator's machine.\n */\n\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { ATELIER_AGENT_TEMPLATES } from \"./atelier-agent-templates.js\";\n\n// ─── Detection ─────────────────────────────────────────────────────────\n\nexport interface ParadigmDetection {\n /**\n * True iff BOTH `<dir>/.paradigm/config.yaml` AND `<dir>/CLAUDE.md`\n * exist on disk. These are the two on-disk markers `paradigm shift`\n * always writes; their joint presence is the canonical \"this directory\n * has been Paradigm-initialized\" signal.\n */\n present: boolean;\n /** `<dir>/CLAUDE.md` — always set, file may or may not exist. */\n claudeMdPath: string;\n /** `<dir>/.mcp.json` — always set. */\n mcpJsonPath: string;\n /** `<dir>/.paradigm/config.yaml` — always set. */\n configYamlPath: string;\n /** `<dir>/.paradigm/personas/index.yaml` — always set. */\n personasIndexPath: string;\n /** `<dir>/.claude/agents` — always set (may not exist yet). */\n agentsDirPath: string;\n}\n\n/**\n * Inspect `dir` for the markers `paradigm shift` leaves behind. Path\n * fields are always populated (they're where files would live, exist or\n * not) so callers can wire augmenters without re-resolving paths.\n *\n * `present` is the load-bearing field — only when both `.paradigm/config.yaml`\n * and `CLAUDE.md` exist do we treat the dir as Paradigm-initialized.\n */\nexport function detectParadigm(dir: string): ParadigmDetection {\n const absDir = resolve(dir);\n const claudeMdPath = join(absDir, \"CLAUDE.md\");\n const configYamlPath = join(absDir, \".paradigm\", \"config.yaml\");\n const present = existsSync(claudeMdPath) && existsSync(configYamlPath);\n return {\n present,\n claudeMdPath,\n mcpJsonPath: join(absDir, \".mcp.json\"),\n configYamlPath,\n personasIndexPath: join(absDir, \".paradigm\", \"personas\", \"index.yaml\"),\n agentsDirPath: join(absDir, \".claude\", \"agents\"),\n };\n}\n\n// ─── CLAUDE.md sentinel-bracketed section ──────────────────────────────\n\nexport const ATELIER_CLAUDE_MD_SECTION_BEGIN =\n \"<!-- ATELIER:BEGIN — managed by atelier studio --new, do not edit by hand -->\";\nexport const ATELIER_CLAUDE_MD_SECTION_END = \"<!-- ATELIER:END -->\";\n\n/**\n * Render the Atelier-owned section of a workspace CLAUDE.md. Tells Claude\n * what's special about this directory: which agents are available, where\n * `workspace.atelier` and `.atelier/active.yaml` live, what lore goes\n * here vs. the engine repo, and which design decisions ground the design.\n *\n * Citations (TD-2026-05-26-275, TD-540, TD-271, TD-229) anchor the\n * local-first, recipe-pure, no-asset-exfiltration constraints — agents\n * read this at session start to calibrate their refusals.\n */\nexport function buildAtelierClaudeMdSection(opts: {\n workspaceName: string;\n learningMode: \"ambient\" | \"explicit\";\n}): string {\n const { workspaceName, learningMode } = opts;\n const lines: string[] = [];\n lines.push(ATELIER_CLAUDE_MD_SECTION_BEGIN);\n lines.push(\"\");\n lines.push(\"## Atelier Creator Workspace\");\n lines.push(\"\");\n lines.push(\n `This directory is an **Atelier creator workspace** named \\`${workspaceName}\\` ` +\n `holding one or more content Projects. It sits ON TOP of the Paradigm scaffold ` +\n `above — Paradigm owns the dev-time agents, lore, and protocols; Atelier owns ` +\n `the creator-facing surface (workspace + project tree, recipes, mind-map).`,\n );\n lines.push(\"\");\n lines.push(\"### Agents available here\");\n lines.push(\"\");\n lines.push(\n \"Three Atelier-specific subagents are dropped into `.claude/agents/` \" +\n \"and addressable by name:\",\n );\n lines.push(\"\");\n lines.push(\n \"- `@atelier-quill` — Showrunner. Owns narrative direction; authors \" +\n \"DESIGN.md and SCRIPT.md.\",\n );\n lines.push(\n \"- `@atelier-lux` — Cinematographer. Owns visual direction; authors \" +\n \"STORYBOARD.md and drives layer/composition mutations.\",\n );\n lines.push(\n \"- `@atelier-iris` — Composer. Owns the build; drives the live MCP \" +\n \"loop, layer composition, and the audio pipeline.\",\n );\n lines.push(\"\");\n lines.push(\n \"Their canonical definitions live in `.paradigm/personas/{quill,lux,iris}/` \" +\n \"(ships with Atelier; TD-2026-05-26-275 / TD-540). The `.claude/agents/*.md` \" +\n \"files are thin Claude Code wrappers that stay in lockstep with those.\",\n );\n lines.push(\"\");\n lines.push(\"### Workspace shape\");\n lines.push(\"\");\n lines.push(\n \"- `workspace.atelier` (YAML) at the root is the workspace manifest — \" +\n \"name, default learning mode, advisory project list.\",\n );\n lines.push(\n \"- `.atelier/active.yaml` is the single source of truth for *which Project* \" +\n \"a workspace-scoped operation should target. Both the studio UI and the \" +\n \"agent team read + write it; an agent invoked from inside a workspace \" +\n \"MUST read this file before touching any artifact.\",\n );\n lines.push(\n \"- `.atelier/learning-mode.yaml` records the creator's autonomy posture \" +\n `(currently: **${learningMode}**; TD-2026-05-26-646). Agents read it at ` +\n \"session start; never auto-prompt to switch.\",\n );\n lines.push(\n \"- `.atelier/mind-map/` is the creator's local asset pool — clips, \" +\n \"graphics, audio. Local-only (TD-2026-05-26-271). Queried via \" +\n \"`atelier_mind_map_query` when binding typed slots in recipes/storyboards.\",\n );\n lines.push(\n \"- `.atelier/recipes/` holds workspace-shared style-as-preset recipes. \" +\n \"Recipes are pure structure with asset-typed slots — assets bind at \" +\n \"install on the recipient's machine (TD-2026-05-26-229). Never bundle \" +\n \"asset bytes into a recipe.\",\n );\n lines.push(\"\");\n lines.push(\"### Lore convention\");\n lines.push(\"\");\n lines.push(\n \"Content lore — creator decisions about voice, palette, rhythm, what \" +\n \"landed and what didn't — goes here in this workspace's `.paradigm/lore/`. \" +\n \"It is SEPARATE from engine-repo lore (which tracks Atelier development). \" +\n \"Don't cross-contaminate; an entry about a specific Project belongs in this \" +\n \"workspace's lore, not in the Atelier engine repo.\",\n );\n lines.push(\"\");\n lines.push(\"### Hard constraints (do not relitigate)\");\n lines.push(\"\");\n lines.push(\n \"- Nothing in this workspace's notebook, mind-map, document, or iteration \" +\n \"history uploads anywhere. Local-first is the floor (TD-2026-05-26-271, TD-540).\",\n );\n lines.push(\n \"- Recipes are inert structure only; no scripts, no executables, no asset \" +\n \"bytes (TD-2026-05-26-229).\",\n );\n lines.push(\n \"- Publication is always an explicit creator action. Agents never \" +\n \"auto-publish.\",\n );\n lines.push(\"\");\n lines.push(ATELIER_CLAUDE_MD_SECTION_END);\n return lines.join(\"\\n\");\n}\n\n/** Result of `augmentClaudeMd`. */\nexport type ClaudeMdAugmentAction = \"appended\" | \"replaced\";\n\n/**\n * Idempotently weave `section` into `claudeMdPath` between\n * `ATELIER_CLAUDE_MD_SECTION_BEGIN` / `ATELIER_CLAUDE_MD_SECTION_END`.\n *\n * - Refuses (throws) when the file does not exist — `detectParadigm` is\n * the contract that callers check first; reaching here without a file\n * is a logic error worth surfacing loudly.\n * - When the sentinel pair is absent: append `\\n\\n${section}\\n` to the\n * end of the file. The Paradigm preamble is preserved byte-for-byte.\n * - When the sentinel pair is present: replace everything BETWEEN\n * (inclusive of the sentinels) with the new section. Re-running with\n * a different section content is the supported replay path.\n */\nexport function augmentClaudeMd(\n claudeMdPath: string,\n section: string,\n): { action: ClaudeMdAugmentAction } {\n if (!existsSync(claudeMdPath)) {\n throw new Error(\n `augmentClaudeMd: ${claudeMdPath} does not exist ` +\n `(detectParadigm should have gated this call)`,\n );\n }\n const raw = readFileSync(claudeMdPath, \"utf-8\");\n const beginIdx = raw.indexOf(ATELIER_CLAUDE_MD_SECTION_BEGIN);\n const endIdx = raw.indexOf(ATELIER_CLAUDE_MD_SECTION_END);\n\n if (beginIdx === -1 || endIdx === -1 || endIdx < beginIdx) {\n // No sentinel pair (or malformed) — append.\n const sep = raw.endsWith(\"\\n\") ? \"\\n\" : \"\\n\\n\";\n writeFileSync(claudeMdPath, raw + sep + section + \"\\n\", \"utf-8\");\n return { action: \"appended\" };\n }\n // Replace inclusive of both sentinels.\n const endAfter = endIdx + ATELIER_CLAUDE_MD_SECTION_END.length;\n const newContent = raw.slice(0, beginIdx) + section + raw.slice(endAfter);\n writeFileSync(claudeMdPath, newContent, \"utf-8\");\n return { action: \"replaced\" };\n}\n\n// ─── .mcp.json merge ───────────────────────────────────────────────────\n\n/** Result of `augmentMcpJson`. */\nexport type McpJsonAugmentAction = \"added\" | \"updated\" | \"skipped\";\n\n/**\n * Idempotently merge an `atelier` MCP server entry into `mcpJsonPath`,\n * mirroring Paradigm's own entry shape (`command`, `args`, `cwd`).\n *\n * - When the file is absent: refuses with throw (caller should have\n * gated via `detectParadigm`).\n * - When `mcpServers.atelier` is absent: add it, return `added`.\n * - When present with identical fields: no write, return `skipped`.\n * - When present but different (e.g. cwd changed): update, return\n * `updated`. Every other server entry and the `permissions` block\n * are preserved verbatim.\n *\n * Output is pretty JSON (2-space indent + trailing newline) matching\n * what `paradigm shift` emits — diffs read cleanly in the creator's git.\n */\nexport function augmentMcpJson(\n mcpJsonPath: string,\n opts: { atelierMcpCwd?: string },\n): { action: McpJsonAugmentAction } {\n if (!existsSync(mcpJsonPath)) {\n throw new Error(\n `augmentMcpJson: ${mcpJsonPath} does not exist ` +\n `(detectParadigm should have gated this call)`,\n );\n }\n const raw = readFileSync(mcpJsonPath, \"utf-8\");\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new Error(\n `augmentMcpJson: ${mcpJsonPath} is not valid JSON — ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new Error(`augmentMcpJson: ${mcpJsonPath} root must be an object`);\n }\n\n const cwd = opts.atelierMcpCwd ?? resolve(dirname(mcpJsonPath));\n const desired = {\n command: \"atelier-mcp\",\n args: [\".\"],\n cwd,\n };\n\n const servers =\n (parsed.mcpServers && typeof parsed.mcpServers === \"object\" && !Array.isArray(parsed.mcpServers)\n ? (parsed.mcpServers as Record<string, unknown>)\n : {});\n parsed.mcpServers = servers;\n\n const existing = servers.atelier;\n const action: McpJsonAugmentAction =\n existing === undefined\n ? \"added\"\n : shallowEqualServerEntry(existing, desired)\n ? \"skipped\"\n : \"updated\";\n\n if (action === \"skipped\") {\n return { action };\n }\n\n servers.atelier = desired;\n writeFileSync(mcpJsonPath, JSON.stringify(parsed, null, 2) + \"\\n\", \"utf-8\");\n return { action };\n}\n\n/** Field-by-field equality on the three fields we write. */\nfunction shallowEqualServerEntry(a: unknown, b: { command: string; args: string[]; cwd: string }): boolean {\n if (a === null || typeof a !== \"object\" || Array.isArray(a)) return false;\n const o = a as Record<string, unknown>;\n if (o.command !== b.command) return false;\n if (o.cwd !== b.cwd) return false;\n if (!Array.isArray(o.args)) return false;\n if (o.args.length !== b.args.length) return false;\n for (let i = 0; i < b.args.length; i++) if (o.args[i] !== b.args[i]) return false;\n return true;\n}\n\n// ─── .paradigm/personas/index.yaml merge ───────────────────────────────\n\n/** Result of `augmentPersonasIndex`. */\nexport interface PersonasIndexAugmentResult {\n action: \"added\" | \"updated\" | \"skipped\";\n /** Persona ids that were freshly inserted (not already in the index). */\n added: string[];\n}\n\n/** The three canonical Atelier persona index entries. Field shape mirrors\n * the persona.yaml + agents.yaml conventions: id-keyed map with\n * symbol/name/role/kind/ships_with/description plus pointer fields. */\ninterface AtelierPersonaEntry {\n id: string;\n symbol: string;\n name: string;\n role: string;\n description: string;\n}\n\nconst ATELIER_PERSONA_ENTRIES: readonly AtelierPersonaEntry[] = [\n {\n id: \"quill-showrunner\",\n symbol: \"#quill-showrunner\",\n name: \"Quill\",\n role: \"Showrunner\",\n description:\n \"Owns narrative direction. Authors DESIGN.md (audience, voice, register, \" +\n \"constraints) and SCRIPT.md (hook → story → proof → CTA). Distills \" +\n \"transcripts and intent into structure Lux can stage and Iris can build.\",\n },\n {\n id: \"lux-cinematographer\",\n symbol: \"#lux-cinematographer\",\n name: \"Lux\",\n role: \"Cinematographer\",\n description:\n \"Owns visual direction. Turns SCRIPT into STORYBOARD (shot/frame/composition, \" +\n \"timing windows, asset slots, animation, transitions, overlay rules). Drives \" +\n \"Atelier's layer and composition mutations through the live MCP surface.\",\n },\n {\n id: \"iris-composer\",\n symbol: \"#iris-composer\",\n name: \"Iris\",\n role: \"Composer\",\n description:\n \"Owns the build. Reads DESIGN/SCRIPT/STORYBOARD, applies the creator's \" +\n \"recipe, and composes the AtelierDocument layer by layer via MCP. Drives the \" +\n \"silence-trim / transcribe / captions pipeline via the Atelier CLI.\",\n },\n];\n\n/**\n * Render a single persona entry as an indented YAML block under the\n * `personas:` map. Two-space indent matches the rest of the index.\n */\nfunction renderPersonaEntry(p: AtelierPersonaEntry): string[] {\n const lines: string[] = [];\n lines.push(` ${p.id}:`);\n lines.push(` symbol: '${p.symbol}'`);\n lines.push(` name: ${p.name}`);\n lines.push(` role: ${p.role}`);\n lines.push(` kind: canonical`);\n lines.push(` ships_with: a-atelier`);\n lines.push(` description: ${yamlBlockScalar(p.description)}`);\n lines.push(` definition: .paradigm/personas/${p.id.split(\"-\")[0]}/persona.yaml`);\n lines.push(` system_prompt: .paradigm/personas/${p.id.split(\"-\")[0]}/system-prompt.md`);\n return lines;\n}\n\n/** Quote a scalar value for inline YAML when it contains characters the\n * hand-rolled reader can't handle unquoted (quotes, colons mid-string,\n * leading punctuation). Plain prose with spaces gets single-quoted with\n * any internal `'` doubled. */\nfunction yamlBlockScalar(value: string): string {\n // Use single-quoted YAML scalar form; double internal single quotes.\n return `'${value.replace(/'/g, \"''\")}'`;\n}\n\n/**\n * Idempotently weave Quill / Lux / Iris entries into the personas index.\n *\n * The personas index has a multi-key top-level shape (`version`,\n * `generated`, `personas`, `chains`, `gate_coverage`, `route_coverage`,\n * `uncovered_routes`). Naive scalar parsing would lose those siblings on\n * round-trip — so we BLOCK-REPLACE on raw text:\n *\n * 1. Read the file (or treat as missing).\n * 2. Parse the existing `personas:` block as a map-of-maps (just enough\n * to know which ids are already there).\n * 3. Merge our three canonical ids in (overwriting existing entries with\n * the canonical shape; preserving any unrelated entries).\n * 4. Splice the rendered `personas:` block back in between the same\n * surrounding bytes; every other top-level key stays byte-for-byte.\n *\n * If the file is missing entirely (older paradigm install or hand-deleted),\n * we create it with the minimal {version, personas: <our three>} shape.\n */\nexport function augmentPersonasIndex(\n personasIndexPath: string,\n): PersonasIndexAugmentResult {\n const dir = dirname(personasIndexPath);\n let raw: string | null = null;\n if (existsSync(personasIndexPath)) {\n raw = readFileSync(personasIndexPath, \"utf-8\");\n }\n\n if (raw === null) {\n // Fresh write — minimal multi-key shape so downstream tooling sees the\n // siblings it expects.\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n const lines: string[] = [\n `version: '1.0'`,\n `personas:`,\n ];\n for (const p of ATELIER_PERSONA_ENTRIES) lines.push(...renderPersonaEntry(p));\n lines.push(`chains: {}`);\n lines.push(`gate_coverage: {}`);\n lines.push(`route_coverage: {}`);\n lines.push(`uncovered_routes: []`);\n writeFileSync(personasIndexPath, lines.join(\"\\n\") + \"\\n\", \"utf-8\");\n return {\n action: \"added\",\n added: ATELIER_PERSONA_ENTRIES.map((p) => p.id),\n };\n }\n\n // Locate the `personas:` block. We rebuild the WHOLE personas block —\n // existing entries are read into a map, our three overwrite their keys,\n // any unrelated entries pass through.\n const { existingIds, prefix, suffix, currentBlock } =\n splitOnPersonasBlock(raw);\n\n const existingMap = parsePersonasBlock(currentBlock);\n const beforeIds = new Set(Object.keys(existingMap));\n const added: string[] = [];\n for (const p of ATELIER_PERSONA_ENTRIES) {\n if (!beforeIds.has(p.id)) added.push(p.id);\n existingMap[p.id] = renderPersonaEntry(p).slice(1); // drop the ` id:` header; we re-emit it\n }\n\n // Re-render the block. Sorted by id for stable output: any pre-existing\n // entries we didn't touch keep their original sub-lines verbatim.\n const idsSorted = Object.keys(existingMap).sort();\n const blockLines: string[] = [`personas:`];\n if (idsSorted.length === 0) {\n blockLines[0] = `personas: {}`;\n } else {\n for (const id of idsSorted) {\n blockLines.push(` ${id}:`);\n for (const ln of existingMap[id]) blockLines.push(ln);\n }\n }\n const newBlock = blockLines.join(\"\\n\");\n\n // Decide action: added > updated > skipped.\n let action: PersonasIndexAugmentResult[\"action\"];\n if (added.length === ATELIER_PERSONA_ENTRIES.length) {\n action = \"added\";\n } else if (added.length > 0) {\n action = \"added\"; // partial add still reports `added`\n } else if (currentBlock.trimEnd() === newBlock.trimEnd()) {\n action = \"skipped\";\n } else {\n action = \"updated\";\n }\n\n if (action === \"skipped\") {\n void existingIds;\n return { action, added: [] };\n }\n\n const finalRaw = prefix + newBlock + (suffix.startsWith(\"\\n\") ? \"\" : \"\\n\") + suffix;\n writeFileSync(personasIndexPath, finalRaw, \"utf-8\");\n return { action, added };\n}\n\n/**\n * Split a personas-index file into prefix / personas-block / suffix.\n *\n * The block starts at the line beginning with `personas:` (top-level,\n * no leading indent) and runs through every subsequent indented line —\n * stopping at the next top-level key (or EOF). When the file has no\n * `personas:` key we synthesize an empty block at the end so the merge\n * still inserts cleanly.\n */\nfunction splitOnPersonasBlock(raw: string): {\n prefix: string;\n currentBlock: string;\n suffix: string;\n existingIds: string[];\n} {\n const lines = raw.split(/\\r?\\n/);\n let startIdx = -1;\n let endIdx = lines.length;\n for (let i = 0; i < lines.length; i++) {\n if (/^personas\\s*:/.test(lines[i])) {\n startIdx = i;\n break;\n }\n }\n if (startIdx === -1) {\n // No personas: block — synthesize empty current and append.\n return {\n prefix: raw.endsWith(\"\\n\") ? raw : raw + \"\\n\",\n currentBlock: \"personas: {}\",\n suffix: \"\",\n existingIds: [],\n };\n }\n for (let i = startIdx + 1; i < lines.length; i++) {\n const ln = lines[i];\n if (ln.length === 0) continue;\n // Top-level key (no leading whitespace, looks like `foo:`) ends the block.\n if (/^[A-Za-z_][\\w-]*\\s*:/.test(ln)) {\n endIdx = i;\n break;\n }\n }\n const prefix = lines.slice(0, startIdx).join(\"\\n\");\n const block = lines.slice(startIdx, endIdx).join(\"\\n\");\n const suffix = lines.slice(endIdx).join(\"\\n\");\n return {\n prefix: prefix.length > 0 ? prefix + \"\\n\" : \"\",\n currentBlock: block,\n suffix,\n existingIds: Object.keys(parsePersonasBlock(block)),\n };\n}\n\n/**\n * Parse a `personas:` block (as emitted by `splitOnPersonasBlock`) into\n * `{ id: rawSubLines[] }`. Sub-lines are captured verbatim so unrelated\n * pre-existing entries round-trip with their original formatting + comments.\n */\nfunction parsePersonasBlock(block: string): Record<string, string[]> {\n const lines = block.split(/\\r?\\n/);\n const out: Record<string, string[]> = {};\n let currentId: string | null = null;\n let buf: string[] = [];\n const flush = (): void => {\n if (currentId !== null) {\n out[currentId] = buf;\n buf = [];\n currentId = null;\n }\n };\n // First line is `personas:` or `personas: {}` — skip it.\n for (let i = 1; i < lines.length; i++) {\n const ln = lines[i];\n // Two-space-indented `<id>:` is a new entry header.\n const m = ln.match(/^ {2}([A-Za-z0-9_-]+)\\s*:\\s*$/);\n if (m) {\n flush();\n currentId = m[1];\n continue;\n }\n if (currentId !== null) {\n // Sub-line of the current entry (any further-indented or blank line).\n if (ln.length === 0 || /^\\s{4,}/.test(ln)) {\n buf.push(ln);\n continue;\n }\n // Anything else ends the current entry; defensive fallback.\n flush();\n }\n }\n flush();\n return out;\n}\n\n// ─── .claude/agents/ drop ──────────────────────────────────────────────\n\n/** Result of `augmentClaudeAgents`. */\nexport interface ClaudeAgentsAugmentResult {\n added: string[];\n updated: string[];\n}\n\n/**\n * Idempotently drop the three atelier-{quill,lux,iris}.md agent files\n * into `agentsDir`. Creates the directory if missing (paradigm shift's\n * `.claude/` scaffold doesn't include an `agents/` subdir by default).\n *\n * Canonical sync — overwrite OK (TD-2026-05-26-210 \"shared personas\").\n * Pre-existing agents with the same names are clobbered with the\n * snapshot; this is the explicit contract. Other agents in the same dir\n * are NOT touched.\n */\nexport function augmentClaudeAgents(agentsDir: string): ClaudeAgentsAugmentResult {\n mkdirSync(agentsDir, { recursive: true });\n const added: string[] = [];\n const updated: string[] = [];\n for (const [filename, content] of Object.entries(ATELIER_AGENT_TEMPLATES)) {\n const absPath = join(agentsDir, filename);\n if (existsSync(absPath)) {\n updated.push(filename);\n } else {\n added.push(filename);\n }\n writeFileSync(absPath, content, \"utf-8\");\n }\n return { added, updated };\n}\n\n// ─── Top-level orchestrator ────────────────────────────────────────────\n\nexport interface ParadigmAugmentationResult {\n claudeMd: ReturnType<typeof augmentClaudeMd>;\n mcpJson: ReturnType<typeof augmentMcpJson>;\n personas: PersonasIndexAugmentResult;\n agents: ClaudeAgentsAugmentResult;\n}\n\nexport interface AugmentParadigmOptions {\n /** Display name written into the CLAUDE.md section. */\n workspaceName: string;\n /** Current learning mode written into the CLAUDE.md section. */\n learningMode: \"ambient\" | \"explicit\";\n /** Override `cwd` written into the atelier MCP server entry. Defaults to `dir`. */\n atelierMcpCwd?: string;\n}\n\n/**\n * Run all four augmentations against `dir`. Caller is responsible for\n * checking `detectParadigm(dir).present` first — this entry point assumes\n * the Paradigm scaffold is already in place. Each sub-step is idempotent,\n * so a re-run produces a stable result (with potentially different\n * action codes — e.g. `appended` becomes `replaced`).\n */\nexport function augmentParadigmWithAtelier(\n dir: string,\n opts: AugmentParadigmOptions,\n): ParadigmAugmentationResult {\n const absDir = resolve(dir);\n const det = detectParadigm(absDir);\n return {\n claudeMd: augmentClaudeMd(\n det.claudeMdPath,\n buildAtelierClaudeMdSection({\n workspaceName: opts.workspaceName,\n learningMode: opts.learningMode,\n }),\n ),\n mcpJson: augmentMcpJson(det.mcpJsonPath, {\n atelierMcpCwd: opts.atelierMcpCwd ?? absDir,\n }),\n personas: augmentPersonasIndex(det.personasIndexPath),\n agents: augmentClaudeAgents(det.agentsDirPath),\n };\n}\n","// #compose-video-project — turn an ingested video + transcript into a playable\n// project.atelier document, supporting MULTI-CLIP timelines.\n//\n// Both `atelier media ingest` (CLI) and `POST /api/media/ingest` (studio HTTP)\n// call this helper so the post-ingest state is identical regardless of entry\n// point. After this runs, opening the project in the studio shows ALL\n// previously-ingested clips on the video track in order, with per-clip caption\n// overlays animating — the \"v1 UX bundle\" payoff, now multi-clip aware.\n//\n// Pipeline (additive; safe to re-run; preserves prior clips):\n// 1. Transcribe the source. The legacy `transcribeProject` /\n// `transcribeMediaFile` writes `transcript.json` (and per-file mirror) AND\n// runs `rewriteCaptionLayers` as a side effect — landing unparented\n// `caption-N` layers + deltas in the doc. We compensate by re-reading\n// the doc and dropping those unparented caption layers; we rebuild\n// properly-parented `cap-<slug>-N` layers below from the same transcript.\n// 2. Ensure the doc has a lazily-created `track-video-1` Group layer.\n// 3. Ensure the doc has a `clip-<slug>` VideoVisual layer parented to that\n// track. New ingest → appended at end-of-track startFrame; re-ingest of\n// the same src → placement preserved, captions refreshed.\n// 4. Append per-clip caption layers parented to `clip-<slug>` with\n// ids `cap-<slug>-N`. Other clips' captions are NOT touched.\n// 5. Recompute the derived `timeline` summary block (track + clips).\n// 6. State duration = max(existing, max(clip.endFrame)).\n//\n// Tag-namespace isolation:\n// - Layers tagged \"caption\" are mutated only when:\n// * unparented (the legacy `rewriteCaptionLayers` byproduct), OR\n// * parented to the clip currently being (re-)ingested.\n// Other clips' caption layers + their deltas pass through untouched.\n// - Layers tagged \"track\" / \"video\" track group are lazy-created and reused.\n// - All other user-authored layers/deltas are preserved.\n//\n// The pure helpers (ensureClipLayer, ensureTrackLayer, endOfTrackFrame,\n// recomputeTimeline, stripStaleCaptionArtifacts, rebuildClipCaptions) live in\n// #timeline-ops so the MCP per-clip surfaces (atelier_transcribe_clip, etc.)\n// can reuse them without dragging the fs / probe wiring of composeVideoProject.\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { basename, dirname, extname, join, resolve } from \"node:path\";\nimport type {\n AtelierDocument,\n State,\n VideoTranscript,\n} from \"@a-company/atelier-types\";\nimport { parseAtelier } from \"@a-company/atelier-schema\";\nimport { transcribeProject, type TranscribeOptions, type TranscribeResult } from \"../commands/transcribe.js\";\nimport { transcribeMediaFile } from \"./transcribe-orchestrator.js\";\nimport { probeDuration } from \"./silence-detect.js\";\nimport { type BuildCaptionsOptions } from \"./caption-builder.js\";\nimport {\n VIDEO_TRACK_ID,\n ensureClipLayer as ensureClipLayerOp,\n recomputeTimeline,\n stripStaleCaptionArtifacts,\n rebuildClipCaptions,\n} from \"./timeline-ops.js\";\n\n/**\n * Read project.atelier handling both JSON and YAML on-disk formats — the\n * studio's YAML tab can save the doc as YAML and our compose pipeline must\n * survive that. Falls back to the raw JSON.parse path if parseAtelier\n * yields a schema error so the caller's catch can still surface a useful\n * message for malformed docs.\n */\nexport function readAtelierDoc(absPath: string): AtelierDocument {\n const raw = readFileSync(absPath, \"utf-8\");\n const parsed = parseAtelier(raw);\n if (parsed.success) return parsed.data as AtelierDocument;\n // Defensive fallback for legacy docs that pre-date schema-strictness.\n return JSON.parse(raw) as AtelierDocument;\n}\n\nexport interface ComposeVideoProjectOptions {\n /** Absolute path to the project root (contains the doc at docPath). */\n projectDir: string;\n /**\n * Project-relative path to the .atelier composition document this helper\n * reads/writes. Required — no default. Caller computes this from the\n * ingest slug (e.g. `\"img-7346.atelier\"`, `\"slides/slide-1.atelier\"`).\n * The parent directory is created on demand. The helper NEVER touches\n * `project.atelier` specifically; only this caller-supplied path.\n */\n docPath: string;\n /**\n * Absolute path to the source media file. Used to (a) compute a canonical\n * assetId/layer src reference, and (b) probe duration via ffprobe.\n */\n sourceFile: string;\n /**\n * Whether to run whisper transcription. Default true. When false, only the\n * state-duration + video-layer wiring runs (useful when re-composing a\n * project that already has transcript.json).\n */\n transcribe?: boolean;\n /** Forwarded to `transcribeProject` — model selection, language, etc. */\n transcribeOptions?: TranscribeOptions;\n /**\n * When set, after `transcribeProject` writes the root `transcript.json`\n * the result is mirrored to `<projectDir>/transcripts/<stem>.json` (per\n * #transcribe-orchestrator). Pass the basename of the file the creator\n * just ingested — e.g. `IMG_7347.MOV` for a drag-drop video, or\n * `source.mp4` for the legacy single-source layout. Omit for\n * single-source projects that only need the root `transcript.json`\n * (preserves the legacy `atelier transcribe` contract).\n */\n mediaBasename?: string;\n /**\n * Test injection point — production wiring uses the real `transcribeProject`.\n * Mirrors the same hook on `runMediaIngest`. Used by both the single-source\n * `transcribeProject` path and the per-file `transcribeMediaFile` path\n * (the orchestrator forwards this fn through unchanged).\n */\n transcribeFn?: (dir: string, opts: TranscribeOptions) => Promise<TranscribeResult>;\n /**\n * Test injection point — production wiring uses `probeDuration` (ffprobe).\n * When omitted and ffprobe fails (or isn't installed), we fall back to the\n * transcript's last word end time.\n */\n probeDurationFn?: (sourcePath: string) => Promise<number>;\n /** Optional caption style overrides passed through to `buildCaptionLayers`. */\n captionOptions?: BuildCaptionsOptions;\n}\n\nexport interface ComposeVideoProjectResult {\n /** The project.atelier as written to disk. */\n doc: AtelierDocument;\n /** True iff a NEW clip layer was added (false on re-ingest of the same src). */\n addedVideoLayer: boolean;\n /** Slug derived from the source basename — `clip-<slug>` is the clip layer id. */\n clipSlug: string;\n /** Id of the clip layer just (re-)ingested. */\n clipLayerId: string;\n /** Name of the state whose duration was set/updated. */\n targetStateName: string;\n /** Total duration in seconds written into the target state. */\n targetDurationSeconds: number;\n /** Transcription result, when transcription ran. */\n transcribe?: TranscribeResult;\n /**\n * Absolute path to the per-file transcript mirror at\n * `<projectDir>/transcripts/<stem>.json`. Present only when caller passed\n * `mediaBasename` and transcription actually ran (and wasn't a dry-run).\n */\n mirroredTranscriptPath?: string;\n}\n\nconst POSSIBLE_SOURCE_EXTS = [\".mp4\", \".mov\", \".webm\", \".mkv\", \".avi\"];\n\n/**\n * Slug an arbitrary basename into a kebab-case identifier suitable for layer /\n * asset ids: lowercase, strip extension, replace runs of non-[a-z0-9] with `-`,\n * trim leading/trailing dashes. e.g. `IMG_7346.MOV` → `img-7346`,\n * `media/B-roll Clip.mp4` → `b-roll-clip`.\n *\n * Exported so tests can derive expected layer ids without hardcoding.\n */\nexport function slugifyBasename(name: string): string {\n const base = basename(name);\n const stem = stripExt(base);\n const lower = stem.toLowerCase();\n const replaced = lower.replace(/[^a-z0-9]+/g, \"-\");\n return replaced.replace(/^-+|-+$/g, \"\");\n}\n\nfunction stripExt(name: string): string {\n const ext = extname(name);\n return ext ? name.slice(0, -ext.length) : name;\n}\n\n/**\n * Compose a playable VideoProject from an ingested source + transcript.\n *\n * - Transcribes (unless `transcribe: false`) — writes the transcript file(s).\n * The legacy caption-rewrite side effect is neutralized below.\n * - Lazy-creates the `track-video-1` Group layer if absent.\n * - Appends a `clip-<slug>` VideoVisual layer parented to the track at the\n * end-of-track startFrame (or preserves placement on re-ingest of the same).\n * - Rebuilds per-clip captions as `cap-<slug>-N` parented to `clip-<slug>`,\n * leaving other clips' captions untouched.\n * - Regenerates the derived `timeline` summary block.\n * - Sets the target state's duration to cover all clips on all tracks.\n */\nexport async function composeVideoProject(\n opts: ComposeVideoProjectOptions,\n): Promise<ComposeVideoProjectResult> {\n const projectDir = resolve(opts.projectDir);\n const compositionPath = join(projectDir, opts.docPath);\n // Ensure the parent dir exists for nested docPaths like \"slides/slide-1.atelier\".\n mkdirSync(dirname(compositionPath), { recursive: true });\n if (!existsSync(compositionPath)) {\n // Scaffold a fresh kind:\"video\" doc — caller didn't pre-seed one. Mirrors\n // the image/carousel helpers' scaffold path so the pivot is consistent\n // across kinds. Downstream steps lazy-create the video track + clip layer.\n const seedDoc: AtelierDocument = {\n version: \"1.0\",\n name: basename(projectDir),\n kind: \"video\",\n canvas: { width: 1080, height: 1920, fps: 30 },\n layers: [],\n states: { default: { duration: 0, deltas: [] } },\n };\n writeFileSync(compositionPath, JSON.stringify(seedDoc, null, 2), \"utf-8\");\n }\n\n const sourceBasename = canonicalSourceName(opts.sourceFile, projectDir);\n const clipSlug = slugifyBasename(sourceBasename);\n if (!clipSlug) {\n throw new Error(\n `composeVideoProject: cannot derive a slug from source \"${opts.sourceFile}\"`,\n );\n }\n const clipLayerId = `clip-${clipSlug}`;\n\n // 1. Transcribe — writes transcript file(s). NOTE: the underlying\n // `transcribeProject` ALSO calls `rewriteCaptionLayers` as a side effect,\n // landing unparented `caption-N` layers + deltas in the doc on disk. We\n // re-read after, then strip those unparented caption artifacts in step 3\n // before rebuilding properly-parented captions for THIS clip.\n let transcribeResult: TranscribeResult | undefined;\n let mirroredTranscriptPath: string | undefined;\n const shouldTranscribe = opts.transcribe !== false;\n if (shouldTranscribe) {\n // POST-FLAT-DOCS-PIVOT: project.atelier may not exist (projects are\n // identified by `.atelier/` metadata, the canonical manifest file is\n // optional). The legacy `transcribeProject` reads project.atelier when\n // !noCaptions to run `rewriteCaptionLayers` — that ENOENTs here. We\n // ALWAYS pass `noCaptions: true` because compose-video-project does\n // its own parented + clip-local caption rebuild below (step 5). The\n // legacy unparented `caption-N` layers were never wanted; skipping\n // their write side-steps the file-existence issue entirely.\n const noCaptionsOverride = { noCaptions: true } as const;\n if (opts.mediaBasename) {\n // Multi-media projects — additive per-file mirror via #transcribe-orchestrator.\n const result = await transcribeMediaFile(projectDir, opts.mediaBasename, {\n ...(opts.transcribeOptions ?? {}),\n ...noCaptionsOverride,\n ...(opts.transcribeFn !== undefined ? { transcribeFn: opts.transcribeFn } : {}),\n });\n transcribeResult = result;\n mirroredTranscriptPath = result.mirroredTranscriptPath;\n } else {\n // Single-source legacy path — only the root transcript is written.\n const fn = opts.transcribeFn ?? transcribeProject;\n transcribeResult = await fn(projectDir, {\n ...(opts.transcribeOptions ?? {}),\n ...noCaptionsOverride,\n });\n }\n }\n\n // 2. Load the (possibly transcribe-mutated) document.\n let doc: AtelierDocument = readAtelierDoc(compositionPath);\n\n // 3. Pre-clean: drop caption artifacts that conflict with the rebuild below.\n // - Unparented `caption-N` layers come from the legacy `rewriteCaptionLayers`\n // side effect of the just-completed transcribe step. They MUST go (we'll\n // replace them with parented `cap-<slug>-N` from the same transcript).\n // - Any prior `cap-<clipSlug>-*` layers belong to a previous ingest of\n // THIS exact clip — drop them, fresh rebuild incoming.\n // - Caption layers parented to OTHER clips pass through untouched (this\n // is the multi-clip preservation invariant).\n // In test environments the stub `transcribeFn` typically doesn't mutate\n // the doc, so the first bullet is a no-op; the logic is for production.\n doc = stripStaleCaptionArtifacts(doc, clipLayerId);\n\n // 4. Ensure the video track group exists, then ensure a `clip-<slug>` layer\n // parented to it. New ingest → append at end-of-track. Re-ingest of the\n // same src → leave placement alone (preserve user trim, etc.).\n const probeFn = opts.probeDurationFn ?? probeDuration;\n let clipDurationSeconds: number;\n try {\n clipDurationSeconds = await probeFn(opts.sourceFile);\n } catch {\n clipDurationSeconds = lastTranscriptWordEnd(transcribeResult?.transcript) ?? 0;\n }\n\n const fps = doc.canvas.fps;\n const clipDurationFrames = Math.max(1, Math.round(clipDurationSeconds * fps));\n\n const { doc: docWithClip, addedLayer } = ensureClipLayerOp(doc, {\n clipLayerId,\n trackId: VIDEO_TRACK_ID,\n sourceName: sourceBasename,\n clipDurationSeconds,\n clipDurationFrames,\n kind: \"video\",\n });\n doc = docWithClip;\n\n // 5. Rebuild captions for THIS clip from the transcript, parented to the\n // clip layer. `buildCaptionLayers` emits clip-LOCAL frame ranges (since\n // transcript times are relative to the source = 0); the renderer's\n // parent-aware clip-local-time pass shifts them into composition frames\n // at render-time. The helper renames ids + sets parentId.\n if (transcribeResult?.transcript) {\n doc = rebuildClipCaptions(\n doc,\n clipLayerId,\n transcribeResult.transcript,\n doc.canvas,\n opts.captionOptions ?? {},\n );\n }\n\n // 6. Recompute the derived `timeline` summary block from current clip layers.\n doc = recomputeTimeline(doc);\n\n // 7. Write the duration into the target state. State `duration` is in FRAMES;\n // must cover the latest clip's endFrame across all tracks.\n const stateNames = Object.keys(doc.states);\n const targetStateName = stateNames.includes(\"default\")\n ? \"default\"\n : (stateNames[0] ?? \"default\");\n\n const totalFrames = doc.timeline?.totalFrames ?? 0;\n\n const states = { ...doc.states };\n const existing = states[targetStateName];\n const updatedState: State = existing\n ? { ...existing, duration: Math.max(existing.duration, totalFrames) }\n : { duration: totalFrames, deltas: [] };\n states[targetStateName] = updatedState;\n doc = { ...doc, states };\n\n writeFileSync(compositionPath, JSON.stringify(doc, null, 2), \"utf-8\");\n\n return {\n doc,\n addedVideoLayer: addedLayer,\n clipSlug,\n clipLayerId,\n targetStateName,\n targetDurationSeconds: clipDurationSeconds,\n ...(transcribeResult !== undefined ? { transcribe: transcribeResult } : {}),\n ...(mirroredTranscriptPath !== undefined ? { mirroredTranscriptPath } : {}),\n };\n}\n\n/**\n * Pick the layer-src string for the ingested video. If the source file lives\n * at the project root as `source.<ext>`, use that name (matches the legacy\n * VideoProject convention). Otherwise use the project-relative path when the\n * source is inside the project tree, else fall back to the file's basename.\n */\nfunction canonicalSourceName(sourceFile: string, projectDir: string): string {\n const abs = resolve(sourceFile);\n const projectAbs = resolve(projectDir);\n const ext = extname(abs);\n // source.<ext> at project root → return as-is\n if (POSSIBLE_SOURCE_EXTS.includes(ext.toLowerCase())) {\n const flat = `${projectAbs}/source${ext}`;\n if (resolve(flat) === abs) return `source${ext}`;\n }\n // inside project tree → relative path\n if (abs.startsWith(`${projectAbs}/`)) {\n return abs.slice(projectAbs.length + 1);\n }\n // foreign path → use basename\n return basename(abs);\n}\n\n/**\n * Backwards-compatible `ensureVideoLayer(doc, sourceName)` — kept for the\n * existing tests and any other call-sites. Internally delegates to\n * `ensureClipLayer` (timeline-ops) with kind:\"video\" so the new multi-clip +\n * parented structure is produced.\n *\n * Note: this signature can't compute a frame-accurate `sourceEnd` (no probe\n * available), so the produced clip has `sourceEnd: undefined` and the\n * timeline block reports zero duration for it. Production code paths go\n * through `composeVideoProject` which does the probe. Tests that exercise\n * `ensureVideoLayer` directly are validating layer-shape, not timeline math.\n */\nexport function ensureVideoLayer(\n doc: AtelierDocument,\n sourceName: string,\n): { doc: AtelierDocument; addedVideoLayer: boolean } {\n const clipSlug = slugifyBasename(sourceName);\n if (!clipSlug) return { doc, addedVideoLayer: false };\n const clipLayerId = `clip-${clipSlug}`;\n\n // Idempotence preserved by id: if a clip with this id exists, skip.\n // Also accept any pre-existing VideoVisual layer that already references\n // this `sourceName` (legacy `source-video` layers from pre-v1 docs) — this\n // matches the prior `ensureVideoLayer` contract and the existing test for\n // a layer with id \"existing\" that references `src: source.mp4`.\n const alreadyReferenced = doc.layers.some((layer) => {\n if (layer.id === clipLayerId) return true;\n const v = layer.visual as { type?: string; src?: string; assetId?: string };\n if (!v || v.type !== \"video\") return false;\n if (v.src === sourceName) return true;\n const assets = doc.assets;\n if (v.assetId && assets && assets[v.assetId]?.src === sourceName) return true;\n return false;\n });\n if (alreadyReferenced) return { doc, addedVideoLayer: false };\n\n const { doc: out, addedLayer } = ensureClipLayerOp(doc, {\n clipLayerId,\n trackId: VIDEO_TRACK_ID,\n sourceName,\n clipDurationSeconds: 0,\n clipDurationFrames: 0,\n kind: \"video\",\n });\n // Match the legacy shape — produce a clip with sourceEnd:undefined so\n // existing tests that don't pre-set a duration keep their semantics.\n // (ensureClipLayerOp sets sourceEnd to clipDurationSeconds (0); for the\n // legacy contract that field was undefined. Patch the new clip if we just\n // added it.)\n if (addedLayer) {\n const patchedLayers = out.layers.map((l) => {\n if (l.id !== clipLayerId) return l;\n if (l.visual.type !== \"video\") return l;\n const { sourceEnd: _drop, ...visualRest } = l.visual;\n void _drop;\n return { ...l, visual: { ...visualRest } };\n });\n return {\n doc: { ...out, layers: patchedLayers },\n addedVideoLayer: addedLayer,\n };\n }\n return { doc: out, addedVideoLayer: addedLayer };\n}\n\n/** Last word end time across all segments, or undefined when no words. */\nfunction lastTranscriptWordEnd(transcript: VideoTranscript | undefined): number | undefined {\n if (!transcript) return undefined;\n let max = 0;\n for (const seg of transcript.segments) {\n for (const w of seg.words) {\n if (w.end > max) max = w.end;\n }\n }\n return max > 0 ? max : undefined;\n}\n","// #transcribe-orchestrator — per-media-file transcription orchestrator.\n//\n// The legacy `transcribeProject(projectDir, opts)` writes a single\n// `transcript.json` at the project root (the VideoProject 1.0 contract). When\n// a project holds multiple media files (the MediaProject convention,\n// `<project>/media/<basename>`), one root transcript can't represent all of\n// them — every new ingest would overwrite the previous one.\n//\n// `transcribeMediaFile(projectDir, mediaBasename, opts)` fixes the gap with a\n// thin wrapper:\n//\n// 1. Call `transcribeProject` — preserves the legacy root `transcript.json`\n// write, so single-source projects, the `atelier transcribe` CLI verb,\n// and every existing caller keep working untouched.\n// 2. Mirror the result to `<projectDir>/transcripts/<basename-no-ext>.json`\n// — the per-file canonical location. Multiple ingests stack instead of\n// overwriting; the studio sidebar can light up a transcript badge for\n// each media file.\n//\n// The mirror is additive. The root `transcript.json` keeps reflecting the\n// most-recent transcription (still useful for the legacy renderers); the\n// per-file mirror provides the multi-source surface.\n\nimport { copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { basename, extname, join, resolve } from \"node:path\";\nimport {\n transcribeProject,\n type TranscribeOptions,\n type TranscribeResult,\n} from \"../commands/transcribe.js\";\nimport {\n probeWhisper,\n runWhisperCpp,\n parseWhisperCppJson,\n} from \"./whisper.js\";\n\n/** Result of `transcribeMediaFile` — extends the base result with the mirror path. */\nexport interface TranscribeMediaFileResult extends TranscribeResult {\n /**\n * Absolute path to the per-file transcript copy under\n * `<projectDir>/transcripts/<stem>.json`. Always written (assuming the\n * underlying `transcribeProject` succeeded) — present even when the\n * basename happens to be `source.<ext>`, in which case both the root\n * `transcript.json` and the mirror exist side-by-side.\n */\n mirroredTranscriptPath: string;\n /**\n * The basename-without-extension used to derive the mirror filename.\n * Surfaced so callers (e.g. ingest broadcasters) can build their own paths\n * without recomputing.\n */\n mediaStem: string;\n}\n\n/**\n * Inject-point for tests so they can stub the underlying `transcribeProject`\n * (the real implementation spawns whisper-cpp). Mirrors the pattern used by\n * `composeVideoProject` and `runMediaIngest`.\n */\nexport type TranscribeProjectFn = (\n dir: string,\n opts: TranscribeOptions,\n) => Promise<TranscribeResult>;\n\nexport interface TranscribeMediaFileOptions extends TranscribeOptions {\n /** Test injection point. Defaults to the real `transcribeProject`. */\n transcribeFn?: TranscribeProjectFn;\n}\n\n/**\n * Run transcription for a single ingested media file and mirror the result\n * to the per-file location.\n *\n * - `projectDir` — project root (contains `project.atelier`, and the legacy\n * `source.<ext>` that `transcribeProject` reads).\n * - `mediaBasename` — basename of the file the creator dropped (e.g.\n * `IMG_7347.MOV` or `interview-cut.mp4`). The mirror's filename is derived\n * by stripping the extension and appending `.json`.\n *\n * Re-running for the same `mediaBasename` overwrites that media's mirror\n * (matching the legacy \"latest wins\" behaviour on the root transcript) but\n * leaves other media files' mirrors untouched.\n */\nexport async function transcribeMediaFile(\n projectDir: string,\n mediaBasename: string,\n options: TranscribeMediaFileOptions = {},\n): Promise<TranscribeMediaFileResult> {\n const absProject = resolve(projectDir);\n const stem = stripExt(basename(mediaBasename));\n if (!stem) {\n throw new Error(\n `transcribeMediaFile: invalid mediaBasename \"${mediaBasename}\" — derived stem is empty`,\n );\n }\n\n const { transcribeFn, ...transcribeOpts } = options;\n const fn = transcribeFn ?? transcribeProject;\n\n // Save the existing root transcript.json so we can restore it after\n // transcribeProject runs. Multi-media ingest must NOT permanently clobber\n // the legacy root transcript (it belongs to the original single-source\n // layout). But transcribeProject's full pipeline (writeTranscript +\n // rewriteCaptionLayers) is the right way to land caption layers in\n // project.atelier, so we let it run, then mirror+restore.\n const rootTranscript = join(absProject, \"transcript.json\");\n const priorRoot = existsSync(rootTranscript) ? readFileSync(rootTranscript) : null;\n\n const result = await fn(absProject, transcribeOpts);\n\n if (transcribeOpts.dryRun) {\n return {\n ...result,\n mirroredTranscriptPath: join(absProject, \"transcripts\", `${stem}.json`),\n mediaStem: stem,\n };\n }\n\n // Mirror the just-written root transcript to the per-file canonical location.\n const transcriptsDir = join(absProject, \"transcripts\");\n if (!existsSync(transcriptsDir)) {\n mkdirSync(transcriptsDir, { recursive: true });\n }\n const mirroredTranscriptPath = join(transcriptsDir, `${stem}.json`);\n if (existsSync(rootTranscript)) {\n copyFileSync(rootTranscript, mirroredTranscriptPath);\n }\n\n // Restore the prior root transcript (or remove if there was none) so each\n // ingest doesn't permanently clobber the legacy single-source transcript.\n // Caption layers + deltas already landed in project.atelier from the run.\n if (priorRoot !== null) {\n writeFileSync(rootTranscript, priorRoot);\n } else {\n try { rmSync(rootTranscript); } catch { /* swallow */ }\n }\n\n return {\n ...result,\n mirroredTranscriptPath,\n mediaStem: stem,\n };\n}\n\n/** Strip the trailing extension (case-insensitive); `IMG_7347.MOV` → `IMG_7347`. */\nfunction stripExt(name: string): string {\n const ext = extname(name);\n if (!ext) return name;\n return name.slice(0, -ext.length);\n}\n\n// ── v1.0 media-bin pivot (Kit-A) ──────────────────────────────────────\n/**\n * Run transcription on a media file in isolation — no doc, no compose.\n *\n * Used by the media-bin add path (post-pivot, TD-2026-05-28): when a creator\n * drops a video into the bin we want the transcript ready by the time they\n * compose with it.\n *\n * Behaviour:\n * - Writes `<projectDir>/transcripts/<stem>.json` (canonical per-file\n * location matching the existing convention).\n * - Does NOT touch project.atelier or any compose target. Pure media-side.\n * - Idempotent: if the transcript already exists, no-op (background work\n * skips duplicates).\n *\n * The legacy `transcribeMediaFile` is left intact for the compose-time path\n * (`composeVideoProject` still calls it). This function bypasses\n * `transcribeProject` entirely so we don't trip the root-transcript +\n * caption-rebuild side effects.\n */\nexport async function transcribeMediaToBin(\n projectDir: string,\n mediaBasename: string,\n options: TranscribeMediaFileOptions = {},\n): Promise<TranscribeMediaFileResult> {\n const absProject = resolve(projectDir);\n const stem = stripExt(basename(mediaBasename));\n if (!stem) {\n throw new Error(\n `transcribeMediaToBin: invalid mediaBasename \"${mediaBasename}\" — derived stem is empty`,\n );\n }\n\n const transcriptsDir = join(absProject, \"transcripts\");\n const mirroredTranscriptPath = join(transcriptsDir, `${stem}.json`);\n\n // Idempotency guard — background workers running multiple times for the\n // same media file must no-op once the transcript exists.\n if (existsSync(mirroredTranscriptPath)) {\n let cached: unknown = null;\n try {\n cached = JSON.parse(readFileSync(mirroredTranscriptPath, \"utf-8\"));\n } catch {\n cached = null;\n }\n return {\n projectDir: absProject,\n backend: \"cached\",\n transcript: cached as never,\n wordCount: 0,\n captionsGenerated: false,\n mirroredTranscriptPath,\n mediaStem: stem,\n };\n }\n\n const { transcribeFn, dryRun, model, modelPath, language, sourcePath } = options;\n const mediaPath = sourcePath ?? join(absProject, \"media\", mediaBasename);\n\n // Test injection: when a stub `transcribeFn` is supplied, reuse it so unit\n // tests don't need a real whisper binary. The stub's result shape matches\n // production runWhisperCpp + parseWhisperCppJson once we mirror it.\n if (transcribeFn) {\n const result = await transcribeFn(absProject, { dryRun, model, modelPath, language, sourcePath: mediaPath });\n if (dryRun) {\n return {\n ...result,\n mirroredTranscriptPath,\n mediaStem: stem,\n };\n }\n // The stub may have written transcript.json to satisfy\n // transcribeProject's legacy contract — for the bin path we MUST NOT keep\n // it around. Mirror it (if present) then remove.\n const rootTranscript = join(absProject, \"transcript.json\");\n if (existsSync(rootTranscript)) {\n if (!existsSync(transcriptsDir)) {\n mkdirSync(transcriptsDir, { recursive: true });\n }\n copyFileSync(rootTranscript, mirroredTranscriptPath);\n try { rmSync(rootTranscript); } catch { /* swallow */ }\n } else {\n // No legacy artifact — write the transcript object directly. Stubs that\n // skip the root-write path still need the mirror produced.\n if (!existsSync(transcriptsDir)) {\n mkdirSync(transcriptsDir, { recursive: true });\n }\n writeFileSync(\n mirroredTranscriptPath,\n JSON.stringify(result.transcript ?? {}, null, 2),\n \"utf-8\",\n );\n }\n return {\n ...result,\n mirroredTranscriptPath,\n mediaStem: stem,\n };\n }\n\n // Production path: probe whisper, run it directly, parse, write mirror.\n // We bypass transcribeProject entirely so neither root transcript.json nor\n // project.atelier are touched.\n const backend = await probeWhisper();\n if (backend === \"none\") {\n throw new Error(\n \"No Whisper backend available. Install whisper.cpp (brew install whisper-cpp).\",\n );\n }\n if (backend === \"openai-api\") {\n throw new Error(\n \"OpenAI API backend is not yet implemented for bin-side transcription.\",\n );\n }\n\n const rawJson = await runWhisperCpp(mediaPath, {\n model,\n modelPath,\n language,\n });\n const transcript = parseWhisperCppJson(rawJson);\n const wordCount = transcript.segments.reduce((n, s) => n + s.words.length, 0);\n\n const result: TranscribeMediaFileResult = {\n projectDir: absProject,\n backend,\n transcript,\n wordCount,\n captionsGenerated: false,\n mirroredTranscriptPath,\n mediaStem: stem,\n };\n\n if (dryRun) return result;\n\n if (!existsSync(transcriptsDir)) {\n mkdirSync(transcriptsDir, { recursive: true });\n }\n writeFileSync(mirroredTranscriptPath, JSON.stringify(transcript, null, 2), \"utf-8\");\n return result;\n}\n// ──────────────────────────────────────────────────────────────────────\n","// #timeline-ops — pure helpers over an AtelierDocument's track + clip layers.\n//\n// Extracted from #compose-video-project and generalized for audio tracks.\n// All helpers are pure (immutable returns) so they're safe to reuse from\n// either the CLI compose path or the MCP/HTTP per-clip ops surface\n// (atelier_transcribe_clip, future slice A). No fs / no logging side effects.\n//\n// Track convention:\n// - track-video-1, track-audio-1 are the stable ids for the primary video\n// and primary audio Group layers. Lazy-created by `ensureTrackLayer`.\n// - Multi-track expansions (track-video-2, etc.) follow the same pattern;\n// `recomputeTimeline` walks any Group layer tagged with \"track\".\n\nimport type {\n AtelierDocument,\n Asset,\n AudioVisual,\n Delta,\n Layer,\n State,\n Timeline,\n TimelineClip,\n TimelineTrack,\n VideoTranscript,\n VideoVisual,\n} from \"@a-company/atelier-types\";\n\nimport { buildCaptionLayers, type BuildCaptionsOptions } from \"./caption-builder.js\";\n\n/** Stable id for the lazily-created primary video track group layer. */\nexport const VIDEO_TRACK_ID = \"track-video-1\";\n\n/** Stable id for the lazily-created primary audio track group layer. */\nexport const AUDIO_TRACK_ID = \"track-audio-1\";\n\n/**\n * Frame at which the next clip on `trackId` should start —\n * max(startFrame + clipDurationFrames) across all children parented to the\n * track, or 0 when the track is empty.\n *\n * Works for both video AND audio children: AudioVisual carries the same\n * startFrame / sourceOffset / sourceEnd fields as VideoVisual, so the math is\n * identical. Layers whose visual type is neither video nor audio are skipped.\n */\nexport function endOfTrackFrame(\n layers: Layer[],\n trackId: string,\n fps: number,\n): number {\n let maxEnd = 0;\n for (const layer of layers) {\n if (layer.parentId !== trackId) continue;\n const v = layer.visual;\n if (v.type !== \"video\" && v.type !== \"audio\") continue;\n const tv = v as VideoVisual | AudioVisual;\n const start = tv.startFrame ?? 0;\n const offset = tv.sourceOffset ?? 0;\n const end = tv.sourceEnd;\n if (end === undefined) continue;\n const durFrames = Math.max(0, Math.round((end - offset) * fps));\n const layerEnd = start + durFrames;\n if (layerEnd > maxEnd) maxEnd = layerEnd;\n }\n return maxEnd;\n}\n\n/**\n * Lazy-create a track Group layer if missing. Idempotent: returns\n * `{ created: false }` and the original doc if a layer with the given id\n * already exists.\n *\n * The track group spans the full canvas and is tagged [\"track\", kind] so\n * `recomputeTimeline` can discriminate video vs audio tracks by tag.\n */\nexport function ensureTrackLayer(\n doc: AtelierDocument,\n trackId: string,\n kind: \"video\" | \"audio\",\n): { doc: AtelierDocument; created: boolean } {\n const exists = doc.layers.some((l) => l.id === trackId);\n if (exists) return { doc, created: false };\n\n const trackLayer: Layer = {\n id: trackId,\n tags: [\"track\", kind],\n description: kind === \"video\" ? \"Video track 1\" : \"Audio track 1\",\n visual: { type: \"group\" },\n frame: { x: 0, y: 0 },\n bounds: { width: doc.canvas.width, height: doc.canvas.height },\n };\n // Prepend so tracks render under their children when read top-down.\n return {\n doc: { ...doc, layers: [trackLayer, ...doc.layers] },\n created: true,\n };\n}\n\n/** Args for ensureClipLayer — supports both video and audio clips. */\nexport interface EnsureClipArgs {\n /** Stable layer id (e.g. \"clip-img-7346\" or \"aud-music\"). */\n clipLayerId: string;\n /** Which track this clip lives on — VIDEO_TRACK_ID or AUDIO_TRACK_ID. */\n trackId: string;\n /** Asset src — e.g. \"media/IMG_7346.MOV\" or \"media/track.mp3\". */\n sourceName: string;\n /** Source duration in seconds (used for sourceEnd default). */\n clipDurationSeconds: number;\n /** Source duration in frames — caller pre-converts to avoid fps coupling. */\n clipDurationFrames: number;\n /** Clip kind. */\n kind: \"video\" | \"audio\";\n /** Explicit composition-frame override; defaults to endOfTrackFrame(). */\n startFrame?: number;\n /** Seconds into source to begin from; default 0. */\n sourceOffset?: number;\n /** Seconds into source to stop at; default clipDurationSeconds. */\n sourceEnd?: number;\n}\n\n/**\n * Idempotent clip-layer placement, generalized for video AND audio.\n *\n * - If a layer with `clipLayerId` already exists: leave it untouched and\n * return `{ addedLayer: false }`. Idempotence preserves user trim / volume\n * on re-ingest of the same source.\n * - Otherwise: lazy-create the track group (via `ensureTrackLayer`) then\n * append a new VideoVisual or AudioVisual clip parented to that track.\n *\n * For video clips: bounds match the canvas, objectFit defaults to \"contain\".\n * For audio clips: bounds are 0×0 (no visible footprint).\n */\nexport function ensureClipLayer(\n doc: AtelierDocument,\n args: EnsureClipArgs,\n): { doc: AtelierDocument; addedLayer: boolean; clipLayerId: string } {\n const existing = doc.layers.find((l) => l.id === args.clipLayerId);\n if (existing) {\n return { doc, addedLayer: false, clipLayerId: args.clipLayerId };\n }\n\n // Lazy-create the track group if needed.\n const ensured = ensureTrackLayer(doc, args.trackId, args.kind);\n const working = ensured.doc;\n\n // Compute end-of-track startFrame when no override supplied.\n const startFrame =\n args.startFrame !== undefined\n ? args.startFrame\n : endOfTrackFrame(working.layers, args.trackId, working.canvas.fps);\n\n const sourceOffset = args.sourceOffset ?? 0;\n const sourceEnd = args.sourceEnd ?? args.clipDurationSeconds;\n\n // Register an asset entry keyed by the layer id (one-to-one).\n const newAsset: Asset = {\n type: args.kind === \"video\" ? \"video\" : \"audio\",\n src: args.sourceName,\n description: `Source: ${args.sourceName}`,\n };\n const assets: Record<string, Asset> = {\n ...(working.assets ?? {}),\n [args.clipLayerId]: newAsset,\n };\n\n let newClip: Layer;\n if (args.kind === \"video\") {\n const visual: VideoVisual = {\n type: \"video\",\n assetId: args.clipLayerId,\n src: args.sourceName,\n startFrame,\n sourceOffset,\n sourceEnd,\n playbackRate: 1.0,\n objectFit: \"contain\",\n };\n newClip = {\n id: args.clipLayerId,\n parentId: args.trackId,\n visual,\n frame: { x: 0, y: 0 },\n bounds: { width: working.canvas.width, height: working.canvas.height },\n };\n } else {\n // Audio: no objectFit / no playbackRate default; zero-bounds (no visible).\n const visual: AudioVisual = {\n type: \"audio\",\n assetId: args.clipLayerId,\n src: args.sourceName,\n startFrame,\n sourceOffset,\n sourceEnd,\n };\n newClip = {\n id: args.clipLayerId,\n parentId: args.trackId,\n visual,\n frame: { x: 0, y: 0 },\n bounds: { width: 0, height: 0 },\n };\n }\n\n return {\n doc: {\n ...working,\n assets,\n layers: [...working.layers, newClip],\n },\n addedLayer: true,\n clipLayerId: args.clipLayerId,\n };\n}\n\n/**\n * Recompute the derived `timeline` summary block from current track + clip\n * layers. Walks every Group layer tagged \"track\" and emits one\n * TimelineTrack per group; each track's `kind` is read from its tags.\n *\n * Children are filtered to match the track's kind — video clips on the\n * video track, audio clips on the audio track. Cross-kind children are\n * skipped (the schema can't enforce this, but tooling should).\n *\n * Preserves byte-stability for legacy single-source projects: if no track\n * groups exist, the doc's timeline field is dropped (returns the doc\n * unchanged when it was already absent).\n */\nexport function recomputeTimeline(doc: AtelierDocument): AtelierDocument {\n const fps = doc.canvas.fps;\n\n const trackGroups = doc.layers.filter(\n (l) => l.visual.type === \"group\" && (l.tags ?? []).includes(\"track\"),\n );\n\n if (trackGroups.length === 0) {\n // Strip any stale timeline summary so we don't leave a misleading block.\n if (doc.timeline === undefined) return doc;\n const { timeline: _omit, ...rest } = doc;\n void _omit;\n return rest as AtelierDocument;\n }\n\n const tracks: TimelineTrack[] = [];\n let totalFrames = 0;\n\n for (const track of trackGroups) {\n const tags = track.tags ?? [];\n const kind: \"video\" | \"audio\" = tags.includes(\"audio\") ? \"audio\" : \"video\";\n\n const children = doc.layers.filter(\n (l) => l.parentId === track.id && l.visual.type === kind,\n );\n\n const clips: TimelineClip[] = children\n .map((layer) => {\n const v = layer.visual as VideoVisual | AudioVisual;\n const start = v.startFrame ?? 0;\n const offset = v.sourceOffset ?? 0;\n const end = v.sourceEnd ?? offset;\n const durFrames = Math.max(0, Math.round((end - offset) * fps));\n return {\n layerId: layer.id,\n startFrame: start,\n endFrame: start + durFrames,\n source: v.src ?? \"\",\n } satisfies TimelineClip;\n })\n .sort((a, b) => a.startFrame - b.startFrame);\n\n const trackEnd = clips.reduce((m, c) => Math.max(m, c.endFrame), 0);\n if (trackEnd > totalFrames) totalFrames = trackEnd;\n\n tracks.push({ id: track.id, kind, clips });\n }\n\n const timeline: Timeline = { fps, totalFrames, tracks };\n return { ...doc, timeline };\n}\n\n/**\n * Drop caption layers + their deltas for ONLY the given clip layer.\n * Caption layers belonging to OTHER clips pass through untouched —\n * the multi-clip preservation invariant.\n *\n * Also drops unparented caption layers (the legacy `rewriteCaptionLayers`\n * byproduct from #caption-builder). Caption layers parented to other\n * clips are preserved verbatim.\n */\nexport function stripStaleCaptionArtifacts(\n doc: AtelierDocument,\n clipLayerId: string,\n): AtelierDocument {\n const droppedIds = new Set<string>();\n const keptLayers = doc.layers.filter((layer) => {\n if (!(layer.tags ?? []).includes(\"caption\")) return true;\n // Unparented caption layer — legacy artifact, drop.\n if (!layer.parentId) {\n droppedIds.add(layer.id);\n return false;\n }\n // Caption belonging to THIS clip — drop, will be rebuilt.\n if (layer.parentId === clipLayerId) {\n droppedIds.add(layer.id);\n return false;\n }\n // Belongs to another clip — preserve.\n return true;\n });\n\n if (droppedIds.size === 0) return doc;\n\n const states: Record<string, State> = {};\n for (const [name, st] of Object.entries(doc.states)) {\n states[name] = {\n ...st,\n deltas: st.deltas.filter((d) => !droppedIds.has(d.layer)),\n };\n }\n return { ...doc, layers: keptLayers, states };\n}\n\n/**\n * Build + attach captions to a clip, parented and clip-local.\n *\n * Drops any prior `cap-<clipSlug>-*` layers for THIS clip + their deltas\n * before rebuilding (idempotent re-run safety). Other clips' captions are\n * untouched.\n *\n * Caption frame ranges are CLIP-LOCAL (transcript times are relative to the\n * source = 0); the renderer's parent-aware clip-local-time pass shifts them\n * into composition frames at render-time.\n *\n * `clipSlug` is derived from `clipLayerId` by stripping the leading\n * `clip-` prefix; fall back to the full id when the prefix isn't present.\n */\nexport function rebuildClipCaptions(\n doc: AtelierDocument,\n clipLayerId: string,\n transcript: VideoTranscript,\n canvas: AtelierDocument[\"canvas\"],\n captionOptions?: BuildCaptionsOptions,\n): AtelierDocument {\n const clipSlug = clipLayerId.startsWith(\"clip-\")\n ? clipLayerId.slice(\"clip-\".length)\n : clipLayerId;\n\n // 1. Drop any prior captions for THIS clip (idempotent re-run).\n let working = stripCaptionsForClip(doc, clipLayerId);\n\n // 2. Build fresh caption layers from the transcript.\n const built = buildCaptionLayers(transcript, canvas, captionOptions ?? {});\n\n // 3. Rename ids to `cap-<clipSlug>-N` and parent to the clip layer.\n const idMap = new Map<string, string>();\n const renamedLayers: Layer[] = built.layers.map((layer, idx) => {\n const newId = `cap-${clipSlug}-${idx}`;\n idMap.set(layer.id, newId);\n return { ...layer, id: newId, parentId: clipLayerId };\n });\n const renamedDeltas: Delta[] = built.deltas.map((d) => ({\n ...d,\n layer: idMap.get(d.layer) ?? d.layer,\n }));\n\n working = { ...working, layers: [...working.layers, ...renamedLayers] };\n\n // 4. Land caption deltas in the target state (\"default\" if present, else\n // first state, else a fresh \"default\").\n const stateNames = Object.keys(working.states);\n const targetStateName = stateNames.includes(\"default\")\n ? \"default\"\n : (stateNames[0] ?? \"default\");\n const states = { ...working.states };\n const existing = states[targetStateName];\n if (existing) {\n states[targetStateName] = {\n ...existing,\n deltas: [...existing.deltas, ...renamedDeltas],\n };\n } else {\n states[targetStateName] = { duration: 0, deltas: renamedDeltas };\n }\n\n return { ...working, states };\n}\n\n/**\n * Internal helper — drops caption layers + deltas tied SOLELY to the given\n * clip. Does NOT touch unparented captions or other clips' captions.\n * Used by rebuildClipCaptions to wipe its own prior output before\n * re-emitting.\n */\nfunction stripCaptionsForClip(\n doc: AtelierDocument,\n clipLayerId: string,\n): AtelierDocument {\n const droppedIds = new Set<string>();\n const keptLayers = doc.layers.filter((layer) => {\n if (!(layer.tags ?? []).includes(\"caption\")) return true;\n if (layer.parentId === clipLayerId) {\n droppedIds.add(layer.id);\n return false;\n }\n return true;\n });\n if (droppedIds.size === 0) return doc;\n\n const states: Record<string, State> = {};\n for (const [name, st] of Object.entries(doc.states)) {\n states[name] = {\n ...st,\n deltas: st.deltas.filter((d) => !droppedIds.has(d.layer)),\n };\n }\n return { ...doc, layers: keptLayers, states };\n}\n","// #compose-image-project — ingest a single image file into a kind:\"image\"\n// Atelier project.\n//\n// Single-image projects (TD-2026-05-27): an \"image\" project holds exactly one\n// ImageVisual layer. To swap the image, rename or delete the existing image\n// before adding another — composeImageProject REFUSES a second image rather\n// than overwriting silently. (Multi-image compositions are carousel projects.)\n//\n// Pipeline (additive; safe to re-run for the same image; refuses a second):\n// 1. Ensure `<projectDir>/media/` exists.\n// 2. Copy the source file into `media/<basename>` (skipped if dest exists).\n// 3. Read `project.atelier` (or scaffold a fresh kind:\"image\" doc).\n// 4. Validate: existing img-* layer with a DIFFERENT basename → throw.\n// Same basename → idempotent (return addedImageLayer:false).\n// 5. Add `img-<slug>` ImageVisual layer + asset entry, bounds = canvas.\n//\n// NO transcription. NO timeline block. Image-kind projects skip both —\n// that's what distinguishes them from video projects.\n\nimport { copyFileSync, existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { basename, dirname, join, resolve } from \"node:path\";\nimport type { AtelierDocument, Layer } from \"@a-company/atelier-types\";\nimport { readAtelierDoc, slugifyBasename } from \"./compose-video-project.js\";\n\nexport interface ComposeImageProjectOptions {\n /** Absolute path to project root (contains or will contain the doc at docPath). */\n projectDir: string;\n /**\n * Project-relative path to the .atelier composition document this helper\n * reads/writes. Required — no default. Caller computes this from the\n * ingest slug (e.g. `\"hero-image.atelier\"`, `\"slides/slide-1.atelier\"`).\n * The parent directory is created on demand. The helper NEVER touches\n * `project.atelier` specifically; only this caller-supplied path.\n */\n docPath: string;\n /** Absolute path to the source image file. */\n sourceFile: string;\n /** Override target basename inside media/. Defaults to basename(sourceFile). */\n mediaBasename?: string;\n}\n\nexport interface ComposeImageProjectResult {\n doc: AtelierDocument;\n /** True iff a new ImageVisual layer was added (vs. doc already had one). */\n addedImageLayer: boolean;\n /** Slug-based layer id, e.g. \"img-photo-1\". One-to-one with assetId. */\n layerId: string;\n /** Same as layerId — the asset key. */\n assetId: string;\n}\n\n/**\n * Compose (or re-compose) an Atelier image project from a single source image.\n *\n * - Single-image invariant: a second, differently-named image throws.\n * - Idempotent on re-ingest of the same basename.\n * - No timeline, no transcript — image projects render as a single frame.\n */\nexport async function composeImageProject(\n opts: ComposeImageProjectOptions,\n): Promise<ComposeImageProjectResult> {\n const projectDir = resolve(opts.projectDir);\n const sourceAbs = resolve(opts.sourceFile);\n const mediaBasename = opts.mediaBasename ?? basename(sourceAbs);\n\n // 1. Ensure media/ dir exists.\n const mediaDir = `${projectDir}/media`;\n if (!existsSync(mediaDir)) mkdirSync(mediaDir, { recursive: true });\n\n // 2. Copy source into media/ (skip if already there).\n const mediaDest = `${mediaDir}/${mediaBasename}`;\n if (!existsSync(mediaDest)) {\n copyFileSync(sourceAbs, mediaDest);\n }\n\n // 3. Read or scaffold the .atelier doc at the caller-supplied docPath.\n const compositionPath = join(projectDir, opts.docPath);\n // Ensure parent dir exists for nested docPaths like \"slides/slide-1.atelier\".\n mkdirSync(dirname(compositionPath), { recursive: true });\n let doc: AtelierDocument;\n if (existsSync(compositionPath)) {\n doc = readAtelierDoc(compositionPath);\n } else {\n doc = {\n version: \"1.0\",\n name: basename(projectDir),\n kind: \"image\",\n canvas: { width: 1080, height: 1920, fps: 30 },\n layers: [],\n states: { default: { duration: 1, deltas: [] } },\n };\n }\n\n const slug = slugifyBasename(mediaBasename);\n if (!slug) {\n throw new Error(\n `composeImageProject: cannot derive a slug from \"${mediaBasename}\"`,\n );\n }\n const layerId = `img-${slug}`;\n const expectedSrc = `media/${mediaBasename}`;\n\n // 4. Single-image invariant: if the doc already has an img-* layer pointing\n // at a DIFFERENT basename, refuse. Compare by the src on disk\n // (media/<basename>) rather than by slug — different basenames CAN slug\n // to the same id, and the on-disk truth is what matters.\n const existingImageLayer = doc.layers.find((l) => {\n if (!l.id.startsWith(\"img-\")) return false;\n const v = l.visual as { type?: string; src?: string };\n return v?.type === \"image\";\n });\n if (existingImageLayer) {\n const existingSrc =\n (existingImageLayer.visual as { src?: string }).src ?? \"\";\n const existingBasename = basename(existingSrc);\n if (existingBasename !== mediaBasename) {\n throw new Error(\n \"image project already has an image; rename or delete the existing image before adding another\",\n );\n }\n // Same basename — idempotent re-ingest.\n return {\n doc,\n addedImageLayer: false,\n layerId: existingImageLayer.id,\n assetId: existingImageLayer.id,\n };\n }\n\n // 5. Add the image layer + asset entry.\n const newLayer: Layer = {\n id: layerId,\n tags: [\"image\"],\n description: mediaBasename,\n visual: {\n type: \"image\",\n assetId: layerId,\n src: expectedSrc,\n },\n frame: { x: 0, y: 0 },\n bounds: { width: doc.canvas.width, height: doc.canvas.height },\n };\n\n const assets = { ...(doc.assets ?? {}) };\n assets[layerId] = {\n type: \"image\",\n src: expectedSrc,\n description: mediaBasename,\n };\n\n doc = {\n ...doc,\n layers: [...doc.layers, newLayer],\n assets,\n };\n\n writeFileSync(compositionPath, JSON.stringify(doc, null, 2), \"utf-8\");\n\n return {\n doc,\n addedImageLayer: true,\n layerId,\n assetId: layerId,\n };\n}\n","// #compose-carousel-project — append/insert a slide ref into a kind:\"carousel\"\n// Atelier project (a manifest of ordered .atelier sub-documents).\n//\n// A carousel project's `slides[]` may point at any .atelier file in the\n// workspace (other carousels, video projects, image projects, ref-visual\n// composites). Cycle safety is non-negotiable: a carousel that refs itself —\n// directly or transitively — locks the resolver, so this helper consults\n// #ref-cycle (`detectCarouselCycle`) BEFORE writing any slide.\n//\n// Pipeline:\n// 1. Read or scaffold `<projectDir>/project.atelier` (kind:\"carousel\").\n// 2. Refuse if the existing doc is kind !== \"carousel\".\n// 3. Run #ref-cycle against (rootPath=projectDir/.atelier, candidate=slidePath,\n// workspaceRoot). Throw on cycle with the chain in the message.\n// 4. Compute the slide src as `relative(workspaceRoot, slidePath)`.\n// 5. Idempotent on duplicate src — return the existing slideIndex.\n// 6. Insert at `opts.insertAt` (default: append).\n//\n// NO layers added. NO transcription. The carousel doc owns no clip layers\n// of its own — render-time the exporter walks slides[] and stitches each\n// sub-document's render.\n\nimport { existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { basename, dirname, join, relative, resolve } from \"node:path\";\nimport type {\n AtelierDocument,\n SlideRef,\n SlideTransition,\n} from \"@a-company/atelier-types\";\nimport { readAtelierDoc } from \"./compose-video-project.js\";\nimport { detectCarouselCycle } from \"./ref-cycle.js\";\n\nexport interface ComposeCarouselProjectOptions {\n /** Absolute path to project root (contains or will contain the doc at docPath). */\n projectDir: string;\n /**\n * Project-relative path to the CAROUSEL .atelier composition document this\n * helper reads/writes. Required — no default. Distinct from `slidePath`,\n * which is the slide being added. Caller computes this from the ingest\n * slug (e.g. `\"gallery.atelier\"`). The parent directory is created on\n * demand. The helper NEVER touches `project.atelier` specifically; only\n * this caller-supplied path.\n */\n docPath: string;\n /** Absolute path to any .atelier file in the workspace — the slide to add. */\n slidePath: string;\n /** Workspace root used for cycle detection. Defaults to dirname(projectDir). */\n workspaceRoot?: string;\n /** Index to insert at; default = append. */\n insertAt?: number;\n transition?: SlideTransition;\n /** For image slides — frames to hold; ignored for video slides. */\n duration?: number;\n label?: string;\n}\n\nexport interface ComposeCarouselProjectResult {\n doc: AtelierDocument;\n slideIndex: number;\n addedSlide: boolean;\n}\n\n/**\n * Add a slide reference to a carousel project, cycle-safe.\n *\n * - Scaffolds a fresh kind:\"carousel\" doc if project.atelier is absent.\n * - Refuses to convert a non-carousel project.\n * - Refuses to introduce a cycle.\n * - Idempotent on duplicate slide src.\n */\nexport async function composeCarouselProject(\n opts: ComposeCarouselProjectOptions,\n): Promise<ComposeCarouselProjectResult> {\n const projectDir = resolve(opts.projectDir);\n const slidePath = resolve(opts.slidePath);\n const workspaceRoot = resolve(opts.workspaceRoot ?? dirname(projectDir));\n const compositionPath = join(projectDir, opts.docPath);\n // Ensure parent dir exists for nested docPaths like \"slides/gallery.atelier\".\n mkdirSync(dirname(compositionPath), { recursive: true });\n\n // 1. Read or scaffold.\n let doc: AtelierDocument;\n if (existsSync(compositionPath)) {\n doc = readAtelierDoc(compositionPath);\n } else {\n doc = {\n version: \"1.0\",\n name: basename(projectDir),\n kind: \"carousel\",\n canvas: { width: 1080, height: 1920, fps: 30 },\n layers: [],\n states: { default: { duration: 1, deltas: [] } },\n slides: [],\n };\n }\n\n // 2. Kind check.\n if (doc.kind !== undefined && doc.kind !== \"carousel\") {\n throw new Error(\"project is not a carousel; cannot add slides\");\n }\n\n // 3. Cycle check — root is THIS carousel, candidate is the new slide.\n const cycleResult = detectCarouselCycle(\n compositionPath,\n slidePath,\n workspaceRoot,\n );\n if (cycleResult.cycle) {\n const chain = cycleResult.path ?? [];\n throw new Error(`carousel cycle detected: ${chain.join(\" → \")}`);\n }\n\n // 4. Compute workspace-relative src.\n const computedSrc = relative(workspaceRoot, slidePath);\n\n // 5. Idempotence — duplicate src returns the existing index.\n const slides: SlideRef[] = doc.slides ? [...doc.slides] : [];\n const existingIndex = slides.findIndex((s) => s.src === computedSrc);\n if (existingIndex >= 0) {\n // Make sure the doc on disk has slides[] — if we just scaffolded a fresh\n // one, write it. But existence-check + read path means we only reach here\n // when an existing slide was found, so a write is unnecessary. Still,\n // we persist for consistency when we scaffolded above.\n if (!existsSync(compositionPath)) {\n doc = { ...doc, slides };\n writeFileSync(compositionPath, JSON.stringify(doc, null, 2), \"utf-8\");\n }\n return {\n doc: { ...doc, slides },\n addedSlide: false,\n slideIndex: existingIndex,\n };\n }\n\n // 6. Build the slide ref + insert.\n const slideRef: SlideRef = { src: computedSrc };\n if (opts.duration !== undefined) slideRef.duration = opts.duration;\n if (opts.transition !== undefined) slideRef.transition = opts.transition;\n if (opts.label !== undefined) slideRef.label = opts.label;\n\n let insertIndex: number;\n if (opts.insertAt !== undefined) {\n insertIndex = Math.max(0, Math.min(opts.insertAt, slides.length));\n slides.splice(insertIndex, 0, slideRef);\n } else {\n insertIndex = slides.length;\n slides.push(slideRef);\n }\n\n doc = { ...doc, slides };\n\n writeFileSync(compositionPath, JSON.stringify(doc, null, 2), \"utf-8\");\n\n return {\n doc,\n addedSlide: true,\n slideIndex: insertIndex,\n };\n}\n","// #ref-cycle — carousel + ref-visual cycle detector.\n//\n// A carousel project's slides[] may point at any `.atelier` file in the\n// workspace (including other carousels, video projects, and ref-visual\n// composites). Without cycle detection, a carousel that refs itself —\n// directly or transitively through any slide or RefVisual layer — locks the\n// resolver. Tooling that walks slides[].src or RefVisual.src MUST consult\n// this detector before adding a new slide / changing a ref.\n//\n// Behavior:\n// - Pure fs walk via #atelier-schema's parseAtelier (handles both YAML and\n// JSON .atelier files).\n// - Track visited absolute paths in a Set (DAG-tolerant — a diamond is not\n// a cycle).\n// - Each visited doc enumerates refs from:\n// (a) doc.slides?.[*].src\n// (b) doc.layers[*].visual where visual.type === \"ref\" → visual.src\n// - Resolve each ref relative to dirname(currentDocPath) first, then fall\n// back to workspaceRoot if not found. Missing paths emit a console.warn\n// and are skipped (not fatal — the walk continues).\n// - Cycle trigger: any resolved ref's absolute path equals rootPath OR\n// candidatePath. Returns { cycle: true, path: [...visited chain..., dup] }.\n// - Depth-limited at MAX_HOPS hops; gracefully returns { cycle: false }\n// when the limit is reached (defends against malformed graphs without\n// throwing).\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, isAbsolute, resolve } from \"node:path\";\nimport type { AtelierDocument, Layer, RefVisual } from \"@a-company/atelier-types\";\nimport { parseAtelier } from \"@a-company/atelier-schema\";\n\n/** Maximum traversal depth before bailing out — defends against pathological inputs. */\nconst MAX_HOPS = 64;\n\nexport interface CycleResult {\n cycle: boolean;\n /**\n * Visited path from rootPath → ... → the duplicate node. Present only\n * when `cycle: true`. The duplicate appears twice (start AND end of the\n * chain when self-ref, or end of chain matching the candidate / root).\n */\n path?: string[];\n}\n\n/**\n * Walks .atelier files via slides[].src AND any RefVisual layer's src,\n * collecting visited absolute paths. Returns `{ cycle: true, path }` if\n * `candidatePath` transitively refs `rootPath` (or itself).\n *\n * - Depth-limited (max 64 hops) — defends against malformed graphs.\n * - Missing slide path → returns `{ cycle: false }` with a console.warn.\n * - Non-carousel .atelier with a RefVisual layer → traversed for refs.\n */\nexport function detectCarouselCycle(\n rootPath: string,\n candidatePath: string,\n workspaceRoot: string,\n): CycleResult {\n const rootAbs = resolve(rootPath);\n const candAbs = resolve(candidatePath);\n const workspaceAbs = resolve(workspaceRoot);\n\n // Direct self-ref: candidatePath equals rootPath. The carousel is about to\n // add itself as a slide.\n if (rootAbs === candAbs) {\n return { cycle: true, path: [rootAbs, candAbs] };\n }\n\n // BFS-ish from candidate: walk the candidate's refs and check whether any\n // transitively-reachable doc points back at rootPath or candidatePath.\n // We track the path PER FRONTIER ENTRY so the returned chain reflects the\n // route by which the cycle was found, starting with rootPath → candidate →\n // ... → duplicate (the spec'd shape consumers can render to humans as\n // \"A refs B refs C refs A\").\n type Frame = { abs: string; chain: string[] };\n const frontier: Frame[] = [{ abs: candAbs, chain: [rootAbs, candAbs] }];\n const visited = new Set<string>();\n\n let hops = 0;\n while (frontier.length > 0) {\n if (hops++ >= MAX_HOPS) {\n // Graceful depth-limit bail. Refuse to claim a cycle we couldn't\n // confirm; downstream tooling should treat this as \"unknown\" and\n // likely refuse the operation, but that's a separate decision.\n return { cycle: false };\n }\n const current = frontier.shift()!;\n if (visited.has(current.abs)) continue;\n visited.add(current.abs);\n\n if (!existsSync(current.abs)) {\n console.warn(`detectCarouselCycle: missing referenced file ${current.abs}`);\n continue;\n }\n\n let doc: AtelierDocument;\n try {\n const raw = readFileSync(current.abs, \"utf-8\");\n const parsed = parseAtelier(raw);\n if (parsed.success) {\n doc = parsed.data as AtelierDocument;\n } else {\n // Malformed doc — try the JSON fallback (parity with compose-video-project).\n doc = JSON.parse(raw) as AtelierDocument;\n }\n } catch (err) {\n console.warn(\n `detectCarouselCycle: failed to parse ${current.abs}: ${(err as Error).message}`,\n );\n continue;\n }\n\n // Enumerate refs out of this doc.\n const refs: string[] = [];\n if (doc.slides) {\n for (const slide of doc.slides) {\n if (typeof slide.src === \"string\" && slide.src) refs.push(slide.src);\n }\n }\n if (doc.layers) {\n for (const layer of doc.layers) {\n const v = layer.visual as Layer[\"visual\"];\n if (v && (v as { type?: string }).type === \"ref\") {\n const r = v as RefVisual;\n if (typeof r.src === \"string\" && r.src) refs.push(r.src);\n }\n }\n }\n\n for (const ref of refs) {\n const resolved = resolveRef(ref, current.abs, workspaceAbs);\n if (resolved === undefined) {\n console.warn(\n `detectCarouselCycle: missing referenced file \"${ref}\" (from ${current.abs})`,\n );\n continue;\n }\n\n // CYCLE CHECK: did we just resolve to root or candidate?\n if (resolved === rootAbs || resolved === candAbs) {\n return { cycle: true, path: [...current.chain, resolved] };\n }\n\n if (!visited.has(resolved)) {\n frontier.push({ abs: resolved, chain: [...current.chain, resolved] });\n }\n }\n }\n\n return { cycle: false };\n}\n\n/**\n * Resolve a ref against the current doc's directory first, falling back to\n * the workspace root. Returns the absolute path or undefined when neither\n * candidate exists on disk.\n *\n * Absolute refs short-circuit straight to existsSync.\n */\nfunction resolveRef(\n ref: string,\n currentDocPath: string,\n workspaceRoot: string,\n): string | undefined {\n if (isAbsolute(ref)) {\n return existsSync(ref) ? resolve(ref) : undefined;\n }\n const docRelative = resolve(dirname(currentDocPath), ref);\n if (existsSync(docRelative)) return docRelative;\n const workspaceRelative = resolve(workspaceRoot, ref);\n if (existsSync(workspaceRelative)) return workspaceRelative;\n return undefined;\n}\n","// #ingest-dispatch — classify drag-drop media + route to the right destination.\n//\n// The studio HTTP server accepts an arbitrary file via `POST /api/media/ingest`\n// (see #studio). Two routing pathways live here:\n//\n// ── v1.0 media-bin pivot (Kit-A) ──────────────────────────────────────\n// `routeIngest(filename)` — NO TARGET path. Post-pivot (TD-2026-05-28) a drop\n// onto the sidebar / page level NO LONGER auto-creates a doc. Files land in\n// `media/` only; composition is explicit (`+ new doc` then sidebar-drag-to-\n// canvas). The dispatcher collapses to a 4-cell truth table:\n//\n// file kind → target\n// ──────────────────────────────\n// video → add-to-bin\n// image → add-to-bin\n// audio → add-to-bin\n// unsupported → reject\n//\n// Project kind no longer matters for the no-target ingest — the file is the\n// file. Composition decisions happen later via the canvas-area drop (which is\n// the target-set path below).\n// ──────────────────────────────────────────────────────────────────────\n//\n// `routeIngestWithTarget(filename, targetDocPath, targetDocKind)` — TARGET-SET\n// path. The browser canvas drop carries the loaded doc's path; routing is\n// then governed by the TARGET doc's kind. Unchanged by the pivot — this is\n// where actual composition happens.\n//\n// Pure / synchronous / no fs — the caller wires the routing decision to its\n// own composer calls and broadcast envelopes.\n\nimport { existsSync, readdirSync, statSync } from \"node:fs\";\nimport { extname, join } from \"node:path\";\nimport type { ProjectKind } from \"@a-company/atelier-types\";\nimport { readAtelierDoc, slugifyBasename } from \"./compose-video-project.js\";\n\n/** Classification result for a media filename. */\nexport type IngestKind = \"video\" | \"image\" | \"audio\" | \"unsupported\";\n\n/**\n * Routing decision for a no-target ingest (file lands in the media bin).\n *\n * Post-pivot (TD-2026-05-28): either the file is a supported media kind and\n * goes to `add-to-bin`, or it's unsupported and we reject. Project kind no\n * longer factors in — composition is a separate, explicit step.\n */\nexport interface BinIngestRoute {\n kind: IngestKind;\n target: \"add-to-bin\" | \"reject\";\n /** When target === \"reject\", the human-readable reason. */\n error?: string;\n}\n\n/**\n * Routing decision for an explicit-target ingest (canvas-area drop).\n *\n * The target-set path still uses the legacy four-way target enum since it\n * really does dispatch to compose-video / add-audio-clip / etc.\n */\nexport interface IngestRoute {\n kind: IngestKind;\n target: \"compose-video\" | \"compose-image\" | \"add-audio-clip\" | \"reject\";\n /** When target === \"reject\", the human-readable reason. */\n error?: string;\n /**\n * When set, the project-relative `.atelier` doc path the route resolved\n * to. Populated by `routeIngestWithTarget` for explicit canvas drops so the\n * server-side handler can pass it straight to the composer / audio-add path\n * without re-deriving.\n */\n docPath?: string;\n}\n\nconst VIDEO_EXTS = new Set([\".mp4\", \".mov\", \".webm\", \".mkv\", \".avi\"]);\nconst IMAGE_EXTS = new Set([\".png\", \".jpg\", \".jpeg\", \".webp\", \".gif\"]);\nconst AUDIO_EXTS = new Set([\".mp3\", \".wav\", \".m4a\", \".aac\", \".flac\", \".ogg\"]);\n\n/**\n * Classify a media filename by its extension. Case-insensitive — `.MOV` and\n * `.mov` both yield `\"video\"`. Returns `\"unsupported\"` for anything outside\n * the known video/image/audio extension lists, including files with no\n * extension at all.\n */\nexport function classifyMediaFile(filename: string): IngestKind {\n const ext = extname(filename).toLowerCase();\n if (!ext) return \"unsupported\";\n if (VIDEO_EXTS.has(ext)) return \"video\";\n if (IMAGE_EXTS.has(ext)) return \"image\";\n if (AUDIO_EXTS.has(ext)) return \"audio\";\n return \"unsupported\";\n}\n\n// ── v1.0 media-bin pivot (Kit-A) ──────────────────────────────────────\n/**\n * Route a no-target ingest into the project's media bin.\n *\n * Post-pivot (TD-2026-05-28) the no-target path doesn't care about project\n * kind — supported media always goes to `add-to-bin`, unsupported types are\n * rejected. Composition is a separate explicit step downstream.\n *\n * Mirrors `classifyMediaFile` for the kind detection; the routing decision is\n * a pure function of the filename.\n */\nexport function routeIngest(filename: string): BinIngestRoute {\n const kind = classifyMediaFile(filename);\n if (kind === \"unsupported\") {\n return { kind, target: \"reject\", error: \"file type not supported\" };\n }\n return { kind, target: \"add-to-bin\" };\n}\n// ──────────────────────────────────────────────────────────────────────\n\n/**\n * Routing matrix for explicit \"add to this specific doc\" drops (canvas-area\n * drag-drop, post-pivot). When the browser knows which doc is loaded in the\n * canvas, it sends `targetDocPath` + the target doc's `kind`; the routing\n * decision is then governed by the TARGET doc's kind — not the project's\n * inferred kind — because the user picked a specific doc.\n *\n * file kind target doc kind → target\n * ─────────────────────────────────────\n * video video → compose-video (docPath = targetDocPath)\n * audio video → add-audio-clip (docPath = targetDocPath)\n * image * → reject (v1)\n * video image / carousel → reject\n * audio image / carousel → reject\n * unsupported * → reject\n *\n * Pure / synchronous. The caller is responsible for verifying the target doc\n * exists on disk and reading its kind — this function only encodes the\n * compatibility matrix and produces the route decision.\n */\nexport function routeIngestWithTarget(\n filename: string,\n targetDocPath: string,\n targetDocKind: ProjectKind | undefined,\n): IngestRoute {\n const kind = classifyMediaFile(filename);\n\n if (kind === \"unsupported\") {\n return { kind, target: \"reject\", error: \"file type not supported\" };\n }\n\n // Caller must supply the target doc's kind once a path is set.\n if (targetDocKind === undefined) {\n return {\n kind,\n target: \"reject\",\n error: \"target doc kind unresolved\",\n };\n }\n\n if (kind === \"video\") {\n if (targetDocKind === \"video\") {\n return { kind, target: \"compose-video\", docPath: targetDocPath };\n }\n return {\n kind,\n target: \"reject\",\n error:\n targetDocKind === \"image\"\n ? \"cannot add video clip to an image doc\"\n : \"cannot add video clip to a carousel doc\",\n };\n }\n\n if (kind === \"audio\") {\n if (targetDocKind === \"video\") {\n return { kind, target: \"add-audio-clip\", docPath: targetDocPath };\n }\n return {\n kind,\n target: \"reject\",\n error:\n targetDocKind === \"image\"\n ? \"cannot add audio to an image doc\"\n : \"cannot add audio to a carousel doc\",\n };\n }\n\n // image — out of scope for v1 explicit-target drops; the user can swap an\n // image doc's source via rename, or compose a carousel via the slides API.\n return {\n kind,\n target: \"reject\",\n error: \"image drag-drop into an existing doc not supported in v1\",\n };\n}\n\n/** Cap on `<slug>-N` collision attempts before we give up. Mirrors createDoc. */\nconst MAX_INGEST_COLLISION_ATTEMPTS = 999;\n\n/**\n * Compute the project-relative `.atelier` doc path a drag-drop ingest should\n * target. Slugs the source basename, then collision-suffixes with `-1`, `-2`,\n * ... up to 999.\n *\n * Examples:\n * - `IMG_7346.MOV` → `img-7346.atelier`\n * - `IMG_7346.MOV` (when `img-7346.atelier` exists) → `img-7346-1.atelier`\n * - `media/B-roll Clip.mp4` → `b-roll-clip.atelier`\n *\n * Returns a project-relative filename (no subdirectory). The caller passes it\n * straight to `composeVideoProject({ docPath })` / `composeImageProject({ docPath })`.\n *\n * Post-pivot: every drag-drop creates its OWN .atelier doc — there is no\n * shared `project.atelier`. See Wave 3 retarget (TD-2026-05-27).\n */\nexport function computeIngestDocPath(\n projectDir: string,\n sourceFilename: string,\n): string {\n const slug = slugifyBasename(sourceFilename);\n if (!slug) {\n throw new Error(\n `computeIngestDocPath: cannot derive a slug from \"${sourceFilename}\"`,\n );\n }\n for (let attempt = 0; attempt <= MAX_INGEST_COLLISION_ATTEMPTS; attempt++) {\n const candidateStem = attempt === 0 ? slug : `${slug}-${attempt}`;\n const candidateName = `${candidateStem}.atelier`;\n const candidateAbs = join(projectDir, candidateName);\n if (!existsSync(candidateAbs)) {\n return candidateName;\n }\n }\n throw new Error(\"computeIngestDocPath: collision suffix exhausted (999 attempts)\");\n}\n\n/**\n * Locate the single kind:\"video\" doc inside a project so an audio drag-drop\n * can target it. Returns the project-relative path or `null` when there is\n * zero or more-than-one video doc.\n *\n * The lenient fallback for the audio-into-video routing post-pivot — there is\n * no studio header that tells the ingest handler \"which doc is loaded\", so we\n * unambiguously route only when the choice is unambiguous. The caller turns a\n * null into a 400 with \"open a video doc first\" so the creator knows why.\n *\n * Scope: project root + `slides/` subdir (mirrors `listDocs`). Reads each\n * candidate to discover its kind; non-parseable files are skipped silently.\n */\nexport function findSingleVideoDoc(projectDir: string): string | null {\n const matches: string[] = [];\n for (const sub of [\"\", \"slides\"] as const) {\n const dirAbs = sub ? join(projectDir, sub) : projectDir;\n let entries: string[];\n try {\n entries = readdirSync(dirAbs);\n } catch {\n continue;\n }\n for (const name of entries) {\n if (!name.endsWith(\".atelier\")) continue;\n if (name === \".atelier\") continue;\n const abs = join(dirAbs, name);\n try {\n const st = statSync(abs);\n if (!st.isFile()) continue;\n } catch {\n continue;\n }\n try {\n const doc = readAtelierDoc(abs);\n if (doc.kind === \"video\") {\n matches.push(sub ? `${sub}/${name}` : name);\n }\n } catch {\n // unparseable — skip\n }\n }\n }\n return matches.length === 1 ? matches[0]! : null;\n}\n","// #doc-management — create / duplicate / delete .atelier docs inside a\n// project, with workspace-wide cascade handling.\n//\n// Three public verbs that the studio's right-click menus + the MCP doc tools\n// share so the on-disk effect is identical regardless of entry point:\n//\n// - `createDoc` — scaffold a fresh kind:\"video\" / \"image\" / \"carousel\" doc\n// at the project root, OR a slide under `slides/` that also gets a\n// SlideRef appended to the carousel's host doc (cycle-safe via\n// `composeCarouselProject`).\n//\n// - `duplicateDoc` — copy an existing doc next to itself with a `-copy`\n// suffix (and `-copy-2`, `-copy-3`, ... on collisions). When the source\n// is a slide and `appendSlideRef: true` is passed, the new doc is also\n// appended into the carousel right after the source's slot.\n//\n// - `deleteDoc` — remove a doc file AND scrub any SlideRef across the\n// workspace that pointed at it. RefVisual layers pointing at the deleted\n// doc are NOT removed (the renderer tolerates missing refs) but are\n// reported as dangling so the UI can surface them.\n//\n// Post-pivot (Wave 1): every .atelier doc is equal. There is no privileged\n// \"project manifest\"; project.atelier is just a doc that happens to share its\n// dir's basename. Cascade walks therefore inspect EVERY .atelier under each\n// project subdir (root + slides/), not just the historical project.atelier.\n//\n// Cascade resolution mirrors `#ref-cycle`'s dual-base pattern: slide srcs\n// may be either project-relative (\"slides/foo.atelier\") or workspace-relative\n// (\"my-carousel/slides/foo.atelier\"). We try the owning project dir first,\n// fall back to workspaceRoot — and only when one of those resolves to the\n// absolute path of the doc under operation do we count it as a match.\n\nimport {\n existsSync,\n mkdirSync,\n readdirSync,\n statSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { basename, dirname, extname, isAbsolute, join, relative, resolve } from \"node:path\";\nimport type {\n AtelierDocument,\n Layer,\n RefVisual,\n SlideRef,\n} from \"@a-company/atelier-types\";\n\nimport {\n type CreateDocOptions,\n type DeleteDocOptions,\n type DocDeleteCascadeReport,\n type DocDuplicateReport,\n type DocKind,\n type DocListEntry,\n type DuplicateDocOptions,\n isValidDocName,\n} from \"./doc-management-types.js\";\nimport {\n readAtelierDoc,\n slugifyBasename,\n} from \"./compose-video-project.js\";\nimport { composeCarouselProject } from \"./compose-carousel-project.js\";\n\n/** Max collision-suffix attempts before we give up. */\nconst MAX_COLLISION_ATTEMPTS = 999;\n\n// ─── createDoc ──────────────────────────────────────────────────────\n\nexport interface CreateDocResult {\n doc: AtelierDocument;\n /** Project-relative. */\n docPath: string;\n /** Present when the new doc was appended as a SlideRef into a carousel. */\n appendedSlideIndex?: number;\n}\n\n/**\n * Scaffold a new .atelier doc inside a project.\n *\n * - Validates `opts.name` via `isValidDocName` (rejects path separators,\n * `..`, control bytes, etc.).\n * - Slugs the name via `slugifyBasename` for the filesystem path.\n * - Collision-suffixes with `-1`, `-2`, ... up to 999.\n * - For `asCarouselSlide: true`, writes under `slides/<slug>.atelier` AND\n * calls `composeCarouselProject` so the cycle check + SlideRef append\n * run as one indivisible step.\n */\nexport async function createDoc(opts: CreateDocOptions): Promise<CreateDocResult> {\n if (!isValidDocName(opts.name)) {\n throw new Error(\"invalid doc name\");\n }\n const slug = slugifyBasename(opts.name);\n if (!slug) {\n throw new Error(\"invalid doc name\");\n }\n\n const projectDir = resolve(opts.projectDir);\n const subdir = opts.asCarouselSlide ? \"slides\" : \"\";\n const subdirAbs = subdir ? join(projectDir, subdir) : projectDir;\n\n // Collision-suffix loop.\n let chosenName: string | undefined;\n let chosenAbs: string | undefined;\n for (let attempt = 0; attempt <= MAX_COLLISION_ATTEMPTS; attempt++) {\n const candidateStem = attempt === 0 ? slug : `${slug}-${attempt}`;\n const candidateName = `${candidateStem}.atelier`;\n const candidateAbs = join(subdirAbs, candidateName);\n if (!existsSync(candidateAbs)) {\n chosenName = candidateName;\n chosenAbs = candidateAbs;\n break;\n }\n }\n if (!chosenName || !chosenAbs) {\n throw new Error(\"collision suffix exhausted (999 attempts)\");\n }\n\n const docPath = subdir ? `${subdir}/${chosenName}` : chosenName;\n const docName = chosenName.slice(0, -\".atelier\".length);\n const doc = scaffoldDoc(docName, opts.kind);\n\n // mkdir parent if needed (slides/ for slide docs).\n if (subdir) {\n mkdirSync(subdirAbs, { recursive: true });\n }\n writeFileSync(chosenAbs, JSON.stringify(doc, null, 2), \"utf-8\");\n\n let appendedSlideIndex: number | undefined;\n if (opts.asCarouselSlide && opts.carouselDocPath) {\n // The carouselDocPath we accept is project-relative or absolute; we\n // derive both the host carousel's projectDir AND its in-project docPath\n // (post-pivot the host doc need not be project.atelier).\n const carouselAbs = isAbsolute(opts.carouselDocPath)\n ? opts.carouselDocPath\n : join(projectDir, opts.carouselDocPath);\n const carouselProjectDir = dirname(carouselAbs);\n const carouselDocRel = relative(carouselProjectDir, carouselAbs);\n try {\n const composeOpts: Parameters<typeof composeCarouselProject>[0] = {\n projectDir: carouselProjectDir,\n docPath: carouselDocRel,\n slidePath: chosenAbs,\n };\n if (opts.insertAt !== undefined) composeOpts.insertAt = opts.insertAt;\n const composeResult = await composeCarouselProject(composeOpts);\n appendedSlideIndex = composeResult.slideIndex;\n } catch (err) {\n // Cycle (or any compose failure) — roll back the new file so the\n // creator doesn't end up with an orphan slide doc on disk.\n try {\n unlinkSync(chosenAbs);\n } catch {\n // best-effort\n }\n throw err;\n }\n }\n\n return {\n doc,\n docPath,\n ...(appendedSlideIndex !== undefined ? { appendedSlideIndex } : {}),\n };\n}\n\n/** Build a fresh AtelierDocument of the given kind with sensible defaults. */\nfunction scaffoldDoc(name: string, kind: DocKind): AtelierDocument {\n const base: AtelierDocument = {\n version: \"1.0\",\n name,\n kind,\n canvas: { width: 1080, height: 1920, fps: 30 },\n layers: [],\n states: { default: { duration: 1, deltas: [] } },\n };\n if (kind === \"carousel\") {\n base.slides = [];\n }\n return base;\n}\n\n// ─── duplicateDoc ───────────────────────────────────────────────────\n\n/**\n * Duplicate an existing doc next to itself with a `-copy` (then `-copy-2`,\n * `-copy-3`, ...) suffix. Layer ids are preserved verbatim (they're\n * doc-local — no cross-doc id collision concern).\n *\n * `appendSlideRef: true` requires the source to live under `slides/`; we\n * also call `composeCarouselProject` for the append so the cycle detector\n * runs defensively even though duplicating a clean slide can't introduce a\n * new cycle.\n */\nexport async function duplicateDoc(opts: DuplicateDocOptions): Promise<DocDuplicateReport> {\n const projectDir = resolve(opts.projectDir);\n const sourceAbs = join(projectDir, opts.sourceDocPath);\n\n if (!existsSync(sourceAbs)) {\n throw new Error(\"source doc not found\");\n }\n\n const doc = readAtelierDoc(sourceAbs);\n\n // Compute new path at the SAME depth.\n const sourceDir = dirname(sourceAbs);\n const sourceBase = basename(sourceAbs);\n const sourceExt = extname(sourceBase); // \".atelier\"\n const sourceStem = sourceExt ? sourceBase.slice(0, -sourceExt.length) : sourceBase;\n\n let chosenAbs: string | undefined;\n let chosenStem: string | undefined;\n for (let attempt = 1; attempt <= MAX_COLLISION_ATTEMPTS; attempt++) {\n const candidateStem = attempt === 1 ? `${sourceStem}-copy` : `${sourceStem}-copy-${attempt}`;\n const candidateAbs = join(sourceDir, `${candidateStem}${sourceExt}`);\n if (!existsSync(candidateAbs)) {\n chosenAbs = candidateAbs;\n chosenStem = candidateStem;\n break;\n }\n }\n if (!chosenAbs || !chosenStem) {\n throw new Error(\"collision suffix exhausted (999 attempts)\");\n }\n\n // Compute project-relative newDocPath. Project root → just basename; under\n // slides/ → slides/<basename>; preserve directory depth verbatim.\n const newDocPath = sourceRelDir(opts.sourceDocPath)\n ? `${sourceRelDir(opts.sourceDocPath)}/${chosenStem}${sourceExt}`\n : `${chosenStem}${sourceExt}`;\n\n // Write the duplicate. Keep doc.name as-is for the duplicate; nothing in\n // the spec says we should rename it and the studio sidebar uses the\n // filename as the display label anyway.\n writeFileSync(chosenAbs, JSON.stringify(doc, null, 2), \"utf-8\");\n\n let appendedSlideIndex: number | undefined;\n if (opts.appendSlideRef) {\n const sourceDirRel = sourceRelDir(opts.sourceDocPath);\n if (sourceDirRel !== \"slides\") {\n // Roll back to avoid leaving an orphan.\n try {\n unlinkSync(chosenAbs);\n } catch {\n // best-effort\n }\n throw new Error(\"appendSlideRef requires a slide source\");\n }\n\n // Find the source's index in the carousel host doc (project.atelier in\n // the duplicateDoc path — pre-pivot behavior preserved here; non-manifest\n // hosts are out of scope for Unit B's duplicateDoc).\n const manifestPath = join(projectDir, \"project.atelier\");\n let sourceIndex = -1;\n if (existsSync(manifestPath)) {\n try {\n const manifest = readAtelierDoc(manifestPath);\n if (manifest.kind === \"carousel\" && Array.isArray(manifest.slides)) {\n const workspaceRoot = dirname(projectDir);\n for (let i = 0; i < manifest.slides.length; i++) {\n const ref = manifest.slides[i]!;\n const resolved = resolveSlideRef(ref.src, projectDir, workspaceRoot);\n if (resolved === sourceAbs) {\n sourceIndex = i;\n break;\n }\n }\n }\n } catch {\n // ignore — sourceIndex stays -1 and we'll append at end\n }\n }\n\n try {\n const composeOpts: Parameters<typeof composeCarouselProject>[0] = {\n projectDir,\n docPath: \"project.atelier\",\n slidePath: chosenAbs,\n };\n if (sourceIndex >= 0) composeOpts.insertAt = sourceIndex + 1;\n const composeResult = await composeCarouselProject(composeOpts);\n appendedSlideIndex = composeResult.slideIndex;\n } catch (err) {\n try {\n unlinkSync(chosenAbs);\n } catch {\n // best-effort\n }\n throw err;\n }\n }\n\n return {\n sourceDocPath: opts.sourceDocPath,\n newDocPath,\n ...(appendedSlideIndex !== undefined ? { appendedSlideIndex } : {}),\n };\n}\n\n/** Return the directory portion of a project-relative path, or \"\". */\nfunction sourceRelDir(relPath: string): string {\n const dir = dirname(relPath);\n return dir === \".\" ? \"\" : dir;\n}\n\n// ─── deleteDoc ──────────────────────────────────────────────────────\n\n/**\n * Delete a doc inside a project AND scrub any workspace-wide references:\n *\n * 1. Walk every project subdir of the workspace. For each project, walk\n * its root + `slides/` for *.atelier files (mirroring listDocs' scope).\n * 2. For each `.atelier` doc found: scan `slides[]` (if kind:carousel) for\n * refs that resolve to the absolute path of the doc being deleted;\n * splice those refs out, persist the updated doc.\n * 3. Same walk, but scan `layers[]` for RefVisual layers (`visual.type ==\n * \"ref\"`) whose `src` resolves to the same absolute path. Collect\n * these as `refLayersDangling` — do NOT remove them; the renderer\n * tolerates missing refs and we want the studio to surface dangling\n * refs as warnings rather than silently nuke them.\n * 4. Unlink the doc.\n *\n * Every .atelier is equal — there is no \"project manifest\" carve-out.\n */\nexport function deleteDoc(opts: DeleteDocOptions): DocDeleteCascadeReport {\n const workspaceAbs = resolve(opts.workspaceDir);\n const projectDir = join(workspaceAbs, opts.projectName);\n const docAbs = join(projectDir, opts.docPath);\n\n if (!existsSync(docAbs)) {\n throw new Error(\"doc not found\");\n }\n\n const slideRefsRemoved: Array<{ project: string; slideIndex: number }> = [];\n const refLayersDangling: Array<{ project: string; docPath: string; layerId: string }> = [];\n\n // Walk workspace subdirs for sibling projects (and the owning project too —\n // self-refs are valid cascade targets).\n let entries: string[];\n try {\n entries = readdirSync(workspaceAbs);\n } catch {\n entries = [];\n }\n for (const entry of entries) {\n if (entry.startsWith(\".\")) continue;\n const otherProjectDir = join(workspaceAbs, entry);\n let st;\n try {\n st = statSync(otherProjectDir);\n } catch {\n continue;\n }\n if (!st.isDirectory()) continue;\n\n // Enumerate all .atelier docs in this project (root + slides/).\n const projectDocs = enumerateProjectAtelierDocs(otherProjectDir);\n\n for (const { absPath, relPath } of projectDocs) {\n // Skip the doc being deleted — we'll unlink it at the end.\n if (absPath === docAbs) continue;\n\n let otherDoc: AtelierDocument;\n try {\n otherDoc = readAtelierDoc(absPath);\n } catch {\n continue;\n }\n\n // ── SlideRef scrub ─────────────────────────────────────────\n let docMutated = false;\n if (otherDoc.kind === \"carousel\" && Array.isArray(otherDoc.slides)) {\n const slides = otherDoc.slides;\n // First pass — collect PRE-removal indices of refs that match.\n const matchIndices: number[] = [];\n for (let i = 0; i < slides.length; i++) {\n const ref = slides[i]!;\n const resolved = resolveSlideRef(ref.src, otherProjectDir, workspaceAbs);\n if (resolved === docAbs) {\n matchIndices.push(i);\n slideRefsRemoved.push({ project: entry, slideIndex: i });\n }\n }\n if (matchIndices.length > 0) {\n const matchSet = new Set(matchIndices);\n const kept: SlideRef[] = slides.filter((_, i) => !matchSet.has(i));\n otherDoc = { ...otherDoc, slides: kept };\n docMutated = true;\n }\n }\n\n // ── RefVisual dangling scan (read-only) ────────────────────\n if (Array.isArray(otherDoc.layers)) {\n for (const layer of otherDoc.layers as Layer[]) {\n const v = layer.visual as Layer[\"visual\"];\n if (v && (v as { type?: string }).type === \"ref\") {\n const refVisual = v as RefVisual;\n if (typeof refVisual.src === \"string\" && refVisual.src.length > 0) {\n const resolved = resolveSlideRef(refVisual.src, otherProjectDir, workspaceAbs);\n if (resolved === docAbs) {\n refLayersDangling.push({\n project: entry,\n docPath: relPath,\n layerId: layer.id,\n });\n }\n }\n }\n }\n }\n\n if (docMutated) {\n writeFileSync(absPath, JSON.stringify(otherDoc, null, 2), \"utf-8\");\n }\n }\n }\n\n // Unlink the doc itself.\n unlinkSync(docAbs);\n\n return {\n docPath: opts.docPath,\n slideRefsRemoved,\n refLayersDangling,\n };\n}\n\n/**\n * Resolve a slide / ref src using the dual-base pattern from `#ref-cycle`:\n * try the doc's owning project dir first (project-relative srcs), then fall\n * back to the workspace root (workspace-relative srcs). Absolute refs are\n * short-circuited via `existsSync`. Returns the absolute resolved path or\n * undefined when neither candidate exists on disk.\n */\nfunction resolveSlideRef(\n src: string,\n ownerProjectDir: string,\n workspaceRoot: string,\n): string | undefined {\n if (isAbsolute(src)) {\n return existsSync(src) ? resolve(src) : undefined;\n }\n const projectRelative = resolve(ownerProjectDir, src);\n if (existsSync(projectRelative)) return projectRelative;\n const workspaceRelative = resolve(workspaceRoot, src);\n if (existsSync(workspaceRelative)) return workspaceRelative;\n return undefined;\n}\n\n// ─── listDocs ───────────────────────────────────────────────────────\n\n/**\n * Enumerate the .atelier docs belonging to a project for the studio sidebar.\n *\n * Post-pivot: every .atelier is equal. Returns a flat alphabetical list of\n * docs in the project's root + `slides/` subdir. The optional `kind` field\n * is filled in when the doc parses and has a top-level `kind`; otherwise\n * omitted.\n *\n * Scope is intentionally non-recursive (root + slides/ only) — per Arky's\n * v1 plan, deeper trees are out of scope for the sidebar.\n *\n * Hardening: the `.atelier/` directory at project root (paradigm artifact\n * sibling) matches `*.atelier` by name but is NOT a doc. We filter by\n * `statSync().isFile()` AND skip basename `.atelier` defensively.\n */\nexport function listDocs(projectDir: string): DocListEntry[] {\n const projectAbs = resolve(projectDir);\n const found = enumerateProjectAtelierDocs(projectAbs);\n\n const out: DocListEntry[] = found\n .map(({ absPath, relPath }) => {\n let kind: \"video\" | \"image\" | \"carousel\" | undefined;\n try {\n const doc = readAtelierDoc(absPath);\n kind = doc.kind;\n } catch {\n kind = undefined;\n }\n const entry: DocListEntry = { path: relPath };\n if (kind !== undefined) entry.kind = kind;\n return entry;\n })\n .sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));\n\n return out;\n}\n\n// ─── shared walk ────────────────────────────────────────────────────\n\ninterface FoundDoc {\n absPath: string;\n /** Project-relative, using forward slashes (\"slides/foo.atelier\"). */\n relPath: string;\n}\n\n/**\n * Enumerate every .atelier doc in a project's root + `slides/` subdir.\n *\n * Filters:\n * - must end with `.atelier`\n * - must NOT have basename exactly `.atelier` (defensive — paradigm dir)\n * - must be a regular file (statSync().isFile()) — the `.atelier/` directory\n * at project root would otherwise match the suffix\n *\n * Used by both `listDocs` (sidebar) and `deleteDoc` (cascade scrub) so the\n * two surfaces see the same set of docs.\n */\nfunction enumerateProjectAtelierDocs(projectAbs: string): FoundDoc[] {\n const out: FoundDoc[] = [];\n\n for (const sub of [\"\", \"slides\"]) {\n const dirAbs = sub ? join(projectAbs, sub) : projectAbs;\n let entries: string[];\n try {\n entries = readdirSync(dirAbs);\n } catch {\n continue;\n }\n for (const name of entries) {\n if (!name.endsWith(\".atelier\")) continue;\n if (name === \".atelier\") continue; // defense: paradigm dir basename\n const abs = join(dirAbs, name);\n try {\n const st = statSync(abs);\n if (!st.isFile()) continue;\n } catch {\n continue;\n }\n const relPath = sub ? `${sub}/${name}` : name;\n out.push({ absPath: abs, relPath });\n }\n }\n\n return out;\n}\n","// #doc-management-types — shared types + pure validators for the doc-management\n// bundle (create / duplicate / delete of .atelier docs inside a project).\n//\n// This module is the foundation that three parallel builders consume. It is\n// intentionally I/O-free and import-free: pure types and two pure functions.\n// Anything that touches disk, the doc schema, or the workspace lives in the\n// builder modules that depend on these contracts.\n//\n// Aegis posture mirrors `isValidMediaBasenameForRename` in `rename-media.ts`:\n// validators reject control bytes, path separators, parent-dir traversal,\n// hidden-dot leaders, and out-of-range lengths. Slugging of the human-typed\n// `name` into a filesystem-safe stem is the caller's concern (via\n// `slugifyBasename`); this module only validates the raw input.\n\n/**\n * Project kind that a NEW doc can be created as.\n *\n * Owner explicitly excluded \"blank\" from v1 — only the three named kinds.\n * Future v1.1 may add `undefined` for blank docs.\n */\nexport type DocKind = \"video\" | \"image\" | \"carousel\";\n\nexport interface CreateDocOptions {\n projectDir: string; // absolute\n name: string; // user-typed display name; slugged internally\n kind: DocKind;\n /** For carousel slides: place file under slides/ and append SlideRef to target carousel. */\n asCarouselSlide?: boolean;\n /** Required when asCarouselSlide:true — the carousel doc whose slides[] should receive the ref. Usually <projectDir>/project.atelier. */\n carouselDocPath?: string;\n /** Insertion index for SlideRef (default = append). */\n insertAt?: number;\n}\n\nexport interface DuplicateDocOptions {\n projectDir: string;\n /** Project-relative path of the doc to duplicate (e.g. \"slides/slide-1.atelier\" or \"scenes/intro.atelier\"). */\n sourceDocPath: string;\n /** When source is a slide, also append a new SlideRef pointing at the duplicate. */\n appendSlideRef?: boolean;\n}\n\nexport interface DeleteDocOptions {\n workspaceDir: string; // absolute — needed for workspace-wide SlideRef scrub\n projectName: string; // project subdir name; with workspaceDir gives projectDir\n /** Project-relative path of the doc to delete. */\n docPath: string;\n}\n\nexport interface DocListEntry {\n /** Project-relative path. */\n path: string;\n /** When determinable from the doc's `kind` field; undefined if doc kind is absent or unrecognized. */\n kind?: \"video\" | \"image\" | \"carousel\";\n}\n\nexport interface DocDeleteCascadeReport {\n /** Project-relative path of the doc that was deleted. */\n docPath: string;\n /** SlideRefs removed across the workspace. Each entry identifies the carousel project name + the index of the removed ref (PRE-removal index, in case multiple were removed). */\n slideRefsRemoved: Array<{ project: string; slideIndex: number }>;\n /** RefVisual layers in any .atelier across the workspace that still point at the deleted doc. These are NOT auto-removed; reported for surfacing as warnings. */\n refLayersDangling: Array<{ project: string; docPath: string; layerId: string }>;\n}\n\nexport interface DocDuplicateReport {\n /** Source doc path (project-relative). */\n sourceDocPath: string;\n /** New doc path (project-relative). */\n newDocPath: string;\n /** When appendSlideRef was set and the source is a carousel slide: the index of the appended SlideRef. */\n appendedSlideIndex?: number;\n}\n\n/**\n * Aegis surface — same posture as `isValidMediaBasenameForRename` in rename-media.ts.\n * Validates a user-typed doc display name.\n *\n * Rejects:\n * - empty / whitespace-only\n * - leading dot (hidden files)\n * - any `/`, `\\`, or `..` substring\n * - control bytes (NUL through 0x1f)\n * - longer than 200 characters\n *\n * The slug (kebab-case derivative) is computed elsewhere via `slugifyBasename`;\n * this validator only checks the raw human input.\n */\nexport function isValidDocName(name: unknown): name is string {\n if (typeof name !== \"string\") return false;\n const trimmed = name.trim();\n if (trimmed.length === 0) return false;\n if (trimmed.length > 200) return false;\n if (trimmed.startsWith(\".\")) return false;\n if (trimmed.includes(\"/\") || trimmed.includes(\"\\\\\") || trimmed.includes(\"..\")) return false;\n // Reject control bytes (NUL through 0x1f). Use the escaped form, not literal\n // bytes — matches the Aegis Finding 4 (TD-2026-05-27) note in rename-media.ts\n // so a future linter / auto-format pass cannot silently mangle the class.\n // eslint-disable-next-line no-control-regex\n if (/[\\x00-\\x1f]/.test(trimmed)) return false;\n return true;\n}"],"mappings":"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","names":["init_dist","import_zod","import_yaml","z","yamlParse","z","z","yamlParse","remainder","yamlText","yamlParse","yamlParse","z","init_dist","import_node_fs","import_node_path","import_node_fs","import_node_path","init_dist","readAndParse","createCanvas","renderFrame","import_node_fs","import_node_path","import_node_fs","import_node_path","init_dist","resolve","loadImage","createCanvas","readAndParse","import_node_fs","import_node_path","init_dist","colorToCSS","readAndParse","import_node_fs","import_node_path","r","g","b","a","isExpression","kfs","buildTransform","readAndParse","import_node_fs","import_node_path","init_dist","preloadImages","loadImage","ImageCache","resolve","createCanvas","renderFrame","readAndParse","import_node_fs","import_node_path","import_node_fs","import_node_path","import_yaml","parseYaml","stringifyYaml","parseDim","import_node_fs","import_node_path","readAndParse","import_node_fs","import_node_path","init_dist","readAndParse","import_node_fs","import_node_path","import_node_child_process","resolve","import_node_child_process","import_node_fs","import_node_path","import_node_os","unlinkSync","resolve","import_node_fs","import_node_path","formatResult","transcript","import_node_fs","import_node_path","import_node_child_process","import_node_fs","import_node_path","import_node_path","import_node_fs","import_node_fs","import_node_path","import_node_fs","import_node_path","import_node_fs","import_node_path","import_node_fs","import_node_path","stripYamlScalar","stripYamlScalar","import_node_fs","import_node_path","import_node_fs","import_node_path","stripExt","import_node_fs","import_node_path","import_node_fs","import_node_path","import_node_fs","import_node_path","import_node_fs","import_node_path","IMAGE_EXTS","import_node_fs","import_node_path"]}