@editframe/create 0.44.0 → 0.45.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.
- package/dist/index.js +16 -28
- package/dist/index.js.map +1 -1
- package/dist/skills/editframe-brand-video-generator/README.md +155 -0
- package/dist/skills/editframe-brand-video-generator/SKILL.md +207 -0
- package/dist/skills/editframe-brand-video-generator/references/brand-examples.md +178 -0
- package/dist/skills/editframe-brand-video-generator/references/color-psychology.md +227 -0
- package/dist/skills/editframe-brand-video-generator/references/composition-patterns.md +383 -0
- package/dist/skills/editframe-brand-video-generator/references/editing.md +66 -0
- package/dist/skills/editframe-brand-video-generator/references/emotional-arcs.md +496 -0
- package/dist/skills/editframe-brand-video-generator/references/genre-selection.md +135 -0
- package/dist/skills/editframe-brand-video-generator/references/transition-styles.md +611 -0
- package/dist/skills/editframe-brand-video-generator/references/typography-personalities.md +326 -0
- package/dist/skills/editframe-brand-video-generator/references/video-archetypes.md +86 -0
- package/dist/skills/editframe-brand-video-generator/references/video-fundamentals.md +169 -0
- package/dist/skills/editframe-brand-video-generator/references/visual-metaphors.md +50 -0
- package/dist/skills/editframe-composition/SKILL.md +169 -0
- package/dist/skills/editframe-composition/references/audio.md +483 -0
- package/dist/skills/editframe-composition/references/captions.md +844 -0
- package/dist/skills/editframe-composition/references/composition-model.md +73 -0
- package/dist/skills/editframe-composition/references/configuration.md +403 -0
- package/dist/skills/editframe-composition/references/css-parts.md +105 -0
- package/dist/skills/editframe-composition/references/css-variables.md +640 -0
- package/dist/skills/editframe-composition/references/entry-points.md +810 -0
- package/dist/skills/editframe-composition/references/events.md +499 -0
- package/dist/skills/editframe-composition/references/getting-started.md +259 -0
- package/dist/skills/editframe-composition/references/hooks.md +234 -0
- package/dist/skills/editframe-composition/references/image.md +241 -0
- package/dist/skills/editframe-composition/references/r3f.md +580 -0
- package/dist/skills/editframe-composition/references/render-api.md +484 -0
- package/dist/skills/editframe-composition/references/render-strategies.md +119 -0
- package/dist/skills/editframe-composition/references/render-to-video.md +1101 -0
- package/dist/skills/editframe-composition/references/scripting.md +606 -0
- package/dist/skills/editframe-composition/references/sequencing.md +116 -0
- package/dist/skills/editframe-composition/references/server-rendering.md +753 -0
- package/dist/skills/editframe-composition/references/surface.md +329 -0
- package/dist/skills/editframe-composition/references/text.md +627 -0
- package/dist/skills/editframe-composition/references/time-model.md +99 -0
- package/dist/skills/editframe-composition/references/timegroup-modes.md +102 -0
- package/dist/skills/editframe-composition/references/timegroup.md +457 -0
- package/dist/skills/editframe-composition/references/timeline-root.md +398 -0
- package/dist/skills/editframe-composition/references/transcription.md +47 -0
- package/dist/skills/editframe-composition/references/transitions.md +608 -0
- package/dist/skills/editframe-composition/references/use-media-info.md +357 -0
- package/dist/skills/editframe-composition/references/video.md +506 -0
- package/dist/skills/editframe-composition/references/waveform.md +327 -0
- package/dist/skills/editframe-editor-gui/SKILL.md +152 -0
- package/dist/skills/editframe-editor-gui/references/active-root-temporal.md +657 -0
- package/dist/skills/editframe-editor-gui/references/canvas.md +947 -0
- package/dist/skills/editframe-editor-gui/references/controls.md +366 -0
- package/dist/skills/editframe-editor-gui/references/dial.md +756 -0
- package/dist/skills/editframe-editor-gui/references/editor-toolkit.md +587 -0
- package/dist/skills/editframe-editor-gui/references/filmstrip.md +460 -0
- package/dist/skills/editframe-editor-gui/references/fit-scale.md +772 -0
- package/dist/skills/editframe-editor-gui/references/focus-overlay.md +561 -0
- package/dist/skills/editframe-editor-gui/references/hierarchy.md +544 -0
- package/dist/skills/editframe-editor-gui/references/overlay-item.md +634 -0
- package/dist/skills/editframe-editor-gui/references/overlay-layer.md +429 -0
- package/dist/skills/editframe-editor-gui/references/pan-zoom.md +568 -0
- package/dist/skills/editframe-editor-gui/references/pause.md +397 -0
- package/dist/skills/editframe-editor-gui/references/play.md +370 -0
- package/dist/skills/editframe-editor-gui/references/preview.md +391 -0
- package/dist/skills/editframe-editor-gui/references/resizable-box.md +749 -0
- package/dist/skills/editframe-editor-gui/references/scrubber.md +588 -0
- package/dist/skills/editframe-editor-gui/references/thumbnail-strip.md +566 -0
- package/dist/skills/editframe-editor-gui/references/time-display.md +492 -0
- package/dist/skills/editframe-editor-gui/references/timeline-ruler.md +489 -0
- package/dist/skills/editframe-editor-gui/references/timeline.md +604 -0
- package/dist/skills/editframe-editor-gui/references/toggle-loop.md +618 -0
- package/dist/skills/editframe-editor-gui/references/toggle-play.md +526 -0
- package/dist/skills/editframe-editor-gui/references/transform-handles.md +924 -0
- package/dist/skills/editframe-editor-gui/references/trim-handles.md +725 -0
- package/dist/skills/editframe-editor-gui/references/workbench.md +453 -0
- package/dist/skills/editframe-motion-design/SKILL.md +101 -0
- package/dist/skills/editframe-motion-design/references/0-editframe.md +299 -0
- package/dist/skills/editframe-motion-design/references/1-intent.md +201 -0
- package/dist/skills/editframe-motion-design/references/2-physics-model.md +405 -0
- package/dist/skills/editframe-motion-design/references/3-attention.md +350 -0
- package/dist/skills/editframe-motion-design/references/4-process.md +418 -0
- package/dist/skills/editframe-vite-plugin/SKILL.md +75 -0
- package/dist/skills/editframe-vite-plugin/references/file-api.md +111 -0
- package/dist/skills/editframe-vite-plugin/references/getting-started.md +96 -0
- package/dist/skills/editframe-vite-plugin/references/jit-transcoding.md +91 -0
- package/dist/skills/editframe-vite-plugin/references/local-assets.md +75 -0
- package/dist/skills/editframe-vite-plugin/references/visual-testing.md +136 -0
- package/dist/skills/editframe-webhooks/SKILL.md +126 -0
- package/dist/skills/editframe-webhooks/references/events.md +382 -0
- package/dist/skills/editframe-webhooks/references/getting-started.md +232 -0
- package/dist/skills/editframe-webhooks/references/security.md +418 -0
- package/dist/skills/editframe-webhooks/references/testing.md +409 -0
- package/dist/skills/editframe-webhooks/references/troubleshooting.md +457 -0
- package/dist/templates/html/AGENTS.md +13 -0
- package/dist/templates/react/AGENTS.md +13 -0
- package/dist/utils.js +15 -16
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
- package/tsdown.config.ts +4 -0
- package/dist/detectAgent.js +0 -89
- package/dist/detectAgent.js.map +0 -1
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Time Model
|
|
3
|
+
description: "How time propagation works in composition trees: duration inheritance, offset calculation, and element scheduling rules."
|
|
4
|
+
type: explanation
|
|
5
|
+
nav:
|
|
6
|
+
parent: "Concepts"
|
|
7
|
+
priority: 11
|
|
8
|
+
related: ["timegroup-modes"]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Time Model
|
|
12
|
+
|
|
13
|
+
Editframe uses a declarative time model. You describe structure — the system computes all timing.
|
|
14
|
+
|
|
15
|
+
## Single Source of Truth
|
|
16
|
+
|
|
17
|
+
Only the root timegroup's `currentTime` is writable. All child times are computed from parent state. This guarantees synchronization — it's impossible for timelines to drift.
|
|
18
|
+
|
|
19
|
+
```javascript
|
|
20
|
+
const root = document.querySelector('ef-timegroup');
|
|
21
|
+
root.currentTime = 5.5; // All children update atomically
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Time Flows Down the Tree
|
|
25
|
+
|
|
26
|
+
Each timegroup computes its `ownCurrentTimeMs` (local time) from the parent's time based on its mode and position in the tree.
|
|
27
|
+
|
|
28
|
+
**Root writes `currentTime`** → parent computes child offsets → each child derives its local time → media elements map to source time.
|
|
29
|
+
|
|
30
|
+
In a sequence, a child at position 5s–8s receives `ownCurrentTimeMs = 0` when the root is at 5s, and `ownCurrentTimeMs = 3000` when the root is at 8s.
|
|
31
|
+
|
|
32
|
+
## Duration Calculation by Mode
|
|
33
|
+
|
|
34
|
+
Each mode computes duration differently:
|
|
35
|
+
|
|
36
|
+
| Mode | Duration rule |
|
|
37
|
+
|------|--------------|
|
|
38
|
+
| `fixed` | Explicit `duration` attribute |
|
|
39
|
+
| `sequence` | Sum of children minus overlaps |
|
|
40
|
+
| `contain` | Maximum child duration |
|
|
41
|
+
| `fit` | Inherited from parent |
|
|
42
|
+
|
|
43
|
+
Duration bubbles up — a `sequence` inside a `contain` resolves its sum, and the `contain` parent takes the max of that and its other children.
|
|
44
|
+
|
|
45
|
+
```html
|
|
46
|
+
<ef-timegroup mode="contain">
|
|
47
|
+
<!-- sequence resolves to 8s (3+5) -->
|
|
48
|
+
<ef-timegroup mode="sequence">
|
|
49
|
+
<ef-timegroup mode="fixed" duration="3s">A</ef-timegroup>
|
|
50
|
+
<ef-timegroup mode="fixed" duration="5s">B</ef-timegroup>
|
|
51
|
+
</ef-timegroup>
|
|
52
|
+
<!-- contain takes max(8s, 6s) = 8s -->
|
|
53
|
+
<ef-timegroup mode="fixed" duration="6s">C</ef-timegroup>
|
|
54
|
+
</ef-timegroup>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Trimming and the Timeline
|
|
58
|
+
|
|
59
|
+
When a video is trimmed, its effective duration changes, which propagates up through the tree:
|
|
60
|
+
|
|
61
|
+
- `sourcein="2s" sourceout="6s"` → effective duration is 4s
|
|
62
|
+
- `trimstart="1s" trimend="2s"` on a 10s source → effective duration is 7s
|
|
63
|
+
|
|
64
|
+
In a `sequence`, changing a trim value shifts all subsequent items automatically. In `contain`, it may change the overall duration if it was the longest child.
|
|
65
|
+
|
|
66
|
+
## Overlap Creates Shared Time
|
|
67
|
+
|
|
68
|
+
In `sequence` mode, `overlap` subtracts time between adjacent items, creating a region where both are active:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
Scene A: |---------|
|
|
72
|
+
Scene B: |---------|
|
|
73
|
+
overlap: |---|
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Total duration: `durationA + durationB - overlap`
|
|
77
|
+
|
|
78
|
+
During the overlap region, both scenes receive valid `ownCurrentTimeMs` values. Scene A is near its end, Scene B is near its start. This shared time is where transitions happen.
|
|
79
|
+
|
|
80
|
+
See [transitions.md](references/transitions.md) for using overlap with CSS animations.
|
|
81
|
+
|
|
82
|
+
## Two Coordinate Systems
|
|
83
|
+
|
|
84
|
+
Every element has two time perspectives:
|
|
85
|
+
|
|
86
|
+
- **`currentTimeMs`** — position in the root timeline (global). Same value for all elements at any given moment.
|
|
87
|
+
- **`ownCurrentTimeMs`** — position in the element's local timeline (local). Resets to 0 at the element's start.
|
|
88
|
+
|
|
89
|
+
Media elements add a third:
|
|
90
|
+
|
|
91
|
+
- **`currentSourceTimeMs`** — position in the source file, accounting for trims. Survives trim changes.
|
|
92
|
+
|
|
93
|
+
> **Note:** `ownCurrentTimeMs` is what you use in `addFrameTask` callbacks and CSS animations. It provides element-relative timing that works regardless of where the element sits in the composition.
|
|
94
|
+
|
|
95
|
+
## See Also
|
|
96
|
+
|
|
97
|
+
- [timegroup-modes.md](references/timegroup-modes.md) — mode examples
|
|
98
|
+
- [timegroup.md](references/timegroup.md) — attribute reference
|
|
99
|
+
- [sequencing.md](references/sequencing.md) — sequence patterns
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Timegroup Modes
|
|
3
|
+
description: How the four timegroup layout modes — par, seq, excl, and media — control duration, ordering, and child element scheduling.
|
|
4
|
+
type: explanation
|
|
5
|
+
nav:
|
|
6
|
+
parent: "Concepts"
|
|
7
|
+
priority: 12
|
|
8
|
+
related: ["timegroup", "sequencing"]
|
|
9
|
+
track: "layout-mastery"
|
|
10
|
+
track_step: 2
|
|
11
|
+
track_title: "Understanding Timeline Modes"
|
|
12
|
+
prerequisites: ["timegroup"]
|
|
13
|
+
next_steps: ["sequencing"]
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Timegroup Modes
|
|
17
|
+
|
|
18
|
+
Every `ef-timegroup` has a `mode` that determines how its duration is calculated and how children are arranged in time.
|
|
19
|
+
|
|
20
|
+
## Fixed
|
|
21
|
+
|
|
22
|
+
Explicit duration. The base case — you set the length directly.
|
|
23
|
+
|
|
24
|
+
```html live
|
|
25
|
+
<ef-timegroup mode="fixed" duration="3s" class="w-[720px] h-[300px] bg-slate-600 flex items-center justify-center">
|
|
26
|
+
<p class="text-white text-4xl">3 Second Scene</p>
|
|
27
|
+
</ef-timegroup>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Use `fixed` when you know the exact duration: title cards, countdowns, static scenes.
|
|
31
|
+
|
|
32
|
+
## Sequence
|
|
33
|
+
|
|
34
|
+
Children play one after another. Duration is the sum of all children minus any overlap.
|
|
35
|
+
|
|
36
|
+
```html live
|
|
37
|
+
<ef-timegroup mode="sequence" class="w-[720px] h-[300px] bg-black">
|
|
38
|
+
<ef-timegroup mode="fixed" duration="2s" class="bg-red-400 text-red-900 text-3xl flex items-center justify-center">
|
|
39
|
+
<p>Scene 1 (2s)</p>
|
|
40
|
+
</ef-timegroup>
|
|
41
|
+
<ef-timegroup mode="fixed" duration="3s" class="bg-green-400 text-green-900 text-3xl flex items-center justify-center">
|
|
42
|
+
<p>Scene 2 (3s)</p>
|
|
43
|
+
</ef-timegroup>
|
|
44
|
+
<ef-timegroup mode="fixed" duration="1s" class="bg-blue-400 text-blue-900 text-3xl flex items-center justify-center">
|
|
45
|
+
<p>Scene 3 (1s)</p>
|
|
46
|
+
</ef-timegroup>
|
|
47
|
+
</ef-timegroup>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Use `sequence` for cut-based editing, multi-scene videos, anything where children play in order. See [sequencing.md](references/sequencing.md).
|
|
51
|
+
|
|
52
|
+
## Contain
|
|
53
|
+
|
|
54
|
+
Children play simultaneously. Duration is the longest child.
|
|
55
|
+
|
|
56
|
+
```html live
|
|
57
|
+
<ef-timegroup mode="contain" class="w-[720px] h-[300px] bg-black">
|
|
58
|
+
<ef-timegroup mode="fixed" duration="5s" class="absolute top-0 left-0 w-full h-1/2 bg-red-400 text-red-900 text-2xl flex items-center justify-center">
|
|
59
|
+
<p>Layer A (5s)</p>
|
|
60
|
+
</ef-timegroup>
|
|
61
|
+
<ef-timegroup mode="fixed" duration="8s" class="absolute top-1/2 left-0 w-full h-1/2 bg-green-400 text-green-900 text-2xl flex items-center justify-center">
|
|
62
|
+
<p>Layer B (8s) — sets total to 8s</p>
|
|
63
|
+
</ef-timegroup>
|
|
64
|
+
</ef-timegroup>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Use `contain` for layered compositions: video with overlays, picture-in-picture, parallel tracks.
|
|
68
|
+
|
|
69
|
+
## Fit
|
|
70
|
+
|
|
71
|
+
Inherits duration from parent. No duration of its own.
|
|
72
|
+
|
|
73
|
+
```html live
|
|
74
|
+
<ef-timegroup mode="contain" class="w-[720px] h-[300px] bg-black">
|
|
75
|
+
<ef-timegroup mode="fixed" duration="4s" class="absolute top-0 left-0 w-full h-1/2 bg-amber-400 text-amber-900 text-2xl flex items-center justify-center">
|
|
76
|
+
<p>Content (4s)</p>
|
|
77
|
+
</ef-timegroup>
|
|
78
|
+
<ef-timegroup mode="fit" class="absolute top-1/2 left-0 w-full h-1/2 bg-blue-900 text-blue-200 text-2xl flex items-center justify-center">
|
|
79
|
+
<p>Fit background — spans full 4s</p>
|
|
80
|
+
</ef-timegroup>
|
|
81
|
+
</ef-timegroup>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Use `fit` for backgrounds, watermarks, or any element that should span its parent's full duration.
|
|
85
|
+
|
|
86
|
+
## Decision Guide
|
|
87
|
+
|
|
88
|
+
| Scenario | Mode | Why |
|
|
89
|
+
|----------|------|-----|
|
|
90
|
+
| Title card with known length | `fixed` | Explicit control |
|
|
91
|
+
| Multi-scene video | `sequence` | Sequential playback |
|
|
92
|
+
| Video with text overlay | `contain` | Parallel layers |
|
|
93
|
+
| Background music/watermark | `fit` | Span parent duration |
|
|
94
|
+
| Scenes with transitions | `sequence` + `overlap` | Shared time for crossfades |
|
|
95
|
+
|
|
96
|
+
> **Note:** Modes compose — a `contain` parent with a `sequence` child and a `fit` background is a common pattern for videos with a background track spanning the full timeline.
|
|
97
|
+
|
|
98
|
+
## See Also
|
|
99
|
+
|
|
100
|
+
- [timegroup.md](references/timegroup.md) — attribute reference
|
|
101
|
+
- [time-model.md](references/time-model.md) — how time propagates through modes
|
|
102
|
+
- [sequencing.md](references/sequencing.md) — sequence patterns
|
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Timegroup Element
|
|
3
|
+
description: Container element for grouping and sequencing composition elements, controlling layout mode, duration, and playback behavior.
|
|
4
|
+
type: reference
|
|
5
|
+
nav:
|
|
6
|
+
parent: "Layout & Timing"
|
|
7
|
+
priority: 10
|
|
8
|
+
related: ["timegroup-modes", "sequencing"]
|
|
9
|
+
track: "layout-mastery"
|
|
10
|
+
track_step: 1
|
|
11
|
+
track_title: "Understanding Timegroups"
|
|
12
|
+
next_steps: ["timegroup-modes"]
|
|
13
|
+
api:
|
|
14
|
+
attributes:
|
|
15
|
+
- name: mode
|
|
16
|
+
type: string
|
|
17
|
+
required: true
|
|
18
|
+
description: Duration calculation mode
|
|
19
|
+
values: ["fixed", "sequence", "contain", "fit"]
|
|
20
|
+
- name: duration
|
|
21
|
+
type: timestring
|
|
22
|
+
description: Explicit duration (for fixed mode)
|
|
23
|
+
- name: overlap
|
|
24
|
+
type: timestring
|
|
25
|
+
description: Overlap time between sequence items (e.g., "1s")
|
|
26
|
+
- name: fps
|
|
27
|
+
type: number
|
|
28
|
+
default: 30
|
|
29
|
+
description: Frame rate for rendering
|
|
30
|
+
- name: auto-init
|
|
31
|
+
type: boolean
|
|
32
|
+
default: false
|
|
33
|
+
description: Auto-seek to frame 0 on load (root only)
|
|
34
|
+
- name: workbench
|
|
35
|
+
type: boolean
|
|
36
|
+
default: false
|
|
37
|
+
description: Enable timeline/hierarchy UI (root only)
|
|
38
|
+
methods:
|
|
39
|
+
- name: renderToVideo()
|
|
40
|
+
signature: "async renderToVideo(options?: RenderToVideoOptions): Promise<Uint8Array | undefined>"
|
|
41
|
+
description: Export timegroup to MP4 video using WebCodecs API
|
|
42
|
+
returns: Promise<Uint8Array | undefined>
|
|
43
|
+
- name: createRenderClone()
|
|
44
|
+
signature: "async createRenderClone(): Promise<RenderCloneResult>"
|
|
45
|
+
description: Create independent off-DOM clone for background rendering without affecting preview
|
|
46
|
+
returns: Promise<RenderCloneResult>
|
|
47
|
+
- name: addFrameTask()
|
|
48
|
+
signature: "addFrameTask(callback: FrameTaskCallback): () => void"
|
|
49
|
+
description: Register callback executed on each frame during rendering or playback
|
|
50
|
+
returns: Cleanup function that removes the callback
|
|
51
|
+
- name: seek()
|
|
52
|
+
signature: "async seek(timeMs: number): Promise<void>"
|
|
53
|
+
description: Seek to specific time position and wait for content to be ready
|
|
54
|
+
returns: Promise<void>
|
|
55
|
+
react:
|
|
56
|
+
generate: true
|
|
57
|
+
componentName: Timegroup
|
|
58
|
+
importPath: "@editframe/react"
|
|
59
|
+
propMapping:
|
|
60
|
+
auto-init: autoInit
|
|
61
|
+
additionalProps:
|
|
62
|
+
- name: className
|
|
63
|
+
type: string
|
|
64
|
+
description: CSS classes for styling
|
|
65
|
+
- name: ref
|
|
66
|
+
type: React.Ref
|
|
67
|
+
description: React ref (useful with hooks)
|
|
68
|
+
nav:
|
|
69
|
+
parent: "Core Concepts"
|
|
70
|
+
priority: 2
|
|
71
|
+
related: ["timeline-root", "hooks"]
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
<!-- html-only -->
|
|
75
|
+
# ef-timegroup
|
|
76
|
+
<!-- /html-only -->
|
|
77
|
+
<!-- react-only -->
|
|
78
|
+
# Timegroup
|
|
79
|
+
<!-- /react-only -->
|
|
80
|
+
|
|
81
|
+
Container for sequencing and grouping elements.
|
|
82
|
+
|
|
83
|
+
<!-- react-only -->
|
|
84
|
+
## Import
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
import { Timegroup } from "@editframe/react";
|
|
88
|
+
```
|
|
89
|
+
<!-- /react-only -->
|
|
90
|
+
|
|
91
|
+
## Modes
|
|
92
|
+
|
|
93
|
+
<!-- html-only -->
|
|
94
|
+
- `fixed` - Uses `duration` attribute
|
|
95
|
+
<!-- /html-only -->
|
|
96
|
+
<!-- react-only -->
|
|
97
|
+
- `fixed` - Uses `duration` prop
|
|
98
|
+
<!-- /react-only -->
|
|
99
|
+
- `sequence` - Sum of children (sequential playback)
|
|
100
|
+
- `contain` - Longest child duration
|
|
101
|
+
- `fit` - Inherit from parent
|
|
102
|
+
|
|
103
|
+
## Root Timegroup
|
|
104
|
+
|
|
105
|
+
<!-- html-only -->
|
|
106
|
+
```html live
|
|
107
|
+
<ef-timegroup mode="sequence" workbench class="w-[720px] h-[480px] bg-black">
|
|
108
|
+
<ef-timegroup mode="fixed" duration="3s" class="absolute w-full h-full flex items-center justify-center">
|
|
109
|
+
<ef-text duration="3s" class="text-white text-3xl">Scene 1</ef-text>
|
|
110
|
+
</ef-timegroup>
|
|
111
|
+
<ef-timegroup mode="fixed" duration="3s" class="absolute w-full h-full flex items-center justify-center">
|
|
112
|
+
<ef-text duration="3s" class="text-white text-3xl">Scene 2</ef-text>
|
|
113
|
+
</ef-timegroup>
|
|
114
|
+
</ef-timegroup>
|
|
115
|
+
```
|
|
116
|
+
<!-- /html-only -->
|
|
117
|
+
<!-- react-only -->
|
|
118
|
+
```tsx
|
|
119
|
+
import { Timegroup } from "@editframe/react";
|
|
120
|
+
|
|
121
|
+
export const Video = () => {
|
|
122
|
+
return (
|
|
123
|
+
<Timegroup workbench mode="sequence" className="w-[800px] h-[500px] bg-black">
|
|
124
|
+
{/* scenes here */}
|
|
125
|
+
</Timegroup>
|
|
126
|
+
);
|
|
127
|
+
};
|
|
128
|
+
```
|
|
129
|
+
<!-- /react-only -->
|
|
130
|
+
|
|
131
|
+
## Scene (Fixed Duration)
|
|
132
|
+
|
|
133
|
+
<!-- html-only -->
|
|
134
|
+
```html
|
|
135
|
+
<ef-timegroup mode="fixed" duration="5s" class="absolute w-full h-full">
|
|
136
|
+
<ef-video src="clip.mp4" class="size-full object-cover"></ef-video>
|
|
137
|
+
<ef-text class="absolute top-4 left-4 text-white">Overlay</ef-text>
|
|
138
|
+
</ef-timegroup>
|
|
139
|
+
```
|
|
140
|
+
<!-- /html-only -->
|
|
141
|
+
<!-- react-only -->
|
|
142
|
+
```tsx
|
|
143
|
+
<Timegroup mode="fixed" duration="5s" className="absolute w-full h-full">
|
|
144
|
+
<Video src="/assets/clip.mp4" className="size-full object-cover" />
|
|
145
|
+
<Text className="absolute top-4 left-4 text-white">Overlay</Text>
|
|
146
|
+
</Timegroup>
|
|
147
|
+
```
|
|
148
|
+
<!-- /react-only -->
|
|
149
|
+
|
|
150
|
+
## Nested Sequence
|
|
151
|
+
|
|
152
|
+
<!-- html-only -->
|
|
153
|
+
```html
|
|
154
|
+
<ef-timegroup mode="sequence">
|
|
155
|
+
<ef-timegroup mode="fixed" duration="3s"><!-- Scene 1 --></ef-timegroup>
|
|
156
|
+
<ef-timegroup mode="fixed" duration="5s"><!-- Scene 2 --></ef-timegroup>
|
|
157
|
+
<ef-timegroup mode="fixed" duration="4s"><!-- Scene 3 --></ef-timegroup>
|
|
158
|
+
</ef-timegroup>
|
|
159
|
+
```
|
|
160
|
+
<!-- /html-only -->
|
|
161
|
+
<!-- react-only -->
|
|
162
|
+
```tsx
|
|
163
|
+
<Timegroup mode="sequence">
|
|
164
|
+
<Timegroup mode="fixed" duration="3s">
|
|
165
|
+
{/* Scene 1 */}
|
|
166
|
+
</Timegroup>
|
|
167
|
+
<Timegroup mode="fixed" duration="5s">
|
|
168
|
+
{/* Scene 2 */}
|
|
169
|
+
</Timegroup>
|
|
170
|
+
<Timegroup mode="fixed" duration="4s">
|
|
171
|
+
{/* Scene 3 */}
|
|
172
|
+
</Timegroup>
|
|
173
|
+
</Timegroup>
|
|
174
|
+
```
|
|
175
|
+
<!-- /react-only -->
|
|
176
|
+
|
|
177
|
+
<!-- react-only -->
|
|
178
|
+
## Dynamic Content
|
|
179
|
+
|
|
180
|
+
Map over data to create scenes:
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
const scenes = [
|
|
184
|
+
{ id: 1, text: "Scene 1", duration: "3s" },
|
|
185
|
+
{ id: 2, text: "Scene 2", duration: "5s" },
|
|
186
|
+
{ id: 3, text: "Scene 3", duration: "4s" },
|
|
187
|
+
];
|
|
188
|
+
|
|
189
|
+
<Timegroup workbench mode="sequence" className="w-[800px] h-[500px]">
|
|
190
|
+
{scenes.map((scene) => (
|
|
191
|
+
<Timegroup
|
|
192
|
+
key={scene.id}
|
|
193
|
+
mode="fixed"
|
|
194
|
+
duration={scene.duration}
|
|
195
|
+
className="absolute w-full h-full"
|
|
196
|
+
>
|
|
197
|
+
<Text className="text-white text-4xl">{scene.text}</Text>
|
|
198
|
+
</Timegroup>
|
|
199
|
+
))}
|
|
200
|
+
</Timegroup>
|
|
201
|
+
```
|
|
202
|
+
<!-- /react-only -->
|
|
203
|
+
|
|
204
|
+
## Sequence with Overlap
|
|
205
|
+
|
|
206
|
+
Use `overlap` to create transitions between items:
|
|
207
|
+
|
|
208
|
+
<!-- html-only -->
|
|
209
|
+
```html
|
|
210
|
+
<ef-timegroup mode="sequence" overlap="1s">
|
|
211
|
+
<ef-timegroup mode="contain"><!-- Scene 1 --></ef-timegroup>
|
|
212
|
+
<ef-timegroup mode="contain"><!-- Scene 2 --></ef-timegroup>
|
|
213
|
+
</ef-timegroup>
|
|
214
|
+
```
|
|
215
|
+
<!-- /html-only -->
|
|
216
|
+
<!-- react-only -->
|
|
217
|
+
```tsx
|
|
218
|
+
<Timegroup mode="sequence" overlap="1s">
|
|
219
|
+
<Timegroup mode="contain">{/* Scene 1 */}</Timegroup>
|
|
220
|
+
<Timegroup mode="contain">{/* Scene 2 */}</Timegroup>
|
|
221
|
+
</Timegroup>
|
|
222
|
+
```
|
|
223
|
+
<!-- /react-only -->
|
|
224
|
+
|
|
225
|
+
See [transitions.md](references/transitions.md) for crossfade examples.
|
|
226
|
+
|
|
227
|
+
<!-- react-only -->
|
|
228
|
+
## With useTimingInfo Hook
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
import { Timegroup } from "@editframe/react";
|
|
232
|
+
import { useTimingInfo } from "@editframe/react";
|
|
233
|
+
|
|
234
|
+
const AnimatedScene = () => {
|
|
235
|
+
const { ref, percentComplete, ownCurrentTimeMs } = useTimingInfo();
|
|
236
|
+
|
|
237
|
+
return (
|
|
238
|
+
<Timegroup ref={ref} mode="contain" className="absolute w-full h-full">
|
|
239
|
+
<div style={{ opacity: percentComplete }}>
|
|
240
|
+
Fading in... {(ownCurrentTimeMs / 1000).toFixed(2)}s
|
|
241
|
+
</div>
|
|
242
|
+
</Timegroup>
|
|
243
|
+
);
|
|
244
|
+
};
|
|
245
|
+
```
|
|
246
|
+
<!-- /react-only -->
|
|
247
|
+
|
|
248
|
+
## Methods
|
|
249
|
+
|
|
250
|
+
### renderToVideo()
|
|
251
|
+
|
|
252
|
+
Export the timegroup composition to MP4 video using the WebCodecs API. See [render-api.md](references/render-api.md) for complete documentation.
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
async renderToVideo(options?: RenderToVideoOptions): Promise<Uint8Array | undefined>
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**Basic Example:**
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
const tg = document.querySelector('ef-timegroup');
|
|
262
|
+
|
|
263
|
+
await tg.renderToVideo({
|
|
264
|
+
fps: 30,
|
|
265
|
+
codec: 'avc',
|
|
266
|
+
filename: 'output.mp4',
|
|
267
|
+
onProgress: (progress) => {
|
|
268
|
+
console.log(`${Math.round(progress.progress * 100)}% complete`);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**RenderToVideoOptions:**
|
|
274
|
+
- `fps` - Frame rate (default: 30)
|
|
275
|
+
- `codec` - Video codec: "avc", "hevc", "vp9", "av1", "vp8" (default: "avc")
|
|
276
|
+
- `bitrate` - Video bitrate in bits/second (default: 5000000)
|
|
277
|
+
- `filename` - Download filename (default: "video.mp4")
|
|
278
|
+
- `scale` - Resolution multiplier (default: 1)
|
|
279
|
+
- `fromMs`, `toMs` - Time range to export
|
|
280
|
+
- `onProgress` - Progress callback receiving `RenderProgress` object
|
|
281
|
+
- `includeAudio` - Include audio tracks (default: true)
|
|
282
|
+
- `signal` - AbortSignal for cancellation
|
|
283
|
+
- `returnBuffer` - Return Uint8Array instead of downloading
|
|
284
|
+
- And more - see [render-api.md](references/render-api.md)
|
|
285
|
+
|
|
286
|
+
### createRenderClone()
|
|
287
|
+
|
|
288
|
+
Create an independent off-DOM clone for background rendering without affecting the preview timeline.
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
async createRenderClone(): Promise<RenderCloneResult>
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
**Example:**
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
const tg = document.querySelector('ef-timegroup');
|
|
298
|
+
|
|
299
|
+
// Create isolated render clone
|
|
300
|
+
const { clone, container, cleanup } = await tg.createRenderClone();
|
|
301
|
+
|
|
302
|
+
try {
|
|
303
|
+
// Clone has independent time state
|
|
304
|
+
await clone.seek(5000); // Seek clone to 5 seconds
|
|
305
|
+
|
|
306
|
+
// Original timeline unaffected
|
|
307
|
+
console.log('Clone time:', clone.currentTimeMs);
|
|
308
|
+
console.log('Original time:', tg.currentTimeMs);
|
|
309
|
+
} finally {
|
|
310
|
+
// Always clean up
|
|
311
|
+
cleanup();
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
**RenderCloneResult:**
|
|
316
|
+
```typescript
|
|
317
|
+
interface RenderCloneResult {
|
|
318
|
+
clone: EFTimegroup; // Cloned timegroup with independent state
|
|
319
|
+
container: HTMLElement; // Off-screen container holding the clone
|
|
320
|
+
cleanup: () => void; // Remove clone from DOM and clean up
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Clones automatically re-run the `initializer` function if set, enabling JavaScript-driven animations in renders.
|
|
325
|
+
|
|
326
|
+
### addFrameTask()
|
|
327
|
+
|
|
328
|
+
Register a callback that executes on each frame during playback or rendering. Returns cleanup function.
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
addFrameTask(callback: FrameTaskCallback): () => void
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
**Example:**
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
const tg = document.querySelector('ef-timegroup');
|
|
338
|
+
|
|
339
|
+
const cleanup = tg.addFrameTask((info) => {
|
|
340
|
+
// Update DOM based on current time
|
|
341
|
+
const progress = info.percentComplete;
|
|
342
|
+
console.log(`Frame at ${info.ownCurrentTimeMs}ms (${Math.round(progress * 100)}%)`);
|
|
343
|
+
|
|
344
|
+
// Modify child elements
|
|
345
|
+
const textEl = info.element.querySelector('.dynamic-text');
|
|
346
|
+
if (textEl) {
|
|
347
|
+
textEl.textContent = `Time: ${(info.ownCurrentTimeMs / 1000).toFixed(2)}s`;
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
// Remove callback when done
|
|
352
|
+
cleanup();
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
**FrameTaskCallback:**
|
|
356
|
+
```typescript
|
|
357
|
+
type FrameTaskCallback = (info: {
|
|
358
|
+
ownCurrentTimeMs: number; // This element's local time
|
|
359
|
+
currentTimeMs: number; // Root timeline's global time
|
|
360
|
+
durationMs: number; // This element's duration
|
|
361
|
+
percentComplete: number; // Completion percentage (0.0 to 1.0)
|
|
362
|
+
element: EFTimegroup; // The timegroup instance
|
|
363
|
+
}) => void | Promise<void>;
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
The callback can be sync or async. Multiple callbacks can be registered and execute in parallel.
|
|
367
|
+
|
|
368
|
+
### seek()
|
|
369
|
+
|
|
370
|
+
Seek to a specific time position and wait for all visible content to be ready.
|
|
371
|
+
|
|
372
|
+
```typescript
|
|
373
|
+
async seek(timeMs: number): Promise<void>
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
**Example:**
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
const tg = document.querySelector('ef-timegroup');
|
|
380
|
+
|
|
381
|
+
// Seek to 3 seconds
|
|
382
|
+
await tg.seek(3000);
|
|
383
|
+
|
|
384
|
+
// Timeline is now at 3s, all visible media loaded
|
|
385
|
+
console.log('Current time:', tg.currentTimeMs);
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
Time is automatically quantized to frame boundaries based on the `fps` attribute.
|
|
389
|
+
|
|
390
|
+
## Scripting
|
|
391
|
+
|
|
392
|
+
<!-- html-only -->
|
|
393
|
+
Add dynamic behavior with JavaScript using the `initializer` property. The initializer function runs on both the original timeline and all render clones.
|
|
394
|
+
|
|
395
|
+
```html
|
|
396
|
+
<ef-timegroup id="dynamic-scene" mode="fixed" duration="5s">
|
|
397
|
+
<div class="dynamic-text"></div>
|
|
398
|
+
</ef-timegroup>
|
|
399
|
+
|
|
400
|
+
<script>
|
|
401
|
+
const tg = document.querySelector('#dynamic-scene');
|
|
402
|
+
|
|
403
|
+
// Initializer runs once per instance (original + clones)
|
|
404
|
+
tg.initializer = (instance) => {
|
|
405
|
+
instance.addFrameTask((info) => {
|
|
406
|
+
const text = instance.querySelector('.dynamic-text');
|
|
407
|
+
text.textContent = `Time: ${(info.ownCurrentTimeMs / 1000).toFixed(2)}s`;
|
|
408
|
+
});
|
|
409
|
+
};
|
|
410
|
+
</script>
|
|
411
|
+
```
|
|
412
|
+
<!-- /html-only -->
|
|
413
|
+
<!-- react-only -->
|
|
414
|
+
Add dynamic behavior with JavaScript. See [scripting.md](references/scripting.md) for details.
|
|
415
|
+
|
|
416
|
+
```tsx
|
|
417
|
+
import { useRef, useEffect } from "react";
|
|
418
|
+
import { Timegroup } from "@editframe/react";
|
|
419
|
+
|
|
420
|
+
const DynamicScene = () => {
|
|
421
|
+
const timegroupRef = useRef<HTMLElement>(null);
|
|
422
|
+
|
|
423
|
+
useEffect(() => {
|
|
424
|
+
const tg = timegroupRef.current;
|
|
425
|
+
if (!tg) return;
|
|
426
|
+
|
|
427
|
+
tg.initializer = (instance) => {
|
|
428
|
+
const cleanup = instance.addFrameTask((info) => {
|
|
429
|
+
// Update content based on time
|
|
430
|
+
console.log(`Time: ${info.ownCurrentTimeMs}ms`);
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
return cleanup;
|
|
434
|
+
};
|
|
435
|
+
}, []);
|
|
436
|
+
|
|
437
|
+
return (
|
|
438
|
+
<Timegroup ref={timegroupRef} mode="fixed" duration="5s">
|
|
439
|
+
{/* Content */}
|
|
440
|
+
</Timegroup>
|
|
441
|
+
);
|
|
442
|
+
};
|
|
443
|
+
```
|
|
444
|
+
<!-- /react-only -->
|
|
445
|
+
|
|
446
|
+
<!-- html-only -->
|
|
447
|
+
**TimegroupInitializer:**
|
|
448
|
+
```typescript
|
|
449
|
+
type TimegroupInitializer = (timegroup: EFTimegroup) => void;
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
**Constraints:**
|
|
453
|
+
- Must be synchronous (no async/await, no Promise return)
|
|
454
|
+
- Must complete quickly (<10ms warning, <100ms error)
|
|
455
|
+
- Should only register callbacks and set up behavior
|
|
456
|
+
- Runs once per instance (prime timeline + each render clone)
|
|
457
|
+
<!-- /html-only -->
|