@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,396 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { buildClipReference, buildTranscriptTweens, emitSceneHtml, insertClipIntoRoot, nextSceneStart, readRootDims, renderTranscriptSpans, slugifySceneName, SCENE_PRESETS, } from "./scene-html-emit.js";
3
+ import { buildEmptyRootHtml } from "./scene-project.js";
4
+ // ── slugifySceneName ────────────────────────────────────────────────────────
5
+ describe("slugifySceneName", () => {
6
+ it.each([
7
+ ["intro", "intro"],
8
+ ["My Intro", "my-intro"],
9
+ ["Product Shot!", "product-shot"],
10
+ ["scene 3 hello", "scene-3-hello"],
11
+ ["__weird__name", "weird-name"],
12
+ ["", "scene"],
13
+ ["!!!", "scene"],
14
+ ])("%j → %j", (input, expected) => {
15
+ expect(slugifySceneName(input)).toBe(expected);
16
+ });
17
+ });
18
+ // ── nextSceneStart ──────────────────────────────────────────────────────────
19
+ describe("nextSceneStart", () => {
20
+ it("returns 0 for an empty root", () => {
21
+ const root = buildEmptyRootHtml({ aspect: "16:9", duration: 10 });
22
+ expect(nextSceneStart(root)).toBe(0);
23
+ });
24
+ it("returns the max end-time of existing clips", () => {
25
+ const root = `
26
+ <div class="clip" data-composition-src="a.html" data-start="0" data-duration="3" data-track-index="1"></div>
27
+ <div class="clip" data-composition-src="b.html" data-start="3" data-duration="2.5" data-track-index="1"></div>
28
+ `;
29
+ expect(nextSceneStart(root)).toBeCloseTo(5.5, 5);
30
+ });
31
+ it("ignores clips with malformed data-start", () => {
32
+ const root = `<div class="clip" data-composition-src="x.html" data-start="oops" data-duration="2" data-track-index="1"></div>`;
33
+ expect(nextSceneStart(root)).toBe(0);
34
+ });
35
+ });
36
+ // ── buildClipReference ──────────────────────────────────────────────────────
37
+ describe("buildClipReference", () => {
38
+ it("builds a Hyperframes-compatible clip div with data-composition-id", () => {
39
+ const out = buildClipReference({ id: "intro", start: 0, duration: 4 });
40
+ expect(out).toContain('class="clip"');
41
+ expect(out).toContain('data-composition-id="intro"');
42
+ expect(out).toContain('data-composition-src="compositions/scene-intro.html"');
43
+ expect(out).toContain('data-start="0"');
44
+ expect(out).toContain('data-duration="4"');
45
+ expect(out).toContain('data-track-index="1"');
46
+ });
47
+ it("respects an explicit track and src override and still emits data-composition-id", () => {
48
+ const out = buildClipReference({ id: "x", start: 1.234, duration: 2.345, trackIndex: 3, src: "custom/path.html" });
49
+ expect(out).toContain('data-composition-id="x"');
50
+ expect(out).toContain('data-composition-src="custom/path.html"');
51
+ expect(out).toContain('data-track-index="3"');
52
+ expect(out).toContain('data-start="1.234"');
53
+ expect(out).toContain('data-duration="2.345"');
54
+ });
55
+ });
56
+ // ── insertClipIntoRoot ──────────────────────────────────────────────────────
57
+ describe("insertClipIntoRoot", () => {
58
+ it("inserts a clip before the root closing div", () => {
59
+ const root = buildEmptyRootHtml({ aspect: "16:9", duration: 10 });
60
+ const updated = insertClipIntoRoot(root, { id: "intro", start: 0, duration: 4 });
61
+ expect(updated).toContain('<div class="clip" data-composition-id="intro" data-composition-src="compositions/scene-intro.html"');
62
+ // The new clip must appear *before* the root's closing </div>
63
+ const clipIdx = updated.indexOf("compositions/scene-intro.html");
64
+ const rootCloseIdx = updated.lastIndexOf("</div>\n\n <script>");
65
+ expect(clipIdx).toBeGreaterThan(0);
66
+ expect(rootCloseIdx).toBeGreaterThan(clipIdx);
67
+ });
68
+ it("grows root data-duration when clip end exceeds it but never shrinks", () => {
69
+ const root = buildEmptyRootHtml({ aspect: "16:9", duration: 10 });
70
+ const grown = insertClipIntoRoot(root, { id: "long", start: 0, duration: 14 });
71
+ expect(grown).toContain('data-duration="14"');
72
+ const stillShort = insertClipIntoRoot(root, { id: "short", start: 0, duration: 4 });
73
+ expect(stillShort).toContain('data-duration="10"');
74
+ });
75
+ it("appends sequentially and tracks the new start via nextSceneStart", () => {
76
+ let root = buildEmptyRootHtml({ aspect: "16:9", duration: 0 });
77
+ root = insertClipIntoRoot(root, { id: "a", start: 0, duration: 3 });
78
+ expect(nextSceneStart(root)).toBe(3);
79
+ root = insertClipIntoRoot(root, { id: "b", start: 3, duration: 2 });
80
+ expect(nextSceneStart(root)).toBe(5);
81
+ expect(root).toContain('data-composition-src="compositions/scene-a.html"');
82
+ expect(root).toContain('data-composition-src="compositions/scene-b.html"');
83
+ });
84
+ it("throws on a root that lacks the expected closing tag", () => {
85
+ const broken = "<html><body>no root</body></html>";
86
+ expect(() => insertClipIntoRoot(broken, { id: "x", start: 0, duration: 1 })).toThrow(/closing/);
87
+ });
88
+ });
89
+ // ── readRootDims ────────────────────────────────────────────────────────────
90
+ describe("readRootDims", () => {
91
+ it("reads dims from the standard scaffold", () => {
92
+ const root = buildEmptyRootHtml({ aspect: "9:16", duration: 8 });
93
+ expect(readRootDims(root)).toEqual({ width: 1080, height: 1920 });
94
+ });
95
+ it("returns null when dims are missing", () => {
96
+ expect(readRootDims('<div id="root"></div>')).toBeNull();
97
+ });
98
+ });
99
+ // ── emitSceneHtml — shared invariants ───────────────────────────────────────
100
+ const baseInput = {
101
+ id: "intro",
102
+ width: 1920,
103
+ height: 1080,
104
+ duration: 4,
105
+ headline: "Welcome to VibeFrame",
106
+ };
107
+ describe.each(SCENE_PRESETS)("emitSceneHtml(%s)", (preset) => {
108
+ it("emits a Hyperframes-compatible composition", () => {
109
+ const html = emitSceneHtml({ ...baseInput, preset });
110
+ expect(html).toContain('<template id="scene-intro-template">');
111
+ expect(html).toContain('data-composition-id="intro"');
112
+ expect(html).toContain('data-start="0"');
113
+ expect(html).toContain('data-duration="4"');
114
+ expect(html).toContain('data-width="1920"');
115
+ expect(html).toContain('data-height="1080"');
116
+ });
117
+ it("registers a paused timeline keyed by the scene id", () => {
118
+ const html = emitSceneHtml({ ...baseInput, preset });
119
+ expect(html).toContain("gsap.timeline({ paused: true })");
120
+ expect(html).toContain('window.__timelines["intro"] = tl');
121
+ });
122
+ it("includes the audio element only when audioPath is given", () => {
123
+ const without = emitSceneHtml({ ...baseInput, preset });
124
+ expect(without).not.toContain("<audio");
125
+ const withAudio = emitSceneHtml({ ...baseInput, preset, audioPath: "assets/narration-intro.mp3" });
126
+ expect(withAudio).toContain('<audio');
127
+ expect(withAudio).toContain('src="assets/narration-intro.mp3"');
128
+ expect(withAudio).toContain('data-track-index="2"');
129
+ });
130
+ it("includes the backdrop image only when imagePath is given", () => {
131
+ const without = emitSceneHtml({ ...baseInput, preset });
132
+ expect(without).not.toContain("background-image: url(");
133
+ const withImage = emitSceneHtml({ ...baseInput, preset, imagePath: "assets/scene-intro.png" });
134
+ expect(withImage).toContain('background-image: url("assets/scene-intro.png")');
135
+ });
136
+ });
137
+ // ── emitSceneHtml — preset specifics ────────────────────────────────────────
138
+ describe("emitSceneHtml — preset specifics", () => {
139
+ it("simple: shows narration as the caption", () => {
140
+ const html = emitSceneHtml({ ...baseInput, preset: "simple", subhead: "Hello world" });
141
+ expect(html).toContain("Hello world");
142
+ expect(html).toContain('id="caption"');
143
+ });
144
+ it("announcement: renders headline as the centered h1", () => {
145
+ const html = emitSceneHtml({ ...baseInput, preset: "announcement" });
146
+ expect(html).toMatch(/<h1[^>]*id="headline">\s*Welcome to VibeFrame\s*<\/h1>/);
147
+ });
148
+ it("explainer: renders kicker + title and includes subtitle when subhead is set", () => {
149
+ const html = emitSceneHtml({
150
+ ...baseInput,
151
+ preset: "explainer",
152
+ kicker: "Episode 1",
153
+ subhead: "Authoring videos with code",
154
+ });
155
+ expect(html).toContain('id="kicker"');
156
+ expect(html).toContain("Episode 1");
157
+ expect(html).toContain('id="subtitle"');
158
+ expect(html).toContain("Authoring videos with code");
159
+ });
160
+ it("explainer: omits the subtitle div when no subhead is provided", () => {
161
+ const html = emitSceneHtml({ ...baseInput, preset: "explainer" });
162
+ expect(html).not.toContain('id="subtitle"');
163
+ });
164
+ it("kinetic-type: emits one span per word", () => {
165
+ const html = emitSceneHtml({ ...baseInput, preset: "kinetic-type" });
166
+ // baseInput.headline = "Welcome to VibeFrame" → 3 words
167
+ expect(html).toContain('id="w-0"');
168
+ expect(html).toContain('id="w-1"');
169
+ expect(html).toContain('id="w-2"');
170
+ expect(html).not.toContain('id="w-3"');
171
+ });
172
+ it("product-shot: applies a Ken-Burns scale tween over the full duration", () => {
173
+ const html = emitSceneHtml({ ...baseInput, preset: "product-shot", duration: 6 });
174
+ expect(html).toContain("scale: 1.08, duration: 6.00");
175
+ });
176
+ it("escapes HTML metacharacters in headline + subhead", () => {
177
+ const html = emitSceneHtml({
178
+ id: "x",
179
+ preset: "simple",
180
+ width: 1920,
181
+ height: 1080,
182
+ duration: 3,
183
+ subhead: '<script>alert("xss")</script>',
184
+ });
185
+ expect(html).not.toContain("<script>alert");
186
+ expect(html).toContain("&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;");
187
+ });
188
+ it("rejects non-positive durations", () => {
189
+ expect(() => emitSceneHtml({ ...baseInput, preset: "simple", duration: 0 })).toThrow(/duration/);
190
+ expect(() => emitSceneHtml({ ...baseInput, preset: "simple", duration: -1 })).toThrow(/duration/);
191
+ });
192
+ it("falls back to humanised id when no headline is provided", () => {
193
+ const html = emitSceneHtml({
194
+ id: "product-shot",
195
+ preset: "announcement",
196
+ width: 1920,
197
+ height: 1080,
198
+ duration: 4,
199
+ });
200
+ expect(html).toContain("Product Shot");
201
+ });
202
+ });
203
+ // ── word-sync helpers ───────────────────────────────────────────────────────
204
+ describe("renderTranscriptSpans", () => {
205
+ it("emits one span per word with sequential data-i indices", () => {
206
+ const out = renderTranscriptSpans([
207
+ { text: "Hello", start: 0, end: 0.5 },
208
+ { text: "world.", start: 0.6, end: 1.0 },
209
+ ]);
210
+ expect(out).toBe(`<span class="word" data-i="0">Hello</span> <span class="word" data-i="1">world.</span>`);
211
+ });
212
+ it("escapes HTML metacharacters inside words", () => {
213
+ const out = renderTranscriptSpans([{ text: '"<x>&', start: 0, end: 0.5 }]);
214
+ expect(out).toContain("&quot;&lt;x&gt;&amp;");
215
+ });
216
+ it("returns empty string for an empty transcript", () => {
217
+ expect(renderTranscriptSpans([])).toBe("");
218
+ });
219
+ });
220
+ describe("buildTranscriptTweens", () => {
221
+ const words = [
222
+ { text: "Hello", start: 0, end: 0.5 },
223
+ { text: "world.", start: 0.6, end: 1.0 },
224
+ ];
225
+ it("emits one absolute-timed fromTo per word", () => {
226
+ const tweens = buildTranscriptTweens(words, "[data-composition-id=\"x\"] .caption .word");
227
+ expect(tweens).toContain(`tl.fromTo('[data-composition-id="x"] .caption .word[data-i="0"]'`);
228
+ expect(tweens).toContain(`tl.fromTo('[data-composition-id="x"] .caption .word[data-i="1"]'`);
229
+ // Absolute start times preserved
230
+ expect(tweens).toContain(", 0)"); // first word at 0
231
+ expect(tweens).toContain(", 0.6)"); // second word at 0.6
232
+ });
233
+ it("clamps negative start times to 0", () => {
234
+ const tweens = buildTranscriptTweens([{ text: "First", start: -0.05, end: 0.3 }], "x");
235
+ expect(tweens).toContain(", 0)");
236
+ });
237
+ });
238
+ // ── per-preset word-sync rendering ──────────────────────────────────────────
239
+ const transcriptFixture = [
240
+ { text: "Ship", start: 0.05, end: 0.5 },
241
+ { text: "videos,", start: 0.55, end: 1.1 },
242
+ { text: "not", start: 1.2, end: 1.4 },
243
+ { text: "clicks.", start: 1.45, end: 2.0 },
244
+ ];
245
+ describe("emitSceneHtml — word-sync rendering", () => {
246
+ describe("simple preset", () => {
247
+ it("renders captions as static text when transcript is absent", () => {
248
+ const html = emitSceneHtml({
249
+ id: "x",
250
+ preset: "simple",
251
+ width: 1920,
252
+ height: 1080,
253
+ duration: 3,
254
+ subhead: "Ship videos.",
255
+ });
256
+ expect(html).toContain('<div class="caption" id="caption">Ship videos.</div>');
257
+ expect(html).not.toContain('class="word"');
258
+ expect(html).not.toContain("tl.fromTo");
259
+ });
260
+ it("splits caption into word spans with absolute GSAP timing when transcript is present", () => {
261
+ const html = emitSceneHtml({
262
+ id: "x",
263
+ preset: "simple",
264
+ width: 1920,
265
+ height: 1080,
266
+ duration: 3,
267
+ subhead: "Ship videos, not clicks.",
268
+ transcript: transcriptFixture,
269
+ });
270
+ // Each word becomes its own span
271
+ expect(html).toContain('<span class="word" data-i="0">Ship</span>');
272
+ expect(html).toContain('<span class="word" data-i="3">clicks.</span>');
273
+ // GSAP timeline uses absolute start times
274
+ expect(html).toContain("tl.fromTo");
275
+ expect(html).toContain(", 0.05)"); // first word
276
+ expect(html).toContain(", 1.45)"); // last word
277
+ // Final fade-out preserved
278
+ expect(html).toContain(", 2.60)"); // dur (3) - 0.4 = 2.6
279
+ });
280
+ });
281
+ describe("explainer preset", () => {
282
+ it("renders subtitle as static text when transcript is absent", () => {
283
+ const html = emitSceneHtml({
284
+ id: "x",
285
+ preset: "explainer",
286
+ width: 1920,
287
+ height: 1080,
288
+ duration: 4,
289
+ headline: "Title",
290
+ subhead: "Subtitle text",
291
+ });
292
+ expect(html).toContain('<div class="subtitle" id="subtitle">Subtitle text</div>');
293
+ expect(html).not.toContain('class="word"');
294
+ });
295
+ it("splits subtitle into word spans when transcript is present (kicker/title stay static)", () => {
296
+ const html = emitSceneHtml({
297
+ id: "x",
298
+ preset: "explainer",
299
+ width: 1920,
300
+ height: 1080,
301
+ duration: 4,
302
+ kicker: "VIDEO AS CODE",
303
+ headline: "Author scenes, not timelines",
304
+ subhead: "Ship videos, not clicks.",
305
+ transcript: transcriptFixture,
306
+ });
307
+ // Subtitle becomes word spans
308
+ expect(html).toContain('<span class="word" data-i="0">Ship</span>');
309
+ // Kicker + title remain static
310
+ expect(html).toContain('<div class="kicker" id="kicker">VIDEO AS CODE</div>');
311
+ expect(html).toContain('<h1 class="title" id="title">Author scenes, not timelines</h1>');
312
+ // GSAP word-sync against #subtitle .word
313
+ expect(html).toContain(`#subtitle .word[data-i="0"]`);
314
+ });
315
+ it("falls back to static subtitle when transcript present but subhead empty", () => {
316
+ const html = emitSceneHtml({
317
+ id: "x",
318
+ preset: "explainer",
319
+ width: 1920,
320
+ height: 1080,
321
+ duration: 4,
322
+ headline: "Title",
323
+ transcript: transcriptFixture,
324
+ });
325
+ // No subtitle div, no word spans (CSS rules can still mention .subtitle)
326
+ expect(html).not.toContain('id="subtitle"');
327
+ expect(html).not.toContain('class="word"');
328
+ });
329
+ });
330
+ describe("kinetic-type preset", () => {
331
+ it("uses even stagger when transcript is absent", () => {
332
+ const html = emitSceneHtml({
333
+ id: "x",
334
+ preset: "kinetic-type",
335
+ width: 1920,
336
+ height: 1080,
337
+ duration: 3,
338
+ headline: "Ship videos",
339
+ });
340
+ expect(html).toContain('id="w-0"');
341
+ expect(html).toContain('id="w-1"');
342
+ // Stagger generates even start values, e.g. 0.05, 0.05+s, ...
343
+ expect(html).toMatch(/, 0\.\d{2}\)/);
344
+ });
345
+ it("uses absolute transcript timing when present (overrides headline source)", () => {
346
+ const html = emitSceneHtml({
347
+ id: "x",
348
+ preset: "kinetic-type",
349
+ width: 1920,
350
+ height: 1080,
351
+ duration: 3,
352
+ headline: "ignored when transcript supplied",
353
+ transcript: transcriptFixture,
354
+ });
355
+ // Transcript words become the visible text
356
+ expect(html).toContain(">Ship<");
357
+ expect(html).toContain(">clicks.<");
358
+ expect(html).not.toContain(">ignored<");
359
+ // Absolute timings present in tweens
360
+ expect(html).toContain(", 0.05)");
361
+ expect(html).toContain(", 1.45)");
362
+ });
363
+ });
364
+ describe("announcement preset (transcript ignored — static headline)", () => {
365
+ it("does not split headline even when transcript is present", () => {
366
+ const html = emitSceneHtml({
367
+ id: "x",
368
+ preset: "announcement",
369
+ width: 1920,
370
+ height: 1080,
371
+ duration: 3,
372
+ headline: "Big Headline",
373
+ transcript: transcriptFixture,
374
+ });
375
+ expect(html).toContain('<h1 id="headline">Big Headline</h1>');
376
+ expect(html).not.toContain('class="word"');
377
+ });
378
+ });
379
+ describe("product-shot preset (transcript ignored)", () => {
380
+ it("does not split headline even when transcript is present", () => {
381
+ const html = emitSceneHtml({
382
+ id: "x",
383
+ preset: "product-shot",
384
+ width: 1920,
385
+ height: 1080,
386
+ duration: 3,
387
+ headline: "Big Product",
388
+ transcript: transcriptFixture,
389
+ });
390
+ expect(html).toContain('id="headline"');
391
+ expect(html).toContain("Big Product");
392
+ expect(html).not.toContain('class="word"');
393
+ });
394
+ });
395
+ });
396
+ //# sourceMappingURL=scene-html-emit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-html-emit.test.js","sourceRoot":"","sources":["../../../src/commands/_shared/scene-html-emit.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,YAAY,EACZ,qBAAqB,EACrB,gBAAgB,EAChB,aAAa,GAGd,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,+EAA+E;AAE/E,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,IAAI,CAAC;QACN,CAAC,OAAO,EAAW,OAAO,CAAC;QAC3B,CAAC,UAAU,EAAQ,UAAU,CAAC;QAC9B,CAAC,eAAe,EAAG,cAAc,CAAC;QAClC,CAAC,gBAAgB,EAAE,eAAe,CAAC;QACnC,CAAC,eAAe,EAAE,YAAY,CAAC;QAC/B,CAAC,EAAE,EAAe,OAAO,CAAC;QAC1B,CAAC,KAAK,EAAY,OAAO,CAAC;KAC3B,CAAC,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE;QAChC,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,+EAA+E;AAE/E,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,IAAI,GAAG,kBAAkB,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QAClE,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,IAAI,GAAG;;;KAGZ,CAAC;QACF,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,IAAI,GAAG,iHAAiH,CAAC;QAC/H,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,+EAA+E;AAE/E,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;QAC3E,MAAM,GAAG,GAAG,kBAAkB,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;QACvE,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QACtC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,6BAA6B,CAAC,CAAC;QACrD,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,sDAAsD,CAAC,CAAC;QAC9E,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QACxC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QAC3C,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iFAAiF,EAAE,GAAG,EAAE;QACzF,MAAM,GAAG,GAAG,kBAAkB,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,GAAG,EAAE,kBAAkB,EAAE,CAAC,CAAC;QACnH,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAC;QACjD,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,yCAAyC,CAAC,CAAC;QACjE,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC;QAC9C,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;QAC5C,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,+EAA+E;AAE/E,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,IAAI,GAAG,kBAAkB,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QAClE,MAAM,OAAO,GAAG,kBAAkB,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;QACjF,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,oGAAoG,CAAC,CAAC;QAChI,8DAA8D;QAC9D,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,+BAA+B,CAAC,CAAC;QACjE,MAAM,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,wBAAwB,CAAC,CAAC;QACnE,MAAM,CAAC,OAAO,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACnC,MAAM,CAAC,YAAY,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,MAAM,IAAI,GAAG,kBAAkB,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QAClE,MAAM,KAAK,GAAG,kBAAkB,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QAC/E,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;QAE9C,MAAM,UAAU,GAAG,kBAAkB,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;QACpF,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,IAAI,IAAI,GAAG,kBAAkB,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;QAE/D,IAAI,GAAG,kBAAkB,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;QACpE,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAErC,IAAI,GAAG,kBAAkB,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;QACpE,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAErC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,kDAAkD,CAAC,CAAC;QAC3E,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,kDAAkD,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,MAAM,GAAG,mCAAmC,CAAC;QACnD,MAAM,CAAC,GAAG,EAAE,CAAC,kBAAkB,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAClG,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,+EAA+E;AAE/E,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,IAAI,GAAG,kBAAkB,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;QACjE,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,CAAC,YAAY,CAAC,uBAAuB,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC3D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,+EAA+E;AAE/E,MAAM,SAAS,GAAG;IAChB,EAAE,EAAE,OAAO;IACX,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,IAAI;IACZ,QAAQ,EAAE,CAAC;IACX,QAAQ,EAAE,sBAAsB;CACjC,CAAC;AAEF,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,mBAAmB,EAAE,CAAC,MAAmB,EAAE,EAAE;IACxE,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,IAAI,GAAG,aAAa,CAAC,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QACrD,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,sCAAsC,CAAC,CAAC;QAC/D,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,6BAA6B,CAAC,CAAC;QACtD,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QAC5C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QAC5C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,IAAI,GAAG,aAAa,CAAC,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QACrD,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,iCAAiC,CAAC,CAAC;QAC1D,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,kCAAkC,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,OAAO,GAAG,aAAa,CAAC,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QACxD,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAExC,MAAM,SAAS,GAAG,aAAa,CAAC,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,4BAA4B,EAAE,CAAC,CAAC;QACnG,MAAM,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACtC,MAAM,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,kCAAkC,CAAC,CAAC;QAChE,MAAM,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,OAAO,GAAG,aAAa,CAAC,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QACxD,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC;QAExD,MAAM,SAAS,GAAG,aAAa,CAAC,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,wBAAwB,EAAE,CAAC,CAAC;QAC/F,MAAM,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,iDAAiD,CAAC,CAAC;IACjF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,+EAA+E;AAE/E,QAAQ,CAAC,kCAAkC,EAAE,GAAG,EAAE;IAChD,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,IAAI,GAAG,aAAa,CAAC,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC;QACvF,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,IAAI,GAAG,aAAa,CAAC,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,CAAC;QACrE,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,wDAAwD,CAAC,CAAC;IACjF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6EAA6E,EAAE,GAAG,EAAE;QACrF,MAAM,IAAI,GAAG,aAAa,CAAC;YACzB,GAAG,SAAS;YACZ,MAAM,EAAE,WAAW;YACnB,MAAM,EAAE,WAAW;YACnB,OAAO,EAAE,4BAA4B;SACtC,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QACtC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACpC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QACxC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,4BAA4B,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,MAAM,IAAI,GAAG,aAAa,CAAC,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;QAClE,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,IAAI,GAAG,aAAa,CAAC,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,CAAC;QACrE,wDAAwD;QACxD,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sEAAsE,EAAE,GAAG,EAAE;QAC9E,MAAM,IAAI,GAAG,aAAa,CAAC,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;QAClF,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,6BAA6B,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,IAAI,GAAG,aAAa,CAAC;YACzB,EAAE,EAAE,GAAG;YACP,MAAM,EAAE,QAAQ;YAChB,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,IAAI;YACZ,QAAQ,EAAE,CAAC;YACX,OAAO,EAAE,+BAA+B;SACzC,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QAC5C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,qDAAqD,CAAC,CAAC;IAChF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACjG,MAAM,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACpG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,IAAI,GAAG,aAAa,CAAC;YACzB,EAAE,EAAE,cAAc;YAClB,MAAM,EAAE,cAAc;YACtB,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,IAAI;YACZ,QAAQ,EAAE,CAAC;SACZ,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,+EAA+E;AAE/E,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,GAAG,GAAG,qBAAqB,CAAC;YAChC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE;YACrC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE;SACzC,CAAC,CAAC;QACH,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CACd,wFAAwF,CACzF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,GAAG,GAAG,qBAAqB,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;QAC3E,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,MAAM,KAAK,GAA0B;QACnC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE;QACrC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE;KACzC,CAAC;IAEF,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,MAAM,GAAG,qBAAqB,CAAC,KAAK,EAAE,4CAA4C,CAAC,CAAC;QAC1F,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,kEAAkE,CAAC,CAAC;QAC7F,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,kEAAkE,CAAC,CAAC;QAC7F,iCAAiC;QACjC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAI,kBAAkB;QACvD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAE,qBAAqB;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,MAAM,GAAG,qBAAqB,CAClC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,EAC3C,GAAG,CACJ,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,+EAA+E;AAE/E,MAAM,iBAAiB,GAA0B;IAC/C,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE;IACvC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE;IAC1C,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE;IACrC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE;CAC3C,CAAC;AAEF,QAAQ,CAAC,qCAAqC,EAAE,GAAG,EAAE;IACnD,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;QAC7B,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;YACnE,MAAM,IAAI,GAAG,aAAa,CAAC;gBACzB,EAAE,EAAE,GAAG;gBACP,MAAM,EAAE,QAAQ;gBAChB,KAAK,EAAE,IAAI;gBACX,MAAM,EAAE,IAAI;gBACZ,QAAQ,EAAE,CAAC;gBACX,OAAO,EAAE,cAAc;aACxB,CAAC,CAAC;YACH,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,sDAAsD,CAAC,CAAC;YAC/E,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;YAC3C,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,qFAAqF,EAAE,GAAG,EAAE;YAC7F,MAAM,IAAI,GAAG,aAAa,CAAC;gBACzB,EAAE,EAAE,GAAG;gBACP,MAAM,EAAE,QAAQ;gBAChB,KAAK,EAAE,IAAI;gBACX,MAAM,EAAE,IAAI;gBACZ,QAAQ,EAAE,CAAC;gBACX,OAAO,EAAE,0BAA0B;gBACnC,UAAU,EAAE,iBAAiB;aAC9B,CAAC,CAAC;YACH,iCAAiC;YACjC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,2CAA2C,CAAC,CAAC;YACpE,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,8CAA8C,CAAC,CAAC;YACvE,0CAA0C;YAC1C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;YACpC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa;YAChD,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY;YAC/C,2BAA2B;YAC3B,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,sBAAsB;QAC3D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAChC,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;YACnE,MAAM,IAAI,GAAG,aAAa,CAAC;gBACzB,EAAE,EAAE,GAAG;gBACP,MAAM,EAAE,WAAW;gBACnB,KAAK,EAAE,IAAI;gBACX,MAAM,EAAE,IAAI;gBACZ,QAAQ,EAAE,CAAC;gBACX,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,eAAe;aACzB,CAAC,CAAC;YACH,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,yDAAyD,CAAC,CAAC;YAClF,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,uFAAuF,EAAE,GAAG,EAAE;YAC/F,MAAM,IAAI,GAAG,aAAa,CAAC;gBACzB,EAAE,EAAE,GAAG;gBACP,MAAM,EAAE,WAAW;gBACnB,KAAK,EAAE,IAAI;gBACX,MAAM,EAAE,IAAI;gBACZ,QAAQ,EAAE,CAAC;gBACX,MAAM,EAAE,eAAe;gBACvB,QAAQ,EAAE,8BAA8B;gBACxC,OAAO,EAAE,0BAA0B;gBACnC,UAAU,EAAE,iBAAiB;aAC9B,CAAC,CAAC;YACH,8BAA8B;YAC9B,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,2CAA2C,CAAC,CAAC;YACpE,+BAA+B;YAC/B,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,qDAAqD,CAAC,CAAC;YAC9E,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,gEAAgE,CAAC,CAAC;YACzF,yCAAyC;YACzC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,6BAA6B,CAAC,CAAC;QACxD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,yEAAyE,EAAE,GAAG,EAAE;YACjF,MAAM,IAAI,GAAG,aAAa,CAAC;gBACzB,EAAE,EAAE,GAAG;gBACP,MAAM,EAAE,WAAW;gBACnB,KAAK,EAAE,IAAI;gBACX,MAAM,EAAE,IAAI;gBACZ,QAAQ,EAAE,CAAC;gBACX,QAAQ,EAAE,OAAO;gBACjB,UAAU,EAAE,iBAAiB;aAC9B,CAAC,CAAC;YACH,yEAAyE;YACzE,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;YAC5C,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;QACnC,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;YACrD,MAAM,IAAI,GAAG,aAAa,CAAC;gBACzB,EAAE,EAAE,GAAG;gBACP,MAAM,EAAE,cAAc;gBACtB,KAAK,EAAE,IAAI;gBACX,MAAM,EAAE,IAAI;gBACZ,QAAQ,EAAE,CAAC;gBACX,QAAQ,EAAE,aAAa;aACxB,CAAC,CAAC;YACH,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;YACnC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;YACnC,8DAA8D;YAC9D,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0EAA0E,EAAE,GAAG,EAAE;YAClF,MAAM,IAAI,GAAG,aAAa,CAAC;gBACzB,EAAE,EAAE,GAAG;gBACP,MAAM,EAAE,cAAc;gBACtB,KAAK,EAAE,IAAI;gBACX,MAAM,EAAE,IAAI;gBACZ,QAAQ,EAAE,CAAC;gBACX,QAAQ,EAAE,kCAAkC;gBAC5C,UAAU,EAAE,iBAAiB;aAC9B,CAAC,CAAC;YACH,2CAA2C;YAC3C,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YACjC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;YACpC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;YACxC,qCAAqC;YACrC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;YAClC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,4DAA4D,EAAE,GAAG,EAAE;QAC1E,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;YACjE,MAAM,IAAI,GAAG,aAAa,CAAC;gBACzB,EAAE,EAAE,GAAG;gBACP,MAAM,EAAE,cAAc;gBACtB,KAAK,EAAE,IAAI;gBACX,MAAM,EAAE,IAAI;gBACZ,QAAQ,EAAE,CAAC;gBACX,QAAQ,EAAE,cAAc;gBACxB,UAAU,EAAE,iBAAiB;aAC9B,CAAC,CAAC;YACH,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,qCAAqC,CAAC,CAAC;YAC9D,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,0CAA0C,EAAE,GAAG,EAAE;QACxD,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;YACjE,MAAM,IAAI,GAAG,aAAa,CAAC;gBACzB,EAAE,EAAE,GAAG;gBACP,MAAM,EAAE,cAAc;gBACtB,KAAK,EAAE,IAAI;gBACX,MAAM,EAAE,IAAI;gBACZ,QAAQ,EAAE,CAAC;gBACX,QAAQ,EAAE,aAAa;gBACvB,UAAU,EAAE,iBAAiB;aAC9B,CAAC,CAAC;YACH,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;YACxC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * @module _shared/scene-lint
3
+ *
4
+ * In-process Hyperframes lint pipeline for VibeFrame scene projects. Wraps
5
+ * `runHyperframeLint` from `@hyperframes/producer` (its public lint entry
6
+ * point) and adapts the output to VibeFrame's two-tier file structure:
7
+ *
8
+ * project/
9
+ * index.html # root composition → linted as-is
10
+ * compositions/*.html # sub-compositions → producer's lint emits two
11
+ * spurious findings here, so
12
+ * we filter them out.
13
+ *
14
+ * Two findings are filtered for sub-compositions because they encode the
15
+ * inverse rule (sub-comps SHOULD be wrapped in `<template>` and SHOULD NOT
16
+ * have an `<html>` wrapper):
17
+ * - `standalone_composition_wrapped_in_template`
18
+ * - `root_composition_missing_html_wrapper`
19
+ *
20
+ * Pure helpers are exported for unit testing; `runProjectLint` does the
21
+ * filesystem walk + lint orchestration. Mechanical `--fix` is intentionally
22
+ * conservative — only `timed_element_missing_clip_class` is auto-fixed in
23
+ * MVP 1.
24
+ */
25
+ export type LintSeverity = "error" | "warning" | "info";
26
+ export interface LintFinding {
27
+ code: string;
28
+ severity: LintSeverity;
29
+ message: string;
30
+ file?: string;
31
+ selector?: string;
32
+ elementId?: string;
33
+ fixHint?: string;
34
+ snippet?: string;
35
+ }
36
+ export interface FileLintResult {
37
+ /** Path relative to the project directory. */
38
+ file: string;
39
+ /** True for files under `compositions/` — used to filter false positives. */
40
+ isSubComposition: boolean;
41
+ findings: LintFinding[];
42
+ }
43
+ export interface FileFixResult {
44
+ file: string;
45
+ /** Codes that were mechanically fixed. */
46
+ codes: string[];
47
+ }
48
+ export interface ProjectLintResult {
49
+ ok: boolean;
50
+ errorCount: number;
51
+ warningCount: number;
52
+ infoCount: number;
53
+ files: FileLintResult[];
54
+ fixed: FileFixResult[];
55
+ }
56
+ /** Producer's two findings that are inverse-rules for sub-compositions. */
57
+ export declare const SUB_COMP_FALSE_POSITIVES: ReadonlySet<string>;
58
+ /**
59
+ * Discover scene files in a project. Returns the root composition (or null
60
+ * if missing) plus every `*.html` file under `compositions/` recursively.
61
+ *
62
+ * Paths returned are absolute. The caller derives project-relative paths via
63
+ * `relative(projectDir, path)`.
64
+ */
65
+ export declare function discoverSceneFiles(opts: {
66
+ projectDir: string;
67
+ rootRel?: string;
68
+ }): Promise<{
69
+ root: string | null;
70
+ subs: string[];
71
+ }>;
72
+ /**
73
+ * Drop findings that producer's lint emits as inverse-rules for sub-comp
74
+ * files. Other findings pass through unchanged.
75
+ */
76
+ export declare function filterSubCompFalsePositives(findings: LintFinding[], isSubComposition: boolean): LintFinding[];
77
+ /**
78
+ * Apply mechanical `--fix` rewrites for known-safe finding codes. Returns the
79
+ * (possibly unchanged) HTML and the list of codes that were fixed at least
80
+ * once.
81
+ *
82
+ * MVP 1 only auto-fixes `timed_element_missing_clip_class` — adding the
83
+ * `clip` class to elements that already carry timing attributes.
84
+ *
85
+ * Other "registration"/"paused" fixes are intentionally NOT mechanical —
86
+ * they require structural reasoning (where to insert the `window.__timelines`
87
+ * block, how to convert an unpaused timeline) that goes beyond a regex.
88
+ * The lint output still surfaces them with `fixHint` for the agent or human
89
+ * to apply.
90
+ */
91
+ export declare function applyMechanicalFixes(html: string, findings: LintFinding[]): {
92
+ html: string;
93
+ fixedCodes: string[];
94
+ };
95
+ export interface RunProjectLintOptions {
96
+ projectDir: string;
97
+ rootRel?: string;
98
+ /** Apply mechanical fixes and write files back. */
99
+ fix?: boolean;
100
+ }
101
+ /**
102
+ * Walk the project, lint every HTML file, optionally apply mechanical fixes,
103
+ * and return aggregated counts. Throws only on FS errors; lint errors are
104
+ * surfaced as findings, not exceptions.
105
+ */
106
+ export declare function runProjectLint(opts: RunProjectLintOptions): Promise<ProjectLintResult>;
107
+ /** True if `root` exists and is a regular file. Used by callers that want to
108
+ * pre-validate before invoking `runProjectLint`. */
109
+ export declare function rootExists(projectDir: string, rootRel?: string): Promise<boolean>;
110
+ //# sourceMappingURL=scene-lint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-lint.d.ts","sourceRoot":"","sources":["../../../src/commands/_shared/scene-lint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAeH,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAExD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,YAAY,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,gBAAgB,EAAE,OAAO,CAAC;IAC1B,QAAQ,EAAE,WAAW,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB;AAMD,2EAA2E;AAC3E,eAAO,MAAM,wBAAwB,EAAE,WAAW,CAAC,MAAM,CAGvD,CAAC;AAEH;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAanD;AAcD;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,WAAW,EAAE,EACvB,gBAAgB,EAAE,OAAO,GACxB,WAAW,EAAE,CAGf;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,WAAW,EAAE,GACtB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAaxC;AA+BD,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA6D5F;AAED;qDACqD;AACrD,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,SAAe,GAAG,OAAO,CAAC,OAAO,CAAC,CAQ7F"}