frameyard 0.8.0 → 0.10.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/CHANGELOG.md +177 -14
- package/GLOSSARY.md +112 -2
- package/README.md +302 -265
- package/dist/assets/integrations.d.ts +8 -4
- package/dist/assets/integrations.d.ts.map +1 -1
- package/dist/assets/integrations.js +8 -4
- package/dist/assets/integrations.js.map +1 -1
- package/dist/assets/provenance.d.ts +2 -2
- package/dist/assets/provenance.d.ts.map +1 -1
- package/dist/assets/provenance.js +5 -3
- package/dist/assets/provenance.js.map +1 -1
- package/dist/assets/tts-supertonic.d.ts +28 -0
- package/dist/assets/tts-supertonic.d.ts.map +1 -0
- package/dist/assets/tts-supertonic.js +156 -0
- package/dist/assets/tts-supertonic.js.map +1 -0
- package/dist/assets/tts.d.ts +18 -6
- package/dist/assets/tts.d.ts.map +1 -1
- package/dist/assets/tts.js +219 -97
- package/dist/assets/tts.js.map +1 -1
- package/dist/cli.js +1685 -93
- package/dist/cli.js.map +1 -1
- package/dist/composition/format.d.ts +1 -1
- package/dist/composition/format.d.ts.map +1 -1
- package/dist/composition/format.js +1 -1
- package/dist/composition/format.js.map +1 -1
- package/dist/context.d.ts +82 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +277 -0
- package/dist/context.js.map +1 -0
- package/dist/contracts.d.ts +25 -0
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/doctor.d.ts +14 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +166 -7
- package/dist/doctor.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/fonts/add.d.ts +41 -0
- package/dist/fonts/add.d.ts.map +1 -0
- package/dist/fonts/add.js +259 -0
- package/dist/fonts/add.js.map +1 -0
- package/dist/fonts/binary.d.ts +4 -0
- package/dist/fonts/binary.d.ts.map +1 -0
- package/dist/fonts/binary.js +21 -0
- package/dist/fonts/binary.js.map +1 -0
- package/dist/fonts/css.d.ts +4 -0
- package/dist/fonts/css.d.ts.map +1 -0
- package/dist/fonts/css.js +50 -0
- package/dist/fonts/css.js.map +1 -0
- package/dist/fonts/fontsource.d.ts +107 -0
- package/dist/fonts/fontsource.d.ts.map +1 -0
- package/dist/fonts/fontsource.js +565 -0
- package/dist/fonts/fontsource.js.map +1 -0
- package/dist/fonts/index.d.ts +11 -0
- package/dist/fonts/index.d.ts.map +1 -0
- package/dist/fonts/index.js +11 -0
- package/dist/fonts/index.js.map +1 -0
- package/dist/fonts/install.d.ts +24 -0
- package/dist/fonts/install.d.ts.map +1 -0
- package/dist/fonts/install.js +145 -0
- package/dist/fonts/install.js.map +1 -0
- package/dist/fonts/manifest.d.ts +21 -0
- package/dist/fonts/manifest.d.ts.map +1 -0
- package/dist/fonts/manifest.js +96 -0
- package/dist/fonts/manifest.js.map +1 -0
- package/dist/fonts/options.d.ts +9 -0
- package/dist/fonts/options.d.ts.map +1 -0
- package/dist/fonts/options.js +66 -0
- package/dist/fonts/options.js.map +1 -0
- package/dist/fonts/schema.d.ts +4 -0
- package/dist/fonts/schema.d.ts.map +1 -0
- package/dist/fonts/schema.js +316 -0
- package/dist/fonts/schema.js.map +1 -0
- package/dist/fonts/summary.d.ts +16 -0
- package/dist/fonts/summary.d.ts.map +1 -0
- package/dist/fonts/summary.js +22 -0
- package/dist/fonts/summary.js.map +1 -0
- package/dist/fonts/types.d.ts +56 -0
- package/dist/fonts/types.d.ts.map +1 -0
- package/dist/fonts/types.js +8 -0
- package/dist/fonts/types.js.map +1 -0
- package/dist/fonts/verify.d.ts +33 -0
- package/dist/fonts/verify.d.ts.map +1 -0
- package/dist/fonts/verify.js +200 -0
- package/dist/fonts/verify.js.map +1 -0
- package/dist/html/validate.d.ts.map +1 -1
- package/dist/html/validate.js +59 -0
- package/dist/html/validate.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +61 -0
- package/dist/inspect.d.ts.map +1 -0
- package/dist/inspect.js +325 -0
- package/dist/inspect.js.map +1 -0
- package/dist/lint.d.ts +63 -0
- package/dist/lint.d.ts.map +1 -0
- package/dist/lint.js +265 -0
- package/dist/lint.js.map +1 -0
- package/dist/policy.d.ts +69 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +361 -0
- package/dist/policy.js.map +1 -0
- package/dist/preview/fonts/LICENSE-figtree.txt +93 -0
- package/dist/preview/fonts/LICENSE-space-mono.txt +93 -0
- package/dist/preview/fonts/README.md +23 -0
- package/dist/preview/fonts/figtree-latin-wght-normal.woff2 +0 -0
- package/dist/preview/fonts/space-mono-latin-400-normal.woff2 +0 -0
- package/dist/preview/fonts/space-mono-latin-700-normal.woff2 +0 -0
- package/dist/preview/main.js +5 -2
- package/dist/preview/main.js.map +1 -1
- package/dist/preview/timeline-data.d.ts +17 -69
- package/dist/preview/timeline-data.d.ts.map +1 -1
- package/dist/preview/timeline-host.d.ts.map +1 -1
- package/dist/preview/timeline-host.js +70 -45
- package/dist/preview/timeline-host.js.map +1 -1
- package/dist/preview/timeline-main.d.ts.map +1 -1
- package/dist/preview/timeline-main.js +88 -27
- package/dist/preview/timeline-main.js.map +1 -1
- package/dist/project.d.ts +10 -3
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +20 -17
- package/dist/project.js.map +1 -1
- package/dist/renderer/audio.d.ts +6 -0
- package/dist/renderer/audio.d.ts.map +1 -1
- package/dist/renderer/audio.js +3 -1
- package/dist/renderer/audio.js.map +1 -1
- package/dist/renderer/browser.d.ts.map +1 -1
- package/dist/renderer/browser.js +13 -0
- package/dist/renderer/browser.js.map +1 -1
- package/dist/renderer/concurrency.d.ts +6 -0
- package/dist/renderer/concurrency.d.ts.map +1 -0
- package/dist/renderer/concurrency.js +27 -0
- package/dist/renderer/concurrency.js.map +1 -0
- package/dist/renderer/ffmpeg.d.ts +104 -5
- package/dist/renderer/ffmpeg.d.ts.map +1 -1
- package/dist/renderer/ffmpeg.js +580 -67
- package/dist/renderer/ffmpeg.js.map +1 -1
- package/dist/renderer/index.js +1 -1
- package/dist/renderer/index.js.map +1 -1
- package/dist/renderer/keyframe-expressions.d.ts +39 -0
- package/dist/renderer/keyframe-expressions.d.ts.map +1 -0
- package/dist/renderer/keyframe-expressions.js +180 -0
- package/dist/renderer/keyframe-expressions.js.map +1 -0
- package/dist/renderer/session-pool.d.ts +28 -0
- package/dist/renderer/session-pool.d.ts.map +1 -0
- package/dist/renderer/session-pool.js +91 -0
- package/dist/renderer/session-pool.js.map +1 -0
- package/dist/renderer/timeline.d.ts +2 -0
- package/dist/renderer/timeline.d.ts.map +1 -1
- package/dist/renderer/timeline.js +600 -147
- package/dist/renderer/timeline.js.map +1 -1
- package/dist/renderer/timings.d.ts +13 -0
- package/dist/renderer/timings.d.ts.map +1 -0
- package/dist/renderer/timings.js +29 -0
- package/dist/renderer/timings.js.map +1 -0
- package/dist/runtime/host.d.ts.map +1 -1
- package/dist/runtime/host.js +4 -0
- package/dist/runtime/host.js.map +1 -1
- package/dist/scaffold.d.ts +3 -0
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/scaffold.js +48 -320
- package/dist/scaffold.js.map +1 -1
- package/dist/server/composition-html.d.ts +2 -0
- package/dist/server/composition-html.d.ts.map +1 -1
- package/dist/server/composition-html.js +7 -0
- package/dist/server/composition-html.js.map +1 -1
- package/dist/server/preview-html.d.ts.map +1 -1
- package/dist/server/preview-html.js +74 -188
- package/dist/server/preview-html.js.map +1 -1
- package/dist/server/preview-style.d.ts +11 -0
- package/dist/server/preview-style.d.ts.map +1 -0
- package/dist/server/preview-style.js +298 -0
- package/dist/server/preview-style.js.map +1 -0
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +31 -1
- package/dist/server/server.js.map +1 -1
- package/dist/server/timeline-preview-data.d.ts.map +1 -1
- package/dist/server/timeline-preview-data.js +12 -34
- package/dist/server/timeline-preview-data.js.map +1 -1
- package/dist/server/timeline-preview-html.d.ts.map +1 -1
- package/dist/server/timeline-preview-html.js +140 -148
- package/dist/server/timeline-preview-html.js.map +1 -1
- package/dist/templates.d.ts +16 -0
- package/dist/templates.d.ts.map +1 -0
- package/dist/templates.js +186 -0
- package/dist/templates.js.map +1 -0
- package/dist/timeline/easing.d.ts +36 -0
- package/dist/timeline/easing.d.ts.map +1 -0
- package/dist/timeline/easing.js +225 -0
- package/dist/timeline/easing.js.map +1 -0
- package/dist/timeline/keyframes.d.ts +23 -0
- package/dist/timeline/keyframes.d.ts.map +1 -0
- package/dist/timeline/keyframes.js +95 -0
- package/dist/timeline/keyframes.js.map +1 -0
- package/dist/timeline/plan.d.ts +6 -12
- package/dist/timeline/plan.d.ts.map +1 -1
- package/dist/timeline/plan.js +17 -30
- package/dist/timeline/plan.js.map +1 -1
- package/dist/timeline/resolve.d.ts +12 -0
- package/dist/timeline/resolve.d.ts.map +1 -1
- package/dist/timeline/resolve.js +105 -9
- package/dist/timeline/resolve.js.map +1 -1
- package/dist/timeline/scene.d.ts +126 -0
- package/dist/timeline/scene.d.ts.map +1 -0
- package/dist/timeline/scene.js +109 -0
- package/dist/timeline/scene.js.map +1 -0
- package/dist/timeline/schema.d.ts +31 -0
- package/dist/timeline/schema.d.ts.map +1 -1
- package/dist/timeline/schema.js +240 -9
- package/dist/timeline/schema.js.map +1 -1
- package/dist/timeline/transitions.d.ts +21 -0
- package/dist/timeline/transitions.d.ts.map +1 -0
- package/dist/timeline/transitions.js +23 -0
- package/dist/timeline/transitions.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/docs/adr/0007-make-html-documents-the-composition-unit.md +1 -1
- package/docs/adr/0010-make-timelines-the-render-unit.md +3 -2
- package/docs/adr/0012-render-timelines-with-segmented-lossless-clip-intermediates.md +1 -1
- package/docs/adr/0014-compose-timeline-clips-with-css-blend-modes.md +2 -1
- package/docs/adr/0015-inherit-composition-format-from-the-project.md +2 -2
- package/docs/adr/0016-author-compositions-as-html-fragments.md +1 -1
- package/docs/adr/0018-animate-timeline-clips-with-keyframes.md +35 -0
- package/docs/adr/0019-make-same-track-overlap-the-transition-window.md +36 -0
- package/docs/adr/0020-crossfade-audio-clips-on-audio-tracks.md +27 -0
- package/docs/adr/0021-project-timeline-scenes-once.md +32 -0
- package/docs/adr/0022-gate-heavy-operations-with-a-workspace-resource-policy.md +35 -0
- package/docs/adr/0023-pin-fonts-as-project-assets.md +98 -0
- package/docs/adr/0024-stream-composition-captures-and-skip-trivial-composites.md +86 -0
- package/docs/adr/0025-capture-timeline-layers-in-parallel-with-a-global-limit.md +67 -0
- package/docs/adr/0026-reuse-loaded-composition-sessions-across-segments.md +70 -0
- package/docs/adr/0027-encode-two-full-frame-layers-through-dual-pipes.md +76 -0
- package/docs/adr/0028-encode-full-frame-video-segments-directly.md +65 -0
- package/docs/migration-0.4.md +2 -2
- package/docs/preview-testing.md +27 -6
- package/docs/{v0.1-design.md → v0.x-design.md} +57 -32
- package/examples/blend/README.md +3 -3
- package/examples/demo/README.md +2 -2
- package/examples/intro/README.md +71 -0
- package/examples/intro/assets/fonts/figtree/LICENSE.txt +93 -0
- package/examples/intro/assets/fonts/figtree/figtree-latin-wght-normal.woff2 +0 -0
- package/examples/intro/assets/fonts/instrument-serif/LICENSE.txt +93 -0
- package/examples/intro/assets/fonts/instrument-serif/instrument-serif-latin-400-italic.woff2 +0 -0
- package/examples/intro/assets/fonts/space-mono/LICENSE.txt +93 -0
- package/examples/intro/assets/fonts/space-mono/space-mono-latin-400-normal.woff2 +0 -0
- package/examples/intro/assets/fonts/space-mono/space-mono-latin-700-normal.woff2 +0 -0
- package/examples/intro/assets/grain.png +0 -0
- package/examples/intro/assets/score.ogg +0 -0
- package/examples/intro/compositions/brand.html +412 -0
- package/examples/intro/compositions/chrome.controller.js +63 -0
- package/examples/intro/compositions/chrome.html +256 -0
- package/examples/intro/compositions/lattice.controller.js +148 -0
- package/examples/intro/compositions/lattice.html +308 -0
- package/examples/intro/compositions/opening.html +504 -0
- package/examples/intro/compositions/studio.html +930 -0
- package/examples/intro/compositions/theme.css +218 -0
- package/examples/intro/fy.config.js +13 -0
- package/examples/intro/fy.fonts.json +100 -0
- package/examples/intro/scripts/make-grain.mjs +79 -0
- package/examples/intro/scripts/make-score.mjs +702 -0
- package/examples/intro/timelines/intro.timeline.json +60 -0
- package/examples/keyframes/README.md +76 -0
- package/examples/keyframes/compositions/backdrop.html +60 -0
- package/examples/keyframes/compositions/card.html +99 -0
- package/examples/keyframes/fy.config.js +10 -0
- package/examples/keyframes/timelines/keyframes.timeline.json +60 -0
- package/examples/nested/README.md +112 -0
- package/examples/nested/assets/pulse.wav +0 -0
- package/examples/nested/compositions/backdrop.html +84 -0
- package/examples/nested/compositions/card.html +275 -0
- package/examples/nested/compositions/overlay.html +307 -0
- package/examples/nested/fy.config.js +13 -0
- package/examples/nested/scripts/make-pulse.sh +20 -0
- package/examples/nested/timelines/chapter-a.timeline.json +39 -0
- package/examples/nested/timelines/chapter-b.timeline.json +39 -0
- package/examples/nested/timelines/nested-reel.timeline.json +52 -0
- package/examples/static/README.md +2 -2
- package/examples/transitions/README.md +128 -0
- package/examples/transitions/assets/alpha.wav +0 -0
- package/examples/transitions/assets/beta.wav +0 -0
- package/examples/transitions/assets/gamma.wav +0 -0
- package/examples/transitions/compositions/alpha.html +56 -0
- package/examples/transitions/compositions/beta.html +56 -0
- package/examples/transitions/compositions/gamma.html +56 -0
- package/examples/transitions/fy.config.js +12 -0
- package/examples/transitions/timelines/audio-crossfade.timeline.json +44 -0
- package/examples/transitions/timelines/transitions.timeline.json +31 -0
- package/examples/video/README.md +4 -4
- package/examples/webgl/README.md +1 -1
- package/package.json +13 -6
- package/scaffolds/init/AGENTS.md +131 -0
- package/scaffolds/init/README.md +56 -0
- package/scaffolds/init/compositions/hello.html +79 -0
- package/scaffolds/init/fy.config.js +9 -0
- package/scaffolds/init/gitignore.template +1 -0
- package/scaffolds/init/package.json +11 -0
- package/scaffolds/init/props/hello.json +4 -0
- package/scaffolds/init/props/main.json +6 -0
- package/scaffolds/init/timelines/main.timeline.json +20 -0
- package/scaffolds/local-models/LOCAL-MODELS.md +77 -0
- package/scaffolds/local-models/fy.policy.json +5 -0
- package/scaffolds/local-models/opencode/opencode.jsonc +17 -0
- package/scaffolds/local-models/pi/APPEND_SYSTEM.md +21 -0
- package/scaffolds/local-models/pi/sandbox.json +15 -0
- package/scaffolds/local-models/pi/settings.json +15 -0
- package/skills/frameyard/README.md +1 -0
- package/skills/frameyard/SKILL.md +59 -61
- package/skills/frameyard/references/audio-assets.md +32 -3
- package/skills/frameyard/references/common-mistakes.md +53 -2
- package/skills/frameyard/references/deterministic-authoring.md +10 -3
- package/skills/frameyard/references/diagnostics.md +31 -1
- package/skills/frameyard/references/failure-recovery.md +32 -6
- package/skills/frameyard/references/fonts.md +132 -0
- package/skills/frameyard/references/json-results.md +119 -6
- package/skills/frameyard/references/quick-reference.md +222 -0
- package/skills/frameyard/references/timelines.md +97 -7
- /package/examples/blend/{video.config.js → fy.config.js} +0 -0
- /package/examples/demo/{video.config.js → fy.config.js} +0 -0
- /package/examples/static/{video.config.js → fy.config.js} +0 -0
- /package/examples/video/{video.config.js → fy.config.js} +0 -0
- /package/examples/webgl/{video.config.js → fy.config.js} +0 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"name": "Nested timelines: A full, B full, A as a group",
|
|
4
|
+
"durationInFrames": 360,
|
|
5
|
+
"background": "#05070d",
|
|
6
|
+
"tracks": [
|
|
7
|
+
{ "id": "v1", "kind": "video", "name": "Chapter A · full frame" },
|
|
8
|
+
{ "id": "v2", "kind": "video", "name": "Chapter B · full frame" },
|
|
9
|
+
{ "id": "v3", "kind": "video", "name": "Chapter A · group transform" },
|
|
10
|
+
{ "id": "v4", "kind": "video", "name": "Overlay" }
|
|
11
|
+
],
|
|
12
|
+
"clips": [
|
|
13
|
+
{
|
|
14
|
+
"id": "chapter-a-full",
|
|
15
|
+
"track": "v1",
|
|
16
|
+
"source": { "kind": "timeline", "id": "chapter-a" },
|
|
17
|
+
"start": 0,
|
|
18
|
+
"duration": 120
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "chapter-b-full",
|
|
22
|
+
"track": "v2",
|
|
23
|
+
"source": { "kind": "timeline", "id": "chapter-b" },
|
|
24
|
+
"start": 120,
|
|
25
|
+
"duration": 120
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "chapter-a-group",
|
|
29
|
+
"track": "v3",
|
|
30
|
+
"source": { "kind": "timeline", "id": "chapter-a" },
|
|
31
|
+
"start": 240,
|
|
32
|
+
"duration": 120,
|
|
33
|
+
"transform": {
|
|
34
|
+
"fit": "cover",
|
|
35
|
+
"width": 480,
|
|
36
|
+
"height": 270,
|
|
37
|
+
"x": 360,
|
|
38
|
+
"y": 180,
|
|
39
|
+
"rotation": 0,
|
|
40
|
+
"opacity": 1
|
|
41
|
+
},
|
|
42
|
+
"audio": { "muted": true }
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"id": "overlay",
|
|
46
|
+
"track": "v4",
|
|
47
|
+
"source": { "kind": "composition", "id": "overlay" },
|
|
48
|
+
"start": 0,
|
|
49
|
+
"duration": 360
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
}
|
|
@@ -7,9 +7,9 @@ timeline uses the same composition as an image layer for 90 frames.
|
|
|
7
7
|
|
|
8
8
|
```sh
|
|
9
9
|
node dist/cli.js check still --project examples/static
|
|
10
|
-
node dist/cli.js render still --project examples/static --out examples/static/out/still.png
|
|
10
|
+
FRAMEYARD_POLICY=full node dist/cli.js render still --project examples/static --out examples/static/out/still.png
|
|
11
11
|
node dist/cli.js check still-timeline --project examples/static
|
|
12
|
-
node dist/cli.js render still-timeline --project examples/static --out examples/static/out/still-timeline.mp4
|
|
12
|
+
FRAMEYARD_POLICY=full node dist/cli.js render still-timeline --project examples/static --out examples/static/out/still-timeline.mp4
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
`render still` defaults to `out/still.png`. Static compositions render to PNG;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Timeline transitions example
|
|
2
|
+
|
|
3
|
+
This project has two timelines:
|
|
4
|
+
|
|
5
|
+
- `transitions` dissolves three timed composition cards on one video track and
|
|
6
|
+
lets their contributed audio crossfade.
|
|
7
|
+
- `audio-crossfade` mutes the composition audio and places the sound directly on
|
|
8
|
+
an audio track, with a `crossfade` between two audio clips.
|
|
9
|
+
|
|
10
|
+
## Visual dissolve with contributed audio
|
|
11
|
+
|
|
12
|
+
`transitions` is a 4-second, 1280×720, 30 fps timeline:
|
|
13
|
+
|
|
14
|
+
- **alpha** `0..60` — the A-major card and the outgoing clip. It keeps rendering
|
|
15
|
+
for the whole overlap, including after `beta` starts.
|
|
16
|
+
- **beta** `45..105` — the C#-major card and the incoming clip. Its first 15
|
|
17
|
+
frames are the `alpha → beta` dissolve, then it runs alone, then it hands off
|
|
18
|
+
to `gamma`.
|
|
19
|
+
- **gamma** `90..120` — the E-major card and the second incoming clip.
|
|
20
|
+
|
|
21
|
+
Each composition is a timed HTML fragment (`duration-in-frames`) with one
|
|
22
|
+
`<audio>` element. The audio belongs to the visual clip, so the dissolve
|
|
23
|
+
crossfades it automatically.
|
|
24
|
+
|
|
25
|
+
## Audio clips on the timeline
|
|
26
|
+
|
|
27
|
+
`audio-crossfade` renders the same alpha and beta cards for 3.5 seconds, but
|
|
28
|
+
mutes their composition audio with `audio.muted: true` and puts the sound on
|
|
29
|
+
`a1` instead:
|
|
30
|
+
|
|
31
|
+
- **chord-alpha** `0..60` — `assets/alpha.wav`, the outgoing chord.
|
|
32
|
+
- **chord-beta** `45..105` — `assets/beta.wav`, the incoming chord.
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
"transitions": [
|
|
36
|
+
{ "from": "alpha", "to": "beta", "kind": "dissolve", "easing": "ease-in-out" },
|
|
37
|
+
{ "from": "chord-alpha", "to": "chord-beta", "kind": "crossfade", "easing": "linear" }
|
|
38
|
+
]
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The visual dissolve and the audio crossfade share the same 15-frame overlap, so
|
|
42
|
+
the picture and the sound hand off together. `crossfade` is the audio-track
|
|
43
|
+
kind: it applies an equal-power gain ramp and changes no pixels.
|
|
44
|
+
|
|
45
|
+
## Transition contract
|
|
46
|
+
|
|
47
|
+
`transitions` is a timeline-level array of `{ "from", "to", "kind", "easing" }`:
|
|
48
|
+
|
|
49
|
+
- `from` and `to` are clip IDs on the **same track**. A transition never spans
|
|
50
|
+
tracks.
|
|
51
|
+
- `kind` is `dissolve` on a video track or `crossfade` on an audio track. A
|
|
52
|
+
dissolve fades the incoming picture in and also crossfades the audio the two
|
|
53
|
+
clips contribute; a crossfade only ramps audio gain. Using the wrong kind for
|
|
54
|
+
the track is an error.
|
|
55
|
+
- `easing` defaults to `linear` and accepts the same CSS timing-function
|
|
56
|
+
vocabulary as clip keyframes.
|
|
57
|
+
- The transition window is the overlap: `to.start .. from.end`. There is no
|
|
58
|
+
`duration` field; move the incoming clip earlier to make the transition
|
|
59
|
+
longer.
|
|
60
|
+
- The geometry must be `from.start < to.start < from.end < to.end`. The incoming
|
|
61
|
+
clip must begin inside the outgoing clip and outlast it.
|
|
62
|
+
- A clip may be the `to` of one transition and the `from` of the next, so
|
|
63
|
+
transitions chain. A clip cannot participate twice on the same side.
|
|
64
|
+
- Overlap without a declared transition is still an error. Touching endpoints
|
|
65
|
+
are not a transition.
|
|
66
|
+
|
|
67
|
+
During a dissolve the incoming clip composites above the outgoing clip with
|
|
68
|
+
alpha `clipOpacity × eased progress`. Clip opacity may be static or keyframed
|
|
69
|
+
and multiplies the transition.
|
|
70
|
+
|
|
71
|
+
## Audio semantics
|
|
72
|
+
|
|
73
|
+
Both kinds use an equal-power ramp over the window: the outgoing clip's gain is
|
|
74
|
+
`sqrt(1 - t)` and the incoming clip's is `sqrt(t)`, after easing. For a
|
|
75
|
+
dissolve, the ramp applies to the audio the two visual clips contribute. For a
|
|
76
|
+
crossfade, it applies to the two audio-track clips themselves.
|
|
77
|
+
|
|
78
|
+
`audio.muted` still zeroes a side, and `audio.gain` plus the track `gain`
|
|
79
|
+
multiply the envelope. A crossfade on an audio track has no visual effect; a
|
|
80
|
+
dissolve on an audio track is rejected.
|
|
81
|
+
|
|
82
|
+
## What to look at
|
|
83
|
+
|
|
84
|
+
The first dissolve uses `ease-in-out`, so it starts and ends gently around a
|
|
85
|
+
faster middle. The second uses `linear`, so its progress tracks the frame clock
|
|
86
|
+
exactly. The read-only preview draws a hatched band over each overlap, including
|
|
87
|
+
the audio crossfade; the band is informational, not editable.
|
|
88
|
+
|
|
89
|
+
Render or preview with sound. In `transitions` the chords hand off at frames 45
|
|
90
|
+
and 90. In `audio-crossfade` the sound is on the timeline, and the A → C#
|
|
91
|
+
crossfade happens at frames 45..60 while the picture dissolves over the same
|
|
92
|
+
window.
|
|
93
|
+
|
|
94
|
+
## Files
|
|
95
|
+
|
|
96
|
+
```text
|
|
97
|
+
examples/transitions/
|
|
98
|
+
assets/alpha.wav A-major chord, 2 s
|
|
99
|
+
assets/beta.wav C#-major chord, 2 s
|
|
100
|
+
assets/gamma.wav E-major chord, 1 s
|
|
101
|
+
compositions/alpha.html outgoing timed card
|
|
102
|
+
compositions/beta.html middle timed card
|
|
103
|
+
compositions/gamma.html incoming timed card
|
|
104
|
+
timelines/transitions.timeline.json
|
|
105
|
+
timelines/audio-crossfade.timeline.json
|
|
106
|
+
fy.config.js
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Commands
|
|
110
|
+
|
|
111
|
+
From the repository root:
|
|
112
|
+
|
|
113
|
+
```sh
|
|
114
|
+
pnpm build
|
|
115
|
+
node dist/cli.js check transitions --project examples/transitions
|
|
116
|
+
node dist/cli.js check audio-crossfade --project examples/transitions
|
|
117
|
+
node dist/cli.js still transitions --frame 0,52,60,97 --project examples/transitions --out transitions-stills
|
|
118
|
+
node dist/cli.js still audio-crossfade --frame 52 --project examples/transitions --out audio-crossfade-still.png
|
|
119
|
+
FRAMEYARD_POLICY=full node dist/cli.js render transitions --project examples/transitions
|
|
120
|
+
FRAMEYARD_POLICY=full node dist/cli.js render audio-crossfade --project examples/transitions
|
|
121
|
+
node dist/cli.js dev --project examples/transitions
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`render` writes `out/<timeline-id>.mp4` inside the project by default. Frame 52
|
|
125
|
+
sits halfway into the first dissolve, frame 59 is the last frame of that overlap,
|
|
126
|
+
and frame 97 is inside the second dissolve. In `audio-crossfade`, frame 52 is
|
|
127
|
+
also inside the audio crossfade. `still` and `render` share the same composite
|
|
128
|
+
path, so a still taken inside a transition matches the rendered frame.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<style>
|
|
2
|
+
:root {
|
|
3
|
+
color-scheme: dark;
|
|
4
|
+
}
|
|
5
|
+
html,
|
|
6
|
+
body {
|
|
7
|
+
margin: 0;
|
|
8
|
+
width: 100%;
|
|
9
|
+
height: 100%;
|
|
10
|
+
background: #0b1220;
|
|
11
|
+
}
|
|
12
|
+
.card {
|
|
13
|
+
position: absolute;
|
|
14
|
+
inset: 0;
|
|
15
|
+
display: grid;
|
|
16
|
+
place-content: center;
|
|
17
|
+
gap: 20px;
|
|
18
|
+
text-align: center;
|
|
19
|
+
color: #e9f1ff;
|
|
20
|
+
font-family:
|
|
21
|
+
ui-sans-serif,
|
|
22
|
+
system-ui,
|
|
23
|
+
-apple-system,
|
|
24
|
+
"Segoe UI",
|
|
25
|
+
sans-serif;
|
|
26
|
+
background:
|
|
27
|
+
radial-gradient(circle at 50% 42%, rgba(88, 140, 255, 0.55), transparent 58%),
|
|
28
|
+
linear-gradient(160deg, #0d1c3a, #0b1220 70%);
|
|
29
|
+
}
|
|
30
|
+
.eyebrow {
|
|
31
|
+
font-size: 20px;
|
|
32
|
+
letter-spacing: 0.42em;
|
|
33
|
+
text-transform: uppercase;
|
|
34
|
+
color: #8fb3ff;
|
|
35
|
+
}
|
|
36
|
+
h1 {
|
|
37
|
+
margin: 0;
|
|
38
|
+
font-size: 148px;
|
|
39
|
+
line-height: 0.92;
|
|
40
|
+
letter-spacing: -0.04em;
|
|
41
|
+
font-weight: 800;
|
|
42
|
+
}
|
|
43
|
+
p {
|
|
44
|
+
margin: 0;
|
|
45
|
+
font-size: 28px;
|
|
46
|
+
color: #9fb0c9;
|
|
47
|
+
}
|
|
48
|
+
</style>
|
|
49
|
+
<fy-composition duration-in-frames="60">
|
|
50
|
+
<div class="card">
|
|
51
|
+
<div class="eyebrow">scene one</div>
|
|
52
|
+
<h1>ALPHA</h1>
|
|
53
|
+
<p>the outgoing clip keeps rendering</p>
|
|
54
|
+
</div>
|
|
55
|
+
<audio src="assets/alpha.wav" data-fy-start="0" data-fy-duration="2"></audio>
|
|
56
|
+
</fy-composition>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<style>
|
|
2
|
+
:root {
|
|
3
|
+
color-scheme: dark;
|
|
4
|
+
}
|
|
5
|
+
html,
|
|
6
|
+
body {
|
|
7
|
+
margin: 0;
|
|
8
|
+
width: 100%;
|
|
9
|
+
height: 100%;
|
|
10
|
+
background: #1d1206;
|
|
11
|
+
}
|
|
12
|
+
.card {
|
|
13
|
+
position: absolute;
|
|
14
|
+
inset: 0;
|
|
15
|
+
display: grid;
|
|
16
|
+
place-content: center;
|
|
17
|
+
gap: 20px;
|
|
18
|
+
text-align: center;
|
|
19
|
+
color: #fff5e6;
|
|
20
|
+
font-family:
|
|
21
|
+
ui-sans-serif,
|
|
22
|
+
system-ui,
|
|
23
|
+
-apple-system,
|
|
24
|
+
"Segoe UI",
|
|
25
|
+
sans-serif;
|
|
26
|
+
background:
|
|
27
|
+
radial-gradient(circle at 50% 42%, rgba(255, 168, 66, 0.6), transparent 58%),
|
|
28
|
+
linear-gradient(160deg, #2a1708, #1d1206 70%);
|
|
29
|
+
}
|
|
30
|
+
.eyebrow {
|
|
31
|
+
font-size: 20px;
|
|
32
|
+
letter-spacing: 0.42em;
|
|
33
|
+
text-transform: uppercase;
|
|
34
|
+
color: #ffc078;
|
|
35
|
+
}
|
|
36
|
+
h1 {
|
|
37
|
+
margin: 0;
|
|
38
|
+
font-size: 148px;
|
|
39
|
+
line-height: 0.92;
|
|
40
|
+
letter-spacing: -0.04em;
|
|
41
|
+
font-weight: 800;
|
|
42
|
+
}
|
|
43
|
+
p {
|
|
44
|
+
margin: 0;
|
|
45
|
+
font-size: 28px;
|
|
46
|
+
color: #d8b892;
|
|
47
|
+
}
|
|
48
|
+
</style>
|
|
49
|
+
<fy-composition duration-in-frames="60">
|
|
50
|
+
<div class="card">
|
|
51
|
+
<div class="eyebrow">scene two</div>
|
|
52
|
+
<h1>BETA</h1>
|
|
53
|
+
<p>the incoming clip fades in above it</p>
|
|
54
|
+
</div>
|
|
55
|
+
<audio src="assets/beta.wav" data-fy-start="0" data-fy-duration="2"></audio>
|
|
56
|
+
</fy-composition>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<style>
|
|
2
|
+
:root {
|
|
3
|
+
color-scheme: dark;
|
|
4
|
+
}
|
|
5
|
+
html,
|
|
6
|
+
body {
|
|
7
|
+
margin: 0;
|
|
8
|
+
width: 100%;
|
|
9
|
+
height: 100%;
|
|
10
|
+
background: #071a18;
|
|
11
|
+
}
|
|
12
|
+
.card {
|
|
13
|
+
position: absolute;
|
|
14
|
+
inset: 0;
|
|
15
|
+
display: grid;
|
|
16
|
+
place-content: center;
|
|
17
|
+
gap: 20px;
|
|
18
|
+
text-align: center;
|
|
19
|
+
color: #e6fffb;
|
|
20
|
+
font-family:
|
|
21
|
+
ui-sans-serif,
|
|
22
|
+
system-ui,
|
|
23
|
+
-apple-system,
|
|
24
|
+
"Segoe UI",
|
|
25
|
+
sans-serif;
|
|
26
|
+
background:
|
|
27
|
+
radial-gradient(circle at 50% 42%, rgba(64, 224, 190, 0.45), transparent 58%),
|
|
28
|
+
linear-gradient(160deg, #0a2823, #071a18 70%);
|
|
29
|
+
}
|
|
30
|
+
.eyebrow {
|
|
31
|
+
font-size: 20px;
|
|
32
|
+
letter-spacing: 0.42em;
|
|
33
|
+
text-transform: uppercase;
|
|
34
|
+
color: #7cf0d8;
|
|
35
|
+
}
|
|
36
|
+
h1 {
|
|
37
|
+
margin: 0;
|
|
38
|
+
font-size: 148px;
|
|
39
|
+
line-height: 0.92;
|
|
40
|
+
letter-spacing: -0.04em;
|
|
41
|
+
font-weight: 800;
|
|
42
|
+
}
|
|
43
|
+
p {
|
|
44
|
+
margin: 0;
|
|
45
|
+
font-size: 28px;
|
|
46
|
+
color: #9ec9c1;
|
|
47
|
+
}
|
|
48
|
+
</style>
|
|
49
|
+
<fy-composition duration-in-frames="30">
|
|
50
|
+
<div class="card">
|
|
51
|
+
<div class="eyebrow">scene three</div>
|
|
52
|
+
<h1>GAMMA</h1>
|
|
53
|
+
<p>one clip can dissolve in and out</p>
|
|
54
|
+
</div>
|
|
55
|
+
<audio src="assets/gamma.wav" data-fy-start="0" data-fy-duration="1"></audio>
|
|
56
|
+
</fy-composition>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
format: { width: 1280, height: 720, fps: 30 },
|
|
3
|
+
compositions: {
|
|
4
|
+
alpha: "./compositions/alpha.html",
|
|
5
|
+
beta: "./compositions/beta.html",
|
|
6
|
+
gamma: "./compositions/gamma.html",
|
|
7
|
+
},
|
|
8
|
+
timelines: {
|
|
9
|
+
transitions: "./timelines/transitions.timeline.json",
|
|
10
|
+
"audio-crossfade": "./timelines/audio-crossfade.timeline.json",
|
|
11
|
+
},
|
|
12
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"name": "Audio crossfade",
|
|
4
|
+
"tracks": [
|
|
5
|
+
{ "id": "v1", "kind": "video" },
|
|
6
|
+
{ "id": "a1", "kind": "audio", "gain": 1 }
|
|
7
|
+
],
|
|
8
|
+
"clips": [
|
|
9
|
+
{
|
|
10
|
+
"id": "alpha",
|
|
11
|
+
"track": "v1",
|
|
12
|
+
"source": { "kind": "composition", "id": "alpha" },
|
|
13
|
+
"start": 0,
|
|
14
|
+
"duration": 60,
|
|
15
|
+
"audio": { "muted": true }
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"id": "beta",
|
|
19
|
+
"track": "v1",
|
|
20
|
+
"source": { "kind": "composition", "id": "beta" },
|
|
21
|
+
"start": 45,
|
|
22
|
+
"duration": 60,
|
|
23
|
+
"audio": { "muted": true }
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "chord-alpha",
|
|
27
|
+
"track": "a1",
|
|
28
|
+
"source": { "kind": "audio", "src": "assets/alpha.wav" },
|
|
29
|
+
"start": 0,
|
|
30
|
+
"duration": 60
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": "chord-beta",
|
|
34
|
+
"track": "a1",
|
|
35
|
+
"source": { "kind": "audio", "src": "assets/beta.wav" },
|
|
36
|
+
"start": 45,
|
|
37
|
+
"duration": 60
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
"transitions": [
|
|
41
|
+
{ "from": "alpha", "to": "beta", "kind": "dissolve", "easing": "ease-in-out" },
|
|
42
|
+
{ "from": "chord-alpha", "to": "chord-beta", "kind": "crossfade", "easing": "linear" }
|
|
43
|
+
]
|
|
44
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"tracks": [{ "id": "main", "kind": "video" }],
|
|
4
|
+
"clips": [
|
|
5
|
+
{
|
|
6
|
+
"id": "alpha",
|
|
7
|
+
"track": "main",
|
|
8
|
+
"source": { "kind": "composition", "id": "alpha" },
|
|
9
|
+
"start": 0,
|
|
10
|
+
"duration": 60
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"id": "beta",
|
|
14
|
+
"track": "main",
|
|
15
|
+
"source": { "kind": "composition", "id": "beta" },
|
|
16
|
+
"start": 45,
|
|
17
|
+
"duration": 60
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": "gamma",
|
|
21
|
+
"track": "main",
|
|
22
|
+
"source": { "kind": "composition", "id": "gamma" },
|
|
23
|
+
"start": 90,
|
|
24
|
+
"duration": 30
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"transitions": [
|
|
28
|
+
{ "from": "alpha", "to": "beta", "kind": "dissolve", "easing": "ease-in-out" },
|
|
29
|
+
{ "from": "beta", "to": "gamma", "kind": "dissolve", "easing": "linear" }
|
|
30
|
+
]
|
|
31
|
+
}
|
package/examples/video/README.md
CHANGED
|
@@ -92,10 +92,10 @@ node dist/cli.js still video --frame 210 --project examples/video --out video-st
|
|
|
92
92
|
node dist/cli.js still video-mp4 --frame 210 --project examples/video --out video-mp4-still.png
|
|
93
93
|
node dist/cli.js still video-timeline --frame 210 --project examples/video --out video-timeline-still.png
|
|
94
94
|
node dist/cli.js still video-timeline-mp4 --frame 210 --project examples/video --out video-timeline-mp4-still.png
|
|
95
|
-
node dist/cli.js render video --project examples/video --props examples/video/props/video.json
|
|
96
|
-
node dist/cli.js render video-mp4 --project examples/video --props examples/video/props/video.json
|
|
97
|
-
node dist/cli.js render video-timeline --project examples/video --props examples/video/props/video-timeline.json
|
|
98
|
-
node dist/cli.js render video-timeline-mp4 --project examples/video --props examples/video/props/video-timeline.json
|
|
95
|
+
FRAMEYARD_POLICY=full node dist/cli.js render video --project examples/video --props examples/video/props/video.json
|
|
96
|
+
FRAMEYARD_POLICY=full node dist/cli.js render video-mp4 --project examples/video --props examples/video/props/video.json
|
|
97
|
+
FRAMEYARD_POLICY=full node dist/cli.js render video-timeline --project examples/video --props examples/video/props/video-timeline.json
|
|
98
|
+
FRAMEYARD_POLICY=full node dist/cli.js render video-timeline-mp4 --project examples/video --props examples/video/props/video-timeline.json
|
|
99
99
|
```
|
|
100
100
|
|
|
101
101
|
Start the interactive preview with:
|
package/examples/webgl/README.md
CHANGED
|
@@ -20,7 +20,7 @@ From the repository root:
|
|
|
20
20
|
pnpm build
|
|
21
21
|
node dist/cli.js check webgl --project examples/webgl
|
|
22
22
|
node dist/cli.js still webgl --frame 210 --project examples/webgl --out webgl-still.png
|
|
23
|
-
node dist/cli.js render webgl --project examples/webgl --props examples/webgl/props/webgl.json
|
|
23
|
+
FRAMEYARD_POLICY=full node dist/cli.js render webgl --project examples/webgl --props examples/webgl/props/webgl.json
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
Start the interactive preview with:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "frameyard",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Local-first, deterministic video composition framework for HTML, CSS, and JavaScript.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"dist",
|
|
44
44
|
"examples",
|
|
45
45
|
"docs",
|
|
46
|
+
"scaffolds",
|
|
46
47
|
"skills",
|
|
47
48
|
"README.md",
|
|
48
49
|
"CHANGELOG.md",
|
|
@@ -63,7 +64,7 @@
|
|
|
63
64
|
"vitest": "5.0.5"
|
|
64
65
|
},
|
|
65
66
|
"scripts": {
|
|
66
|
-
"build": "tsc -p tsconfig.json",
|
|
67
|
+
"build": "tsc -p tsconfig.json && node scripts/copy-preview-fonts.mjs",
|
|
67
68
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
68
69
|
"typecheck:test": "tsc -p tsconfig.test.json",
|
|
69
70
|
"typecheck:browser": "tsc -p tsconfig.browser.json",
|
|
@@ -71,17 +72,23 @@
|
|
|
71
72
|
"lint": "oxlint",
|
|
72
73
|
"format": "oxfmt",
|
|
73
74
|
"format:check": "oxfmt --check",
|
|
74
|
-
"check:static": "pnpm check:scripts && pnpm lint && pnpm format:check && pnpm
|
|
75
|
+
"check:static": "pnpm check:scripts && pnpm lint && pnpm format:check && pnpm build && pnpm typecheck:test && pnpm typecheck:browser",
|
|
75
76
|
"compare-stills": "node scripts/compare-stills.mjs",
|
|
76
|
-
"check": "pnpm check:static && pnpm test",
|
|
77
|
-
"check:preview": "pnpm check:static &&
|
|
77
|
+
"check": "pnpm check:static && pnpm test:built && node scripts/check-stamp.mjs record",
|
|
78
|
+
"check:preview": "pnpm check:static && vitest run test/preview-runtime.test.ts test/timeline-preview.test.ts test/timeline-preview-runtime.test.ts",
|
|
79
|
+
"check:changelog": "node scripts/check-changelog.mjs",
|
|
78
80
|
"test": "pnpm build && vitest run",
|
|
81
|
+
"test:built": "vitest run",
|
|
82
|
+
"test:integration": "pnpm build && vitest run --project integration",
|
|
83
|
+
"test:examples": "pnpm build && vitest run test/examples.test.ts",
|
|
79
84
|
"test:preview": "pnpm build && vitest run test/preview-runtime.test.ts test/timeline-preview.test.ts test/timeline-preview-runtime.test.ts",
|
|
80
|
-
"test:unit": "vitest run
|
|
85
|
+
"test:unit": "vitest run --project unit",
|
|
81
86
|
"bench": "pnpm build && node scripts/bench-render.mjs",
|
|
82
87
|
"bench:static": "pnpm build && node bench/static-composition.mjs",
|
|
88
|
+
"eval:local": "node evals/runner.mjs",
|
|
83
89
|
"set-version": "node scripts/set-version.mjs",
|
|
84
90
|
"verify-release": "node scripts/verify-release.mjs",
|
|
91
|
+
"verify-package": "node scripts/verify-package.mjs",
|
|
85
92
|
"retag-release": "node scripts/retag-release.mjs"
|
|
86
93
|
}
|
|
87
94
|
}
|