@vibeframe/cli 0.52.1 → 0.54.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 (92) hide show
  1. package/LICENSE +21 -0
  2. package/dist/agent/tools/index.d.ts +1 -0
  3. package/dist/agent/tools/index.d.ts.map +1 -1
  4. package/dist/agent/tools/index.js +3 -0
  5. package/dist/agent/tools/index.js.map +1 -1
  6. package/dist/agent/tools/scene.d.ts +17 -0
  7. package/dist/agent/tools/scene.d.ts.map +1 -0
  8. package/dist/agent/tools/scene.js +263 -0
  9. package/dist/agent/tools/scene.js.map +1 -0
  10. package/dist/agent/tools/scene.test.d.ts +2 -0
  11. package/dist/agent/tools/scene.test.d.ts.map +1 -0
  12. package/dist/agent/tools/scene.test.js +139 -0
  13. package/dist/agent/tools/scene.test.js.map +1 -0
  14. package/dist/commands/_shared/scene-html-emit.d.ts +120 -0
  15. package/dist/commands/_shared/scene-html-emit.d.ts.map +1 -0
  16. package/dist/commands/_shared/scene-html-emit.js +422 -0
  17. package/dist/commands/_shared/scene-html-emit.js.map +1 -0
  18. package/dist/commands/_shared/scene-html-emit.test.d.ts +2 -0
  19. package/dist/commands/_shared/scene-html-emit.test.d.ts.map +1 -0
  20. package/dist/commands/_shared/scene-html-emit.test.js +396 -0
  21. package/dist/commands/_shared/scene-html-emit.test.js.map +1 -0
  22. package/dist/commands/_shared/scene-lint.d.ts +110 -0
  23. package/dist/commands/_shared/scene-lint.d.ts.map +1 -0
  24. package/dist/commands/_shared/scene-lint.js +207 -0
  25. package/dist/commands/_shared/scene-lint.js.map +1 -0
  26. package/dist/commands/_shared/scene-lint.test.d.ts +2 -0
  27. package/dist/commands/_shared/scene-lint.test.d.ts.map +1 -0
  28. package/dist/commands/_shared/scene-lint.test.js +208 -0
  29. package/dist/commands/_shared/scene-lint.test.js.map +1 -0
  30. package/dist/commands/_shared/scene-project.d.ts +101 -0
  31. package/dist/commands/_shared/scene-project.d.ts.map +1 -0
  32. package/dist/commands/_shared/scene-project.js +281 -0
  33. package/dist/commands/_shared/scene-project.js.map +1 -0
  34. package/dist/commands/_shared/scene-project.test.d.ts +2 -0
  35. package/dist/commands/_shared/scene-project.test.d.ts.map +1 -0
  36. package/dist/commands/_shared/scene-project.test.js +203 -0
  37. package/dist/commands/_shared/scene-project.test.js.map +1 -0
  38. package/dist/commands/_shared/scene-render.d.ts +79 -0
  39. package/dist/commands/_shared/scene-render.d.ts.map +1 -0
  40. package/dist/commands/_shared/scene-render.js +138 -0
  41. package/dist/commands/_shared/scene-render.js.map +1 -0
  42. package/dist/commands/_shared/scene-render.test.d.ts +2 -0
  43. package/dist/commands/_shared/scene-render.test.d.ts.map +1 -0
  44. package/dist/commands/_shared/scene-render.test.js +145 -0
  45. package/dist/commands/_shared/scene-render.test.js.map +1 -0
  46. package/dist/commands/_shared/segments-to-scenes.d.ts +85 -0
  47. package/dist/commands/_shared/segments-to-scenes.d.ts.map +1 -0
  48. package/dist/commands/_shared/segments-to-scenes.js +202 -0
  49. package/dist/commands/_shared/segments-to-scenes.js.map +1 -0
  50. package/dist/commands/_shared/segments-to-scenes.test.d.ts +2 -0
  51. package/dist/commands/_shared/segments-to-scenes.test.d.ts.map +1 -0
  52. package/dist/commands/_shared/segments-to-scenes.test.js +219 -0
  53. package/dist/commands/_shared/segments-to-scenes.test.js.map +1 -0
  54. package/dist/commands/_shared/tts-resolve.d.ts +75 -0
  55. package/dist/commands/_shared/tts-resolve.d.ts.map +1 -0
  56. package/dist/commands/_shared/tts-resolve.js +89 -0
  57. package/dist/commands/_shared/tts-resolve.js.map +1 -0
  58. package/dist/commands/_shared/tts-resolve.test.d.ts +6 -0
  59. package/dist/commands/_shared/tts-resolve.test.d.ts.map +1 -0
  60. package/dist/commands/_shared/tts-resolve.test.js +147 -0
  61. package/dist/commands/_shared/tts-resolve.test.js.map +1 -0
  62. package/dist/commands/ai-script-pipeline-cli.d.ts.map +1 -1
  63. package/dist/commands/ai-script-pipeline-cli.js +49 -0
  64. package/dist/commands/ai-script-pipeline-cli.js.map +1 -1
  65. package/dist/commands/ai-script-pipeline.d.ts +16 -0
  66. package/dist/commands/ai-script-pipeline.d.ts.map +1 -1
  67. package/dist/commands/ai-script-pipeline.js +28 -0
  68. package/dist/commands/ai-script-pipeline.js.map +1 -1
  69. package/dist/commands/ai.test.js +4 -1
  70. package/dist/commands/ai.test.js.map +1 -1
  71. package/dist/commands/scene.d.ts +86 -0
  72. package/dist/commands/scene.d.ts.map +1 -0
  73. package/dist/commands/scene.js +719 -0
  74. package/dist/commands/scene.js.map +1 -0
  75. package/dist/commands/scene.test.d.ts +2 -0
  76. package/dist/commands/scene.test.d.ts.map +1 -0
  77. package/dist/commands/scene.test.js +219 -0
  78. package/dist/commands/scene.test.js.map +1 -0
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/index.js +2 -0
  81. package/dist/index.js.map +1 -1
  82. package/dist/pipeline/renderers/chrome.d.ts +26 -0
  83. package/dist/pipeline/renderers/chrome.d.ts.map +1 -0
  84. package/dist/pipeline/renderers/chrome.js +48 -0
  85. package/dist/pipeline/renderers/chrome.js.map +1 -0
  86. package/dist/pipeline/renderers/hyperframes.d.ts.map +1 -1
  87. package/dist/pipeline/renderers/hyperframes.js +2 -32
  88. package/dist/pipeline/renderers/hyperframes.js.map +1 -1
  89. package/dist/utils/provider-resolver.d.ts.map +1 -1
  90. package/dist/utils/provider-resolver.js +6 -2
  91. package/dist/utils/provider-resolver.js.map +1 -1
  92. package/package.json +32 -13
@@ -0,0 +1,120 @@
1
+ /**
2
+ * @module _shared/scene-html-emit
3
+ *
4
+ * Pure HTML emitters for `vibe scene add`. Each preset produces a self-contained
5
+ * Hyperframes-compatible scene composition: a `<template>`-wrapped `<div
6
+ * data-composition-id>` with scoped CSS, optional audio/image, and a paused
7
+ * GSAP timeline registered on `window.__timelines`.
8
+ *
9
+ * Also exposes helpers that mutate the root index.html:
10
+ * - `nextSceneStart()` — sum existing clip end-times to choose the new start
11
+ * - `insertClipIntoRoot()` — splice a `<div class="clip">` reference before the
12
+ * root's closing div, preserving the user's hand-edited markup
13
+ * - `slugifySceneName()` — name → kebab-case scene id
14
+ *
15
+ * Pure functions only — no I/O. The orchestrating command (`scene add`) wires
16
+ * these to the filesystem.
17
+ */
18
+ export type ScenePreset = "simple" | "announcement" | "explainer" | "kinetic-type" | "product-shot";
19
+ export declare const SCENE_PRESETS: readonly ScenePreset[];
20
+ /**
21
+ * One word from a Whisper word-level transcript. Mirrors
22
+ * {@link import("@vibeframe/ai-providers").TranscriptWord} but is duplicated
23
+ * here so this module stays pure (no provider package import).
24
+ */
25
+ export interface SceneTranscriptWord {
26
+ text: string;
27
+ start: number;
28
+ end: number;
29
+ }
30
+ export interface EmitSceneInput {
31
+ /** Kebab-case scene id; appears in `data-composition-id` and template id. */
32
+ id: string;
33
+ preset: ScenePreset;
34
+ /** Canvas width in px (must match the root composition). */
35
+ width: number;
36
+ /** Canvas height in px (must match the root composition). */
37
+ height: number;
38
+ /** Visible composition duration in seconds. */
39
+ duration: number;
40
+ /** Headline text — required for non-simple presets; defaulted from id. */
41
+ headline?: string;
42
+ /** Secondary text (subhead/caption). Often the narration. */
43
+ subhead?: string;
44
+ /** Optional small label above the headline (explainer / product-shot). */
45
+ kicker?: string;
46
+ /** Project-relative image path (e.g. "assets/scene-intro.png"). */
47
+ imagePath?: string;
48
+ /** Project-relative narration audio path (e.g. "assets/narration-intro.mp3"). */
49
+ audioPath?: string;
50
+ /**
51
+ * Word-level timings (Whisper output) for narration sync. When supplied, the
52
+ * `simple`, `explainer`, and `kinetic-type` presets render each word as its
53
+ * own `<span class="word">` and animate them at their absolute audio start
54
+ * times via GSAP. `announcement` and `product-shot` ignore this field —
55
+ * their headlines are intentionally static.
56
+ */
57
+ transcript?: SceneTranscriptWord[];
58
+ }
59
+ /**
60
+ * Slugify a free-form scene name to a kebab-case id usable in HTML attributes
61
+ * and filenames. Strips diacritics, collapses whitespace, lowercases.
62
+ */
63
+ export declare function slugifySceneName(name: string): string;
64
+ /**
65
+ * Render a transcript as `<span class="word" data-i="N">…</span>` markup,
66
+ * separated by literal whitespace so kerning and wrapping behave naturally.
67
+ * Returns the empty string when there's nothing to render.
68
+ */
69
+ export declare function renderTranscriptSpans(transcript: SceneTranscriptWord[]): string;
70
+ /**
71
+ * Build absolute-timing GSAP tweens that fade each transcript word in at its
72
+ * narration `start` time. The `targetSelector` parametrises the scope so the
73
+ * same routine can drive `simple` (`.caption .word`), `explainer`
74
+ * (`#subtitle .word`), or `kinetic-type` (`.kinetic .word`).
75
+ *
76
+ * Each tween is `tl.fromTo(sel, {opacity:0,y:10}, {opacity:1,y:0,duration:0.18}, start)`
77
+ * — short enough to feel responsive at any speech rate, long enough to avoid
78
+ * popping at 30fps. Words clamp to non-negative starts.
79
+ */
80
+ export declare function buildTranscriptTweens(transcript: SceneTranscriptWord[], targetSelector: string): string;
81
+ /**
82
+ * Emit the full per-scene HTML. Returns a complete `<template>`-wrapped
83
+ * composition ready to write to `compositions/scene-<id>.html`.
84
+ */
85
+ export declare function emitSceneHtml(input: EmitSceneInput): string;
86
+ /**
87
+ * Sum existing `<div class="clip">` end-times to find where the next scene
88
+ * should start. Returns 0 for an empty root.
89
+ */
90
+ export declare function nextSceneStart(rootHtml: string): number;
91
+ export interface ClipReferenceInput {
92
+ /** Scene id — used to derive the composition src path. */
93
+ id: string;
94
+ start: number;
95
+ duration: number;
96
+ trackIndex?: number;
97
+ /** Override the default `compositions/scene-<id>.html` src. */
98
+ src?: string;
99
+ }
100
+ /** Build the `<div class="clip" data-composition-id=... data-composition-src=...>` reference string. */
101
+ export declare function buildClipReference(opts: ClipReferenceInput): string;
102
+ /**
103
+ * Insert a clip reference inside the root composition's `#root` div, just
104
+ * before its closing `</div>`. Throws if the expected closing tag isn't
105
+ * found — callers should surface this as a structured error so users know
106
+ * to inspect a hand-edited root.
107
+ *
108
+ * Also grows the root's `data-duration` to fit the new clip's end-time.
109
+ */
110
+ export declare function insertClipIntoRoot(rootHtml: string, clip: ClipReferenceInput): string;
111
+ /**
112
+ * Read the root composition's canvas dims by parsing `data-width`/`data-height`
113
+ * on the `#root` div. Returns null if either is missing — caller should fall
114
+ * back to the project's aspect config.
115
+ */
116
+ export declare function readRootDims(rootHtml: string): {
117
+ width: number;
118
+ height: number;
119
+ } | null;
120
+ //# sourceMappingURL=scene-html-emit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-html-emit.d.ts","sourceRoot":"","sources":["../../../src/commands/_shared/scene-html-emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,WAAW,GACnB,QAAQ,GACR,cAAc,GACd,WAAW,GACX,cAAc,GACd,cAAc,CAAC;AAEnB,eAAO,MAAM,aAAa,EAAE,SAAS,WAAW,EAMtC,CAAC;AAEX;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,cAAc;IAC7B,6EAA6E;IAC7E,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,WAAW,CAAC;IACpB,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,mBAAmB,EAAE,CAAC;CACpC;AA4BD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASrD;AAMD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,mBAAmB,EAAE,GAAG,MAAM,CAI/E;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,mBAAmB,EAAE,EACjC,cAAc,EAAE,MAAM,GACrB,MAAM,CAQR;AAwPD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,CA2C3D;AAMD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CASvD;AAED,MAAM,WAAW,kBAAkB;IACjC,0DAA0D;IAC1D,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,wGAAwG;AACxG,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAMnE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAiCrF;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAKvF"}
@@ -0,0 +1,422 @@
1
+ export const SCENE_PRESETS = [
2
+ "simple",
3
+ "announcement",
4
+ "explainer",
5
+ "kinetic-type",
6
+ "product-shot",
7
+ ];
8
+ const GSAP_CDN = "https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js";
9
+ /** Minimal HTML escaper for text we inline into elements/attributes. */
10
+ function esc(text) {
11
+ return text
12
+ .replace(/&/g, "&amp;")
13
+ .replace(/</g, "&lt;")
14
+ .replace(/>/g, "&gt;")
15
+ .replace(/"/g, "&quot;")
16
+ .replace(/'/g, "&#39;");
17
+ }
18
+ /** Normalise whitespace and trim — used on user-supplied headlines/subheads. */
19
+ function clean(text) {
20
+ return (text ?? "").replace(/\s+/g, " ").trim();
21
+ }
22
+ /** Humanise a scene id — "product-shot" → "Product Shot". */
23
+ function humanise(id) {
24
+ return id
25
+ .split(/[-_]+/)
26
+ .filter(Boolean)
27
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
28
+ .join(" ");
29
+ }
30
+ /**
31
+ * Slugify a free-form scene name to a kebab-case id usable in HTML attributes
32
+ * and filenames. Strips diacritics, collapses whitespace, lowercases.
33
+ */
34
+ export function slugifySceneName(name) {
35
+ const normalised = name
36
+ .normalize("NFKD")
37
+ .replace(/[̀-ͯ]/g, "");
38
+ const slug = normalised
39
+ .toLowerCase()
40
+ .replace(/[^a-z0-9]+/g, "-")
41
+ .replace(/^-+|-+$/g, "");
42
+ return slug || "scene";
43
+ }
44
+ // ---------------------------------------------------------------------------
45
+ // Word-sync helpers
46
+ // ---------------------------------------------------------------------------
47
+ /**
48
+ * Render a transcript as `<span class="word" data-i="N">…</span>` markup,
49
+ * separated by literal whitespace so kerning and wrapping behave naturally.
50
+ * Returns the empty string when there's nothing to render.
51
+ */
52
+ export function renderTranscriptSpans(transcript) {
53
+ return transcript
54
+ .map((w, i) => `<span class="word" data-i="${i}">${esc(w.text)}</span>`)
55
+ .join(" ");
56
+ }
57
+ /**
58
+ * Build absolute-timing GSAP tweens that fade each transcript word in at its
59
+ * narration `start` time. The `targetSelector` parametrises the scope so the
60
+ * same routine can drive `simple` (`.caption .word`), `explainer`
61
+ * (`#subtitle .word`), or `kinetic-type` (`.kinetic .word`).
62
+ *
63
+ * Each tween is `tl.fromTo(sel, {opacity:0,y:10}, {opacity:1,y:0,duration:0.18}, start)`
64
+ * — short enough to feel responsive at any speech rate, long enough to avoid
65
+ * popping at 30fps. Words clamp to non-negative starts.
66
+ */
67
+ export function buildTranscriptTweens(transcript, targetSelector) {
68
+ return transcript
69
+ .map((w, i) => {
70
+ const start = Math.max(0, Number(w.start.toFixed(3)));
71
+ const sel = `${targetSelector}[data-i="${i}"]`;
72
+ return `tl.fromTo('${sel}', { opacity: 0, y: 10 }, { opacity: 1, y: 0, duration: 0.18, ease: 'power2.out' }, ${start});`;
73
+ })
74
+ .join("\n ");
75
+ }
76
+ function buildPreset(input) {
77
+ const id = input.id;
78
+ const scope = `[data-composition-id="${id}"]`;
79
+ const headline = clean(input.headline) || humanise(id);
80
+ const subhead = clean(input.subhead);
81
+ const kicker = clean(input.kicker);
82
+ const dur = input.duration;
83
+ const hasImage = !!input.imagePath;
84
+ const backdrop = hasImage
85
+ ? `${scope} .backdrop {
86
+ position: absolute;
87
+ inset: 0;
88
+ width: 100%;
89
+ height: 100%;
90
+ background-image: url("${esc(input.imagePath)}");
91
+ background-size: cover;
92
+ background-position: center;
93
+ }
94
+ ${scope} .backdrop::after {
95
+ content: "";
96
+ position: absolute;
97
+ inset: 0;
98
+ background: linear-gradient(180deg, rgba(0,0,0,0.0) 30%, rgba(0,0,0,0.55) 100%);
99
+ }`
100
+ : `${scope} .backdrop {
101
+ position: absolute;
102
+ inset: 0;
103
+ background: radial-gradient(ellipse at center, #1a1a2e 0%, #000 70%);
104
+ }`;
105
+ const backdropMarkup = `<div class="backdrop"></div>`;
106
+ switch (input.preset) {
107
+ case "simple": {
108
+ const transcript = input.transcript;
109
+ const useWordSync = !!(transcript && transcript.length > 0);
110
+ const captionText = subhead || headline;
111
+ const captionInner = useWordSync
112
+ ? renderTranscriptSpans(transcript)
113
+ : esc(captionText);
114
+ const wordCss = useWordSync
115
+ ? `\n ${scope} .caption .word { display: inline-block; opacity: 0; }`
116
+ : "";
117
+ const timeline = useWordSync
118
+ ? `${buildTranscriptTweens(transcript, `${scope} .caption .word`)}
119
+ tl.to('${scope} .caption', { opacity: 0, duration: 0.4, ease: 'power2.in' }, ${(dur - 0.4).toFixed(2)});`
120
+ : `tl.from('${scope} .caption', { opacity: 0, y: 28, duration: 0.6, ease: 'power2.out' }, 0.1);
121
+ tl.to('${scope} .caption', { opacity: 0, duration: 0.4, ease: 'power2.in' }, ${(dur - 0.4).toFixed(2)});`;
122
+ return {
123
+ css: `${scope} {
124
+ position: absolute; inset: 0; width: 100%; height: 100%;
125
+ font-family: -apple-system, "Helvetica Neue", Arial, sans-serif;
126
+ color: #fff; overflow: hidden; background: #000;
127
+ }
128
+ ${backdrop}
129
+ ${scope} .caption {
130
+ position: absolute;
131
+ left: 8%; right: 8%; bottom: 12%;
132
+ text-align: center;
133
+ font-size: 56px;
134
+ font-weight: 700;
135
+ line-height: 1.2;
136
+ text-shadow: 0 4px 20px rgba(0,0,0,0.65);
137
+ }${wordCss}`,
138
+ body: `${backdropMarkup}
139
+ <div class="caption" id="caption">${captionInner}</div>`,
140
+ timeline,
141
+ };
142
+ }
143
+ case "announcement": {
144
+ return {
145
+ css: `${scope} {
146
+ position: absolute; inset: 0; width: 100%; height: 100%;
147
+ font-family: -apple-system, "Helvetica Neue", Arial, sans-serif;
148
+ color: #fff; overflow: hidden; background: #000;
149
+ }
150
+ ${backdrop}
151
+ ${scope} .announce {
152
+ position: absolute; inset: 0;
153
+ display: flex; align-items: center; justify-content: center;
154
+ text-align: center;
155
+ padding: 0 8%;
156
+ }
157
+ ${scope} .announce h1 {
158
+ margin: 0;
159
+ font-size: 160px;
160
+ font-weight: 900;
161
+ letter-spacing: -4px;
162
+ line-height: 1;
163
+ background: linear-gradient(90deg, #8e2de2, #00c9ff);
164
+ -webkit-background-clip: text; background-clip: text; color: transparent;
165
+ text-shadow: 0 8px 40px rgba(142,45,226,0.35);
166
+ }`,
167
+ body: `${backdropMarkup}
168
+ <div class="announce"><h1 id="headline">${esc(headline)}</h1></div>`,
169
+ timeline: `tl.from('${scope} #headline', { opacity: 0, scale: 0.8, duration: 0.9, ease: 'back.out(1.6)' }, 0.15);
170
+ tl.to('${scope} #headline', { opacity: 0, duration: 0.4, ease: 'power2.in' }, ${(dur - 0.4).toFixed(2)});`,
171
+ };
172
+ }
173
+ case "explainer": {
174
+ const k = kicker || humanise(id).toUpperCase();
175
+ const sub = subhead || "";
176
+ const transcript = input.transcript;
177
+ const useWordSync = !!(transcript && transcript.length > 0 && sub);
178
+ const subtitleInner = useWordSync ? renderTranscriptSpans(transcript) : esc(sub);
179
+ const wordCss = useWordSync
180
+ ? `\n ${scope} #subtitle .word { display: inline-block; opacity: 0; }`
181
+ : "";
182
+ const subtitleTween = useWordSync
183
+ ? buildTranscriptTweens(transcript, `${scope} #subtitle .word`)
184
+ : sub
185
+ ? `tl.from('${scope} #subtitle', { opacity: 0, y: 30, duration: 0.55, ease: 'power3.out' }, 0.55);`
186
+ : "";
187
+ return {
188
+ css: `${scope} {
189
+ position: absolute; inset: 0; width: 100%; height: 100%;
190
+ font-family: -apple-system, "Helvetica Neue", Arial, sans-serif;
191
+ color: #fff; overflow: hidden; background: #000;
192
+ }
193
+ ${backdrop}
194
+ ${scope} .stage {
195
+ position: absolute; inset: 0;
196
+ display: flex; flex-direction: column; justify-content: center;
197
+ gap: 24px; padding: 0 10%;
198
+ }
199
+ ${scope} .kicker {
200
+ font-size: 28px; letter-spacing: 6px; text-transform: uppercase;
201
+ color: #00c9ff; font-weight: 600;
202
+ }
203
+ ${scope} .title {
204
+ font-size: 110px; font-weight: 800; letter-spacing: -2px;
205
+ line-height: 1.05; margin: 0;
206
+ }
207
+ ${scope} .subtitle {
208
+ font-size: 38px; font-weight: 300; color: #c0c0d0; max-width: 80%;
209
+ }${wordCss}`,
210
+ body: `${backdropMarkup}
211
+ <div class="stage">
212
+ <div class="kicker" id="kicker">${esc(k)}</div>
213
+ <h1 class="title" id="title">${esc(headline)}</h1>${sub ? `
214
+ <div class="subtitle" id="subtitle">${subtitleInner}</div>` : ""}
215
+ </div>`,
216
+ timeline: `tl.from('${scope} #kicker', { opacity: 0, y: 16, duration: 0.4, ease: 'power2.out' }, 0.1);
217
+ tl.from('${scope} #title', { opacity: 0, y: 60, duration: 0.7, ease: 'power3.out' }, 0.25);
218
+ ${subtitleTween}`,
219
+ };
220
+ }
221
+ case "kinetic-type": {
222
+ const transcript = input.transcript;
223
+ const useWordSync = !!(transcript && transcript.length > 0);
224
+ // When transcript is supplied, drive word entries from it (narration is
225
+ // the ground truth — what's spoken is what's shown). Otherwise fall back
226
+ // to splitting the headline.
227
+ const words = useWordSync
228
+ ? transcript.map((w) => w.text)
229
+ : headline.split(/\s+/).filter(Boolean);
230
+ const wordSpans = words
231
+ .map((w, i) => `<span class="word" data-i="${i}" id="w-${i}">${esc(w)}</span>`)
232
+ .join(" ");
233
+ const stagger = Math.max(0.08, Math.min(0.3, (dur - 0.6) / Math.max(words.length, 1)));
234
+ const tweens = useWordSync
235
+ ? transcript
236
+ .map((w, i) => {
237
+ const start = Math.max(0, Number(w.start.toFixed(3)));
238
+ return `tl.from('${scope} #w-${i}', { opacity: 0, y: 80, scale: 0.8, duration: 0.35, ease: 'back.out(1.8)' }, ${start});`;
239
+ })
240
+ .join("\n ")
241
+ : words
242
+ .map((_, i) => {
243
+ const start = (0.05 + i * stagger).toFixed(2);
244
+ return `tl.from('${scope} #w-${i}', { opacity: 0, y: 80, scale: 0.8, duration: 0.45, ease: 'back.out(1.8)' }, ${start});`;
245
+ })
246
+ .join("\n ");
247
+ return {
248
+ css: `${scope} {
249
+ position: absolute; inset: 0; width: 100%; height: 100%;
250
+ font-family: "Helvetica Neue", Arial, sans-serif;
251
+ color: #fff; overflow: hidden; background: #000;
252
+ }
253
+ ${backdrop}
254
+ ${scope} .kinetic {
255
+ position: absolute; inset: 0;
256
+ display: flex; align-items: center; justify-content: center;
257
+ text-align: center; padding: 0 6%;
258
+ font-size: 180px; font-weight: 900; letter-spacing: -6px;
259
+ line-height: 1; text-shadow: 0 6px 30px rgba(0,0,0,0.6);
260
+ }
261
+ ${scope} .kinetic .word { display: inline-block; margin: 0 12px; }`,
262
+ body: `${backdropMarkup}
263
+ <div class="kinetic">${wordSpans}</div>`,
264
+ timeline: tweens,
265
+ };
266
+ }
267
+ case "product-shot": {
268
+ const label = kicker || humanise(id);
269
+ return {
270
+ css: `${scope} {
271
+ position: absolute; inset: 0; width: 100%; height: 100%;
272
+ font-family: -apple-system, "Helvetica Neue", Arial, sans-serif;
273
+ color: #fff; overflow: hidden; background: #000;
274
+ }
275
+ ${backdrop}
276
+ ${scope} .backdrop { transform-origin: center; }
277
+ ${scope} .label {
278
+ position: absolute; top: 8%; left: 8%;
279
+ padding: 12px 24px; border-radius: 999px;
280
+ background: rgba(0,0,0,0.55); backdrop-filter: blur(6px);
281
+ font-size: 24px; font-weight: 600; letter-spacing: 2px;
282
+ text-transform: uppercase;
283
+ }
284
+ ${scope} .product-headline {
285
+ position: absolute; left: 8%; right: 8%; bottom: 14%;
286
+ font-size: 72px; font-weight: 800; letter-spacing: -1px;
287
+ line-height: 1.1; text-shadow: 0 4px 20px rgba(0,0,0,0.7);
288
+ }${subhead ? `
289
+ ${scope} .product-sub {
290
+ position: absolute; left: 8%; right: 8%; bottom: 8%;
291
+ font-size: 28px; font-weight: 400; color: #d0d0e0;
292
+ text-shadow: 0 2px 10px rgba(0,0,0,0.7);
293
+ }` : ""}`,
294
+ body: `${backdropMarkup}
295
+ <div class="label" id="label">${esc(label)}</div>
296
+ <div class="product-headline" id="headline">${esc(headline)}</div>${subhead ? `
297
+ <div class="product-sub" id="subhead">${esc(subhead)}</div>` : ""}`,
298
+ timeline: `tl.fromTo('${scope} .backdrop', { scale: 1.0 }, { scale: 1.08, duration: ${dur.toFixed(2)}, ease: 'none' }, 0);
299
+ tl.from('${scope} #label', { opacity: 0, x: -30, duration: 0.5, ease: 'power3.out' }, 0.2);
300
+ tl.from('${scope} #headline', { opacity: 0, y: 40, duration: 0.6, ease: 'power3.out' }, 0.4);${subhead ? `
301
+ tl.from('${scope} #subhead', { opacity: 0, y: 20, duration: 0.5, ease: 'power3.out' }, 0.65);` : ""}`,
302
+ };
303
+ }
304
+ }
305
+ }
306
+ /**
307
+ * Emit the full per-scene HTML. Returns a complete `<template>`-wrapped
308
+ * composition ready to write to `compositions/scene-<id>.html`.
309
+ */
310
+ export function emitSceneHtml(input) {
311
+ if (input.duration <= 0) {
312
+ throw new Error(`Scene duration must be > 0, got ${input.duration}`);
313
+ }
314
+ if (input.width <= 0 || input.height <= 0) {
315
+ throw new Error(`Invalid canvas dims: ${input.width}x${input.height}`);
316
+ }
317
+ const id = input.id;
318
+ const dur = Number(input.duration.toFixed(3));
319
+ const parts = buildPreset({ ...input, id, duration: dur });
320
+ const audioBlock = input.audioPath
321
+ ? `\n <audio
322
+ id="narration"
323
+ data-start="0"
324
+ data-duration="auto"
325
+ data-track-index="2"
326
+ src="${esc(input.audioPath)}"
327
+ data-volume="1"
328
+ ></audio>\n`
329
+ : "";
330
+ return `<template id="scene-${id}-template">
331
+ <div data-composition-id="${id}" data-start="0" data-duration="${dur}" data-width="${input.width}" data-height="${input.height}">
332
+ <style>
333
+ ${parts.css}
334
+ </style>
335
+
336
+ ${parts.body}
337
+ ${audioBlock}
338
+ <script src="${GSAP_CDN}"></script>
339
+ <script>
340
+ window.__timelines = window.__timelines || {};
341
+ const tl = gsap.timeline({ paused: true });
342
+
343
+ ${parts.timeline}
344
+
345
+ window.__timelines["${id}"] = tl;
346
+ </script>
347
+ </div>
348
+ </template>
349
+ `;
350
+ }
351
+ // ---------------------------------------------------------------------------
352
+ // Root index.html mutation
353
+ // ---------------------------------------------------------------------------
354
+ /**
355
+ * Sum existing `<div class="clip">` end-times to find where the next scene
356
+ * should start. Returns 0 for an empty root.
357
+ */
358
+ export function nextSceneStart(rootHtml) {
359
+ const clipRegex = /<div\s+class="clip"[^>]*?\sdata-start="([\d.]+)"[^>]*?\sdata-duration="([\d.]+)"/gi;
360
+ let maxEnd = 0;
361
+ let match;
362
+ while ((match = clipRegex.exec(rootHtml)) !== null) {
363
+ const end = parseFloat(match[1]) + parseFloat(match[2]);
364
+ if (Number.isFinite(end) && end > maxEnd)
365
+ maxEnd = end;
366
+ }
367
+ return maxEnd;
368
+ }
369
+ /** Build the `<div class="clip" data-composition-id=... data-composition-src=...>` reference string. */
370
+ export function buildClipReference(opts) {
371
+ const start = Number(opts.start.toFixed(3));
372
+ const duration = Number(opts.duration.toFixed(3));
373
+ const track = opts.trackIndex ?? 1;
374
+ const src = opts.src ?? `compositions/scene-${opts.id}.html`;
375
+ return `<div class="clip" data-composition-id="${esc(opts.id)}" data-composition-src="${esc(src)}" data-start="${start}" data-duration="${duration}" data-track-index="${track}"></div>`;
376
+ }
377
+ /**
378
+ * Insert a clip reference inside the root composition's `#root` div, just
379
+ * before its closing `</div>`. Throws if the expected closing tag isn't
380
+ * found — callers should surface this as a structured error so users know
381
+ * to inspect a hand-edited root.
382
+ *
383
+ * Also grows the root's `data-duration` to fit the new clip's end-time.
384
+ */
385
+ export function insertClipIntoRoot(rootHtml, clip) {
386
+ const clipDiv = buildClipReference(clip);
387
+ // Find the root closing tag. Match `</div>` followed by blank line + `<script>`,
388
+ // which matches `buildEmptyRootHtml` and any user-edited root that keeps the
389
+ // standard structure.
390
+ const closeMatch = rootHtml.match(/\n(\s*)<\/div>(\s*\n\s*<script>)/);
391
+ if (!closeMatch) {
392
+ throw new Error("Could not find root composition closing </div>. Ensure index.html follows the layout from `vibe scene init`.");
393
+ }
394
+ const closeIdx = closeMatch.index;
395
+ const closeIndent = closeMatch[1];
396
+ const childIndent = closeIndent + " ";
397
+ // Splice the clip just above the closing div, indented one level deeper.
398
+ const insertion = `\n${childIndent}${clipDiv}`;
399
+ let updated = rootHtml.slice(0, closeIdx) + insertion + rootHtml.slice(closeIdx);
400
+ // Grow root data-duration to fit the new clip end (never shrink — preserves
401
+ // user-set padding).
402
+ const newEnd = clip.start + clip.duration;
403
+ updated = updated.replace(/(<div\b[^>]*\bid="root"[^>]*\bdata-duration=")([\d.]+)(")/, (_full, prefix, value, suffix) => {
404
+ const current = parseFloat(value);
405
+ const next = Math.max(current, newEnd);
406
+ return `${prefix}${Number(next.toFixed(3))}${suffix}`;
407
+ });
408
+ return updated;
409
+ }
410
+ /**
411
+ * Read the root composition's canvas dims by parsing `data-width`/`data-height`
412
+ * on the `#root` div. Returns null if either is missing — caller should fall
413
+ * back to the project's aspect config.
414
+ */
415
+ export function readRootDims(rootHtml) {
416
+ const widthMatch = rootHtml.match(/<div\b[^>]*\bid="root"[^>]*\bdata-width="(\d+)"/);
417
+ const heightMatch = rootHtml.match(/<div\b[^>]*\bid="root"[^>]*\bdata-height="(\d+)"/);
418
+ if (!widthMatch || !heightMatch)
419
+ return null;
420
+ return { width: parseInt(widthMatch[1], 10), height: parseInt(heightMatch[1], 10) };
421
+ }
422
+ //# sourceMappingURL=scene-html-emit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-html-emit.js","sourceRoot":"","sources":["../../../src/commands/_shared/scene-html-emit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=scene-html-emit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-html-emit.test.d.ts","sourceRoot":"","sources":["../../../src/commands/_shared/scene-html-emit.test.ts"],"names":[],"mappings":""}