@warpgogol/forge 0.14.0 → 0.15.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/AGENTS.md +3 -3
- package/README.md +58 -5
- package/dist/bin/cli.js +0 -0
- package/dist/os/core/core.module.d.ts.map +1 -1
- package/dist/os/core/core.module.js +9 -0
- package/dist/os/core/core.module.js.map +1 -1
- package/dist/src/onboarding/create.d.ts.map +1 -1
- package/dist/src/onboarding/create.js +5 -2
- package/dist/src/onboarding/create.js.map +1 -1
- package/dist/src/onboarding/doctor.d.ts.map +1 -1
- package/dist/src/onboarding/doctor.js +38 -0
- package/dist/src/onboarding/doctor.js.map +1 -1
- package/dist/src/onboarding/invariant-engine.d.ts.map +1 -1
- package/dist/src/onboarding/invariant-engine.js +14 -7
- package/dist/src/onboarding/invariant-engine.js.map +1 -1
- package/dist/src/onboarding/scaffold-project.d.ts.map +1 -1
- package/dist/src/onboarding/scaffold-project.js +25 -5
- package/dist/src/onboarding/scaffold-project.js.map +1 -1
- package/dist/src/profiles/profile-schema.d.ts +73 -8
- package/dist/src/profiles/profile-schema.d.ts.map +1 -1
- package/dist/src/profiles/profile-schema.js +35 -10
- package/dist/src/profiles/profile-schema.js.map +1 -1
- package/dist/src/profiles/stack-profile.d.ts +26 -2
- package/dist/src/profiles/stack-profile.d.ts.map +1 -1
- package/dist/src/profiles/stack-profile.js +4 -0
- package/dist/src/profiles/stack-profile.js.map +1 -1
- package/package.json +162 -60
- package/profiles/editframe-templates/composition-agents.md +70 -0
- package/profiles/editframe-templates/composition.tsx +33 -0
- package/profiles/{editframe-html-templates/composition-agents.md → editframe-templates/html-composition-agents.md} +15 -16
- package/profiles/{editframe-html.yaml → editframe.yaml} +170 -56
- package/skills/fo/ef-brand-video-generator/SKILL.md +101 -0
- package/skills/fo/ef-composition/SKILL.md +147 -0
- package/skills/fo/ef-composition-review/SKILL.md +10 -10
- package/skills/fo/ef-dev-server/SKILL.md +107 -0
- package/skills/fo/ef-editor-gui/SKILL.md +131 -0
- package/skills/fo/ef-motion-design/SKILL.md +118 -0
- package/skills/fo/ef-onboard/SKILL.md +16 -29
- package/skills/fo/ef-render-verify/SKILL.md +2 -2
- package/skills/fo/ef-webhooks/SKILL.md +95 -0
- package/skills/fo/fo-session-retro/SKILL.md +2 -2
- package/profiles/editframe-html-templates/composition.html +0 -32
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
schema: forge/stack-profile@1
|
|
2
|
-
id: editframe
|
|
3
|
-
displayName: Editframe
|
|
2
|
+
id: editframe
|
|
3
|
+
displayName: Editframe Video
|
|
4
4
|
detect:
|
|
5
5
|
anyOf:
|
|
6
6
|
- editframe.config.*
|
|
@@ -20,8 +20,8 @@ devServer:
|
|
|
20
20
|
artifacts:
|
|
21
21
|
- id: composition
|
|
22
22
|
extensions:
|
|
23
|
-
- .html
|
|
24
23
|
- .tsx
|
|
24
|
+
- .html
|
|
25
25
|
produce:
|
|
26
26
|
command: editframe render
|
|
27
27
|
output: dist/{composition}.mp4
|
|
@@ -30,6 +30,7 @@ artifacts:
|
|
|
30
30
|
determinism:
|
|
31
31
|
hashable: true
|
|
32
32
|
inputs:
|
|
33
|
+
- compositions/**/*.tsx
|
|
33
34
|
- compositions/**/*.html
|
|
34
35
|
- assets/**
|
|
35
36
|
assets:
|
|
@@ -48,8 +49,29 @@ release:
|
|
|
48
49
|
target: local
|
|
49
50
|
outputDir: release
|
|
50
51
|
manifestName: release-manifest.json
|
|
52
|
+
prerequisites:
|
|
53
|
+
- id: nodejs
|
|
54
|
+
name: Node.js 18+
|
|
55
|
+
check: node --version
|
|
56
|
+
installHint: "https://nodejs.org/en/download/"
|
|
57
|
+
severity: error
|
|
58
|
+
- id: ffmpeg
|
|
59
|
+
name: FFmpeg
|
|
60
|
+
check: ffmpeg -version
|
|
61
|
+
installHint: "https://ffmpeg.org/download.html"
|
|
62
|
+
severity: error
|
|
51
63
|
workspaceTypes:
|
|
52
64
|
- id: composition
|
|
65
|
+
detect:
|
|
66
|
+
glob: "*.tsx"
|
|
67
|
+
contains: TimelineRoot
|
|
68
|
+
packageJsonDep: "@editframe/react"
|
|
69
|
+
skills:
|
|
70
|
+
- ef-onboard
|
|
71
|
+
- ef-composition-review
|
|
72
|
+
- ef-render-verify
|
|
73
|
+
agentsMdTemplate: editframe-templates/composition-agents.md
|
|
74
|
+
- id: html-composition
|
|
53
75
|
detect:
|
|
54
76
|
glob: "*.html"
|
|
55
77
|
contains: ef-timegroup
|
|
@@ -58,77 +80,85 @@ workspaceTypes:
|
|
|
58
80
|
- ef-onboard
|
|
59
81
|
- ef-composition-review
|
|
60
82
|
- ef-render-verify
|
|
61
|
-
agentsMdTemplate: editframe-html-
|
|
83
|
+
agentsMdTemplate: editframe-templates/html-composition-agents.md
|
|
62
84
|
invariants:
|
|
63
85
|
- id: VIDEO-01
|
|
64
86
|
rule: Composition filenames must use kebab-case (lowercase letters, digits, hyphens only)
|
|
65
87
|
severity: error
|
|
66
88
|
check:
|
|
67
89
|
kind: filename-pattern
|
|
68
|
-
glob: "compositions/**/*.{html
|
|
69
|
-
pattern: "^[a-z0-9-]+\\.(html
|
|
90
|
+
glob: "compositions/**/*.{tsx,html}"
|
|
91
|
+
pattern: "^[a-z0-9-]+\\.(tsx|html)$"
|
|
70
92
|
- id: VIDEO-02
|
|
71
93
|
rule: Scene durations must use contain mode by default to avoid unexpected cropping
|
|
72
94
|
severity: warning
|
|
73
95
|
check:
|
|
74
96
|
kind: file-contains
|
|
75
|
-
glob: "compositions/**/*.html"
|
|
97
|
+
glob: "compositions/**/*.{tsx,html}"
|
|
76
98
|
pattern: contain
|
|
77
99
|
- id: VIDEO-03
|
|
78
|
-
rule: All speech audio elements must have corresponding
|
|
100
|
+
rule: All speech audio elements must have corresponding Captions components for accessibility
|
|
79
101
|
severity: error
|
|
80
102
|
check:
|
|
81
103
|
kind: file-contains
|
|
82
|
-
glob: "compositions/**/*.html"
|
|
83
|
-
pattern:
|
|
104
|
+
glob: "compositions/**/*.{tsx,html}"
|
|
105
|
+
pattern: Captions
|
|
84
106
|
- id: VIDEO-04
|
|
85
|
-
rule:
|
|
107
|
+
rule: Root Timegroup must declare duration or use mode="contain" or mode="fit"
|
|
86
108
|
severity: error
|
|
87
109
|
check:
|
|
88
110
|
kind: file-contains
|
|
89
|
-
glob: "compositions/**/*.html"
|
|
90
|
-
pattern: 'ef-timegroup[^>]*(duration|mode="contain"|mode="fit")[^>]*>'
|
|
111
|
+
glob: "compositions/**/*.{tsx,html}"
|
|
112
|
+
pattern: 'Timegroup[^>]*(duration|mode="contain"|mode="fit")[^>]*>|ef-timegroup[^>]*(duration|mode="contain"|mode="fit")[^>]*>'
|
|
91
113
|
- id: VIDEO-05
|
|
92
114
|
rule: All duration values must be valid CSS time strings (e.g. 5s, 500ms, 2.5s)
|
|
93
115
|
severity: error
|
|
94
116
|
check:
|
|
95
|
-
kind:
|
|
96
|
-
glob: "compositions/**/*.html"
|
|
97
|
-
|
|
117
|
+
kind: attribute-pattern
|
|
118
|
+
glob: "compositions/**/*.{tsx,html}"
|
|
119
|
+
elements:
|
|
120
|
+
- ef-timegroup
|
|
121
|
+
- Timegroup
|
|
98
122
|
attribute: "duration"
|
|
99
123
|
pattern: "^\\d+(\\.\\d+)?(s|ms)$"
|
|
100
124
|
- id: VIDEO-06
|
|
101
125
|
rule: "mode attribute must be one of: sequence, fixed, contain, fit"
|
|
102
126
|
severity: error
|
|
103
127
|
check:
|
|
104
|
-
kind:
|
|
105
|
-
glob: "compositions/**/*.html"
|
|
106
|
-
|
|
128
|
+
kind: attribute-pattern
|
|
129
|
+
glob: "compositions/**/*.{tsx,html}"
|
|
130
|
+
elements:
|
|
131
|
+
- ef-timegroup
|
|
132
|
+
- Timegroup
|
|
107
133
|
attribute: "mode"
|
|
108
134
|
pattern: "^(sequence|fixed|contain|fit)$"
|
|
109
135
|
- id: VIDEO-07
|
|
110
136
|
rule: fps must be a positive integer (default 30)
|
|
111
137
|
severity: warning
|
|
112
138
|
check:
|
|
113
|
-
kind:
|
|
114
|
-
glob: "compositions/**/*.html"
|
|
115
|
-
|
|
139
|
+
kind: attribute-pattern
|
|
140
|
+
glob: "compositions/**/*.{tsx,html}"
|
|
141
|
+
elements:
|
|
142
|
+
- ef-timegroup
|
|
143
|
+
- Timegroup
|
|
116
144
|
attribute: "fps"
|
|
117
145
|
pattern: "^[1-9]\\d*$"
|
|
118
146
|
- id: VIDEO-08
|
|
119
|
-
rule: loop attribute should only be used on the root
|
|
147
|
+
rule: loop attribute should only be used on the root Timegroup (no effect on render for nested groups)
|
|
120
148
|
severity: warning
|
|
121
149
|
check:
|
|
122
150
|
kind: file-not-contains
|
|
123
|
-
glob: "compositions/**/*.html"
|
|
124
|
-
negatedPattern: "<ef-timegroup[^>]*loop[^>]*>[\\s\\S]*<ef-timegroup[^>]*loop"
|
|
151
|
+
glob: "compositions/**/*.{tsx,html}"
|
|
152
|
+
negatedPattern: "<(Timegroup|ef-timegroup)[^>]*loop[^>]*>[\\s\\S]*<(Timegroup|ef-timegroup)[^>]*loop"
|
|
125
153
|
- id: VIDEO-09
|
|
126
154
|
rule: offset values must be valid CSS time strings (e.g. 0.5s, 3s, 500ms)
|
|
127
155
|
severity: warning
|
|
128
156
|
check:
|
|
129
|
-
kind:
|
|
130
|
-
glob: "compositions/**/*.html"
|
|
131
|
-
|
|
157
|
+
kind: attribute-pattern
|
|
158
|
+
glob: "compositions/**/*.{tsx,html}"
|
|
159
|
+
elements:
|
|
160
|
+
- ef-timegroup
|
|
161
|
+
- Timegroup
|
|
132
162
|
attribute: "offset"
|
|
133
163
|
pattern: "^\\d+(\\.\\d+)?(s|ms)$"
|
|
134
164
|
workspace:
|
|
@@ -331,34 +361,118 @@ firstWorkspace:
|
|
|
331
361
|
"check": "editframe check"
|
|
332
362
|
}
|
|
333
363
|
}
|
|
334
|
-
- path: composition.
|
|
364
|
+
- path: composition.tsx
|
|
335
365
|
content: |
|
|
336
|
-
|
|
337
|
-
Editframe
|
|
338
|
-
This file declares a video composition using
|
|
366
|
+
/*
|
|
367
|
+
Editframe React composition — my-first-video
|
|
368
|
+
This file declares a video composition using @editframe/react components.
|
|
339
369
|
Run `editframe preview` to preview, `editframe render` to produce output.
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
370
|
+
*/
|
|
371
|
+
import { TimelineRoot, Timegroup, Video, Text, Audio, Captions } from "@editframe/react";
|
|
372
|
+
|
|
373
|
+
export default function Composition() {
|
|
374
|
+
return (
|
|
375
|
+
<TimelineRoot>
|
|
376
|
+
<Timegroup duration="10s">
|
|
377
|
+
<Video src="assets/background.mp4" fit="contain" duration="10s" />
|
|
378
|
+
<Text
|
|
379
|
+
text="Hello, Editframe!"
|
|
380
|
+
x="50%"
|
|
381
|
+
y="50%"
|
|
382
|
+
fontSize="48px"
|
|
383
|
+
color="white"
|
|
384
|
+
textAlign="center"
|
|
385
|
+
duration="5s"
|
|
386
|
+
/>
|
|
387
|
+
<Audio src="assets/narration.mp3" />
|
|
388
|
+
<Captions src="assets/captions.vtt" />
|
|
389
|
+
</Timegroup>
|
|
390
|
+
</TimelineRoot>
|
|
391
|
+
);
|
|
392
|
+
}
|
|
363
393
|
install:
|
|
364
|
-
- pnpm add -D vitest
|
|
394
|
+
- pnpm add -D @editframe/react @editframe/vite-plugin react react-dom typescript @types/react @types/react-dom vite @vitejs/plugin-react tailwindcss @tailwindcss/vite vitest
|
|
395
|
+
templates:
|
|
396
|
+
- id: react
|
|
397
|
+
default: true
|
|
398
|
+
firstWorkspace:
|
|
399
|
+
path: compositions/my-first-video
|
|
400
|
+
files:
|
|
401
|
+
- path: package.json
|
|
402
|
+
content: |
|
|
403
|
+
{
|
|
404
|
+
"name": "my-first-video",
|
|
405
|
+
"version": "0.1.0",
|
|
406
|
+
"private": true,
|
|
407
|
+
"type": "module",
|
|
408
|
+
"scripts": {
|
|
409
|
+
"preview": "editframe preview",
|
|
410
|
+
"render": "editframe render -o dist/my-first-video.mp4",
|
|
411
|
+
"check": "editframe check"
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
- path: composition.tsx
|
|
415
|
+
content: |
|
|
416
|
+
/*
|
|
417
|
+
Editframe React composition — my-first-video
|
|
418
|
+
This file declares a video composition using @editframe/react components.
|
|
419
|
+
Run `editframe preview` to preview, `editframe render` to produce output.
|
|
420
|
+
*/
|
|
421
|
+
import { TimelineRoot, Timegroup, Video, Text, Audio, Captions } from "@editframe/react";
|
|
422
|
+
|
|
423
|
+
export default function Composition() {
|
|
424
|
+
return (
|
|
425
|
+
<TimelineRoot>
|
|
426
|
+
<Timegroup duration="10s">
|
|
427
|
+
<Video src="assets/background.mp4" fit="contain" duration="10s" />
|
|
428
|
+
<Text
|
|
429
|
+
text="Hello, Editframe!"
|
|
430
|
+
x="50%"
|
|
431
|
+
y="50%"
|
|
432
|
+
fontSize="48px"
|
|
433
|
+
color="white"
|
|
434
|
+
textAlign="center"
|
|
435
|
+
duration="5s"
|
|
436
|
+
/>
|
|
437
|
+
<Audio src="assets/narration.mp3" />
|
|
438
|
+
<Captions src="assets/captions.vtt" />
|
|
439
|
+
</Timegroup>
|
|
440
|
+
</TimelineRoot>
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
install:
|
|
444
|
+
- pnpm add -D @editframe/react @editframe/vite-plugin react react-dom typescript @types/react @types/react-dom vite @vitejs/plugin-react tailwindcss @tailwindcss/vite vitest
|
|
445
|
+
- id: html
|
|
446
|
+
firstWorkspace:
|
|
447
|
+
path: compositions/my-first-video
|
|
448
|
+
files:
|
|
449
|
+
- path: package.json
|
|
450
|
+
content: |
|
|
451
|
+
{
|
|
452
|
+
"name": "my-first-video",
|
|
453
|
+
"version": "0.1.0",
|
|
454
|
+
"private": true,
|
|
455
|
+
"type": "module",
|
|
456
|
+
"scripts": {
|
|
457
|
+
"preview": "editframe preview",
|
|
458
|
+
"render": "editframe render -o dist/my-first-video.mp4",
|
|
459
|
+
"check": "editframe check"
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
- path: index.html
|
|
463
|
+
content: |
|
|
464
|
+
<!--
|
|
465
|
+
Editframe HTML composition — my-first-video
|
|
466
|
+
This file declares a video composition using Editframe web components.
|
|
467
|
+
Run `editframe preview` to preview, `editframe render` to produce output.
|
|
468
|
+
-->
|
|
469
|
+
<ef-timeline duration="10s">
|
|
470
|
+
<ef-timegroup duration="10s" mode="contain">
|
|
471
|
+
<ef-video src="assets/background.mp4" fit="contain" duration="10s"></ef-video>
|
|
472
|
+
<ef-text text="Hello, Editframe!" x="50%" y="50%" font-size="48px" color="white" text-align="center" duration="5s"></ef-text>
|
|
473
|
+
<ef-audio src="assets/narration.mp3"></ef-audio>
|
|
474
|
+
<ef-captions src="assets/captions.vtt"></ef-captions>
|
|
475
|
+
</ef-timegroup>
|
|
476
|
+
</ef-timeline>
|
|
477
|
+
install:
|
|
478
|
+
- pnpm add -D vitest
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ef-brand-video-generator
|
|
3
|
+
description: "Generate brand video compositions from templates — logo animation, intro/outro, brand colors, typography. Use when the operator asks to generate a brand video."
|
|
4
|
+
invocation: user
|
|
5
|
+
category: fo
|
|
6
|
+
concerns: content-mutation
|
|
7
|
+
dependsOn: []
|
|
8
|
+
languagePolicy: ref(PREFERENCES.md)
|
|
9
|
+
triggers:
|
|
10
|
+
- "generate a brand video"
|
|
11
|
+
- "create a logo animation"
|
|
12
|
+
- "brand intro video"
|
|
13
|
+
- "brand outro video"
|
|
14
|
+
source: domain-knowledge
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
<!-- skill-lint-disable SKILL-17 -->
|
|
18
|
+
|
|
19
|
+
Before starting, read `PREFERENCES.md` at the repository root. If the file is missing or `aiLanguage` is unset, ask the operator once and create the file using the `my-preferences` skill semantics.
|
|
20
|
+
|
|
21
|
+
# Brand Video Generator
|
|
22
|
+
|
|
23
|
+
Generate brand video compositions from reusable templates. Brand videos typically include logo animations, intro/outro sequences, brand color overlays, and typography that matches brand guidelines.
|
|
24
|
+
|
|
25
|
+
## Template structure
|
|
26
|
+
|
|
27
|
+
A brand video composition uses `@editframe/react` components with brand-specific assets:
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import { TimelineRoot, Timegroup, Video, Image, Text } from "@editframe/react";
|
|
31
|
+
|
|
32
|
+
export default function BrandIntro() {
|
|
33
|
+
return (
|
|
34
|
+
<TimelineRoot>
|
|
35
|
+
<Timegroup duration="3s" mode="sequence">
|
|
36
|
+
<Image src="assets/logo.svg" duration="1s" fit="contain" />
|
|
37
|
+
<Text
|
|
38
|
+
text="Your Brand"
|
|
39
|
+
x="50%"
|
|
40
|
+
y="50%"
|
|
41
|
+
fontSize="64px"
|
|
42
|
+
color="#FF6B00"
|
|
43
|
+
duration="1s"
|
|
44
|
+
/>
|
|
45
|
+
<Video src="assets/brand-bg.mp4" fit="cover" duration="1s" />
|
|
46
|
+
</Timegroup>
|
|
47
|
+
</TimelineRoot>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Brand asset checklist
|
|
53
|
+
|
|
54
|
+
- **Logo** — SVG or PNG with transparent background
|
|
55
|
+
- **Brand colors** — primary, secondary, accent as CSS color values
|
|
56
|
+
- **Typography** — brand font family, weights, sizes
|
|
57
|
+
- **Background video** — optional branded background clip
|
|
58
|
+
- **Audio** — optional brand jingle or voiceover
|
|
59
|
+
|
|
60
|
+
## Common brand video patterns
|
|
61
|
+
|
|
62
|
+
### Logo animation
|
|
63
|
+
|
|
64
|
+
Animate the logo in with a `Timegroup` using `mode="sequence"`:
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
<Timegroup duration="2s" mode="sequence">
|
|
68
|
+
<Image src="assets/logo.svg" duration="0.5s" fit="contain" />
|
|
69
|
+
<Text text="Your Brand" fontSize="48px" color="#FF6B00" duration="1.5s" />
|
|
70
|
+
</Timegroup>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Intro/outro pair
|
|
74
|
+
|
|
75
|
+
Create separate compositions for intro and outro, then combine them in a parent `Timegroup`:
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
<TimelineRoot>
|
|
79
|
+
<Timegroup duration="15s" mode="sequence">
|
|
80
|
+
<BrandIntro />
|
|
81
|
+
<MainContent />
|
|
82
|
+
<BrandOutro />
|
|
83
|
+
</Timegroup>
|
|
84
|
+
</TimelineRoot>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Brand color overlay
|
|
88
|
+
|
|
89
|
+
Use a semi-transparent `Image` or `Text` overlay with brand colors:
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
<Image src="assets/brand-overlay.png" duration="10s" fit="cover" />
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Rendering
|
|
96
|
+
|
|
97
|
+
Run `ref(forge.yaml bindings.commands.build)` to render the brand video. Use `ref(forge.yaml bindings.commands.validate)` to check the composition before rendering.
|
|
98
|
+
|
|
99
|
+
## Customization
|
|
100
|
+
|
|
101
|
+
Each brand video template accepts parameters for logo, colors, text, and timing. Adjust the `duration`, `mode`, and asset paths to match the brand guidelines.
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ef-composition
|
|
3
|
+
description: "Guide creating a video composition with Editframe React components — time model, media elements, rendering. Use when the operator asks to create or build a video composition."
|
|
4
|
+
invocation: user
|
|
5
|
+
category: fo
|
|
6
|
+
concerns: content-mutation
|
|
7
|
+
dependsOn: []
|
|
8
|
+
languagePolicy: ref(PREFERENCES.md)
|
|
9
|
+
triggers:
|
|
10
|
+
- "create a video composition"
|
|
11
|
+
- "build a video with editframe"
|
|
12
|
+
- "create a composition"
|
|
13
|
+
- "add a scene to my video"
|
|
14
|
+
source: https://editframe.com/skills/composition.md
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
<!-- skill-lint-disable SKILL-17 -->
|
|
18
|
+
|
|
19
|
+
Before starting, read `PREFERENCES.md` at the repository root. If the file is missing or `aiLanguage` is unset, ask the operator once and create the file using the `my-preferences` skill semantics.
|
|
20
|
+
|
|
21
|
+
# Video Composition
|
|
22
|
+
|
|
23
|
+
Build video scenes with React components from `@editframe/react`, for example `<Timegroup>` and `<Video>`. Both HTML web component syntax (`<ef-timegroup>`) and React syntax (`<Timegroup>`) share the same composition model and rendering pipeline.
|
|
24
|
+
|
|
25
|
+
Web component attributes use kebab-case (`file-id`, `api-host`). React props use camelCase (`fileId`, `apiHost`). Four attributes break this pattern: `sourcein`, `sourceout`, `trimstart`, `trimend` — same lowercase string in both forms.
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import { TimelineRoot, Timegroup, Video, Text, Audio, Captions } from "@editframe/react";
|
|
31
|
+
|
|
32
|
+
export default function Composition() {
|
|
33
|
+
return (
|
|
34
|
+
<TimelineRoot>
|
|
35
|
+
<Timegroup duration="10s">
|
|
36
|
+
<Video src="assets/background.mp4" fit="contain" duration="10s" />
|
|
37
|
+
<Text text="Hello, Editframe!" x="50%" y="50%" fontSize="48px" color="white" duration="5s" />
|
|
38
|
+
<Audio src="assets/narration.mp3" />
|
|
39
|
+
<Captions src="assets/captions.vtt" />
|
|
40
|
+
</Timegroup>
|
|
41
|
+
</TimelineRoot>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Run `ref(forge.yaml bindings.commands.devServer)` to preview. Run `ref(forge.yaml bindings.commands.build)` to render.
|
|
47
|
+
|
|
48
|
+
## Duration units
|
|
49
|
+
|
|
50
|
+
- `5s` — seconds
|
|
51
|
+
- `500ms` — milliseconds
|
|
52
|
+
- `2.5s` — fractional seconds
|
|
53
|
+
|
|
54
|
+
## Core concepts
|
|
55
|
+
|
|
56
|
+
### Time model
|
|
57
|
+
|
|
58
|
+
A composition is a tree of `Timegroup` elements. The root `Timegroup` defines the composition's total duration. Child elements inherit timing context from their parent.
|
|
59
|
+
|
|
60
|
+
### Timegroups and sequencing
|
|
61
|
+
|
|
62
|
+
- **`mode="sequence"`** — children play one after another
|
|
63
|
+
- **`mode="fixed"`** — children play at absolute offsets
|
|
64
|
+
- **`mode="contain"`** (default) — children fit within parent duration
|
|
65
|
+
- **`mode="fit"`** — children scale to fit
|
|
66
|
+
|
|
67
|
+
### Transitions
|
|
68
|
+
|
|
69
|
+
Use `ef-transition` or the `transition` prop to declare CSS-like transitions between scenes.
|
|
70
|
+
|
|
71
|
+
### Scripting
|
|
72
|
+
|
|
73
|
+
Compositions support inline scripting via `ef-script` or the `script` prop for dynamic behavior during rendering.
|
|
74
|
+
|
|
75
|
+
## Media elements
|
|
76
|
+
|
|
77
|
+
- **`Video`** (`ef-video`) — video source with `fit` mode (`contain`, `cover`, `fill`)
|
|
78
|
+
- **`Audio`** (`ef-audio`) — audio source with FFT analysis (`fftSize`, `fftGain`, `fftDecay`)
|
|
79
|
+
- **`Image`** (`ef-image`) — image source
|
|
80
|
+
- **`Text`** (`ef-text`) — text overlay with positioning. Splits text into `ef-text-segment` children (`split="word"`, `split="char"`, `split="line"`). Set `stagger` to delay each segment's start. Segments render in light DOM with `data-active` attribute and `--ef-index`, `--ef-word-index`, `--ef-stagger-offset`, `--ef-seed` custom properties.
|
|
81
|
+
- **`Captions`** (`ef-captions`) — synchronized captions with word-level highlighting. Accepts caption data via `captionsSrc` (URL to JSON), `captionsScript` (inline script id), or `captionsData` (JS property). JSON uses `segments` or `word_segments` shape with `start`/`end` times.
|
|
82
|
+
- **`Waveform`** (`ef-waveform`) — visualizes an `ef-audio` or `ef-video` `target`. Modes: `bars`, `line`, `curve`, `bricks`, `pixel`, `wave`, `spikes`, `roundBars`.
|
|
83
|
+
- **`Surface`** (`ef-surface`) — mirrors another element's pixels onto its own canvas. Set `target` to a canvas or any `HTMLElement` to reuse a video's decoded frames without re-decoding.
|
|
84
|
+
- **`PanZoom`** (`ef-pan-zoom`) — pan and zoom control for video/image elements.
|
|
85
|
+
- **`MotionBlur`** (`ef-motionblur`) — motion blur effect.
|
|
86
|
+
- **`Configuration`** (`ef-configuration`) — opt-in element for API authentication. Add when deploying against the Editframe API: `apiHost` and `signingUrl` for signed-URL auth on cross-origin media, `imageProxy` for cross-origin image proxying. Skip for compositions using only local files.
|
|
87
|
+
|
|
88
|
+
## Rendering
|
|
89
|
+
|
|
90
|
+
**Browser export.** Call `renderTimegroupToVideo(timegroup, options)` from `@editframe/elements` to encode a live `ef-timegroup` to a video file. Uses WebCodecs. Key options: `width`/`height`/`fps`, `from`/`to` (export range in seconds), `videoCodec`/`audioCodec`/`videoBitrate`/`audioBitrate`, `target` (mediabunny output target), `signal` (AbortSignal), `onProgress`.
|
|
91
|
+
|
|
92
|
+
For in-app export from an interactive preview, use `ef-workbench`'s `exportVideo()` method (see the `ef-editor-gui` skill). For CLI/cloud rendering (`editframe render`), the `window.EF_RENDER` path handles offscreen cloning automatically.
|
|
93
|
+
|
|
94
|
+
**Custom render data.** A CLI or Playwright host can inject arbitrary JSON into `window.EF_RENDER_DATA` before running the composition. Read it with `getRenderData<T>()` (module-level function) or `useRenderData<T>()` (React hook). Use this to parameterize a render without templating the composition.
|
|
95
|
+
|
|
96
|
+
Run `ref(forge.yaml bindings.commands.build)` to produce the final video output. Use `ref(forge.yaml bindings.commands.validate)` to check composition structure before rendering.
|
|
97
|
+
|
|
98
|
+
## React
|
|
99
|
+
|
|
100
|
+
Import components from `@editframe/react`. React props use camelCase. The composition is a standard React component that returns JSX.
|
|
101
|
+
|
|
102
|
+
### React Three Fiber
|
|
103
|
+
|
|
104
|
+
Wrap a `<Timegroup>` child in `<CompositionCanvas>` from `@editframe/react/r3f` to get an R3F `<Canvas>` synced to composition time. Read the clock with `useCompositionTime()` which returns `{ time, duration }` in seconds.
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
<Timegroup mode="fixed" duration="14s">
|
|
108
|
+
<CompositionCanvas shadows>
|
|
109
|
+
<MyScene />
|
|
110
|
+
</CompositionCanvas>
|
|
111
|
+
</Timegroup>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
For 3D in a Web Worker, use `OffscreenCompositionCanvas` with `@react-three/offscreen`.
|
|
115
|
+
|
|
116
|
+
### Server-side rendering
|
|
117
|
+
|
|
118
|
+
`@editframe/react/server` and `@editframe/elements/server` export composition components and types only — no custom-element registration, no DOM, canvas, or WebCodecs code. Import safely in Next.js or Remix server code. These server entry points do not include GUI components or hooks — render those on the client with `dynamic(() => import("@editframe/react"), { ssr: false })`.
|
|
119
|
+
|
|
120
|
+
### Package entry points
|
|
121
|
+
|
|
122
|
+
| Import | Environment | Contains |
|
|
123
|
+
| --- | --- | --- |
|
|
124
|
+
| `@editframe/elements` | Browser | All custom elements, canvas/WebCodecs rendering |
|
|
125
|
+
| `@editframe/elements/server` | Browser, Node, SSR | Types only, plus `getRenderInfo()` (browser-only at runtime) |
|
|
126
|
+
| `@editframe/elements/gui` | Browser | Editor GUI custom elements (timeline, scrubber, handles) |
|
|
127
|
+
| `@editframe/elements/styles.css` | Browser | Base + theme styles |
|
|
128
|
+
| `@editframe/react` | Browser | React composition + GUI components, hooks |
|
|
129
|
+
| `@editframe/react/server` | Browser, Node, SSR | Composition components only, no hooks/GUI |
|
|
130
|
+
| `@editframe/react/r3f` | Browser | `CompositionCanvas`, `OffscreenCompositionCanvas`, `useCompositionTime` |
|
|
131
|
+
|
|
132
|
+
## Element reference
|
|
133
|
+
|
|
134
|
+
| Element | React component | Key props |
|
|
135
|
+
| --- | --- | --- |
|
|
136
|
+
| `ef-timegroup` | `Timegroup` | `duration`, `mode`, `fps`, `offset`, `loop` |
|
|
137
|
+
| `ef-video` | `Video` | `src`, `fit`, `duration`, `fileId`, `sourcein`, `sourceout`, `trimstart`, `trimend` |
|
|
138
|
+
| `ef-audio` | `Audio` | `src`, `duration`, `volume`, `mute`, `loop`, `fftSize`, `fftGain` |
|
|
139
|
+
| `ef-text` | `Text` | `text`, `x`, `y`, `fontSize`, `color`, `textAlign`, `duration`, `split`, `stagger` |
|
|
140
|
+
| `ef-captions` | `Captions` | `captionsSrc`, `captionsScript`, `target`, `wordStyle`, `duration` |
|
|
141
|
+
| `ef-image` | `Image` | `src`, `fit`, `duration`, `fileId` |
|
|
142
|
+
| `ef-waveform` | `Waveform` | `target`, `mode`, `color` |
|
|
143
|
+
| `ef-surface` | `Surface` | `target` |
|
|
144
|
+
| `ef-pan-zoom` | `PanZoom` | `target`, `pan`, `zoom` |
|
|
145
|
+
| `ef-configuration` | `Configuration` | `apiHost`, `signingUrl`, `imageProxy` |
|
|
146
|
+
| `ef-transition` | `Transition` | `type`, `duration` |
|
|
147
|
+
| `ef-script` | `Script` | `src`, `inline` |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ef-composition-review
|
|
3
|
-
description: Review an Editframe
|
|
3
|
+
description: Review an Editframe React composition for time model correctness, accessibility, and best practices
|
|
4
4
|
invocation: user
|
|
5
5
|
category: fo
|
|
6
6
|
concerns: read-only
|
|
@@ -14,41 +14,41 @@ languagePolicy: ref(PREFERENCES.md)
|
|
|
14
14
|
|
|
15
15
|
Before starting, read `PREFERENCES.md` at the repository root. If the file is missing or `aiLanguage` is unset, ask the operator once and create the file using the `my-preferences` skill semantics.
|
|
16
16
|
|
|
17
|
-
A read-only review of an Editframe
|
|
17
|
+
A read-only review of an Editframe React composition file. The skill checks time model correctness, accessibility, asset references, automated invariants, and manual best practices. It does **not** modify any file — it produces a structured report and stops.
|
|
18
18
|
|
|
19
19
|
## Scope
|
|
20
20
|
|
|
21
|
-
This skill reviews `.
|
|
21
|
+
This skill reviews `.tsx` composition files that use `@editframe/react` components (`TimelineRoot`, `Timegroup`, `Video`, `Audio`, `Text`, `Captions`). It is designed for projects using the `editframe` stack profile.
|
|
22
22
|
|
|
23
23
|
## Process
|
|
24
24
|
|
|
25
25
|
### 1. Empty state check
|
|
26
26
|
|
|
27
|
-
Scan the `compositions/` directory for `.
|
|
27
|
+
Scan the `compositions/` directory for `.tsx` files. If no compositions are found, report "No compositions found — nothing to review" and stop. Do not report false positives on an empty project.
|
|
28
28
|
|
|
29
29
|
### 2. Time model review
|
|
30
30
|
|
|
31
31
|
For each composition file:
|
|
32
32
|
|
|
33
|
-
- Check that the root `
|
|
33
|
+
- Check that the root `Timegroup` declares a `duration` prop or uses `mode="contain"` / `mode="fit"`.
|
|
34
34
|
- Check that all `duration` and `offset` values are valid CSS time strings (e.g. `5s`, `300ms`, `2.5s`).
|
|
35
35
|
- Check that `mode` values are one of: `sequence`, `fixed`, `contain`, `fit`.
|
|
36
36
|
- Check that `fps` is a positive integer (e.g. `30`, `60`).
|
|
37
|
-
- Check that `loop` is only present on the root `
|
|
37
|
+
- Check that `loop` is only present on the root `Timegroup` — nested Timegroups should not loop.
|
|
38
38
|
|
|
39
39
|
### 3. Accessibility review
|
|
40
40
|
|
|
41
|
-
- Check that all `
|
|
42
|
-
- Check that `
|
|
41
|
+
- Check that all `Audio` components with speech content have corresponding `Captions` components.
|
|
42
|
+
- Check that `Text` components have sufficient contrast between foreground and background colors (if declared via inline styles or CSS classes).
|
|
43
43
|
|
|
44
44
|
### 4. Asset reference review
|
|
45
45
|
|
|
46
|
-
- Check that all `src`
|
|
46
|
+
- Check that all `src` props in `Video`, `Audio`, and `Image` components point to files that exist in the `assets/` directory.
|
|
47
47
|
- Check that asset filenames use kebab-case (lowercase letters, digits, hyphens only).
|
|
48
48
|
|
|
49
49
|
### 5. Invariant check
|
|
50
50
|
|
|
51
|
-
Run `forge doctor` to check all profile invariants automatically. For the `editframe
|
|
51
|
+
Run `forge doctor` to check all profile invariants automatically. For the `editframe` profile, this covers VIDEO-01 through VIDEO-09 (filename conventions, scene fit modes, captions, and time model invariants). Review the output and include any violations in the report.
|
|
52
52
|
|
|
53
53
|
### 6. Manual best practices
|
|
54
54
|
|