@editframe/create 0.51.9 → 0.52.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.
- package/dist/skills/editframe-composition/references/configuration.md +77 -29
- package/dist/skills/editframe-composition/references/events.md +68 -54
- package/dist/skills/editframe-composition/references/render-strategies.md +17 -17
- package/dist/templates/html/package.json +3 -3
- package/dist/templates/nextjs/next.config.mjs +2 -5
- package/dist/templates/nextjs/package.json +3 -3
- package/dist/templates/nextjs/src/app/layout.tsx +1 -5
- package/dist/templates/nextjs/src/app/page.tsx +3 -11
- package/dist/templates/react/package.json +3 -3
- package/package.json +1 -1
|
@@ -38,20 +38,26 @@ react:
|
|
|
38
38
|
---
|
|
39
39
|
|
|
40
40
|
<!-- html-only -->
|
|
41
|
+
|
|
41
42
|
# ef-configuration
|
|
43
|
+
|
|
42
44
|
<!-- /html-only -->
|
|
43
45
|
<!-- react-only -->
|
|
46
|
+
|
|
44
47
|
# Configuration
|
|
48
|
+
|
|
45
49
|
<!-- /react-only -->
|
|
46
50
|
|
|
47
51
|
Configuration wrapper for media engine selection and API settings.
|
|
48
52
|
|
|
49
53
|
<!-- react-only -->
|
|
54
|
+
|
|
50
55
|
## Import
|
|
51
56
|
|
|
52
57
|
```tsx
|
|
53
58
|
import { Configuration } from "@editframe/react";
|
|
54
59
|
```
|
|
60
|
+
|
|
55
61
|
<!-- /react-only -->
|
|
56
62
|
|
|
57
63
|
## Basic Usage
|
|
@@ -59,15 +65,21 @@ import { Configuration } from "@editframe/react";
|
|
|
59
65
|
Wrap your composition to configure media handling:
|
|
60
66
|
|
|
61
67
|
<!-- html-only -->
|
|
68
|
+
|
|
62
69
|
```html
|
|
63
70
|
<ef-configuration media-engine="cloud">
|
|
64
71
|
<ef-timegroup mode="contain" class="w-[720px] h-[480px] bg-black">
|
|
65
|
-
<ef-video
|
|
72
|
+
<ef-video
|
|
73
|
+
src="https://assets.editframe.com/bars-n-tone.mp4"
|
|
74
|
+
class="size-full"
|
|
75
|
+
></ef-video>
|
|
66
76
|
</ef-timegroup>
|
|
67
77
|
</ef-configuration>
|
|
68
78
|
```
|
|
79
|
+
|
|
69
80
|
<!-- /html-only -->
|
|
70
81
|
<!-- react-only -->
|
|
82
|
+
|
|
71
83
|
```tsx
|
|
72
84
|
// Video.tsx
|
|
73
85
|
import { Timegroup } from "@editframe/react";
|
|
@@ -88,38 +100,50 @@ import { Video } from "./Video";
|
|
|
88
100
|
ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
89
101
|
<Configuration apiHost="https://api.example.com" mediaEngine="local">
|
|
90
102
|
<TimelineRoot id="root" component={Video} />
|
|
91
|
-
</Configuration
|
|
103
|
+
</Configuration>,
|
|
92
104
|
);
|
|
93
105
|
```
|
|
106
|
+
|
|
94
107
|
<!-- /react-only -->
|
|
95
108
|
|
|
96
109
|
## Media Engine Options
|
|
97
110
|
|
|
98
111
|
<!-- html-only -->
|
|
112
|
+
|
|
99
113
|
The `media-engine` attribute controls how media files are loaded and processed.
|
|
114
|
+
|
|
100
115
|
<!-- /html-only -->
|
|
101
116
|
<!-- react-only -->
|
|
117
|
+
|
|
102
118
|
Controls how media files are loaded and processed. Three options are available:
|
|
119
|
+
|
|
103
120
|
<!-- /react-only -->
|
|
104
121
|
|
|
105
122
|
### Cloud Mode (Default)
|
|
106
123
|
|
|
107
124
|
Automatically selects the appropriate engine based on URL:
|
|
125
|
+
|
|
108
126
|
- Remote URLs (`http://`, `https://`) use JIT transcoding
|
|
109
127
|
- Local paths use asset engine
|
|
110
128
|
|
|
111
129
|
<!-- html-only -->
|
|
130
|
+
|
|
112
131
|
```html live
|
|
113
132
|
<ef-configuration media-engine="cloud">
|
|
114
133
|
<ef-timegroup mode="contain" class="w-[720px] h-[480px] bg-black">
|
|
115
|
-
<ef-video
|
|
134
|
+
<ef-video
|
|
135
|
+
src="https://assets.editframe.com/bars-n-tone.mp4"
|
|
136
|
+
class="size-full object-contain"
|
|
137
|
+
></ef-video>
|
|
116
138
|
</ef-timegroup>
|
|
117
139
|
</ef-configuration>
|
|
118
140
|
```
|
|
119
141
|
|
|
120
142
|
**Use when:** Building production applications with mixed local and remote assets.
|
|
143
|
+
|
|
121
144
|
<!-- /html-only -->
|
|
122
145
|
<!-- react-only -->
|
|
146
|
+
|
|
123
147
|
```tsx
|
|
124
148
|
<Configuration mediaEngine="cloud">
|
|
125
149
|
<Timegroup mode="sequence">
|
|
@@ -128,6 +152,7 @@ Automatically selects the appropriate engine based on URL:
|
|
|
128
152
|
</Timegroup>
|
|
129
153
|
</Configuration>
|
|
130
154
|
```
|
|
155
|
+
|
|
131
156
|
<!-- /react-only -->
|
|
132
157
|
|
|
133
158
|
### Local Mode
|
|
@@ -135,6 +160,7 @@ Automatically selects the appropriate engine based on URL:
|
|
|
135
160
|
Forces all sources through the local asset engine using `@ef-*` URLs:
|
|
136
161
|
|
|
137
162
|
<!-- html-only -->
|
|
163
|
+
|
|
138
164
|
```html
|
|
139
165
|
<ef-configuration media-engine="local">
|
|
140
166
|
<ef-timegroup mode="contain" class="w-[720px] h-[480px]">
|
|
@@ -144,11 +170,12 @@ Forces all sources through the local asset engine using `@ef-*` URLs:
|
|
|
144
170
|
```
|
|
145
171
|
|
|
146
172
|
**Use when:**
|
|
173
|
+
|
|
147
174
|
- Development with local files only
|
|
148
175
|
- Testing without network access
|
|
149
176
|
- Working with bundled assets
|
|
150
|
-
<!-- /html-only -->
|
|
151
|
-
<!-- react-only -->
|
|
177
|
+
<!-- /html-only -->
|
|
178
|
+
<!-- react-only -->
|
|
152
179
|
- All sources use local asset endpoints
|
|
153
180
|
- No transcoding applied
|
|
154
181
|
- Best for local development with Vite plugin
|
|
@@ -160,6 +187,7 @@ Forces all sources through the local asset engine using `@ef-*` URLs:
|
|
|
160
187
|
</Timegroup>
|
|
161
188
|
</Configuration>
|
|
162
189
|
```
|
|
190
|
+
|
|
163
191
|
<!-- /react-only -->
|
|
164
192
|
|
|
165
193
|
### JIT Mode
|
|
@@ -167,6 +195,7 @@ Forces all sources through the local asset engine using `@ef-*` URLs:
|
|
|
167
195
|
Forces all sources through JIT transcoding using `/api/v1/transcode/*` URLs:
|
|
168
196
|
|
|
169
197
|
<!-- html-only -->
|
|
198
|
+
|
|
170
199
|
```html
|
|
171
200
|
<ef-configuration media-engine="jit">
|
|
172
201
|
<ef-timegroup mode="contain" class="w-[720px] h-[480px]">
|
|
@@ -176,11 +205,12 @@ Forces all sources through JIT transcoding using `/api/v1/transcode/*` URLs:
|
|
|
176
205
|
```
|
|
177
206
|
|
|
178
207
|
**Use when:**
|
|
208
|
+
|
|
179
209
|
- On-demand transcoding needed
|
|
180
210
|
- Working with incompatible video formats
|
|
181
211
|
- Development with Editframe Vite plugin
|
|
182
|
-
<!-- /html-only -->
|
|
183
|
-
<!-- react-only -->
|
|
212
|
+
<!-- /html-only -->
|
|
213
|
+
<!-- react-only -->
|
|
184
214
|
- All sources are transcoded on-demand
|
|
185
215
|
- Useful for testing transcoding behavior
|
|
186
216
|
- May have slower initial load
|
|
@@ -192,6 +222,7 @@ Forces all sources through JIT transcoding using `/api/v1/transcode/*` URLs:
|
|
|
192
222
|
</Timegroup>
|
|
193
223
|
</Configuration>
|
|
194
224
|
```
|
|
225
|
+
|
|
195
226
|
<!-- /react-only -->
|
|
196
227
|
|
|
197
228
|
## API Host Override
|
|
@@ -201,10 +232,11 @@ When no `ef-configuration` ancestor is present, all source-based elements (`ef-v
|
|
|
201
232
|
Use `ef-configuration` with `api-host` when you need to override that default:
|
|
202
233
|
|
|
203
234
|
<!-- html-only -->
|
|
235
|
+
|
|
204
236
|
Override the API host for asset resolution in production:
|
|
205
237
|
|
|
206
238
|
```html
|
|
207
|
-
<ef-configuration api-host="https://
|
|
239
|
+
<ef-configuration api-host="https://editframe.com">
|
|
208
240
|
<ef-timegroup mode="contain" class="w-[720px] h-[480px]">
|
|
209
241
|
<ef-video src="video.mp4" class="size-full"></ef-video>
|
|
210
242
|
</ef-timegroup>
|
|
@@ -212,20 +244,20 @@ Override the API host for asset resolution in production:
|
|
|
212
244
|
```
|
|
213
245
|
|
|
214
246
|
**Use when:**
|
|
247
|
+
|
|
215
248
|
- Deploying to custom domains
|
|
216
249
|
- Using proxy servers
|
|
217
250
|
- Testing against staging environments
|
|
218
|
-
<!-- /html-only -->
|
|
219
|
-
<!-- react-only -->
|
|
220
|
-
Set the API endpoint for transcription and rendering:
|
|
251
|
+
<!-- /html-only -->
|
|
252
|
+
<!-- react-only -->
|
|
253
|
+
Set the API endpoint for transcription and rendering:
|
|
221
254
|
|
|
222
255
|
```tsx
|
|
223
|
-
<Configuration apiHost="https://
|
|
224
|
-
<Timegroup mode="sequence">
|
|
225
|
-
{/* Composition */}
|
|
226
|
-
</Timegroup>
|
|
256
|
+
<Configuration apiHost="https://editframe.com">
|
|
257
|
+
<Timegroup mode="sequence">{/* Composition */}</Timegroup>
|
|
227
258
|
</Configuration>
|
|
228
259
|
```
|
|
260
|
+
|
|
229
261
|
<!-- /react-only -->
|
|
230
262
|
|
|
231
263
|
## Signing URL
|
|
@@ -233,6 +265,7 @@ Set the API endpoint for transcription and rendering:
|
|
|
233
265
|
Configure the endpoint for generating signed URLs:
|
|
234
266
|
|
|
235
267
|
<!-- html-only -->
|
|
268
|
+
|
|
236
269
|
```html
|
|
237
270
|
<ef-configuration signing-url="/api/sign-url">
|
|
238
271
|
<ef-timegroup mode="contain" class="w-[720px] h-[480px]">
|
|
@@ -242,35 +275,37 @@ Configure the endpoint for generating signed URLs:
|
|
|
242
275
|
```
|
|
243
276
|
|
|
244
277
|
**Use when:**
|
|
278
|
+
|
|
245
279
|
- Custom authentication flows
|
|
246
280
|
- Non-standard URL signing
|
|
247
281
|
- Backend integration requirements
|
|
248
|
-
<!-- /html-only -->
|
|
249
|
-
<!-- react-only -->
|
|
250
|
-
Defaults to `/@ef-sign-url`:
|
|
282
|
+
<!-- /html-only -->
|
|
283
|
+
<!-- react-only -->
|
|
284
|
+
Defaults to `/@ef-sign-url`:
|
|
251
285
|
|
|
252
286
|
```tsx
|
|
253
287
|
<Configuration
|
|
254
288
|
apiHost="https://api.example.com"
|
|
255
289
|
signingURL="/custom-signing-endpoint"
|
|
256
290
|
>
|
|
257
|
-
<Timegroup mode="sequence">
|
|
258
|
-
{/* Composition */}
|
|
259
|
-
</Timegroup>
|
|
291
|
+
<Timegroup mode="sequence">{/* Composition */}</Timegroup>
|
|
260
292
|
</Configuration>
|
|
261
293
|
```
|
|
262
294
|
|
|
263
295
|
The signing URL is used to:
|
|
296
|
+
|
|
264
297
|
- Generate signed URLs for secure media access
|
|
265
298
|
- Authenticate transcoding requests
|
|
266
299
|
- Control access to media files
|
|
267
300
|
|
|
268
301
|
In development, the Vite plugin provides this endpoint automatically.
|
|
302
|
+
|
|
269
303
|
<!-- /react-only -->
|
|
270
304
|
|
|
271
305
|
## Development vs Production
|
|
272
306
|
|
|
273
307
|
<!-- html-only -->
|
|
308
|
+
|
|
274
309
|
### Local Development Setup
|
|
275
310
|
|
|
276
311
|
Use `local` mode with Vite plugin for fast iteration:
|
|
@@ -290,12 +325,17 @@ Use `cloud` mode with custom API host:
|
|
|
290
325
|
```html
|
|
291
326
|
<ef-configuration media-engine="cloud" api-host="https://api.yourdomain.com">
|
|
292
327
|
<ef-timegroup mode="contain" class="w-[720px] h-[480px]">
|
|
293
|
-
<ef-video
|
|
328
|
+
<ef-video
|
|
329
|
+
src="https://cdn.yourdomain.com/video.mp4"
|
|
330
|
+
class="size-full"
|
|
331
|
+
></ef-video>
|
|
294
332
|
</ef-timegroup>
|
|
295
333
|
</ef-configuration>
|
|
296
334
|
```
|
|
335
|
+
|
|
297
336
|
<!-- /html-only -->
|
|
298
337
|
<!-- react-only -->
|
|
338
|
+
|
|
299
339
|
```tsx
|
|
300
340
|
const apiHost = import.meta.env.VITE_API_HOST || "http://localhost:3000";
|
|
301
341
|
const mediaEngine = import.meta.env.PROD ? "cloud" : "local";
|
|
@@ -310,11 +350,13 @@ export const Video = () => {
|
|
|
310
350
|
);
|
|
311
351
|
};
|
|
312
352
|
```
|
|
353
|
+
|
|
313
354
|
<!-- /react-only -->
|
|
314
355
|
|
|
315
356
|
## Context Provision
|
|
316
357
|
|
|
317
358
|
<!-- html-only -->
|
|
359
|
+
|
|
318
360
|
`ef-configuration` provides settings via Lit context to all descendant elements. This means you only need one configuration wrapper at the root level:
|
|
319
361
|
|
|
320
362
|
```html
|
|
@@ -327,8 +369,10 @@ export const Video = () => {
|
|
|
327
369
|
</ef-timegroup>
|
|
328
370
|
</ef-configuration>
|
|
329
371
|
```
|
|
372
|
+
|
|
330
373
|
<!-- /html-only -->
|
|
331
374
|
<!-- react-only -->
|
|
375
|
+
|
|
332
376
|
If you don't need API features (transcription) or want default settings, you can omit Configuration:
|
|
333
377
|
|
|
334
378
|
```tsx
|
|
@@ -340,18 +384,23 @@ export const Video = () => {
|
|
|
340
384
|
);
|
|
341
385
|
};
|
|
342
386
|
```
|
|
387
|
+
|
|
343
388
|
<!-- /react-only -->
|
|
344
389
|
|
|
345
390
|
## Multiple Configurations
|
|
346
391
|
|
|
347
392
|
<!-- html-only -->
|
|
393
|
+
|
|
348
394
|
Nest configurations to override settings for specific subtrees:
|
|
349
395
|
|
|
350
396
|
```html
|
|
351
397
|
<ef-configuration media-engine="cloud">
|
|
352
398
|
<ef-timegroup mode="sequence" class="w-[720px] h-[480px]">
|
|
353
399
|
<!-- Remote video uses cloud mode -->
|
|
354
|
-
<ef-video
|
|
400
|
+
<ef-video
|
|
401
|
+
src="https://cdn.example.com/intro.mp4"
|
|
402
|
+
class="size-full"
|
|
403
|
+
></ef-video>
|
|
355
404
|
|
|
356
405
|
<!-- Local videos use local mode -->
|
|
357
406
|
<ef-configuration media-engine="local">
|
|
@@ -362,8 +411,10 @@ Nest configurations to override settings for specific subtrees:
|
|
|
362
411
|
```
|
|
363
412
|
|
|
364
413
|
> **Note:** Inner configurations override outer settings only for their descendants. Settings merge — unspecified attributes inherit from parent configuration.
|
|
414
|
+
|
|
365
415
|
<!-- /html-only -->
|
|
366
416
|
<!-- react-only -->
|
|
417
|
+
|
|
367
418
|
## Typical Setup
|
|
368
419
|
|
|
369
420
|
```tsx
|
|
@@ -372,11 +423,7 @@ import { Timegroup, Video, Audio } from "@editframe/react";
|
|
|
372
423
|
|
|
373
424
|
export const VideoComposition = () => {
|
|
374
425
|
return (
|
|
375
|
-
<Timegroup
|
|
376
|
-
|
|
377
|
-
mode="sequence"
|
|
378
|
-
className="w-[1920px] h-[1080px] bg-black"
|
|
379
|
-
>
|
|
426
|
+
<Timegroup mode="sequence" className="w-[1920px] h-[1080px] bg-black">
|
|
380
427
|
<Timegroup mode="fixed" duration="10s" className="absolute w-full h-full">
|
|
381
428
|
<Video src="/assets/intro.mp4" className="size-full object-cover" />
|
|
382
429
|
<Audio src="/assets/music.mp3" volume={0.3} />
|
|
@@ -397,7 +444,8 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
|
397
444
|
mediaEngine="local"
|
|
398
445
|
>
|
|
399
446
|
<TimelineRoot id="root" component={VideoComposition} />
|
|
400
|
-
</Configuration
|
|
447
|
+
</Configuration>,
|
|
401
448
|
);
|
|
402
449
|
```
|
|
450
|
+
|
|
403
451
|
<!-- /react-only -->
|
|
@@ -128,8 +128,14 @@ interface TrimValue {
|
|
|
128
128
|
**Example: Listen to trim changes**
|
|
129
129
|
|
|
130
130
|
```html live
|
|
131
|
-
<ef-configuration apiHost="https://
|
|
132
|
-
<ef-timegroup
|
|
131
|
+
<ef-configuration apiHost="https://editframe.com">
|
|
132
|
+
<ef-timegroup
|
|
133
|
+
id="composition"
|
|
134
|
+
duration="5000"
|
|
135
|
+
width="1920"
|
|
136
|
+
height="1080"
|
|
137
|
+
background="black"
|
|
138
|
+
>
|
|
133
139
|
<ef-video
|
|
134
140
|
id="video1"
|
|
135
141
|
src="https://assets.editframe.com/sample-5s.mp4"
|
|
@@ -145,15 +151,17 @@ interface TrimValue {
|
|
|
145
151
|
></ef-trim-handles>
|
|
146
152
|
|
|
147
153
|
<script>
|
|
148
|
-
document
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
154
|
+
document
|
|
155
|
+
.querySelector("ef-trim-handles")
|
|
156
|
+
.addEventListener("trim-change", (e) => {
|
|
157
|
+
const { type, value } = e.detail;
|
|
158
|
+
console.log(`Trim ${type}: ${value.startMs}ms - ${value.endMs}ms`);
|
|
159
|
+
|
|
160
|
+
// Update video element
|
|
161
|
+
const video = document.getElementById("video1");
|
|
162
|
+
video.trimStartMs = value.startMs;
|
|
163
|
+
video.trimEndMs = value.endMs;
|
|
164
|
+
});
|
|
157
165
|
</script>
|
|
158
166
|
</ef-configuration>
|
|
159
167
|
```
|
|
@@ -220,9 +228,9 @@ interface DialChangeDetail {
|
|
|
220
228
|
<ef-dial value="45"></ef-dial>
|
|
221
229
|
|
|
222
230
|
<script>
|
|
223
|
-
document.querySelector(
|
|
231
|
+
document.querySelector("ef-dial").addEventListener("change", (e) => {
|
|
224
232
|
const { value } = e.detail;
|
|
225
|
-
console.log(
|
|
233
|
+
console.log("Dial rotation:", value, "°");
|
|
226
234
|
|
|
227
235
|
// Apply rotation to element
|
|
228
236
|
targetElement.style.transform = `rotate(${value}deg)`;
|
|
@@ -242,9 +250,9 @@ Fired by `ef-scrubber` when the playhead position changes. Detail contains the n
|
|
|
242
250
|
<ef-scrubber target="composition"></ef-scrubber>
|
|
243
251
|
|
|
244
252
|
<script>
|
|
245
|
-
document.querySelector(
|
|
253
|
+
document.querySelector("ef-scrubber").addEventListener("seek", (e) => {
|
|
246
254
|
const timeMs = e.detail;
|
|
247
|
-
console.log(
|
|
255
|
+
console.log("Seek to:", timeMs, "ms");
|
|
248
256
|
});
|
|
249
257
|
</script>
|
|
250
258
|
```
|
|
@@ -270,17 +278,19 @@ interface ScrubSegmentLoadingDetail {
|
|
|
270
278
|
<div id="progress" style="display: none;">Loading scrub preview...</div>
|
|
271
279
|
|
|
272
280
|
<script>
|
|
273
|
-
document
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
281
|
+
document
|
|
282
|
+
.getElementById("myVideo")
|
|
283
|
+
.addEventListener("scrub-segment-loading", (e) => {
|
|
284
|
+
const { loaded, total, status } = e.detail;
|
|
285
|
+
const progressEl = document.getElementById("progress");
|
|
286
|
+
|
|
287
|
+
if (status === "loading") {
|
|
288
|
+
progressEl.style.display = "block";
|
|
289
|
+
progressEl.textContent = `Loading ${loaded}/${total}...`;
|
|
290
|
+
} else {
|
|
291
|
+
progressEl.style.display = "none";
|
|
292
|
+
}
|
|
293
|
+
});
|
|
284
294
|
</script>
|
|
285
295
|
```
|
|
286
296
|
|
|
@@ -329,16 +339,18 @@ interface HierarchySelectDetail {
|
|
|
329
339
|
<ef-hierarchy target="composition"></ef-hierarchy>
|
|
330
340
|
|
|
331
341
|
<script>
|
|
332
|
-
document
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
+
document
|
|
343
|
+
.querySelector("ef-hierarchy")
|
|
344
|
+
.addEventListener("hierarchy-select", (e) => {
|
|
345
|
+
const { elementId } = e.detail;
|
|
346
|
+
|
|
347
|
+
// Update canvas selection
|
|
348
|
+
const canvas = document.querySelector("ef-canvas");
|
|
349
|
+
const element = document.getElementById(elementId);
|
|
350
|
+
if (element) {
|
|
351
|
+
canvas.selectElement(element);
|
|
352
|
+
}
|
|
353
|
+
});
|
|
342
354
|
</script>
|
|
343
355
|
```
|
|
344
356
|
|
|
@@ -358,20 +370,22 @@ interface HierarchyReorderDetail {
|
|
|
358
370
|
|
|
359
371
|
```html
|
|
360
372
|
<script>
|
|
361
|
-
document
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
373
|
+
document
|
|
374
|
+
.querySelector("ef-hierarchy")
|
|
375
|
+
.addEventListener("hierarchy-reorder", (e) => {
|
|
376
|
+
const { sourceId, targetId, position } = e.detail;
|
|
377
|
+
|
|
378
|
+
const source = document.getElementById(sourceId);
|
|
379
|
+
const target = document.getElementById(targetId);
|
|
380
|
+
|
|
381
|
+
if (position === "before") {
|
|
382
|
+
target.parentElement.insertBefore(source, target);
|
|
383
|
+
} else if (position === "after") {
|
|
384
|
+
target.parentElement.insertBefore(source, target.nextSibling);
|
|
385
|
+
} else if (position === "inside") {
|
|
386
|
+
target.appendChild(source);
|
|
387
|
+
}
|
|
388
|
+
});
|
|
375
389
|
</script>
|
|
376
390
|
```
|
|
377
391
|
|
|
@@ -451,8 +465,8 @@ All custom events are dispatched with `bubbles: true` and `composed: true`, allo
|
|
|
451
465
|
|
|
452
466
|
<script>
|
|
453
467
|
// Can listen at any ancestor level
|
|
454
|
-
document.getElementById(
|
|
455
|
-
console.log(
|
|
468
|
+
document.getElementById("app").addEventListener("row-select", (e) => {
|
|
469
|
+
console.log("Row selected:", e.detail.elementId);
|
|
456
470
|
});
|
|
457
471
|
</script>
|
|
458
472
|
```
|
|
@@ -476,13 +490,13 @@ Many events are designed to work together for coordinated UI updates:
|
|
|
476
490
|
|
|
477
491
|
```typescript
|
|
478
492
|
// Coordinate timeline selection with canvas
|
|
479
|
-
hierarchy.addEventListener(
|
|
493
|
+
hierarchy.addEventListener("hierarchy-select", (e) => {
|
|
480
494
|
const element = document.getElementById(e.detail.elementId);
|
|
481
495
|
canvas.selectElement(element);
|
|
482
496
|
});
|
|
483
497
|
|
|
484
498
|
// Coordinate canvas selection with transform handles
|
|
485
|
-
canvas.addEventListener(
|
|
499
|
+
canvas.addEventListener("selectionchange", (e) => {
|
|
486
500
|
const selected = Array.from(e.detail.selection)[0];
|
|
487
501
|
if (selected) {
|
|
488
502
|
transformHandles.bounds = getBoundsFromElement(selected);
|
|
@@ -34,7 +34,7 @@ Access the data inside the composition with `getRenderData()`:
|
|
|
34
34
|
|
|
35
35
|
const data = getRenderData();
|
|
36
36
|
if (data) {
|
|
37
|
-
document.querySelector(
|
|
37
|
+
document.querySelector("ef-text").textContent = data.title;
|
|
38
38
|
}
|
|
39
39
|
</script>
|
|
40
40
|
```
|
|
@@ -47,15 +47,15 @@ The browser path uses the WebCodecs API for video encoding and FFmpeg.wasm for m
|
|
|
47
47
|
|
|
48
48
|
```html
|
|
49
49
|
<script type="module">
|
|
50
|
-
const timegroup = document.querySelector(
|
|
50
|
+
const timegroup = document.querySelector("ef-timegroup");
|
|
51
51
|
|
|
52
52
|
await timegroup.renderToVideo({
|
|
53
53
|
fps: 30,
|
|
54
|
-
codec:
|
|
55
|
-
filename:
|
|
54
|
+
codec: "avc",
|
|
55
|
+
filename: "export.mp4",
|
|
56
56
|
onProgress: (progress) => {
|
|
57
57
|
console.log(`${Math.round(progress.progress * 100)}%`);
|
|
58
|
-
}
|
|
58
|
+
},
|
|
59
59
|
});
|
|
60
60
|
</script>
|
|
61
61
|
```
|
|
@@ -64,7 +64,7 @@ Browser support: Chrome 94+, Edge 94+, Safari 16.4+. Check availability at runti
|
|
|
64
64
|
|
|
65
65
|
```html
|
|
66
66
|
<script type="module">
|
|
67
|
-
const supported =
|
|
67
|
+
const supported = "VideoEncoder" in window && "VideoDecoder" in window;
|
|
68
68
|
</script>
|
|
69
69
|
```
|
|
70
70
|
|
|
@@ -76,7 +76,7 @@ The cloud path uploads the composition bundle to a server, which processes it wi
|
|
|
76
76
|
|
|
77
77
|
```bash
|
|
78
78
|
# POST composition bundle to render API
|
|
79
|
-
curl -X POST https://
|
|
79
|
+
curl -X POST https://editframe.com/v1/renders \
|
|
80
80
|
-H "Authorization: Bearer $API_KEY" \
|
|
81
81
|
-F "bundle=@composition.tgz"
|
|
82
82
|
```
|
|
@@ -85,16 +85,16 @@ Results are delivered via webhook or polling. The cloud path is suited for produ
|
|
|
85
85
|
|
|
86
86
|
## Comparison
|
|
87
87
|
|
|
88
|
-
| Aspect
|
|
89
|
-
|
|
90
|
-
| Runs in
|
|
91
|
-
| Encoding
|
|
92
|
-
| Audio muxing
|
|
93
|
-
| Dynamic data
|
|
94
|
-
| Progress feedback
|
|
95
|
-
| Browser required
|
|
96
|
-
| Codec control
|
|
97
|
-
| Concurrent renders | Limited by local CPU
|
|
88
|
+
| Aspect | CLI (Playwright) | Browser (WebCodecs) | Cloud (API) |
|
|
89
|
+
| ------------------ | --------------------- | ------------------------ | -------------------------- |
|
|
90
|
+
| Runs in | Local machine | User's browser | Remote server |
|
|
91
|
+
| Encoding | Server-side FFmpeg | WebCodecs + FFmpeg.wasm | Server-side FFmpeg |
|
|
92
|
+
| Audio muxing | Automatic | Automatic | Automatic |
|
|
93
|
+
| Dynamic data | `--data` flag | JavaScript state | Request payload |
|
|
94
|
+
| Progress feedback | Terminal output | `onProgress` callback | Webhook / polling |
|
|
95
|
+
| Browser required | Playwright (headless) | User's browser | None (server-side) |
|
|
96
|
+
| Codec control | Full FFmpeg options | Browser-supported codecs | Full FFmpeg options |
|
|
97
|
+
| Concurrent renders | Limited by local CPU | One per tab | Scales with infrastructure |
|
|
98
98
|
|
|
99
99
|
## Architecture Differences
|
|
100
100
|
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
"start": "editframe preview"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@editframe/cli": "0.
|
|
11
|
-
"@editframe/elements": "0.
|
|
12
|
-
"@editframe/vite-plugin": "0.
|
|
10
|
+
"@editframe/cli": "0.52.1",
|
|
11
|
+
"@editframe/elements": "0.52.1",
|
|
12
|
+
"@editframe/vite-plugin": "0.52.1",
|
|
13
13
|
"@tailwindcss/vite": "^4.0.0",
|
|
14
14
|
"tailwindcss": "^4.0.0",
|
|
15
15
|
"vite": "^8.0.0",
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import { withEditframe } from "@editframe/nextjs-plugin";
|
|
2
2
|
|
|
3
|
-
// Ensure ffprobe/ffmpeg are reachable — Homebrew installs to /opt/homebrew/bin
|
|
4
|
-
process.env.PATH = `/opt/homebrew/bin:${process.env.PATH}`;
|
|
5
|
-
|
|
6
3
|
/** @type {import('next').NextConfig} */
|
|
7
4
|
const nextConfig = {};
|
|
8
5
|
|
|
9
6
|
export default withEditframe(
|
|
10
7
|
{
|
|
11
|
-
root: "./
|
|
12
|
-
cacheRoot: "./cache",
|
|
8
|
+
root: "./src",
|
|
9
|
+
cacheRoot: "./src/assets/cache",
|
|
13
10
|
},
|
|
14
11
|
nextConfig,
|
|
15
12
|
);
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
"dev": "next dev"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@editframe/elements": "0.
|
|
11
|
-
"@editframe/nextjs-plugin": "0.
|
|
12
|
-
"@editframe/react": "0.
|
|
10
|
+
"@editframe/elements": "0.52.1",
|
|
11
|
+
"@editframe/nextjs-plugin": "0.52.1",
|
|
12
|
+
"@editframe/react": "0.52.1",
|
|
13
13
|
"next": "^16.0.0",
|
|
14
14
|
"react": "^19.0.0",
|
|
15
15
|
"react-dom": "^19.0.0"
|
|
@@ -5,11 +5,7 @@ export const metadata: Metadata = {
|
|
|
5
5
|
title: "Editframe Project",
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
-
export default function RootLayout({
|
|
9
|
-
children,
|
|
10
|
-
}: {
|
|
11
|
-
children: React.ReactNode;
|
|
12
|
-
}) {
|
|
8
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
13
9
|
return (
|
|
14
10
|
<html lang="en">
|
|
15
11
|
<body>{children}</body>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { Timegroup,
|
|
3
|
+
import { Timegroup, Video } from "@editframe/react";
|
|
4
4
|
|
|
5
5
|
export default function Home() {
|
|
6
6
|
return (
|
|
@@ -13,15 +13,9 @@ export default function Home() {
|
|
|
13
13
|
background: "#0f172a",
|
|
14
14
|
}}
|
|
15
15
|
>
|
|
16
|
-
<Timegroup
|
|
17
|
-
workbench
|
|
18
|
-
mode="sequence"
|
|
19
|
-
style={{ width: "1920px", height: "1080px" }}
|
|
20
|
-
>
|
|
16
|
+
<Timegroup workbench mode="sequence" style={{ width: "1920px", height: "1080px" }}>
|
|
21
17
|
{/* Add your composition here */}
|
|
22
18
|
<Timegroup
|
|
23
|
-
mode="fixed"
|
|
24
|
-
duration="5s"
|
|
25
19
|
style={{
|
|
26
20
|
position: "absolute",
|
|
27
21
|
width: "100%",
|
|
@@ -32,9 +26,7 @@ export default function Home() {
|
|
|
32
26
|
background: "black",
|
|
33
27
|
}}
|
|
34
28
|
>
|
|
35
|
-
<
|
|
36
|
-
Your video starts here
|
|
37
|
-
</Text>
|
|
29
|
+
<Video src="https://assets.editframe.com/bars-n-tone.mp4" />
|
|
38
30
|
</Timegroup>
|
|
39
31
|
</Timegroup>
|
|
40
32
|
</main>
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
"start": "editframe preview"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@editframe/cli": "0.
|
|
11
|
-
"@editframe/react": "0.
|
|
12
|
-
"@editframe/vite-plugin": "0.
|
|
10
|
+
"@editframe/cli": "0.52.1",
|
|
11
|
+
"@editframe/react": "0.52.1",
|
|
12
|
+
"@editframe/vite-plugin": "0.52.1",
|
|
13
13
|
"@tailwindcss/vite": "^4.0.0",
|
|
14
14
|
"@vitejs/plugin-react": "^6.0.0",
|
|
15
15
|
"tailwindcss": "^4.0.0",
|