frameyard 0.4.0 → 0.6.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 +101 -0
- package/GLOSSARY.md +156 -24
- package/README.md +103 -31
- package/dist/cli.js +112 -19
- package/dist/cli.js.map +1 -1
- package/dist/composition/adapter.d.ts +30 -0
- package/dist/composition/adapter.d.ts.map +1 -0
- package/dist/composition/adapter.js +2 -0
- package/dist/composition/adapter.js.map +1 -0
- package/dist/composition/animation.d.ts +19 -0
- package/dist/composition/animation.d.ts.map +1 -0
- package/dist/composition/animation.js +55 -0
- package/dist/composition/animation.js.map +1 -0
- package/dist/composition/attributes.d.ts +7 -0
- package/dist/composition/attributes.d.ts.map +1 -0
- package/dist/composition/attributes.js +24 -0
- package/dist/composition/attributes.js.map +1 -0
- package/dist/composition/descriptor.d.ts +30 -0
- package/dist/composition/descriptor.d.ts.map +1 -0
- package/dist/composition/descriptor.js +188 -0
- package/dist/composition/descriptor.js.map +1 -0
- package/dist/composition/diagnostics.d.ts +12 -0
- package/dist/composition/diagnostics.d.ts.map +1 -0
- package/dist/composition/diagnostics.js +38 -0
- package/dist/composition/diagnostics.js.map +1 -0
- package/dist/composition/index.d.ts +12 -0
- package/dist/composition/index.d.ts.map +1 -0
- package/dist/composition/index.js +7 -0
- package/dist/composition/index.js.map +1 -0
- package/dist/composition/media.d.ts +27 -0
- package/dist/composition/media.d.ts.map +1 -0
- package/dist/composition/media.js +162 -0
- package/dist/composition/media.js.map +1 -0
- package/dist/composition/paths.d.ts +5 -0
- package/dist/composition/paths.d.ts.map +1 -0
- package/dist/composition/paths.js +16 -0
- package/dist/composition/paths.js.map +1 -0
- package/dist/composition/transcripts.d.ts +19 -0
- package/dist/composition/transcripts.d.ts.map +1 -0
- package/dist/composition/transcripts.js +57 -0
- package/dist/composition/transcripts.js.map +1 -0
- package/dist/composition/walk.d.ts +10 -0
- package/dist/composition/walk.d.ts.map +1 -0
- package/dist/composition/walk.js +32 -0
- package/dist/composition/walk.js.map +1 -0
- package/dist/contracts.d.ts +14 -0
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.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/html/document-adapter.d.ts +5 -0
- package/dist/html/document-adapter.d.ts.map +1 -0
- package/dist/html/document-adapter.js +17 -0
- package/dist/html/document-adapter.js.map +1 -0
- package/dist/html/parse.d.ts +2 -6
- package/dist/html/parse.d.ts.map +1 -1
- package/dist/html/parse.js.map +1 -1
- package/dist/html/validate.d.ts.map +1 -1
- package/dist/html/validate.js +92 -375
- package/dist/html/validate.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/preview/main.d.ts.map +1 -1
- package/dist/preview/main.js +93 -13
- package/dist/preview/main.js.map +1 -1
- package/dist/preview/timeline-data.d.ts +85 -0
- package/dist/preview/timeline-data.d.ts.map +1 -0
- package/dist/preview/timeline-data.js +2 -0
- package/dist/preview/timeline-data.js.map +1 -0
- package/dist/preview/timeline-host.d.ts +19 -0
- package/dist/preview/timeline-host.d.ts.map +1 -0
- package/dist/preview/timeline-host.js +297 -0
- package/dist/preview/timeline-host.js.map +1 -0
- package/dist/preview/timeline-main.d.ts +17 -0
- package/dist/preview/timeline-main.d.ts.map +1 -0
- package/dist/preview/timeline-main.js +364 -0
- package/dist/preview/timeline-main.js.map +1 -0
- package/dist/project.d.ts +16 -1
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +81 -26
- package/dist/project.js.map +1 -1
- package/dist/renderer/browser.d.ts +2 -0
- package/dist/renderer/browser.d.ts.map +1 -1
- package/dist/renderer/browser.js +38 -6
- package/dist/renderer/browser.js.map +1 -1
- package/dist/renderer/ffmpeg.d.ts +61 -0
- package/dist/renderer/ffmpeg.d.ts.map +1 -1
- package/dist/renderer/ffmpeg.js +198 -0
- package/dist/renderer/ffmpeg.js.map +1 -1
- package/dist/renderer/index.d.ts.map +1 -1
- package/dist/renderer/index.js +18 -7
- package/dist/renderer/index.js.map +1 -1
- package/dist/renderer/timeline.d.ts +109 -0
- package/dist/renderer/timeline.d.ts.map +1 -0
- package/dist/renderer/timeline.js +984 -0
- package/dist/renderer/timeline.js.map +1 -0
- package/dist/runtime/document-adapter.d.ts +4 -0
- package/dist/runtime/document-adapter.d.ts.map +1 -0
- package/dist/runtime/document-adapter.js +19 -0
- package/dist/runtime/document-adapter.js.map +1 -0
- package/dist/runtime/host.d.ts +2 -0
- package/dist/runtime/host.d.ts.map +1 -1
- package/dist/runtime/host.js +78 -183
- package/dist/runtime/host.js.map +1 -1
- package/dist/runtime/media.d.ts.map +1 -1
- package/dist/runtime/media.js +19 -50
- package/dist/runtime/media.js.map +1 -1
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/scaffold.js +93 -23
- package/dist/scaffold.js.map +1 -1
- package/dist/server/preview-html.d.ts.map +1 -1
- package/dist/server/preview-html.js +15 -1
- package/dist/server/preview-html.js.map +1 -1
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +39 -1
- package/dist/server/server.js.map +1 -1
- package/dist/server/timeline-preview-data.d.ts +4 -0
- package/dist/server/timeline-preview-data.d.ts.map +1 -0
- package/dist/server/timeline-preview-data.js +97 -0
- package/dist/server/timeline-preview-data.js.map +1 -0
- package/dist/server/timeline-preview-html.d.ts +4 -0
- package/dist/server/timeline-preview-html.d.ts.map +1 -0
- package/dist/server/timeline-preview-html.js +227 -0
- package/dist/server/timeline-preview-html.js.map +1 -0
- package/dist/timeline/plan.d.ts +32 -0
- package/dist/timeline/plan.d.ts.map +1 -0
- package/dist/timeline/plan.js +68 -0
- package/dist/timeline/plan.js.map +1 -0
- package/dist/timeline/resolve.d.ts +60 -0
- package/dist/timeline/resolve.d.ts.map +1 -0
- package/dist/timeline/resolve.js +523 -0
- package/dist/timeline/resolve.js.map +1 -0
- package/dist/timeline/schema.d.ts +71 -0
- package/dist/timeline/schema.d.ts.map +1 -0
- package/dist/timeline/schema.js +425 -0
- package/dist/timeline/schema.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/adr/0002-make-frame-and-props-the-only-animation-state.md +2 -0
- package/docs/adr/0005-keep-parallel-rendering-opt-in-and-frame-ordered.md +2 -0
- package/docs/adr/0006-seek-embedded-media-from-the-frame-clock.md +4 -0
- package/docs/adr/0007-make-html-documents-the-composition-unit.md +1 -1
- package/docs/adr/0008-share-the-composition-document-grammar.md +18 -0
- package/docs/adr/0009-composite-production-frames-in-software.md +37 -0
- package/docs/adr/0010-make-timelines-the-render-unit.md +24 -0
- package/docs/adr/0011-compose-timeline-frames-in-a-live-browser-host.md +22 -0
- package/docs/adr/0012-render-timelines-with-segmented-lossless-clip-intermediates.md +31 -0
- package/docs/v0.1-design.md +135 -85
- package/examples/demo/README.md +11 -4
- package/examples/demo/timelines/demo.timeline.json +12 -0
- package/examples/demo/video.config.js +4 -0
- package/examples/video/README.md +105 -0
- package/examples/video/assets/clip.mp4 +0 -0
- package/examples/video/assets/clip.webm +0 -0
- package/examples/video/compositions/video-mp4.html +362 -0
- package/examples/video/compositions/video-overlay.html +350 -0
- package/examples/video/compositions/video.html +362 -0
- package/examples/video/props/video-timeline.json +10 -0
- package/examples/video/props/video.json +8 -0
- package/examples/video/scripts/make-clip.sh +109 -0
- package/examples/video/timelines/video-timeline-mp4.timeline.json +44 -0
- package/examples/video/timelines/video-timeline.timeline.json +44 -0
- package/examples/video/video.config.js +12 -0
- package/examples/webgl/README.md +30 -0
- package/examples/webgl/compositions/webgl.controller.js +200 -0
- package/examples/webgl/compositions/webgl.html +215 -0
- package/examples/webgl/props/webgl.json +6 -0
- package/examples/webgl/video.config.js +5 -0
- package/package.json +4 -2
- package/skills/frameyard/SKILL.md +18 -13
- package/skills/frameyard/references/json-results.md +2 -2
- package/skills/frameyard/references/timelines.md +141 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "frameyard",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Local-first, deterministic video composition framework for HTML, CSS, and JavaScript.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -66,11 +66,13 @@
|
|
|
66
66
|
"build": "tsc -p tsconfig.json",
|
|
67
67
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
68
68
|
"typecheck:test": "tsc -p tsconfig.test.json",
|
|
69
|
+
"typecheck:browser": "tsc -p tsconfig.browser.json",
|
|
69
70
|
"check:scripts": "node scripts/check-scripts.mjs",
|
|
70
71
|
"lint": "oxlint",
|
|
71
72
|
"format": "oxfmt",
|
|
72
73
|
"format:check": "oxfmt --check",
|
|
73
|
-
"check:static": "pnpm check:scripts && pnpm lint && pnpm format:check && pnpm typecheck && pnpm typecheck:test",
|
|
74
|
+
"check:static": "pnpm check:scripts && pnpm lint && pnpm format:check && pnpm typecheck && pnpm typecheck:test && pnpm typecheck:browser",
|
|
75
|
+
"compare-stills": "node scripts/compare-stills.mjs",
|
|
74
76
|
"check": "pnpm check:static && pnpm test",
|
|
75
77
|
"test": "pnpm build && vitest run",
|
|
76
78
|
"test:unit": "vitest run test/contracts.test.ts test/docs.test.ts test/random.test.ts test/release.test.ts test/scaffold.test.ts test/transcript.test.ts",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: frameyard
|
|
3
|
-
description: Create, validate, inspect, and render deterministic Frameyard videos authored with HTML
|
|
4
|
-
frameyard-api-version: 0.
|
|
3
|
+
description: Create, validate, inspect, and render deterministic Frameyard videos authored with HTML compositions and JSON timelines. Use when a user asks to build or operate a Frameyard project, produce parameterized video, assemble clips into a timeline, add a local TTS or transcription asset through Frameyard's wrappers, or diagnose a Frameyard render. Do not use for general-purpose media generation outside a Frameyard project.
|
|
4
|
+
frameyard-api-version: 0.6.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Frameyard
|
|
@@ -11,19 +11,20 @@ Use Frameyard's public project format and CLI. Do not introduce an agent-only ru
|
|
|
11
11
|
## First decisions
|
|
12
12
|
|
|
13
13
|
1. Find the project: look for `video.config.js` from the working directory upward, or run `npx frameyard init <directory>` to scaffold one.
|
|
14
|
-
2. Read the
|
|
15
|
-
3. Read the composition HTML, its optional controller, and the props
|
|
14
|
+
2. Read the `format`, `compositions`, and `timelines` maps in `video.config.js`. Entries are explicit `.html` and `.json` documents; there is no directory scanning.
|
|
15
|
+
3. Read the timeline JSON, the referenced composition HTML, its optional controller, and the props files before changing behavior.
|
|
16
16
|
4. If the task needs speech or transcription, confirm that inference is requested or explicitly approved before producing assets.
|
|
17
17
|
|
|
18
18
|
## Workflow
|
|
19
19
|
|
|
20
|
-
1. **Author**
|
|
21
|
-
2. **
|
|
22
|
-
3. **
|
|
23
|
-
4. **
|
|
24
|
-
5. **
|
|
25
|
-
6. **
|
|
26
|
-
7. **
|
|
20
|
+
1. **Author compositions** as full HTML documents with one `<fy-composition>` root. Put metadata, default props, markup, CSS animation, bindings, audio, and transcripts in the document. Use an optional controller for Canvas 2D, WebGL, karaoke, or per-frame computation CSS cannot express. Read [deterministic authoring](references/deterministic-authoring.md), [bindings](references/bindings.md), and [controllers](references/controllers.md).
|
|
21
|
+
2. **Assemble the timeline** in JSON: project format, video/audio tracks, composition, media, and nested-timeline clips, source ranges, static transforms, and audio. Read [timelines](references/timelines.md).
|
|
22
|
+
3. **Provide props** as JSON. Composition props merge over defaults; timeline render props are a map keyed by composition clip ID. Validate domain-specific requirements with a controller's `validateProps()`.
|
|
23
|
+
4. **Produce optional assets** with `npx frameyard tts` and `npx frameyard transcribe`, never with the upstream executables. Read [audio assets](references/audio-assets.md). Do not overwrite or regenerate existing generated assets without permission.
|
|
24
|
+
5. **Validate** with `npx frameyard check <id> --json`, where `<id>` is a timeline or composition. Static validation runs before Chromium; then every frame is evaluated. Read the structured `diagnostics` array. Read [diagnostics](references/diagnostics.md) and [common mistakes](references/common-mistakes.md).
|
|
25
|
+
6. **Inspect** representative frames with `npx frameyard still <id> --frame <n> --out <file.png> --json`. View the PNGs when the host supports image viewing. If visual inspection is impossible, say so explicitly.
|
|
26
|
+
7. **Render** with `npx frameyard render <id> [--out <file.mp4>] [--props <file>] --json`. Composition renders accept `--concurrency <n>`; timeline renders accept `--workers <n>` and `--scratch <dir>`. The default output is `out/<id>.mp4` in the project root.
|
|
27
|
+
8. **Report** the project path, timeline and composition entries, controller, props, generated assets, provenance manifest, validation results, diagnostics, still paths and inspection status, final video path, and any failures.
|
|
27
28
|
|
|
28
29
|
Never claim success when `check` fails, a still is missing, a render exits nonzero, or visual inspection was skipped. A partial or unpublished render is not a successful result.
|
|
29
30
|
|
|
@@ -32,13 +33,16 @@ Never claim success when `check` fails, a still is missing, a render exits nonze
|
|
|
32
33
|
- Use `--json` for every non-interactive command. Parse stdout as the structured result; treat stderr as progress and diagnostics.
|
|
33
34
|
- Keep the current output files unless the user explicitly approves replacement. Use `--force` only with that approval.
|
|
34
35
|
- Use `--project <dir>` when not running from the project root.
|
|
35
|
-
-
|
|
36
|
+
- `render` writes to `out/<id>.mp4` in the project root unless `--out` is passed. Explicit `--out` paths are relative to the current working directory.
|
|
37
|
+
- Use `--concurrency <n>` only for composition renders; the default is `1` and the maximum is `16`. Use `--workers <n>` for timeline renders; the default is `8`. Timeline renders also accept `--scratch <dir>` for FFV1 intermediates.
|
|
38
|
+
- For a timeline, `--props` is a JSON object keyed by top-level composition clip ID. For a composition, it is the props object.
|
|
36
39
|
- Use `npx frameyard doctor --json` before reporting an environment problem. It inspects without repairing or downloading.
|
|
37
40
|
- Read [JSON results](references/json-results.md) for result and error shapes, and [failure recovery](references/failure-recovery.md) for actionable error codes.
|
|
38
41
|
|
|
39
42
|
## Authoring rules
|
|
40
43
|
|
|
41
44
|
- A composition is a full HTML document with one `<fy-composition width height fps duration-in-frames>` root and a `data-fy="props"` default-props block.
|
|
45
|
+
- A timeline is a versioned JSON document with explicit tracks and clips. Sources are composition IDs, nested timeline IDs, or local video/image/audio paths. Timeline frames, sourceIn, and duration are integer frames. No remote URLs.
|
|
42
46
|
- Bindings are explicit: `data-fy-text`, `data-fy-attr-*`, `data-fy-class-*`, `data-fy-if`, and `data-fy-repeat` on `<template>`. Primitive top-level props are also `--fy-prop-<name>` CSS variables.
|
|
43
47
|
- CSS `@keyframes` and the Web Animations API are seeked from the frame clock. `data-fy-anim-start`, `data-fy-anim-duration`, and `data-fy-anim-easing` schedule one animation per element in frames.
|
|
44
48
|
- Frame-state variables on `:root`: `--fy-frame`, `--fy-time`, `--fy-progress`, `--fy-fps`, `--fy-duration`, and `--fy-duration-frames`.
|
|
@@ -49,7 +53,7 @@ Never claim success when `check` fails, a still is missing, a render exits nonze
|
|
|
49
53
|
|
|
50
54
|
## Determinism guardrails
|
|
51
55
|
|
|
52
|
-
- A frame may depend only on its integer `frame`, immutable `props`, and assets loaded before frame production.
|
|
56
|
+
- A composition frame may depend only on its integer `frame`, immutable `props`, and assets loaded before frame production. A timeline frame derives from its active clips and the same composition rules.
|
|
53
57
|
- Use `random("stable:key")` from `frameyard/runtime`; never use `Math.random()`.
|
|
54
58
|
- Never use `Date.now()`, `performance.now()`, timers, independent `requestAnimationFrame` loops, or CSS transitions as animation inputs.
|
|
55
59
|
- Finish remote data, fonts, and image loading during the loading phase. Network requests are blocked during frame capture.
|
|
@@ -79,6 +83,7 @@ When the CLI version differs from `frameyard-api-version`, pin it with `npx fram
|
|
|
79
83
|
|
|
80
84
|
## References
|
|
81
85
|
|
|
86
|
+
- [Timelines](references/timelines.md)
|
|
82
87
|
- [Deterministic HTML authoring](references/deterministic-authoring.md)
|
|
83
88
|
- [Bindings](references/bindings.md)
|
|
84
89
|
- [Controllers](references/controllers.md)
|
|
@@ -118,7 +118,7 @@ Static validation diagnostics include source locations. Runtime diagnostics may
|
|
|
118
118
|
"composition": "hello",
|
|
119
119
|
"entry": "compositions/hello.html",
|
|
120
120
|
"controller": { "kind": "inline" },
|
|
121
|
-
"path": "/absolute/path/hello.mp4",
|
|
121
|
+
"path": "/absolute/path/out/hello.mp4",
|
|
122
122
|
"width": 1920,
|
|
123
123
|
"height": 1080,
|
|
124
124
|
"fps": 30,
|
|
@@ -134,7 +134,7 @@ Static validation diagnostics include source locations. Runtime diagnostics may
|
|
|
134
134
|
}
|
|
135
135
|
```
|
|
136
136
|
|
|
137
|
-
A successful render means the final path exists and was published atomically. A nonzero exit means no new partial video was published. `concurrency` is the number of browser sessions actually used; it is capped by `durationInFrames` and by the CLI maximum of 16.
|
|
137
|
+
A successful render means the final path exists and was published atomically. A nonzero exit means no new partial video was published. Without `--out`, the final path is `out/<composition-id>.mp4` inside the project root. `concurrency` is the number of browser sessions actually used; it is capped by `durationInFrames` and by the CLI maximum of 16.
|
|
138
138
|
|
|
139
139
|
## tts
|
|
140
140
|
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Timelines
|
|
2
|
+
|
|
3
|
+
A timeline is a project-local JSON document that assembles composition and media clips on tracks and renders as one video. Compositions remain reusable HTML sources; a composition clip is one placement of a composition with its own source range and props.
|
|
4
|
+
|
|
5
|
+
## Project configuration
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
export default {
|
|
9
|
+
format: { width: 1920, height: 1080, fps: 30 },
|
|
10
|
+
compositions: {
|
|
11
|
+
intro: "./compositions/intro.html",
|
|
12
|
+
},
|
|
13
|
+
timelines: {
|
|
14
|
+
main: "./timelines/main.timeline.json",
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- `format` fixes width, height, and fps for every timeline. It is required when the project lists timelines.
|
|
20
|
+
- Composition and timeline IDs share one namespace and must be unique.
|
|
21
|
+
- A composition used in a timeline must match the project fps exactly. A different size is fitted by the clip.
|
|
22
|
+
- Composition-only projects remain valid without `format` or `timelines`.
|
|
23
|
+
|
|
24
|
+
## Timeline document
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"version": 1,
|
|
29
|
+
"name": "Main edit",
|
|
30
|
+
"tracks": [
|
|
31
|
+
{ "id": "v1", "kind": "video" },
|
|
32
|
+
{ "id": "v2", "kind": "video" },
|
|
33
|
+
{ "id": "a1", "kind": "audio", "gain": 1, "muted": false }
|
|
34
|
+
],
|
|
35
|
+
"clips": [
|
|
36
|
+
{
|
|
37
|
+
"id": "intro",
|
|
38
|
+
"track": "v1",
|
|
39
|
+
"source": { "kind": "composition", "id": "intro" },
|
|
40
|
+
"start": 0,
|
|
41
|
+
"duration": 150,
|
|
42
|
+
"sourceIn": 0,
|
|
43
|
+
"props": { "title": "Hello" },
|
|
44
|
+
"transform": { "fit": "contain", "x": 0, "y": 0, "scale": 1, "rotation": 0, "opacity": 1 },
|
|
45
|
+
"audio": { "muted": false, "gain": 1 }
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "logo",
|
|
49
|
+
"track": "v2",
|
|
50
|
+
"source": { "kind": "image", "src": "assets/logo.png" },
|
|
51
|
+
"start": 30,
|
|
52
|
+
"duration": 60,
|
|
53
|
+
"transform": { "fit": "contain", "width": 480, "height": 270, "x": 640, "y": -320, "rotation": 0, "opacity": 0.9 }
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"id": "music",
|
|
57
|
+
"track": "a1",
|
|
58
|
+
"source": { "kind": "audio", "src": "assets/music.wav" },
|
|
59
|
+
"start": 0,
|
|
60
|
+
"duration": 210,
|
|
61
|
+
"audio": { "gain": 0.5 }
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
- `version` is `1`.
|
|
68
|
+
- `tracks` and `clips` are required. Track order is bottom-to-top for video and irrelevant for audio.
|
|
69
|
+
- `background` is an optional color; the final video composites over black.
|
|
70
|
+
- `durationInFrames` is optional. The duration is derived from the latest clip end unless declared; a declared duration may extend but not cut clips.
|
|
71
|
+
- Unknown fields are errors. No transitions, effects, keyframes, or track solo/visibility in 0.6.
|
|
72
|
+
|
|
73
|
+
## Sources
|
|
74
|
+
|
|
75
|
+
| `source.kind` | Fields | Notes |
|
|
76
|
+
| --- | --- | --- |
|
|
77
|
+
| `composition` | `id` | Project composition ID. The clip may set `props`. |
|
|
78
|
+
| `timeline` | `id` | Nested timeline ID. Rendered as a group. |
|
|
79
|
+
| `video` | `src` | Local MP4/WebM, project-relative. |
|
|
80
|
+
| `image` | `src` | Local PNG/JPEG/WebP. `duration` is required. |
|
|
81
|
+
| `audio` | `src` | Local WAV/MP3/M4A/AAC or a video file used only for audio. |
|
|
82
|
+
|
|
83
|
+
Media paths resolve from the project root. No remote URLs.
|
|
84
|
+
|
|
85
|
+
## Time and trims
|
|
86
|
+
|
|
87
|
+
- `start` is the timeline frame where the clip begins.
|
|
88
|
+
- `sourceIn` is the first source frame. It defaults to `0` and must be `0` for images.
|
|
89
|
+
- `duration` is the number of timeline frames. It is optional for compositions, nested timelines, video, and audio; it defaults to the rest of the source. Images require it.
|
|
90
|
+
- A clip that exceeds its source is an error.
|
|
91
|
+
- Clips on one track cannot overlap. Touching endpoints are allowed. Use separate tracks for overlays.
|
|
92
|
+
|
|
93
|
+
## Presentation
|
|
94
|
+
|
|
95
|
+
Visual clips accept a static transform:
|
|
96
|
+
|
|
97
|
+
- `fit`: `contain`, `cover`, or `stretch`.
|
|
98
|
+
- `width`, `height`: optional clip box in output pixels. Both are required together and cannot be combined with `scale`. `fit` applies inside the box, and `x`/`y` offset the box center. Without them, the project frame is the box.
|
|
99
|
+
- `x`, `y`: pixel offsets from the centered fitted result.
|
|
100
|
+
- `scale`: multiplier after fitting to the project frame; an alternative to `width`/`height`.
|
|
101
|
+
- `rotation`: degrees around the center.
|
|
102
|
+
- `opacity`: 0–1.
|
|
103
|
+
|
|
104
|
+
No keyframes or animated transforms in 0.6.
|
|
105
|
+
|
|
106
|
+
## Audio
|
|
107
|
+
|
|
108
|
+
- Composition, video, and nested-timeline clips contribute audio automatically unless `audio.muted` is true.
|
|
109
|
+
- Timeline audio clips live on audio tracks. Audio tracks support `gain` and `muted`.
|
|
110
|
+
- `audio.gain` on a clip is 0–1 and multiplies the source audio.
|
|
111
|
+
- There are no linked A/V pairs. For a J/L cut, mute the visual clip and add an audio clip with a different `sourceIn`.
|
|
112
|
+
- Audio is mixed once for the whole timeline and muxed into the final MP4.
|
|
113
|
+
|
|
114
|
+
## Nested timelines
|
|
115
|
+
|
|
116
|
+
A nested timeline clip behaves as one layer: its internal tracks are composited first, then the parent clip's opacity and transform apply to the whole group. Nested audio is included. Cycles are rejected statically. Timeline clips do not override props of compositions inside a nested timeline in 0.6.
|
|
117
|
+
|
|
118
|
+
## Commands
|
|
119
|
+
|
|
120
|
+
```sh
|
|
121
|
+
npx frameyard check main --json
|
|
122
|
+
npx frameyard still main --frame 42 --out main-42.png --json
|
|
123
|
+
npx frameyard render main --props props/main.json --workers 8 --scratch /path/to/scratch --json
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
- `--props` on a timeline is a JSON object keyed by top-level composition clip ID; values merge over the clip's `props`.
|
|
127
|
+
- `--workers` is 1–16. The default is 8.
|
|
128
|
+
- `--scratch` selects the FFV1 intermediate directory. Use a disk with enough space; intermediates are cleaned after the render.
|
|
129
|
+
- `--concurrency` is composition-only.
|
|
130
|
+
- `render` defaults to `out/<timeline-id>.mp4`.
|
|
131
|
+
- `check` evaluates every timeline frame and composition frame. `still` captures one frame through the same clip capture and composite path.
|
|
132
|
+
- `npx frameyard dev` lists timelines in the preview; the timeline view has a program monitor, read-only tracks and clips, playhead, playback, audio controls, and diagnostics.
|
|
133
|
+
|
|
134
|
+
## Determinism and performance
|
|
135
|
+
|
|
136
|
+
- A timeline frame derives only from its active clips, their composition frames, props, assets, and the render plan.
|
|
137
|
+
- Composition frames are captured with Chromium; transparent compositions preserve alpha.
|
|
138
|
+
- Timeline production captures active composition and nested-timeline clips to lossless FFV1, composites segments with FFmpeg, concatenates the ordered segments, and mixes audio once.
|
|
139
|
+
- Preview composites in Chromium; production composites with FFmpeg. Small scaling, alpha, and video color differences are expected. Parity is guaranteed at clip capture, not at final encoded pixels.
|
|
140
|
+
- Final MP4 bytes are not a reproducibility guarantee.
|
|
141
|
+
- Long timelines are segmented and bounded. The reference target and measured worst cases are documented in `docs/adr/0012-render-timelines-with-segmented-lossless-clip-intermediates.md`.
|