@editframe/create 0.44.0 → 0.45.1

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 (98) hide show
  1. package/dist/index.js +16 -28
  2. package/dist/index.js.map +1 -1
  3. package/dist/skills/editframe-brand-video-generator/README.md +155 -0
  4. package/dist/skills/editframe-brand-video-generator/SKILL.md +207 -0
  5. package/dist/skills/editframe-brand-video-generator/references/brand-examples.md +178 -0
  6. package/dist/skills/editframe-brand-video-generator/references/color-psychology.md +227 -0
  7. package/dist/skills/editframe-brand-video-generator/references/composition-patterns.md +383 -0
  8. package/dist/skills/editframe-brand-video-generator/references/editing.md +66 -0
  9. package/dist/skills/editframe-brand-video-generator/references/emotional-arcs.md +496 -0
  10. package/dist/skills/editframe-brand-video-generator/references/genre-selection.md +135 -0
  11. package/dist/skills/editframe-brand-video-generator/references/transition-styles.md +611 -0
  12. package/dist/skills/editframe-brand-video-generator/references/typography-personalities.md +326 -0
  13. package/dist/skills/editframe-brand-video-generator/references/video-archetypes.md +86 -0
  14. package/dist/skills/editframe-brand-video-generator/references/video-fundamentals.md +169 -0
  15. package/dist/skills/editframe-brand-video-generator/references/visual-metaphors.md +50 -0
  16. package/dist/skills/editframe-composition/SKILL.md +169 -0
  17. package/dist/skills/editframe-composition/references/audio.md +483 -0
  18. package/dist/skills/editframe-composition/references/captions.md +844 -0
  19. package/dist/skills/editframe-composition/references/composition-model.md +73 -0
  20. package/dist/skills/editframe-composition/references/configuration.md +403 -0
  21. package/dist/skills/editframe-composition/references/css-parts.md +105 -0
  22. package/dist/skills/editframe-composition/references/css-variables.md +640 -0
  23. package/dist/skills/editframe-composition/references/entry-points.md +810 -0
  24. package/dist/skills/editframe-composition/references/events.md +499 -0
  25. package/dist/skills/editframe-composition/references/getting-started.md +259 -0
  26. package/dist/skills/editframe-composition/references/hooks.md +234 -0
  27. package/dist/skills/editframe-composition/references/image.md +241 -0
  28. package/dist/skills/editframe-composition/references/r3f.md +580 -0
  29. package/dist/skills/editframe-composition/references/render-api.md +484 -0
  30. package/dist/skills/editframe-composition/references/render-strategies.md +119 -0
  31. package/dist/skills/editframe-composition/references/render-to-video.md +1101 -0
  32. package/dist/skills/editframe-composition/references/scripting.md +606 -0
  33. package/dist/skills/editframe-composition/references/sequencing.md +116 -0
  34. package/dist/skills/editframe-composition/references/server-rendering.md +753 -0
  35. package/dist/skills/editframe-composition/references/surface.md +329 -0
  36. package/dist/skills/editframe-composition/references/text.md +627 -0
  37. package/dist/skills/editframe-composition/references/time-model.md +99 -0
  38. package/dist/skills/editframe-composition/references/timegroup-modes.md +102 -0
  39. package/dist/skills/editframe-composition/references/timegroup.md +457 -0
  40. package/dist/skills/editframe-composition/references/timeline-root.md +398 -0
  41. package/dist/skills/editframe-composition/references/transcription.md +47 -0
  42. package/dist/skills/editframe-composition/references/transitions.md +608 -0
  43. package/dist/skills/editframe-composition/references/use-media-info.md +357 -0
  44. package/dist/skills/editframe-composition/references/video.md +506 -0
  45. package/dist/skills/editframe-composition/references/waveform.md +327 -0
  46. package/dist/skills/editframe-editor-gui/SKILL.md +152 -0
  47. package/dist/skills/editframe-editor-gui/references/active-root-temporal.md +657 -0
  48. package/dist/skills/editframe-editor-gui/references/canvas.md +947 -0
  49. package/dist/skills/editframe-editor-gui/references/controls.md +366 -0
  50. package/dist/skills/editframe-editor-gui/references/dial.md +756 -0
  51. package/dist/skills/editframe-editor-gui/references/editor-toolkit.md +587 -0
  52. package/dist/skills/editframe-editor-gui/references/filmstrip.md +460 -0
  53. package/dist/skills/editframe-editor-gui/references/fit-scale.md +772 -0
  54. package/dist/skills/editframe-editor-gui/references/focus-overlay.md +561 -0
  55. package/dist/skills/editframe-editor-gui/references/hierarchy.md +544 -0
  56. package/dist/skills/editframe-editor-gui/references/overlay-item.md +634 -0
  57. package/dist/skills/editframe-editor-gui/references/overlay-layer.md +429 -0
  58. package/dist/skills/editframe-editor-gui/references/pan-zoom.md +568 -0
  59. package/dist/skills/editframe-editor-gui/references/pause.md +397 -0
  60. package/dist/skills/editframe-editor-gui/references/play.md +370 -0
  61. package/dist/skills/editframe-editor-gui/references/preview.md +391 -0
  62. package/dist/skills/editframe-editor-gui/references/resizable-box.md +749 -0
  63. package/dist/skills/editframe-editor-gui/references/scrubber.md +588 -0
  64. package/dist/skills/editframe-editor-gui/references/thumbnail-strip.md +566 -0
  65. package/dist/skills/editframe-editor-gui/references/time-display.md +492 -0
  66. package/dist/skills/editframe-editor-gui/references/timeline-ruler.md +489 -0
  67. package/dist/skills/editframe-editor-gui/references/timeline.md +604 -0
  68. package/dist/skills/editframe-editor-gui/references/toggle-loop.md +618 -0
  69. package/dist/skills/editframe-editor-gui/references/toggle-play.md +526 -0
  70. package/dist/skills/editframe-editor-gui/references/transform-handles.md +924 -0
  71. package/dist/skills/editframe-editor-gui/references/trim-handles.md +725 -0
  72. package/dist/skills/editframe-editor-gui/references/workbench.md +453 -0
  73. package/dist/skills/editframe-motion-design/SKILL.md +101 -0
  74. package/dist/skills/editframe-motion-design/references/0-editframe.md +299 -0
  75. package/dist/skills/editframe-motion-design/references/1-intent.md +201 -0
  76. package/dist/skills/editframe-motion-design/references/2-physics-model.md +405 -0
  77. package/dist/skills/editframe-motion-design/references/3-attention.md +350 -0
  78. package/dist/skills/editframe-motion-design/references/4-process.md +418 -0
  79. package/dist/skills/editframe-vite-plugin/SKILL.md +75 -0
  80. package/dist/skills/editframe-vite-plugin/references/file-api.md +111 -0
  81. package/dist/skills/editframe-vite-plugin/references/getting-started.md +96 -0
  82. package/dist/skills/editframe-vite-plugin/references/jit-transcoding.md +91 -0
  83. package/dist/skills/editframe-vite-plugin/references/local-assets.md +75 -0
  84. package/dist/skills/editframe-vite-plugin/references/visual-testing.md +136 -0
  85. package/dist/skills/editframe-webhooks/SKILL.md +126 -0
  86. package/dist/skills/editframe-webhooks/references/events.md +382 -0
  87. package/dist/skills/editframe-webhooks/references/getting-started.md +232 -0
  88. package/dist/skills/editframe-webhooks/references/security.md +418 -0
  89. package/dist/skills/editframe-webhooks/references/testing.md +409 -0
  90. package/dist/skills/editframe-webhooks/references/troubleshooting.md +457 -0
  91. package/dist/templates/html/AGENTS.md +13 -0
  92. package/dist/templates/react/AGENTS.md +13 -0
  93. package/dist/utils.js +15 -16
  94. package/dist/utils.js.map +1 -1
  95. package/package.json +1 -1
  96. package/tsdown.config.ts +4 -0
  97. package/dist/detectAgent.js +0 -89
  98. package/dist/detectAgent.js.map +0 -1
@@ -0,0 +1,606 @@
1
+ ---
2
+ title: Scripting
3
+ description: Add dynamic JavaScript behavior to timegroups with initializer callbacks and per-frame task functions for procedural animation.
4
+ type: reference
5
+ nav:
6
+ parent: "Rendering"
7
+ priority: 20
8
+ related: ["timegroup"]
9
+ react:
10
+ generate: true
11
+ componentName: "Scripting"
12
+ importPath: "@editframe/react"
13
+ nav:
14
+ parent: "Advanced"
15
+ priority: 52
16
+ related: ["hooks", "timegroup"]
17
+ ---
18
+
19
+ # Scripting
20
+
21
+ <!-- html-only -->
22
+ Add dynamic JavaScript behavior to timegroups.
23
+ <!-- /html-only -->
24
+ <!-- react-only -->
25
+ Add dynamic JavaScript behavior to timegroups using React refs.
26
+ <!-- /react-only -->
27
+
28
+ ## Initializer
29
+
30
+ <!-- html-only -->
31
+ Set up behavior that runs once per instance (prime timeline and render clones).
32
+
33
+ ### Basic Usage
34
+
35
+ ```html
36
+ <ef-timegroup id="my-scene" mode="fixed" duration="5s">
37
+ <div class="content"></div>
38
+ </ef-timegroup>
39
+
40
+ <script>
41
+ const tg = document.querySelector('#my-scene');
42
+ tg.initializer = (instance) => {
43
+ // Runs once on prime timeline, once on each render clone
44
+ console.log('Initializer running');
45
+ };
46
+ </script>
47
+ ```
48
+ <!-- /html-only -->
49
+ <!-- react-only -->
50
+ Set up behavior that runs once when the element is initialized.
51
+
52
+ ### Basic Usage with Refs
53
+
54
+ ```tsx
55
+ import { useRef, useEffect } from "react";
56
+ import { Timegroup } from "@editframe/react";
57
+
58
+ const DynamicScene = () => {
59
+ const timegroupRef = useRef<HTMLElement>(null);
60
+
61
+ useEffect(() => {
62
+ const tg = timegroupRef.current;
63
+ if (!tg) return;
64
+
65
+ tg.initializer = (instance) => {
66
+ // Runs once when element is initialized
67
+ console.log('Initializer running');
68
+ };
69
+ }, []);
70
+
71
+ return (
72
+ <Timegroup ref={timegroupRef} mode="fixed" duration="5s">
73
+ <div>Content</div>
74
+ </Timegroup>
75
+ );
76
+ };
77
+ ```
78
+ <!-- /react-only -->
79
+
80
+ ### Constraints
81
+
82
+ - **Must be synchronous** - No async/await, no Promise return
83
+ <!-- html-only -->
84
+ - **Must complete quickly** - Less than 100ms (error thrown) or less than 10ms (warning logged)
85
+ <!-- /html-only -->
86
+ <!-- react-only -->
87
+ - **Must complete quickly** - &lt;100ms (error thrown) or &lt;10ms (warning logged)
88
+ <!-- /react-only -->
89
+ - **Register callbacks only** - Don't do expensive work in initializer
90
+
91
+ ### Timing
92
+
93
+ - Set before connection: Runs after element connects to DOM
94
+ - Set after connection: Runs immediately
95
+ <!-- html-only -->
96
+ - Clones: Automatically copy and run initializer
97
+ <!-- /html-only -->
98
+
99
+ ## Frame Tasks
100
+
101
+ Register callbacks that execute on each frame during rendering.
102
+
103
+ ### addFrameTask()
104
+
105
+ <!-- html-only -->
106
+ ```javascript
107
+ const tg = document.querySelector('ef-timegroup');
108
+ tg.initializer = (instance) => {
109
+ const cleanup = instance.addFrameTask((info) => {
110
+ // Called on each frame
111
+ // info contains: ownCurrentTimeMs, durationMs, percentComplete, etc.
112
+ });
113
+
114
+ // cleanup() removes the callback when called
115
+ };
116
+ ```
117
+ <!-- /html-only -->
118
+ <!-- react-only -->
119
+ ```tsx
120
+ useEffect(() => {
121
+ const tg = timegroupRef.current;
122
+ if (!tg) return;
123
+
124
+ tg.initializer = (instance) => {
125
+ const cleanup = instance.addFrameTask((info) => {
126
+ // Called on each frame
127
+ // info contains: ownCurrentTimeMs, durationMs, percentComplete, etc.
128
+ });
129
+
130
+ return cleanup;
131
+ };
132
+ }, []);
133
+ ```
134
+ <!-- /react-only -->
135
+
136
+ ### Callback Info
137
+
138
+ Frame task callbacks receive timing information:
139
+
140
+ ```javascript
141
+ instance.addFrameTask((info) => {
142
+ console.log(info.ownCurrentTimeMs); // Current time in ms
143
+ console.log(info.durationMs); // Total duration
144
+ console.log(info.percentComplete); // 0-1 progress
145
+ });
146
+ ```
147
+
148
+ ### Multiple Callbacks
149
+
150
+ Register multiple frame tasks - they execute in parallel:
151
+
152
+ <!-- html-only -->
153
+ ```javascript
154
+ tg.initializer = (instance) => {
155
+ instance.addFrameTask((info) => {
156
+ // Update text
157
+ });
158
+
159
+ instance.addFrameTask((info) => {
160
+ // Update position
161
+ });
162
+ };
163
+ ```
164
+ <!-- /html-only -->
165
+ <!-- react-only -->
166
+ ```tsx
167
+ useEffect(() => {
168
+ const tg = timegroupRef.current;
169
+ if (!tg) return;
170
+
171
+ tg.initializer = (instance) => {
172
+ const cleanup1 = instance.addFrameTask((info) => {
173
+ // Update text
174
+ const text = instance.querySelector('.text');
175
+ if (text) {
176
+ text.textContent = `${info.ownCurrentTimeMs}ms`;
177
+ }
178
+ });
179
+
180
+ const cleanup2 = instance.addFrameTask((info) => {
181
+ // Update position
182
+ const box = instance.querySelector('.box');
183
+ if (box) {
184
+ (box as HTMLElement).style.left = `${info.percentComplete * 100}%`;
185
+ }
186
+ });
187
+
188
+ // Return combined cleanup
189
+ return () => {
190
+ cleanup1();
191
+ cleanup2();
192
+ };
193
+ };
194
+ }, []);
195
+ ```
196
+ <!-- /react-only -->
197
+
198
+ <!-- react-only -->
199
+ ### Cleanup Pattern
200
+
201
+ Use React's useEffect cleanup for proper teardown:
202
+
203
+ ```tsx
204
+ useEffect(() => {
205
+ const tg = timegroupRef.current;
206
+ if (!tg) return;
207
+
208
+ tg.initializer = (instance) => {
209
+ const cleanup = instance.addFrameTask((info) => {
210
+ // Frame callback logic
211
+ });
212
+
213
+ return cleanup;
214
+ };
215
+
216
+ // Cleanup on unmount
217
+ return () => {
218
+ if (tg) {
219
+ tg.initializer = undefined;
220
+ }
221
+ };
222
+ }, []);
223
+ ```
224
+ <!-- /react-only -->
225
+
226
+ ## Examples
227
+
228
+ ### Dynamic Text Updates
229
+
230
+ <!-- html-only -->
231
+ ```html
232
+ <ef-timegroup id="counter" mode="fixed" duration="10s">
233
+ <div class="text-4xl text-white counter-text"></div>
234
+ </ef-timegroup>
235
+
236
+ <script>
237
+ const tg = document.querySelector('#counter');
238
+ tg.initializer = (instance) => {
239
+ instance.addFrameTask((info) => {
240
+ const text = instance.querySelector('.counter-text');
241
+ const seconds = (info.ownCurrentTimeMs / 1000).toFixed(2);
242
+ text.textContent = `Time: ${seconds}s`;
243
+ });
244
+ };
245
+ </script>
246
+ ```
247
+ <!-- /html-only -->
248
+ <!-- react-only -->
249
+ ```tsx
250
+ import { useRef, useEffect } from "react";
251
+ import { Timegroup } from "@editframe/react";
252
+
253
+ const Counter = () => {
254
+ const timegroupRef = useRef<HTMLElement>(null);
255
+ const textRef = useRef<HTMLDivElement>(null);
256
+
257
+ useEffect(() => {
258
+ const tg = timegroupRef.current;
259
+ if (!tg) return;
260
+
261
+ tg.initializer = (instance) => {
262
+ return instance.addFrameTask((info) => {
263
+ const text = instance.querySelector('.counter-text');
264
+ if (text) {
265
+ const seconds = (info.ownCurrentTimeMs / 1000).toFixed(2);
266
+ text.textContent = `Time: ${seconds}s`;
267
+ }
268
+ });
269
+ };
270
+ }, []);
271
+
272
+ return (
273
+ <Timegroup ref={timegroupRef} mode="fixed" duration="10s">
274
+ <div ref={textRef} className="text-4xl text-white counter-text" />
275
+ </Timegroup>
276
+ );
277
+ };
278
+ ```
279
+ <!-- /react-only -->
280
+
281
+ ### Procedural Animation
282
+
283
+ <!-- html-only -->
284
+ ```html
285
+ <ef-timegroup id="animated" mode="fixed" duration="5s">
286
+ <div class="box"></div>
287
+ </ef-timegroup>
288
+
289
+ <script>
290
+ const tg = document.querySelector('#animated');
291
+ tg.initializer = (instance) => {
292
+ instance.addFrameTask((info) => {
293
+ const box = instance.querySelector('.box');
294
+ const progress = info.percentComplete;
295
+
296
+ // Move box across screen
297
+ box.style.transform = `translateX(${progress * 500}px)`;
298
+
299
+ // Rotate based on time
300
+ const rotation = (info.ownCurrentTimeMs / 10) % 360;
301
+ box.style.transform += ` rotate(${rotation}deg)`;
302
+ });
303
+ };
304
+ </script>
305
+ ```
306
+ <!-- /html-only -->
307
+ <!-- react-only -->
308
+ ```tsx
309
+ const AnimatedBox = () => {
310
+ const timegroupRef = useRef<HTMLElement>(null);
311
+
312
+ useEffect(() => {
313
+ const tg = timegroupRef.current;
314
+ if (!tg) return;
315
+
316
+ tg.initializer = (instance) => {
317
+ return instance.addFrameTask((info) => {
318
+ const box = instance.querySelector('.box');
319
+ if (!box) return;
320
+
321
+ const progress = info.percentComplete;
322
+
323
+ // Move box across screen
324
+ const x = progress * 500;
325
+
326
+ // Rotate based on time
327
+ const rotation = (info.ownCurrentTimeMs / 10) % 360;
328
+
329
+ (box as HTMLElement).style.transform =
330
+ `translateX(${x}px) rotate(${rotation}deg)`;
331
+ });
332
+ };
333
+ }, []);
334
+
335
+ return (
336
+ <Timegroup ref={timegroupRef} mode="fixed" duration="5s" className="relative w-full h-full">
337
+ <div className="box w-24 h-24 bg-red-500" />
338
+ </Timegroup>
339
+ );
340
+ };
341
+ ```
342
+ <!-- /react-only -->
343
+
344
+ ### Data-Driven Content
345
+
346
+ <!-- html-only -->
347
+ ```html
348
+ <ef-timegroup id="data-scene" mode="fixed" duration="8s">
349
+ <div class="data-display"></div>
350
+ </ef-timegroup>
351
+
352
+ <script>
353
+ const data = [
354
+ { time: 0, value: 10 },
355
+ { time: 2000, value: 25 },
356
+ { time: 4000, value: 40 },
357
+ { time: 6000, value: 60 },
358
+ ];
359
+
360
+ const tg = document.querySelector('#data-scene');
361
+ tg.initializer = (instance) => {
362
+ instance.addFrameTask((info) => {
363
+ const display = instance.querySelector('.data-display');
364
+
365
+ // Find current data point
366
+ const current = data.find((d, i) => {
367
+ const next = data[i + 1];
368
+ return info.ownCurrentTimeMs >= d.time &&
369
+ (!next || info.ownCurrentTimeMs < next.time);
370
+ });
371
+
372
+ if (current) {
373
+ display.textContent = `Value: ${current.value}`;
374
+ }
375
+ });
376
+ };
377
+ </script>
378
+ ```
379
+ <!-- /html-only -->
380
+ <!-- react-only -->
381
+ ```tsx
382
+ interface DataPoint {
383
+ time: number;
384
+ value: number;
385
+ }
386
+
387
+ const DataScene = ({ data }: { data: DataPoint[] }) => {
388
+ const timegroupRef = useRef<HTMLElement>(null);
389
+
390
+ useEffect(() => {
391
+ const tg = timegroupRef.current;
392
+ if (!tg) return;
393
+
394
+ tg.initializer = (instance) => {
395
+ return instance.addFrameTask((info) => {
396
+ const display = instance.querySelector('.data-display');
397
+ if (!display) return;
398
+
399
+ // Find current data point
400
+ const current = data.find((d, i) => {
401
+ const next = data[i + 1];
402
+ return info.ownCurrentTimeMs >= d.time &&
403
+ (!next || info.ownCurrentTimeMs < next.time);
404
+ });
405
+
406
+ if (current) {
407
+ display.textContent = `Value: ${current.value}`;
408
+ }
409
+ });
410
+ };
411
+ }, [data]);
412
+
413
+ return (
414
+ <Timegroup ref={timegroupRef} mode="fixed" duration="8s">
415
+ <div className="data-display text-2xl" />
416
+ </Timegroup>
417
+ );
418
+ };
419
+
420
+ // Usage
421
+ const data = [
422
+ { time: 0, value: 10 },
423
+ { time: 2000, value: 25 },
424
+ { time: 4000, value: 40 },
425
+ { time: 6000, value: 60 },
426
+ ];
427
+
428
+ <DataScene data={data} />
429
+ ```
430
+ <!-- /react-only -->
431
+
432
+ <!-- html-only -->
433
+ ### Cleanup Pattern
434
+
435
+ ```javascript
436
+ tg.initializer = (instance) => {
437
+ // Set up resources
438
+ const state = { count: 0 };
439
+
440
+ const cleanup = instance.addFrameTask((info) => {
441
+ state.count++;
442
+ console.log(`Frame ${state.count}`);
443
+ });
444
+
445
+ // Cleanup is automatic when instance is removed
446
+ // But you can manually cleanup if needed:
447
+ // cleanup();
448
+ };
449
+ ```
450
+ <!-- /html-only -->
451
+
452
+ <!-- react-only -->
453
+ ### Integration with React State
454
+
455
+ ```tsx
456
+ const InteractiveScene = () => {
457
+ const timegroupRef = useRef<HTMLElement>(null);
458
+ const [currentTime, setCurrentTime] = useState(0);
459
+
460
+ useEffect(() => {
461
+ const tg = timegroupRef.current;
462
+ if (!tg) return;
463
+
464
+ tg.initializer = (instance) => {
465
+ return instance.addFrameTask((info) => {
466
+ // Update React state (throttle for performance)
467
+ const timeInSeconds = Math.floor(info.ownCurrentTimeMs / 1000);
468
+ setCurrentTime(timeInSeconds);
469
+ });
470
+ };
471
+ }, []);
472
+
473
+ return (
474
+ <>
475
+ <div className="text-white mb-4">Current Time: {currentTime}s</div>
476
+ <Timegroup ref={timegroupRef} mode="fixed" duration="10s">
477
+ <div className="content">Scene content</div>
478
+ </Timegroup>
479
+ </>
480
+ );
481
+ };
482
+ ```
483
+ <!-- /react-only -->
484
+
485
+ ## <!-- html-only -->Prime Timeline vs Render Clone<!-- /html-only --><!-- react-only -->Consistent Behavior<!-- /react-only -->
486
+
487
+ <!-- html-only -->
488
+ The initializer runs on both:
489
+
490
+ - **Prime timeline**: Interactive preview in browser
491
+ - **Render clone**: Headless rendering for video export
492
+
493
+ Same code runs in both contexts, ensuring consistent behavior.
494
+
495
+ ```javascript
496
+ tg.initializer = (instance) => {
497
+ // This code runs identically on prime timeline and render clones
498
+ instance.addFrameTask((info) => {
499
+ // Update content based on time
500
+ });
501
+ };
502
+ ```
503
+ <!-- /html-only -->
504
+ <!-- react-only -->
505
+ The initializer ensures your code runs consistently in both preview and rendering:
506
+
507
+ ```tsx
508
+ tg.initializer = (instance) => {
509
+ // This code runs the same way in preview and rendering
510
+ return instance.addFrameTask((info) => {
511
+ // Update content based on time
512
+ });
513
+ };
514
+ ```
515
+
516
+ **Important**: For React components, you must use `TimelineRoot` to ensure React hooks and state work correctly. See [timeline-root.md](references/timeline-root.md) for details.
517
+ <!-- /react-only -->
518
+
519
+ ## Best Practices
520
+
521
+ <!-- html-only -->
522
+ 1. **Keep initializer fast** - Register callbacks, don't do heavy work
523
+ 2. **Use frame tasks for updates** - All time-based logic goes in frame callbacks
524
+ 3. **Avoid side effects** - Don't modify external state, keep logic contained
525
+ 4. **Test in both contexts** - Preview in browser AND render to video
526
+ 5. **Handle missing elements** - Check if elements exist before updating them
527
+
528
+ ```javascript
529
+ // Good: Check before updating
530
+ instance.addFrameTask((info) => {
531
+ const el = instance.querySelector('.my-element');
532
+ if (el) {
533
+ el.textContent = `Time: ${info.ownCurrentTimeMs}`;
534
+ }
535
+ });
536
+
537
+ // Bad: Assumes element exists
538
+ instance.addFrameTask((info) => {
539
+ instance.querySelector('.my-element').textContent = `Time: ${info.ownCurrentTimeMs}`;
540
+ });
541
+ ```
542
+ <!-- /html-only -->
543
+ <!-- react-only -->
544
+ 1. **Use refs to access timegroup** - Don't query the DOM directly from React
545
+ 2. **Set initializer in useEffect** - Ensures element is mounted
546
+ 3. **Return cleanup functions** - Prevent memory leaks
547
+ 4. **Keep initializer fast** - Register callbacks, don't do heavy work
548
+ 5. **Use frame tasks for updates** - All time-based logic goes in frame callbacks
549
+ 6. **Handle missing elements** - Check if elements exist before updating them
550
+ 7. **Avoid React state in frame tasks** - Can cause performance issues; use DOM updates instead
551
+
552
+ ```tsx
553
+ // Good: Direct DOM updates in frame task
554
+ instance.addFrameTask((info) => {
555
+ const el = instance.querySelector('.my-element');
556
+ if (el) {
557
+ el.textContent = `Time: ${info.ownCurrentTimeMs}`;
558
+ }
559
+ });
560
+
561
+ // Avoid: React state updates in frame task (performance issue)
562
+ instance.addFrameTask((info) => {
563
+ setTime(info.ownCurrentTimeMs); // Called every frame!
564
+ });
565
+ ```
566
+
567
+ ## TypeScript Types
568
+
569
+ ```tsx
570
+ import { useRef, useEffect } from "react";
571
+
572
+ interface TimegroupElement extends HTMLElement {
573
+ initializer?: (instance: TimegroupElement) => (() => void) | void;
574
+ addFrameTask: (callback: (info: FrameInfo) => void) => () => void;
575
+ }
576
+
577
+ interface FrameInfo {
578
+ ownCurrentTimeMs: number;
579
+ durationMs: number;
580
+ percentComplete: number;
581
+ }
582
+
583
+ const MyScene = () => {
584
+ const timegroupRef = useRef<TimegroupElement>(null);
585
+
586
+ useEffect(() => {
587
+ const tg = timegroupRef.current;
588
+ if (!tg) return;
589
+
590
+ tg.initializer = (instance: TimegroupElement) => {
591
+ return instance.addFrameTask((info: FrameInfo) => {
592
+ // Typed callback
593
+ });
594
+ };
595
+ }, []);
596
+
597
+ return <Timegroup ref={timegroupRef} mode="fixed" duration="5s" />;
598
+ };
599
+ ```
600
+
601
+ ## See Also
602
+
603
+ - [timeline-root.md](references/timeline-root.md) - TimelineRoot wrapper (required for React)
604
+ - [hooks.md](references/hooks.md) - useTimingInfo and other React hooks
605
+ - [timegroup.md](references/timegroup.md) - Timegroup component reference
606
+ <!-- /react-only -->
@@ -0,0 +1,116 @@
1
+ ---
2
+ title: Sequencing Scenes
3
+ description: Create sequential scene playback using seq-mode timegroups, with automatic cuts and configurable transition timing between scenes.
4
+ type: how-to
5
+ nav:
6
+ parent: "Layout & Timing"
7
+ priority: 11
8
+ related: ["timegroup-modes", "transitions"]
9
+ track: "layout-mastery"
10
+ track_step: 3
11
+ track_title: "Building Sequences"
12
+ prerequisites: ["timegroup", "timegroup-modes"]
13
+ ---
14
+
15
+ # Sequencing Scenes
16
+
17
+ Create multi-scene compositions using `mode="sequence"` on a timegroup.
18
+
19
+ ## Basic Sequence
20
+
21
+ Children play one after another. Total duration is the sum of all children.
22
+
23
+ ```html live
24
+ <ef-timegroup mode="sequence" class="w-[720px] h-[300px] bg-black">
25
+ <ef-timegroup mode="fixed" duration="2s" class="bg-red-500 text-white text-3xl flex items-center justify-center">
26
+ <p>Scene 1</p>
27
+ </ef-timegroup>
28
+ <ef-timegroup mode="fixed" duration="3s" class="bg-blue-500 text-white text-3xl flex items-center justify-center">
29
+ <p>Scene 2</p>
30
+ </ef-timegroup>
31
+ <ef-timegroup mode="fixed" duration="2s" class="bg-green-500 text-white text-3xl flex items-center justify-center">
32
+ <p>Scene 3</p>
33
+ </ef-timegroup>
34
+ </ef-timegroup>
35
+ ```
36
+
37
+ ## Sequences with Overlap
38
+
39
+ Add `overlap` to create shared time between adjacent items — the foundation for transitions.
40
+
41
+ ```html live
42
+ <ef-timegroup mode="sequence" overlap="1s" class="w-[720px] h-[300px] bg-black">
43
+ <ef-timegroup mode="fixed" duration="3s" class="absolute w-full h-full bg-red-500 text-white text-3xl flex items-center justify-center">
44
+ <p>Scene 1</p>
45
+ </ef-timegroup>
46
+ <ef-timegroup mode="fixed" duration="3s" class="absolute w-full h-full bg-blue-500 text-white text-3xl flex items-center justify-center">
47
+ <p>Scene 2</p>
48
+ </ef-timegroup>
49
+ <ef-timegroup mode="fixed" duration="3s" class="absolute w-full h-full bg-green-500 text-white text-3xl flex items-center justify-center">
50
+ <p>Scene 3</p>
51
+ </ef-timegroup>
52
+ </ef-timegroup>
53
+ ```
54
+
55
+ Total duration with overlap: `sum(children) - (overlap × (count - 1))`. Three 3s scenes with 1s overlap = 7s.
56
+
57
+ See [transitions.md](references/transitions.md) for crossfade and slide effects during overlap.
58
+
59
+ ## Nested Sequences
60
+
61
+ A sequence can contain other sequences. The inner sequence resolves to a single duration.
62
+
63
+ ```html
64
+ <ef-timegroup mode="sequence" overlap="1s">
65
+ <ef-timegroup mode="sequence">
66
+ <ef-timegroup mode="fixed" duration="3s">Sub A1</ef-timegroup>
67
+ <ef-timegroup mode="fixed" duration="2s">Sub A2</ef-timegroup>
68
+ </ef-timegroup>
69
+ <ef-timegroup mode="fixed" duration="4s">Scene B</ef-timegroup>
70
+ </ef-timegroup>
71
+ ```
72
+
73
+ The inner sequence (5s total) is treated as a single 5s item in the outer sequence.
74
+
75
+ ## Mixed Content
76
+
77
+ Sequences work with any element — video, images, text, or combinations:
78
+
79
+ ```html live
80
+ <ef-timegroup mode="sequence" class="w-[720px] h-[480px] bg-black">
81
+ <ef-timegroup class="flex flex-col items-center justify-center">
82
+ <ef-video src="https://assets.editframe.com/bars-n-tone.mp4" sourceout="3s" class="absolute top-0 left-0 size-full object-contain z-0"></ef-video>
83
+ <h1 class="relative text-white text-4xl bg-black/50 p-4">Video Scene</h1>
84
+ </ef-timegroup>
85
+ <ef-timegroup mode="fixed" duration="3s" class="bg-slate-900 flex items-center justify-center">
86
+ <h1 class="text-white text-5xl">Text Interlude</h1>
87
+ </ef-timegroup>
88
+ <ef-timegroup class="flex items-center justify-center">
89
+ <ef-video src="https://assets.editframe.com/bars-n-tone.mp4" sourcein="5s" class="absolute top-0 left-0 size-full object-contain z-0"></ef-video>
90
+ <h1 class="relative text-white text-4xl bg-black/50 p-4">Final Scene</h1>
91
+ </ef-timegroup>
92
+ </ef-timegroup>
93
+ ```
94
+
95
+ ## Background Track Pattern
96
+
97
+ Wrap a sequence with a `contain` parent and add a `fit` sibling for a background that spans the full timeline:
98
+
99
+ ```html
100
+ <ef-timegroup mode="contain">
101
+ <ef-timegroup mode="sequence">
102
+ <ef-timegroup mode="fixed" duration="3s">Intro</ef-timegroup>
103
+ <ef-timegroup mode="fixed" duration="10s">Main</ef-timegroup>
104
+ <ef-timegroup mode="fixed" duration="2s">Outro</ef-timegroup>
105
+ </ef-timegroup>
106
+ <ef-audio src="music.mp3" mode="fit"></ef-audio>
107
+ </ef-timegroup>
108
+ ```
109
+
110
+ > **Note:** The `fit` element inherits the 15s total from its `contain` parent, which gets its duration from the `sequence` child.
111
+
112
+ ## See Also
113
+
114
+ - [timegroup-modes.md](references/timegroup-modes.md) — mode explanations
115
+ - [transitions.md](references/transitions.md) — crossfade and slide transitions
116
+ - [time-model.md](references/time-model.md) — how time propagates through sequences