@warpgogol/forge 1.0.1 → 1.2.2
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 +7 -19
- package/README.md +64 -50
- package/README.uk.md +64 -50
- package/dist/os/mission/handlers/archive.d.ts.map +1 -1
- package/dist/os/mission/handlers/archive.js +7 -1
- package/dist/os/mission/handlers/archive.js.map +1 -1
- package/package.json +16 -3
- package/profiles/astro-typescript-turborepo.yaml +1 -1
- package/profiles/godot-csharp.yaml +1 -1
- package/profiles/phaser-turborepo.yaml +1 -1
- package/skills/fo/fo-spec-ingest/SKILL.md +1 -1
- package/profiles/editframe-templates/composition-agents.md +0 -69
- package/profiles/editframe-templates/composition.tsx +0 -81
- package/profiles/editframe-templates/html-composition-agents.md +0 -62
- package/profiles/editframe.yaml +0 -729
- package/profiles/obsidian-vault.yaml +0 -103
- package/skills/fo/ef-brand-video-generator/SKILL.md +0 -92
- package/skills/fo/ef-composition/SKILL.md +0 -156
- package/skills/fo/ef-composition-review/SKILL.md +0 -68
- package/skills/fo/ef-dev-server/SKILL.md +0 -107
- package/skills/fo/ef-editor-gui/SKILL.md +0 -131
- package/skills/fo/ef-motion-design/SKILL.md +0 -118
- package/skills/fo/ef-onboard/SKILL.md +0 -77
- package/skills/fo/ef-render-verify/SKILL.md +0 -56
- package/skills/fo/ef-webhooks/SKILL.md +0 -95
package/profiles/editframe.yaml
DELETED
|
@@ -1,729 +0,0 @@
|
|
|
1
|
-
schema: forge/stack-profile@1
|
|
2
|
-
id: editframe
|
|
3
|
-
displayName: Editframe Video
|
|
4
|
-
detect:
|
|
5
|
-
anyOf:
|
|
6
|
-
- editframe.config.*
|
|
7
|
-
domain: video
|
|
8
|
-
register: creative
|
|
9
|
-
terminology:
|
|
10
|
-
artifact: composition
|
|
11
|
-
artifactPlural: compositions
|
|
12
|
-
module: scene
|
|
13
|
-
source: composition file
|
|
14
|
-
output: render
|
|
15
|
-
verify: render-verify
|
|
16
|
-
operator: director
|
|
17
|
-
devServer:
|
|
18
|
-
command: editframe preview
|
|
19
|
-
port: 4321
|
|
20
|
-
artifacts:
|
|
21
|
-
- id: composition
|
|
22
|
-
extensions:
|
|
23
|
-
- .tsx
|
|
24
|
-
- .html
|
|
25
|
-
produce:
|
|
26
|
-
command: editframe render
|
|
27
|
-
output: dist/{composition}.mp4
|
|
28
|
-
validate:
|
|
29
|
-
command: editframe check
|
|
30
|
-
determinism:
|
|
31
|
-
hashable: true
|
|
32
|
-
inputs:
|
|
33
|
-
- compositions/**/*.tsx
|
|
34
|
-
- compositions/**/*.html
|
|
35
|
-
- assets/**
|
|
36
|
-
assets:
|
|
37
|
-
dir: assets
|
|
38
|
-
types:
|
|
39
|
-
- id: video
|
|
40
|
-
extensions: [.mp4, .webm]
|
|
41
|
-
referencePattern: 'src="([^"]+\.(?:mp4|webm))"'
|
|
42
|
-
- id: audio
|
|
43
|
-
extensions: [.mp3, .wav]
|
|
44
|
-
referencePattern: 'src="([^"]+\.(?:mp3|wav))"'
|
|
45
|
-
- id: image
|
|
46
|
-
extensions: [.png, .jpg, .jpeg, .webp, .svg]
|
|
47
|
-
referencePattern: 'src="([^"]+\.(?:png|jpe?g|webp|svg))"'
|
|
48
|
-
release:
|
|
49
|
-
target: local
|
|
50
|
-
outputDir: release
|
|
51
|
-
manifestName: release-manifest.json
|
|
52
|
-
prerequisites:
|
|
53
|
-
- id: nodejs
|
|
54
|
-
name: Node.js 24+
|
|
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
|
|
63
|
-
workspaceTypes:
|
|
64
|
-
- id: composition
|
|
65
|
-
detect:
|
|
66
|
-
glob: "*.tsx"
|
|
67
|
-
contains: Timegroup
|
|
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
|
|
75
|
-
detect:
|
|
76
|
-
glob: "*.html"
|
|
77
|
-
contains: ef-timegroup
|
|
78
|
-
packageJsonDep: "@editframe/cli"
|
|
79
|
-
skills:
|
|
80
|
-
- ef-onboard
|
|
81
|
-
- ef-composition-review
|
|
82
|
-
- ef-render-verify
|
|
83
|
-
agentsMdTemplate: editframe-templates/html-composition-agents.md
|
|
84
|
-
invariants:
|
|
85
|
-
- id: VIDEO-01
|
|
86
|
-
rule: Composition filenames must use kebab-case (lowercase letters, digits, hyphens only)
|
|
87
|
-
severity: error
|
|
88
|
-
check:
|
|
89
|
-
kind: filename-pattern
|
|
90
|
-
glob: "compositions/**/*.{tsx,html}"
|
|
91
|
-
pattern: "^[a-z0-9-]+\\.(tsx|html)$"
|
|
92
|
-
- id: VIDEO-02
|
|
93
|
-
rule: Scene durations must use contain mode by default to avoid unexpected cropping
|
|
94
|
-
severity: warning
|
|
95
|
-
check:
|
|
96
|
-
kind: file-contains
|
|
97
|
-
glob: "compositions/**/*.{tsx,html}"
|
|
98
|
-
pattern: contain
|
|
99
|
-
- id: VIDEO-03
|
|
100
|
-
rule: All speech audio elements must have corresponding Captions components for accessibility
|
|
101
|
-
severity: error
|
|
102
|
-
check:
|
|
103
|
-
kind: file-contains
|
|
104
|
-
glob: "compositions/**/*.{tsx,html}"
|
|
105
|
-
pattern: Captions
|
|
106
|
-
- id: VIDEO-04
|
|
107
|
-
rule: Root Timegroup must declare duration or use mode="contain" or mode="fit"
|
|
108
|
-
severity: error
|
|
109
|
-
check:
|
|
110
|
-
kind: file-contains
|
|
111
|
-
glob: "compositions/**/*.{tsx,html}"
|
|
112
|
-
pattern: 'Timegroup[^>]*(duration|mode="contain"|mode="fit")[^>]*>|ef-timegroup[^>]*(duration|mode="contain"|mode="fit")[^>]*>'
|
|
113
|
-
- id: VIDEO-05
|
|
114
|
-
rule: All duration values must be valid CSS time strings (e.g. 5s, 500ms, 2.5s)
|
|
115
|
-
severity: error
|
|
116
|
-
check:
|
|
117
|
-
kind: attribute-pattern
|
|
118
|
-
glob: "compositions/**/*.{tsx,html}"
|
|
119
|
-
elements:
|
|
120
|
-
- ef-timegroup
|
|
121
|
-
- Timegroup
|
|
122
|
-
attribute: "duration"
|
|
123
|
-
pattern: "^\\d+(\\.\\d+)?(s|ms)$"
|
|
124
|
-
- id: VIDEO-06
|
|
125
|
-
rule: "mode attribute must be one of: sequence, fixed, contain, fit"
|
|
126
|
-
severity: error
|
|
127
|
-
check:
|
|
128
|
-
kind: attribute-pattern
|
|
129
|
-
glob: "compositions/**/*.{tsx,html}"
|
|
130
|
-
elements:
|
|
131
|
-
- ef-timegroup
|
|
132
|
-
- Timegroup
|
|
133
|
-
attribute: "mode"
|
|
134
|
-
pattern: "^(sequence|fixed|contain|fit)$"
|
|
135
|
-
- id: VIDEO-07
|
|
136
|
-
rule: fps must be a positive integer (default 30)
|
|
137
|
-
severity: warning
|
|
138
|
-
check:
|
|
139
|
-
kind: attribute-pattern
|
|
140
|
-
glob: "compositions/**/*.{tsx,html}"
|
|
141
|
-
elements:
|
|
142
|
-
- ef-timegroup
|
|
143
|
-
- Timegroup
|
|
144
|
-
attribute: "fps"
|
|
145
|
-
pattern: "^[1-9]\\d*$"
|
|
146
|
-
- id: VIDEO-08
|
|
147
|
-
rule: loop attribute should only be used on the root Timegroup (no effect on render for nested groups)
|
|
148
|
-
severity: warning
|
|
149
|
-
check:
|
|
150
|
-
kind: file-not-contains
|
|
151
|
-
glob: "compositions/**/*.{tsx,html}"
|
|
152
|
-
negatedPattern: "<(Timegroup|ef-timegroup)[^>]*loop[^>]*>[\\s\\S]*<(Timegroup|ef-timegroup)[^>]*loop"
|
|
153
|
-
- id: VIDEO-09
|
|
154
|
-
rule: offset values must be valid CSS time strings (e.g. 0.5s, 3s, 500ms)
|
|
155
|
-
severity: warning
|
|
156
|
-
check:
|
|
157
|
-
kind: attribute-pattern
|
|
158
|
-
glob: "compositions/**/*.{tsx,html}"
|
|
159
|
-
elements:
|
|
160
|
-
- ef-timegroup
|
|
161
|
-
- Timegroup
|
|
162
|
-
attribute: "offset"
|
|
163
|
-
pattern: "^\\d+(\\.\\d+)?(s|ms)$"
|
|
164
|
-
workspace:
|
|
165
|
-
dirs:
|
|
166
|
-
- compositions
|
|
167
|
-
- packages
|
|
168
|
-
- services
|
|
169
|
-
files:
|
|
170
|
-
- path: pnpm-workspace.yaml
|
|
171
|
-
content: |
|
|
172
|
-
packages:
|
|
173
|
-
- "compositions/*"
|
|
174
|
-
- "packages/*"
|
|
175
|
-
- "services/*"
|
|
176
|
-
allowBuilds:
|
|
177
|
-
"@editframe/cli": true
|
|
178
|
-
node-av: true
|
|
179
|
-
- path: turbo.json
|
|
180
|
-
content: |
|
|
181
|
-
{
|
|
182
|
-
"$schema": "https://turbo.build/schema.json",
|
|
183
|
-
"tasks": {
|
|
184
|
-
"build": {
|
|
185
|
-
"dependsOn": ["^build"],
|
|
186
|
-
"outputs": ["dist/**"]
|
|
187
|
-
},
|
|
188
|
-
"build:check": {
|
|
189
|
-
"dependsOn": ["^build:check"]
|
|
190
|
-
},
|
|
191
|
-
"test": {
|
|
192
|
-
"dependsOn": ["^build:check"]
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
- path: .gitignore
|
|
197
|
-
content: |
|
|
198
|
-
node_modules
|
|
199
|
-
dist
|
|
200
|
-
.turbo
|
|
201
|
-
.cache
|
|
202
|
-
operator-profile.md
|
|
203
|
-
- path: .github/workflows/ci.yml
|
|
204
|
-
content: |
|
|
205
|
-
name: CI
|
|
206
|
-
|
|
207
|
-
on:
|
|
208
|
-
pull_request:
|
|
209
|
-
push:
|
|
210
|
-
branches: [main]
|
|
211
|
-
workflow_dispatch:
|
|
212
|
-
|
|
213
|
-
concurrency:
|
|
214
|
-
group: ${{ github.workflow }}-${{ github.ref }}
|
|
215
|
-
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
216
|
-
|
|
217
|
-
permissions:
|
|
218
|
-
contents: read
|
|
219
|
-
|
|
220
|
-
jobs:
|
|
221
|
-
ci:
|
|
222
|
-
runs-on: ubuntu-latest
|
|
223
|
-
timeout-minutes: 15
|
|
224
|
-
name: Lint, type-check, build, test
|
|
225
|
-
env:
|
|
226
|
-
TZ: UTC
|
|
227
|
-
|
|
228
|
-
steps:
|
|
229
|
-
- name: Checkout
|
|
230
|
-
uses: actions/checkout@v5
|
|
231
|
-
|
|
232
|
-
- name: Enable Corepack
|
|
233
|
-
run: corepack enable
|
|
234
|
-
|
|
235
|
-
- name: Setup Node.js
|
|
236
|
-
uses: actions/setup-node@v5
|
|
237
|
-
with:
|
|
238
|
-
node-version: "24"
|
|
239
|
-
cache: "pnpm"
|
|
240
|
-
|
|
241
|
-
- name: Install dependencies
|
|
242
|
-
run: pnpm install --frozen-lockfile
|
|
243
|
-
|
|
244
|
-
- name: Format check
|
|
245
|
-
run: pnpm format:check
|
|
246
|
-
|
|
247
|
-
- name: Type check
|
|
248
|
-
run: pnpm build:check
|
|
249
|
-
|
|
250
|
-
- name: Build
|
|
251
|
-
run: pnpm build
|
|
252
|
-
|
|
253
|
-
- name: Test
|
|
254
|
-
run: pnpm test
|
|
255
|
-
|
|
256
|
-
# Windows CI (optional — uncomment if Windows checks are needed):
|
|
257
|
-
#
|
|
258
|
-
# windows-ci:
|
|
259
|
-
# runs-on: windows-latest
|
|
260
|
-
# name: Lint, type-check, build, test (Windows)
|
|
261
|
-
# env:
|
|
262
|
-
# GIT_CONFIG_COUNT: 1
|
|
263
|
-
# GIT_CONFIG_KEY_0: core.longpaths
|
|
264
|
-
# GIT_CONFIG_VALUE_0: "true"
|
|
265
|
-
# steps:
|
|
266
|
-
# - name: Checkout
|
|
267
|
-
# uses: actions/checkout@v5
|
|
268
|
-
# - name: Enable Corepack
|
|
269
|
-
# run: corepack enable
|
|
270
|
-
# - name: Setup Node.js
|
|
271
|
-
# uses: actions/setup-node@v5
|
|
272
|
-
# with:
|
|
273
|
-
# node-version: "24"
|
|
274
|
-
# cache: "pnpm"
|
|
275
|
-
# - name: Install dependencies
|
|
276
|
-
# run: pnpm install --frozen-lockfile
|
|
277
|
-
# - name: Format check
|
|
278
|
-
# run: pnpm format:check
|
|
279
|
-
# - name: Type check
|
|
280
|
-
# run: pnpm build:check
|
|
281
|
-
# - name: Build
|
|
282
|
-
# run: pnpm build
|
|
283
|
-
# - name: Test
|
|
284
|
-
# run: pnpm test
|
|
285
|
-
#
|
|
286
|
-
# The GIT_CONFIG_COUNT env block MUST be set before the checkout step
|
|
287
|
-
# so git can handle long file paths during clone on Windows.
|
|
288
|
-
# Do not add Windows to the matrix "just in case" — it requires
|
|
289
|
-
# separate path-length and platform-specific dependency verification.
|
|
290
|
-
- path: package.json
|
|
291
|
-
content: |
|
|
292
|
-
{
|
|
293
|
-
"name": "__PROJECT_NAME__",
|
|
294
|
-
"version": "0.1.0",
|
|
295
|
-
"private": true,
|
|
296
|
-
"type": "module",
|
|
297
|
-
"packageManager": "pnpm@11.10.0",
|
|
298
|
-
"engines": {
|
|
299
|
-
"node": ">=24 <25"
|
|
300
|
-
},
|
|
301
|
-
"scripts": {
|
|
302
|
-
"clean": "node scripts/clean.mjs",
|
|
303
|
-
"format": "prettier --write .",
|
|
304
|
-
"format:check": "prettier --check .",
|
|
305
|
-
"build": "turbo run build",
|
|
306
|
-
"build:check": "turbo run build:check",
|
|
307
|
-
"test": "turbo run test",
|
|
308
|
-
"upgrade-packages": "pnpm -r up"
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
- path: .prettierrc.mjs
|
|
312
|
-
content: |
|
|
313
|
-
/** @type {import("prettier").Config} */
|
|
314
|
-
export default {
|
|
315
|
-
semi: true,
|
|
316
|
-
singleQuote: false,
|
|
317
|
-
trailingComma: "all",
|
|
318
|
-
printWidth: 100,
|
|
319
|
-
tabWidth: 2,
|
|
320
|
-
};
|
|
321
|
-
- path: scripts/clean.mjs
|
|
322
|
-
content: |
|
|
323
|
-
import { readdir, rm } from "node:fs/promises";
|
|
324
|
-
import { join } from "node:path";
|
|
325
|
-
import { fileURLToPath } from "node:url";
|
|
326
|
-
|
|
327
|
-
const TARGETS = new Set(["node_modules", ".turbo", "dist", ".cache"]);
|
|
328
|
-
|
|
329
|
-
const repoRoot = join(fileURLToPath(import.meta.url), "..", "..");
|
|
330
|
-
|
|
331
|
-
async function walk(dir, toDelete) {
|
|
332
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
333
|
-
for (const entry of entries) {
|
|
334
|
-
if (!entry.isDirectory()) continue;
|
|
335
|
-
const name = entry.name;
|
|
336
|
-
const fullPath = join(dir, name);
|
|
337
|
-
if (TARGETS.has(name)) {
|
|
338
|
-
toDelete.push(fullPath);
|
|
339
|
-
continue;
|
|
340
|
-
}
|
|
341
|
-
await walk(fullPath, toDelete);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
const toDelete = [];
|
|
346
|
-
await walk(repoRoot, toDelete);
|
|
347
|
-
|
|
348
|
-
if (toDelete.length === 0) {
|
|
349
|
-
console.log("Nothing to clean.");
|
|
350
|
-
process.exit(0);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
await Promise.all(toDelete.map((p) => rm(p, { recursive: true, force: true })));
|
|
354
|
-
|
|
355
|
-
for (const p of toDelete) {
|
|
356
|
-
console.log("deleted", p.replace(repoRoot + "/", ""));
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
console.log(`Cleaned ${toDelete.length} directories.`);
|
|
360
|
-
install:
|
|
361
|
-
- pnpm add -w -D @editframe/cli @warpgogol/forge turbo prettier
|
|
362
|
-
firstWorkspace:
|
|
363
|
-
path: compositions/my-first-video
|
|
364
|
-
files:
|
|
365
|
-
- path: package.json
|
|
366
|
-
content: |
|
|
367
|
-
{
|
|
368
|
-
"name": "my-first-video",
|
|
369
|
-
"version": "0.1.0",
|
|
370
|
-
"private": true,
|
|
371
|
-
"type": "module",
|
|
372
|
-
"scripts": {
|
|
373
|
-
"preview": "editframe preview",
|
|
374
|
-
"render": "editframe render -o dist/my-first-video.mp4",
|
|
375
|
-
"check": "editframe check"
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
- path: index.html
|
|
379
|
-
content: |
|
|
380
|
-
<!doctype html>
|
|
381
|
-
<html lang="en">
|
|
382
|
-
<head>
|
|
383
|
-
<meta charset="UTF-8" />
|
|
384
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
385
|
-
<title>my-first-video</title>
|
|
386
|
-
</head>
|
|
387
|
-
<body>
|
|
388
|
-
<div id="root"></div>
|
|
389
|
-
<script type="module" src="/src/main.tsx"></script>
|
|
390
|
-
</body>
|
|
391
|
-
</html>
|
|
392
|
-
- path: src/main.tsx
|
|
393
|
-
content: |
|
|
394
|
-
import React from "react";
|
|
395
|
-
import { createRoot } from "react-dom/client";
|
|
396
|
-
import "@editframe/elements/gui";
|
|
397
|
-
import "@editframe/elements/styles.css";
|
|
398
|
-
import Composition from "../composition";
|
|
399
|
-
|
|
400
|
-
document.documentElement.style.height = "100%";
|
|
401
|
-
document.body.style.height = "100%";
|
|
402
|
-
document.body.style.margin = "0";
|
|
403
|
-
const rootEl = document.getElementById("root");
|
|
404
|
-
if (rootEl) rootEl.style.height = "100%";
|
|
405
|
-
|
|
406
|
-
const styleOverride = document.createElement("style");
|
|
407
|
-
styleOverride.textContent = "ef-workbench { display: grid; }";
|
|
408
|
-
document.head.appendChild(styleOverride);
|
|
409
|
-
|
|
410
|
-
const root = document.getElementById("root");
|
|
411
|
-
if (!root) throw new Error("#root element not found");
|
|
412
|
-
|
|
413
|
-
createRoot(root).render(
|
|
414
|
-
<React.StrictMode>
|
|
415
|
-
<Composition />
|
|
416
|
-
</React.StrictMode>,
|
|
417
|
-
);
|
|
418
|
-
- path: vite.config.ts
|
|
419
|
-
content: |
|
|
420
|
-
import { defineConfig } from "vite";
|
|
421
|
-
import react from "@vitejs/plugin-react";
|
|
422
|
-
import { vitePluginEditframe as editframePlugin } from "@editframe/vite-plugin";
|
|
423
|
-
import tailwindcss from "@tailwindcss/vite";
|
|
424
|
-
import path from "node:path";
|
|
425
|
-
|
|
426
|
-
const root = import.meta.dirname;
|
|
427
|
-
const cacheRoot = path.join(root, ".ef-cache");
|
|
428
|
-
|
|
429
|
-
export default defineConfig({
|
|
430
|
-
plugins: [react(), tailwindcss(), editframePlugin({ root, cacheRoot })],
|
|
431
|
-
});
|
|
432
|
-
- path: tsconfig.json
|
|
433
|
-
content: |
|
|
434
|
-
{
|
|
435
|
-
"compilerOptions": {
|
|
436
|
-
"target": "ES2022",
|
|
437
|
-
"module": "ESNext",
|
|
438
|
-
"moduleResolution": "bundler",
|
|
439
|
-
"jsx": "react-jsx",
|
|
440
|
-
"strict": true,
|
|
441
|
-
"esModuleInterop": true,
|
|
442
|
-
"skipLibCheck": true,
|
|
443
|
-
"noEmit": true,
|
|
444
|
-
"lib": ["ES2022", "DOM", "DOM.Iterable"]
|
|
445
|
-
},
|
|
446
|
-
"include": ["src", "composition.tsx", "vite.config.ts"]
|
|
447
|
-
}
|
|
448
|
-
- path: src/index.css
|
|
449
|
-
content: |
|
|
450
|
-
@import "tailwindcss";
|
|
451
|
-
- path: composition.tsx
|
|
452
|
-
content: |
|
|
453
|
-
/*
|
|
454
|
-
Editframe React composition — my-first-video
|
|
455
|
-
This file declares a video composition using @editframe/react components.
|
|
456
|
-
Run `editframe preview` to preview, `editframe render` to produce output.
|
|
457
|
-
*/
|
|
458
|
-
import { useEffect, useRef } from "react";
|
|
459
|
-
import { Configuration, Timegroup, Text, Workbench } from "@editframe/react";
|
|
460
|
-
|
|
461
|
-
const EFWorkbench = Workbench as any;
|
|
462
|
-
const EFConfiguration = Configuration as any;
|
|
463
|
-
const EFTimegroup = Timegroup as any;
|
|
464
|
-
const EFText = Text as any;
|
|
465
|
-
|
|
466
|
-
export default function Composition() {
|
|
467
|
-
const workbenchRef = useRef<HTMLElement>(null);
|
|
468
|
-
|
|
469
|
-
useEffect(() => {
|
|
470
|
-
const wb = workbenchRef.current;
|
|
471
|
-
if (!wb) return;
|
|
472
|
-
|
|
473
|
-
const fixReplay = () => {
|
|
474
|
-
const config = wb.querySelector("ef-configuration");
|
|
475
|
-
const controller = (config as any)?.playback;
|
|
476
|
-
if (!controller) return;
|
|
477
|
-
|
|
478
|
-
const originalPlay = controller.play.bind(controller);
|
|
479
|
-
controller.play = (opts?: any) => {
|
|
480
|
-
if (!opts?.from && controller.currentTime >= controller.duration) {
|
|
481
|
-
originalPlay({ from: 0, to: opts?.to });
|
|
482
|
-
return;
|
|
483
|
-
}
|
|
484
|
-
originalPlay(opts);
|
|
485
|
-
};
|
|
486
|
-
};
|
|
487
|
-
|
|
488
|
-
fixReplay();
|
|
489
|
-
wb.addEventListener("playback-attached", fixReplay);
|
|
490
|
-
return () => wb.removeEventListener("playback-attached", fixReplay);
|
|
491
|
-
}, []);
|
|
492
|
-
|
|
493
|
-
return (
|
|
494
|
-
<EFWorkbench ref={workbenchRef} resolution="1920x1080">
|
|
495
|
-
<EFConfiguration>
|
|
496
|
-
<EFTimegroup
|
|
497
|
-
id="root"
|
|
498
|
-
duration="10s"
|
|
499
|
-
mode="contain"
|
|
500
|
-
style={{
|
|
501
|
-
width: "1920px",
|
|
502
|
-
height: "1080px",
|
|
503
|
-
background: "#000",
|
|
504
|
-
display: "flex",
|
|
505
|
-
alignItems: "center",
|
|
506
|
-
justifyContent: "center",
|
|
507
|
-
}}
|
|
508
|
-
>
|
|
509
|
-
<EFText
|
|
510
|
-
duration="10s"
|
|
511
|
-
style={{
|
|
512
|
-
color: "white",
|
|
513
|
-
fontSize: "72px",
|
|
514
|
-
textAlign: "center",
|
|
515
|
-
fontFamily: "ui-sans-serif, system-ui, sans-serif",
|
|
516
|
-
opacity: "min(1, var(--ef-progress, 0) * 5)",
|
|
517
|
-
transform: "scale(min(1, 0.8 + var(--ef-progress, 0) * 0.2))",
|
|
518
|
-
}}
|
|
519
|
-
>
|
|
520
|
-
Hello, Editframe!
|
|
521
|
-
</EFText>
|
|
522
|
-
</EFTimegroup>
|
|
523
|
-
</EFConfiguration>
|
|
524
|
-
</EFWorkbench>
|
|
525
|
-
);
|
|
526
|
-
}
|
|
527
|
-
install:
|
|
528
|
-
- pnpm add -D @editframe/react @editframe/elements @editframe/vite-plugin @editframe/dev-server react react-dom typescript @types/react @types/react-dom vite @vitejs/plugin-react tailwindcss @tailwindcss/vite vitest
|
|
529
|
-
templates:
|
|
530
|
-
- id: react
|
|
531
|
-
default: true
|
|
532
|
-
firstWorkspace:
|
|
533
|
-
path: compositions/my-first-video
|
|
534
|
-
files:
|
|
535
|
-
- path: package.json
|
|
536
|
-
content: |
|
|
537
|
-
{
|
|
538
|
-
"name": "my-first-video",
|
|
539
|
-
"version": "0.1.0",
|
|
540
|
-
"private": true,
|
|
541
|
-
"type": "module",
|
|
542
|
-
"scripts": {
|
|
543
|
-
"preview": "editframe preview",
|
|
544
|
-
"render": "editframe render -o dist/my-first-video.mp4",
|
|
545
|
-
"check": "editframe check"
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
- path: index.html
|
|
549
|
-
content: |
|
|
550
|
-
<!doctype html>
|
|
551
|
-
<html lang="en">
|
|
552
|
-
<head>
|
|
553
|
-
<meta charset="UTF-8" />
|
|
554
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
555
|
-
<title>my-first-video</title>
|
|
556
|
-
</head>
|
|
557
|
-
<body>
|
|
558
|
-
<div id="root"></div>
|
|
559
|
-
<script type="module" src="/src/main.tsx"></script>
|
|
560
|
-
</body>
|
|
561
|
-
</html>
|
|
562
|
-
- path: src/main.tsx
|
|
563
|
-
content: |
|
|
564
|
-
import React from "react";
|
|
565
|
-
import { createRoot } from "react-dom/client";
|
|
566
|
-
import "@editframe/elements/gui";
|
|
567
|
-
import "@editframe/elements/styles.css";
|
|
568
|
-
import Composition from "../composition";
|
|
569
|
-
|
|
570
|
-
document.documentElement.style.height = "100%";
|
|
571
|
-
document.body.style.height = "100%";
|
|
572
|
-
document.body.style.margin = "0";
|
|
573
|
-
const rootEl = document.getElementById("root");
|
|
574
|
-
if (rootEl) rootEl.style.height = "100%";
|
|
575
|
-
|
|
576
|
-
const styleOverride = document.createElement("style");
|
|
577
|
-
styleOverride.textContent = "ef-workbench { display: grid; }";
|
|
578
|
-
document.head.appendChild(styleOverride);
|
|
579
|
-
|
|
580
|
-
const root = document.getElementById("root");
|
|
581
|
-
if (!root) throw new Error("#root element not found");
|
|
582
|
-
|
|
583
|
-
createRoot(root).render(
|
|
584
|
-
<React.StrictMode>
|
|
585
|
-
<Composition />
|
|
586
|
-
</React.StrictMode>,
|
|
587
|
-
);
|
|
588
|
-
- path: vite.config.ts
|
|
589
|
-
content: |
|
|
590
|
-
import { defineConfig } from "vite";
|
|
591
|
-
import react from "@vitejs/plugin-react";
|
|
592
|
-
import { vitePluginEditframe as editframePlugin } from "@editframe/vite-plugin";
|
|
593
|
-
import tailwindcss from "@tailwindcss/vite";
|
|
594
|
-
import path from "node:path";
|
|
595
|
-
|
|
596
|
-
const root = import.meta.dirname;
|
|
597
|
-
const cacheRoot = path.join(root, ".ef-cache");
|
|
598
|
-
|
|
599
|
-
export default defineConfig({
|
|
600
|
-
plugins: [react(), tailwindcss(), editframePlugin({ root, cacheRoot })],
|
|
601
|
-
});
|
|
602
|
-
- path: tsconfig.json
|
|
603
|
-
content: |
|
|
604
|
-
{
|
|
605
|
-
"compilerOptions": {
|
|
606
|
-
"target": "ES2022",
|
|
607
|
-
"module": "ESNext",
|
|
608
|
-
"moduleResolution": "bundler",
|
|
609
|
-
"jsx": "react-jsx",
|
|
610
|
-
"strict": true,
|
|
611
|
-
"esModuleInterop": true,
|
|
612
|
-
"skipLibCheck": true,
|
|
613
|
-
"noEmit": true,
|
|
614
|
-
"lib": ["ES2022", "DOM", "DOM.Iterable"]
|
|
615
|
-
},
|
|
616
|
-
"include": ["src", "composition.tsx", "vite.config.ts"]
|
|
617
|
-
}
|
|
618
|
-
- path: src/index.css
|
|
619
|
-
content: |
|
|
620
|
-
@import "tailwindcss";
|
|
621
|
-
- path: composition.tsx
|
|
622
|
-
content: |
|
|
623
|
-
/*
|
|
624
|
-
Editframe React composition — my-first-video
|
|
625
|
-
This file declares a video composition using @editframe/react components.
|
|
626
|
-
Run `editframe preview` to preview, `editframe render` to produce output.
|
|
627
|
-
*/
|
|
628
|
-
import { useEffect, useRef } from "react";
|
|
629
|
-
import { Configuration, Timegroup, Text, Workbench } from "@editframe/react";
|
|
630
|
-
|
|
631
|
-
const EFWorkbench = Workbench as any;
|
|
632
|
-
const EFConfiguration = Configuration as any;
|
|
633
|
-
const EFTimegroup = Timegroup as any;
|
|
634
|
-
const EFText = Text as any;
|
|
635
|
-
|
|
636
|
-
export default function Composition() {
|
|
637
|
-
const workbenchRef = useRef<HTMLElement>(null);
|
|
638
|
-
|
|
639
|
-
useEffect(() => {
|
|
640
|
-
const wb = workbenchRef.current;
|
|
641
|
-
if (!wb) return;
|
|
642
|
-
|
|
643
|
-
const fixReplay = () => {
|
|
644
|
-
const config = wb.querySelector("ef-configuration");
|
|
645
|
-
const controller = (config as any)?.playback;
|
|
646
|
-
if (!controller) return;
|
|
647
|
-
|
|
648
|
-
const originalPlay = controller.play.bind(controller);
|
|
649
|
-
controller.play = (opts?: any) => {
|
|
650
|
-
if (!opts?.from && controller.currentTime >= controller.duration) {
|
|
651
|
-
originalPlay({ from: 0, to: opts?.to });
|
|
652
|
-
return;
|
|
653
|
-
}
|
|
654
|
-
originalPlay(opts);
|
|
655
|
-
};
|
|
656
|
-
};
|
|
657
|
-
|
|
658
|
-
fixReplay();
|
|
659
|
-
wb.addEventListener("playback-attached", fixReplay);
|
|
660
|
-
return () => wb.removeEventListener("playback-attached", fixReplay);
|
|
661
|
-
}, []);
|
|
662
|
-
|
|
663
|
-
return (
|
|
664
|
-
<EFWorkbench ref={workbenchRef} resolution="1920x1080">
|
|
665
|
-
<EFConfiguration>
|
|
666
|
-
<EFTimegroup
|
|
667
|
-
id="root"
|
|
668
|
-
duration="10s"
|
|
669
|
-
mode="contain"
|
|
670
|
-
style={{
|
|
671
|
-
width: "1920px",
|
|
672
|
-
height: "1080px",
|
|
673
|
-
background: "#000",
|
|
674
|
-
display: "flex",
|
|
675
|
-
alignItems: "center",
|
|
676
|
-
justifyContent: "center",
|
|
677
|
-
}}
|
|
678
|
-
>
|
|
679
|
-
<EFText
|
|
680
|
-
duration="10s"
|
|
681
|
-
style={{
|
|
682
|
-
color: "white",
|
|
683
|
-
fontSize: "72px",
|
|
684
|
-
textAlign: "center",
|
|
685
|
-
fontFamily: "ui-sans-serif, system-ui, sans-serif",
|
|
686
|
-
opacity: "min(1, var(--ef-progress, 0) * 5)",
|
|
687
|
-
transform: "scale(min(1, 0.8 + var(--ef-progress, 0) * 0.2))",
|
|
688
|
-
}}
|
|
689
|
-
>
|
|
690
|
-
Hello, Editframe!
|
|
691
|
-
</EFText>
|
|
692
|
-
</EFTimegroup>
|
|
693
|
-
</EFConfiguration>
|
|
694
|
-
</EFWorkbench>
|
|
695
|
-
);
|
|
696
|
-
}
|
|
697
|
-
install:
|
|
698
|
-
- pnpm add -D @editframe/react @editframe/elements @editframe/vite-plugin @editframe/dev-server react react-dom typescript @types/react @types/react-dom vite @vitejs/plugin-react tailwindcss @tailwindcss/vite vitest
|
|
699
|
-
- id: html
|
|
700
|
-
firstWorkspace:
|
|
701
|
-
path: compositions/my-first-video
|
|
702
|
-
files:
|
|
703
|
-
- path: package.json
|
|
704
|
-
content: |
|
|
705
|
-
{
|
|
706
|
-
"name": "my-first-video",
|
|
707
|
-
"version": "0.1.0",
|
|
708
|
-
"private": true,
|
|
709
|
-
"type": "module",
|
|
710
|
-
"scripts": {
|
|
711
|
-
"preview": "editframe preview",
|
|
712
|
-
"render": "editframe render -o dist/my-first-video.mp4",
|
|
713
|
-
"check": "editframe check"
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
- path: index.html
|
|
717
|
-
content: |
|
|
718
|
-
<!--
|
|
719
|
-
Editframe HTML composition — my-first-video
|
|
720
|
-
This file declares a video composition using Editframe web components.
|
|
721
|
-
Run `editframe preview` to preview, `editframe render` to produce output.
|
|
722
|
-
-->
|
|
723
|
-
<ef-timeline duration="10s">
|
|
724
|
-
<ef-timegroup duration="10s" mode="contain" style="width:1920px;height:1080px;background:#000;display:flex;align-items:center;justify-content:center;">
|
|
725
|
-
<ef-text duration="5s" style="color:white;font-size:48px;text-align:center;">Hello, Editframe!</ef-text>
|
|
726
|
-
</ef-timegroup>
|
|
727
|
-
</ef-timeline>
|
|
728
|
-
install:
|
|
729
|
-
- pnpm add -D vitest
|