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
|
@@ -1,92 +1,90 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: frameyard
|
|
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.
|
|
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, search or install project fonts, 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.10.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Frameyard
|
|
8
8
|
|
|
9
|
-
Use
|
|
9
|
+
Use the public project format and CLI. Do not add an agent-only runtime, a hosted service, or direct calls to `qwen-tts`, `sherpa-onnx-offline-tts`, or `whisper-cli`.
|
|
10
10
|
|
|
11
|
-
## First
|
|
11
|
+
## First moves
|
|
12
12
|
|
|
13
|
-
1. Find
|
|
14
|
-
2.
|
|
15
|
-
3.
|
|
16
|
-
4. If
|
|
13
|
+
1. Find `fy.config.js` from the working directory upward, or run `npx frameyard init <directory>`.
|
|
14
|
+
2. Run `npx frameyard context --json`; read `format`, `compositions`, `timelines`, `fonts`, the target, its props, and its controller before editing.
|
|
15
|
+
3. Confirm that speech or transcription is requested or explicitly approved before producing assets.
|
|
16
|
+
4. If context is small, read [quick reference](references/quick-reference.md) first. Router: static/card → quick reference; timelines → [timelines](references/timelines.md); fonts → [fonts](references/fonts.md); captions → [subtitles and karaoke](references/subtitles-karaoke.md); failures → [diagnostics](references/diagnostics.md) and [failure recovery](references/failure-recovery.md); Canvas/WebGL → [controllers](references/controllers.md).
|
|
17
17
|
|
|
18
18
|
## Workflow
|
|
19
19
|
|
|
20
|
-
1. **Author
|
|
21
|
-
2. **Assemble
|
|
22
|
-
3. **Provide props** as JSON. Composition props merge over defaults; timeline
|
|
23
|
-
4. **
|
|
24
|
-
5. **
|
|
25
|
-
6. **Inspect**
|
|
26
|
-
7. **Render** with `npx frameyard render <id> [--out <file>] [--props <file>] --json`.
|
|
27
|
-
8. **Report** the project
|
|
20
|
+
1. **Author** one `<fy-composition>` fragment. Props, the controller, import maps, and styles are top-level siblings; never add a document shell. Omit `duration-in-frames` and `fps` only for a static composition, which renders to PNG and cannot use animation/media/transcripts. Use a controller only for Canvas, WebGL, karaoke, or per-frame math. Read [deterministic authoring](references/deterministic-authoring.md).
|
|
21
|
+
2. **Assemble** a version-1 timeline with explicit tracks and clips. Sources are composition IDs, nested timeline IDs, or local media. `start`, `sourceIn`, and `duration` are integer frames; no remote URLs. Read [timelines](references/timelines.md).
|
|
22
|
+
3. **Provide props** as JSON. Composition props merge over defaults; timeline overrides are keyed by composition clip ID. Use `validateProps()` for domain rules.
|
|
23
|
+
4. **Lint** with `npx frameyard lint <id> --json`. It validates the document/timeline, resolves references, projects every timeline scene/keyframe channel, scans controller determinism, and probes media metadata without Chromium. It is the edit loop, not the full gate.
|
|
24
|
+
5. **Check** with `npx frameyard check <id> --json`. Every animated frame is evaluated, or a static composition once. Read `diagnostics` and `kind`; treat warnings as actionable.
|
|
25
|
+
6. **Inspect** with `npx frameyard still <id> --frame <spec> --out <path> --json`. A spec is a frame, an inclusive range, or a comma-separated list. The policy allows at most 32 unique frames in `full` (4 in `authoring`, 1 in `light`). One frame uses a `.png` file; multiple frames use a directory. Use `npx frameyard inspect`/`compare` for text facts, and view the PNGs when the host supports it; if not, say so.
|
|
26
|
+
7. **Render** with `npx frameyard render <id> [--out <file>] [--props <file>] --json`. Composition renders accept `--concurrency`; timeline renders accept `--workers` and `--scratch`; `--container`, `--codec`, and `--encoder` select delivery. A static composition rejects those four flags. If the policy blocks render, use `--dry-run` and hand off the command; do not claim a render.
|
|
27
|
+
8. **Report** the project, entries, controller, props, generated assets, validation, diagnostics, still paths and inspection status, video path, and failures. Never claim success when `check` fails, a still is missing, a render exits nonzero, or inspection was skipped.
|
|
28
|
+
|
|
29
|
+
## Authoring contract
|
|
30
|
+
|
|
31
|
+
- A composition is a partial fragment with one top-level `<fy-composition>` and a top-level `data-fy="props"` block. Do not use `data-fy-host` or `data-fy="format"`. `width`, `height`, and `fps` inherit the project `format`; declare both dimensions together.
|
|
32
|
+
- A static composition has neither `duration-in-frames` nor `fps`; it runs `mount()`/`validateProps()`, not `update()`, and has no frame-state variables. Declaring `fps` without `duration-in-frames` is an error. A static timeline clip needs an explicit `duration` and `sourceIn: 0`.
|
|
33
|
+
- Bindings: `data-fy-text`, `data-fy-attr-*`, `data-fy-class-*`, `data-fy-if`, and `<template data-fy-repeat>`. Primitive top-level props are also `--fy-prop-<name>` CSS variables. Bindings apply once, before `mount`.
|
|
34
|
+
- CSS/WAAPI animations are seeked from the frame clock. `data-fy-anim-start`, `data-fy-anim-duration`, and `data-fy-anim-easing` schedule one animation in frames. Frame state: `--fy-frame`, `--fy-time`, `--fy-progress`, `--fy-fps`, `--fy-duration`, `--fy-duration-frames`.
|
|
35
|
+
- Exactly one optional controller, inline or external, is the only executable code. It may export `mount`, `update`, `dispose`, and `validateProps`; at least one is required. Other scripts and `on*` handlers are rejected.
|
|
36
|
+
- Media and transcript `src` values resolve from the project root; controller `src` and ordinary HTML URLs resolve from the composition URL. Embedded media uses `data-fy-start` (frames), `data-fy-trim` (seconds), optional `data-fy-duration` (seconds), and `data-fy-volume` (0-1). Transcripts use `<fy-transcript src words>`.
|
|
37
|
+
- An optional top-level import map may add bare specifiers for controllers; `frameyard` and `frameyard/` are reserved.
|
|
38
|
+
|
|
39
|
+
## Determinism
|
|
40
|
+
|
|
41
|
+
- A composition frame depends only on its integer `frame`, immutable `props`, and assets loaded before frame production. A timeline frame derives from its active clips.
|
|
42
|
+
- Use `random("stable:key")` from `frameyard/runtime`; never `Math.random()`. Never use `Date.now()`, `performance.now()`, timers, independent `requestAnimationFrame` loops, or CSS transitions as animation inputs.
|
|
43
|
+
- Load remote data, fonts, and images before frame production. Network is blocked during capture. Prefer project fonts installed with `npx frameyard font add`; remote font URLs produce `FONT_REMOTE` and are not reproducible. Keep `update({ frame, props })` synchronous and seek-safe; do not carry state between frames.
|
|
44
|
+
- Let the runtime seek embedded video; do not play, pause, or set `currentTime` in `update()`. Redraw WebGL from the current frame; production pins software WebGL.
|
|
45
|
+
- Do not run model inference inside `mount()`, `update()`, or frame production.
|
|
28
46
|
|
|
29
|
-
|
|
47
|
+
## Resource policy
|
|
30
48
|
|
|
31
|
-
|
|
49
|
+
`fy.policy.json` is read from the working directory upward, independent of `fy.config.js`; `FRAMEYARD_POLICY` overrides it.
|
|
32
50
|
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
- `
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
- For a timeline, `--props` is a JSON object keyed by top-level composition clip ID. For a composition, it is the props object.
|
|
41
|
-
- Use `npx frameyard doctor --json` before reporting an environment problem. It inspects without repairing or downloading.
|
|
42
|
-
- Read [JSON results](references/json-results.md) for result and error shapes, and [failure recovery](references/failure-recovery.md) for actionable error codes.
|
|
43
|
-
|
|
44
|
-
## Authoring rules
|
|
45
|
-
|
|
46
|
-
- A composition is a partial HTML fragment with one top-level `<fy-composition>` root and a top-level `data-fy="props"` default-props block. Props, the controller, and import maps are top-level siblings of the root; top-level `<style>` and `<link rel="stylesheet">` elements are hoisted into the generated head. Do not use the reserved `data-fy-host` attribute or `data-fy="format"` block. `width`, `height`, and `fps` are optional and inherit from the project `format`; declare them to override it.
|
|
47
|
-
- A composition with neither `duration-in-frames` nor `fps` is static. It is captured once and renders to PNG. It may use `<img>`, CSS, SVG, and canvas, but not `data-fy-anim-*`, `<video>`, `<audio>`, or `<fy-transcript>`. Its controller runs `mount()` and `validateProps()`; `update()` is not called and frame-state variables are not set. CSS animations are paused at time 0. Declaring `fps` without `duration-in-frames` is an error. A static composition clip in a timeline needs an explicit `duration` and `sourceIn: 0`.
|
|
48
|
-
- 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.
|
|
49
|
-
- 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.
|
|
50
|
-
- 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.
|
|
51
|
-
- Frame-state variables on `:root`: `--fy-frame`, `--fy-time`, `--fy-progress`, `--fy-fps`, `--fy-duration`, and `--fy-duration-frames`.
|
|
52
|
-
- A composition has exactly one optional controller, inline or external. It may export `mount`, `update`, `dispose`, and `validateProps`; at least one is required. Use inline for small controllers; extract to an external module for Canvas 2D, WebGL, karaoke, reuse, tests, or a large controller.
|
|
53
|
-
- The controller is the only executable code. Other executable scripts and `on*` handlers are rejected.
|
|
54
|
-
- Media and transcript `src` values resolve from the project root. The controller `src` and ordinary HTML asset URLs resolve relative to the composition URL.
|
|
55
|
-
- Embedded video/audio use `data-fy-start` (frames), `data-fy-trim` (seconds), optional `data-fy-duration` (seconds), and `data-fy-volume` (0-1). Transcripts use `<fy-transcript src words>`.
|
|
56
|
-
- An optional top-level `<script type="importmap">` may add bare specifiers for controllers. Frameyard hoists it before the host module; `frameyard` and `frameyard/` specifiers are reserved.
|
|
57
|
-
|
|
58
|
-
## Determinism guardrails
|
|
59
|
-
|
|
60
|
-
- 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.
|
|
61
|
-
- Use `random("stable:key")` from `frameyard/runtime`; never use `Math.random()`.
|
|
62
|
-
- Never use `Date.now()`, `performance.now()`, timers, independent `requestAnimationFrame` loops, or CSS transitions as animation inputs.
|
|
63
|
-
- Finish remote data, fonts, and image loading during the loading phase. Network requests are blocked during frame capture.
|
|
64
|
-
- Keep `update({ frame, props })` synchronous and seek-safe. Do not carry state between frames.
|
|
65
|
-
- Let the runtime seek embedded `<video>` elements. Do not play, pause, or set `currentTime` in `update()`.
|
|
66
|
-
- Redraw WebGL canvases from the current frame. Production pins software WebGL; do not depend on GPU-specific results or the previous frame's drawing buffer.
|
|
67
|
-
- Do not run model inference inside `mount()`, `update()`, or frame production.
|
|
51
|
+
- `light`: one still frame; blocks `check`, `tts`, `transcribe`, and `render`.
|
|
52
|
+
- `authoring`: four still frames and `check` at low priority; blocks `tts`, `transcribe`, and `render`.
|
|
53
|
+
- `full`: everything up to the 32-frame still cap.
|
|
54
|
+
- `lint`, `context`, `doctor`, `add`, `inspect`, `compare`, and `--dry-run` are available in every tier. `font search`, `font list`, and `font verify` are available in every tier; `font add` requires `authoring` or `full`; `font remove`, `font add --force`, and `font add --url` without `--license`/`--license-file` require `full`.
|
|
55
|
+
- When blocked, run `--dry-run`, report `deferred: true`, and give the user `commandLine`, `prerequisites`, and `blockers`. Never claim a render or asset exists.
|
|
56
|
+
- `--profile shared-cpu` caps timeline workers at 2 and composition concurrency at 1 and lowers priority; `--low-priority` lowers priority without caps. Values above a cap fail with `RESOURCE_PROFILE_CONFLICT`; do not clamp silently.
|
|
57
|
+
- `light`/`authoring` deny `--force` and `--overwrite`; ask before replacing an output. Do not edit the policy file, host configuration, or skill source. The policy is a guardrail, not a security boundary.
|
|
68
58
|
|
|
69
|
-
##
|
|
59
|
+
## Command rules
|
|
60
|
+
|
|
61
|
+
- Use `--json` for non-interactive commands; parse stdout, keep stderr for progress/diagnostics. Use `--project <dir>` outside the project root.
|
|
62
|
+
- Outputs default to `out/<id>.<ext>` for video and `out/<id>.png` for static compositions; explicit `--out` is relative to the current directory. Replace outputs only with approval.
|
|
63
|
+
- For a timeline, `--props` is a map keyed by top-level composition clip ID; for a composition, it is the props object. Use `--concurrency` for animated compositions, `--workers`/`--scratch` for timelines. `--container <mp4|mov|mkv|webm>`, `--codec <h264|hevc|vp9>`, and `--encoder <auto|software|nvenc|qsv>` select delivery; the output extension must match the container.
|
|
64
|
+
- Produce speech and transcripts only with `npx frameyard tts` and `npx frameyard transcribe`, never upstream executables; read [audio assets](references/audio-assets.md). Do not overwrite generated assets without approval.
|
|
65
|
+
- Use `npx frameyard add --list --json` and `add composition|timeline <id> --template <name> --json` for templates; merge the returned registration object into `fy.config.js`. `add` never edits the config or overwrites an entry.
|
|
66
|
+
- Use `npx frameyard font search <query> --json` to find a Fontsource family, then `npx frameyard font add <fontsource-id> --json` to pin and vendor it. Use `font add --file` for a local or brand font, or `font add --url` for an explicit HTTPS source. Reference the returned CSS family. `font list`, `font verify`, and `font remove` manage the manifest; never edit `fy.fonts.json` hashes by hand. Read [fonts](references/fonts.md).
|
|
67
|
+
- Use `npx frameyard doctor --json` before reporting an environment problem. Read [JSON results](references/json-results.md) and [failure recovery](references/failure-recovery.md).
|
|
70
68
|
|
|
71
|
-
|
|
69
|
+
## Diagnostics and karaoke
|
|
72
70
|
|
|
73
|
-
|
|
71
|
+
`check`, `still`, and `render` run static validation before Chromium; errors stop, warnings allow frames. Diagnostics carry a stable `code`, message, and optional source location/suggestion. Treat `BINDING_PATH_MISSING`, `ANIMATION_*`, `STATIC_*`, and `DETERMINISM_WARNING` as actionable.
|
|
74
72
|
|
|
75
|
-
Use
|
|
73
|
+
Use transcripts from `npx frameyard transcribe`. Convert source-relative milliseconds to composition frames with the clip start, trim start, and `fps`. Karaoke requires measured `words`; never interpolate segment timings.
|
|
76
74
|
|
|
77
75
|
## CLI invocation
|
|
78
76
|
|
|
79
|
-
Use `npx frameyard`
|
|
77
|
+
Use `npx frameyard` or an installed binary. `scripts/frameyard.mjs` is the fallback; set `FRAMEYARD_CLI` when the command is unavailable. Pin the CLI with `npx frameyard@<version>` when it differs from `frameyard-api-version`.
|
|
80
78
|
|
|
81
79
|
```sh
|
|
82
80
|
npx frameyard check <id> --json
|
|
83
|
-
node scripts/frameyard.mjs check <id> --json
|
|
81
|
+
node scripts/frameyard.mjs check <id> --json
|
|
84
82
|
```
|
|
85
83
|
|
|
86
|
-
When the CLI version differs from `frameyard-api-version`, pin it with `npx frameyard@<version>`.
|
|
87
|
-
|
|
88
84
|
## References
|
|
89
85
|
|
|
86
|
+
- [Quick reference](references/quick-reference.md)
|
|
87
|
+
- [Fonts](references/fonts.md)
|
|
90
88
|
- [Timelines](references/timelines.md)
|
|
91
89
|
- [Deterministic HTML authoring](references/deterministic-authoring.md)
|
|
92
90
|
- [Bindings](references/bindings.md)
|
|
@@ -34,7 +34,9 @@ Declare required transcripts with:
|
|
|
34
34
|
|
|
35
35
|
## Local speech synthesis
|
|
36
36
|
|
|
37
|
-
Use Frameyard
|
|
37
|
+
Use Frameyard instead of calling the upstream executables directly:
|
|
38
|
+
|
|
39
|
+
### qwentts.cpp (default)
|
|
38
40
|
|
|
39
41
|
```sh
|
|
40
42
|
npx frameyard tts \
|
|
@@ -47,6 +49,7 @@ npx frameyard tts \
|
|
|
47
49
|
--json
|
|
48
50
|
```
|
|
49
51
|
|
|
52
|
+
- `--engine qwen` is the default and may be omitted.
|
|
50
53
|
- CustomVoice only: `--speaker` is required.
|
|
51
54
|
- Text comes from `--text-file` or stdin.
|
|
52
55
|
- `--style` maps to the upstream style instruction.
|
|
@@ -58,6 +61,28 @@ npx frameyard tts \
|
|
|
58
61
|
- Executable resolution: `--executable`, then `FRAMEYARD_QWEN_TTS`, then `PATH`.
|
|
59
62
|
- Frameyard does not build qwentts.cpp, download models, or redistribute weights.
|
|
60
63
|
|
|
64
|
+
### Supertonic 3
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
npx frameyard tts \
|
|
68
|
+
--engine supertonic \
|
|
69
|
+
--speaker 0 \
|
|
70
|
+
--model-dir models/sherpa-onnx-supertonic-3-tts-int8-2026-05-11 \
|
|
71
|
+
--language es \
|
|
72
|
+
--text-file script.txt \
|
|
73
|
+
--out assets/voice-es.wav \
|
|
74
|
+
--json
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
- The engine covers 31 languages and requires one of their codes (`es`, `en`, `fr`, `de`, ...); `--language` is mandatory.
|
|
78
|
+
- `--speaker` is the numeric `--sid`, not a name.
|
|
79
|
+
- `--model-dir` points at the extracted Supertonic 3 archive with its seven files; do not pass individual files.
|
|
80
|
+
- `--style` and `--device` are qwen-only. Put Supertonic expression tags such as `<laugh>`, `<breath>`, and `<sigh>` in the text.
|
|
81
|
+
- The engine writes its native WAV; Frameyard normalizes it to 24 kHz mono and records `sourceSampleRate` and `sourceChannels` in the manifest.
|
|
82
|
+
- `--force` is required to replace an existing asset. Do not pass it without user approval.
|
|
83
|
+
- Executable resolution: `--executable`, then `FRAMEYARD_SHERPA_ONNX_TTS`, then `PATH`.
|
|
84
|
+
- Frameyard does not build sherpa-onnx, download the model archive, or redistribute weights. The upstream Supertonic repository is archived and its weights use the OpenRAIL-M license.
|
|
85
|
+
|
|
61
86
|
## Local transcription
|
|
62
87
|
|
|
63
88
|
```sh
|
|
@@ -77,9 +102,13 @@ npx frameyard transcribe assets/voice.wav \
|
|
|
77
102
|
- Executable resolution: `--executable`, then `FRAMEYARD_WHISPER_CLI`, then `PATH`.
|
|
78
103
|
- Frameyard does not download Whisper models or execute Whisper's generated karaoke shell script.
|
|
79
104
|
|
|
105
|
+
## Pinned upstream revisions
|
|
106
|
+
|
|
107
|
+
The wrappers target the CLI surfaces of qwentts.cpp commit `5916bf7c79dbb4e66fc059cdc507a79a23a28bb4`, sherpa-onnx commit `af2d8d81dbfa61c4cea4c568da78f3982edca005` (v2.0.1), and whisper.cpp commit `284029c07012d2000941a08d38a31ffb71f4734e`.
|
|
108
|
+
|
|
80
109
|
## Provenance manifest
|
|
81
110
|
|
|
82
|
-
Both commands update `
|
|
111
|
+
Both commands update `fy.assets.json` in the project root unless `--manifest <file>` is supplied. The manifest records:
|
|
83
112
|
|
|
84
113
|
- artifact path, content hash, and size;
|
|
85
114
|
- generator command, Frameyard version, executable, and integration revision;
|
|
@@ -93,5 +122,5 @@ Commit the manifest with the project. It explains how generated inputs were prod
|
|
|
93
122
|
- Do not regenerate or overwrite an existing generated asset without permission.
|
|
94
123
|
- Do not silently switch models, speakers, language, or text.
|
|
95
124
|
- Report the manifest path and the exact asset paths after success.
|
|
96
|
-
- `npx frameyard doctor` verifies manifest integrity, including artifact, source, model, and derived-export hashes, and optional integration executables; it does not download or repair anything. Pass `--talker-model`, `--codec-model`, or `--model` to inspect model readability and identity before generation.
|
|
125
|
+
- `npx frameyard doctor` verifies manifest integrity, including artifact, source, model, and derived-export hashes, and optional integration executables; it does not download or repair anything. Pass `--talker-model`, `--codec-model`, `--model-dir`, or `--model` to inspect model readability and identity before generation.
|
|
97
126
|
- Once assets are frozen, checking and rendering do not need the model or inference executable.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Using the old authoring model
|
|
4
4
|
|
|
5
|
-
- **JavaScript composition modules / `defineComposition`** were removed in 0.4. A composition is a `.html` fragment listed in `
|
|
5
|
+
- **JavaScript composition modules / `defineComposition`** were removed in 0.4. A composition is a `.html` fragment listed in `fy.config.js`.
|
|
6
6
|
- **`data-frameyard-*`** media attributes were removed in 0.4. Use `data-fy-*`.
|
|
7
7
|
|
|
8
8
|
## Document structure
|
|
@@ -47,8 +47,18 @@
|
|
|
47
47
|
- Importing libraries from executable `<script>` tags; import them from the controller instead.
|
|
48
48
|
- Using `Math.random()`, `Date.now()`, `performance.now()`, timers, independent `requestAnimationFrame` loops, or CSS transitions.
|
|
49
49
|
|
|
50
|
-
##
|
|
50
|
+
## Fonts
|
|
51
|
+
|
|
52
|
+
- Using a remote Google Fonts `<link>` in a composition that must render reproducibly. Search Fontsource with `font search` and vendor the file; remote fonts produce `FONT_REMOTE`.
|
|
53
|
+
- Installing a Fontsource variable family and then expecting static weights from the same record. Use `--static --weight 400,700` for static files, or `--force` to replace the family.
|
|
54
|
+
- Passing `--license` with a Fontsource id; the provider supplies the license and the flag is rejected. Use `--license` only with `--file`/`--url`.
|
|
55
|
+
- Importing a font by URL without a license. It records `unknown`, warns, and requires `full`; add `--license` and `--license-file` when the license allows redistribution.
|
|
56
|
+
- Adding a `.woff2` file but forgetting the license or `--family`; `font add` needs the family name and warns when the license is unknown.
|
|
57
|
+
- Hand-editing a path or hash in `fy.fonts.json`. Reinstall the face; `check`, `still`, and `render` verify hashes before Chromium.
|
|
58
|
+
- Installing only weight 400 and using `font-weight: 700`, which asks the browser to synthesize a bold face. Add the weight or a variable range.
|
|
59
|
+
- Moving or renaming a vendored font file without updating the manifest; `font verify` reports `FONT_FILE_MISSING`.
|
|
51
60
|
|
|
61
|
+
## Media and transcripts
|
|
52
62
|
- Using remote, live, or unseekable media sources.
|
|
53
63
|
- Using `<source>` or `<track>` children; use a local `src` and `<fy-transcript>`.
|
|
54
64
|
- Adding or changing media in `update()`; prepare it in the fragment or in `mount()`.
|
|
@@ -56,6 +66,15 @@
|
|
|
56
66
|
- Assuming media/transcript `src` is document-relative. It is project-root-relative.
|
|
57
67
|
- Assuming the controller `src` is project-root-relative. It is relative to the composition URL.
|
|
58
68
|
|
|
69
|
+
## Generated audio
|
|
70
|
+
|
|
71
|
+
- Passing `--talker-model` or `--codec-model` with `--engine supertonic`, or `--model-dir` with `--engine qwen`. Flags are engine-specific.
|
|
72
|
+
- Using a speaker name with `--engine supertonic`; `--speaker` is the numeric `--sid`.
|
|
73
|
+
- Omitting `--language` with Supertonic or using a code outside its 31-language list.
|
|
74
|
+
- Expecting `--style` or `--device` to work with Supertonic; both are qwen-only. Put expression tags in the text.
|
|
75
|
+
- Pointing `--model-dir` at individual files instead of the extracted seven-file archive.
|
|
76
|
+
- Regenerating an existing speech asset without `--force` and user approval (`OUTPUT_EXISTS`).
|
|
77
|
+
|
|
59
78
|
## Timelines
|
|
60
79
|
|
|
61
80
|
- Putting `blend` on an audio clip. It is only valid on visual clips.
|
|
@@ -66,6 +85,18 @@
|
|
|
66
85
|
- Omitting `duration` on a static composition clip, or setting `sourceIn` above 0. A static source has no intrinsic duration or frames.
|
|
67
86
|
- Expecting a static composition clip to contribute audio or transcripts. It is visual-only.
|
|
68
87
|
|
|
88
|
+
## Timeline keyframes
|
|
89
|
+
|
|
90
|
+
- Treating `frame` as a timeline frame or a source frame. Keyframe frames are clip-local; `0` is the clip start boundary and `duration` is the end boundary.
|
|
91
|
+
- Expecting keyframes to extend a clip. A key past `duration` is a `TIMELINE_CLIP_OUT_OF_RANGE` error; declare a longer clip instead.
|
|
92
|
+
- Declaring the same channel as a scalar in `transform` and as a keyframe track. Keyed channels and static channels are separate; defaults do not conflict.
|
|
93
|
+
- Combining `keyframes.scale` with a `transform.width`/`height` box. Use a keyed scale without a box, or keep the box and animate `x`, `y`, `rotation`, and `opacity`.
|
|
94
|
+
- Putting keyframes on an audio clip. Only visual clips have presentation channels.
|
|
95
|
+
- Using seconds instead of integer clip-local frames, or putting keys out of order.
|
|
96
|
+
- Expecting the final key’s `easing` to do anything. Easing belongs to the interval after a key; the last key has no following interval.
|
|
97
|
+
- Expecting preview and encoded keyframe motion to match exactly. Preview uses the exact CSS curve; production approximates it in FFmpeg and may differ by a subpixel.
|
|
98
|
+
- Expecting an animated `blend` mode. Blend remains static; only `opacity` can animate the layer’s contribution to a blend.
|
|
99
|
+
|
|
69
100
|
## Static compositions
|
|
70
101
|
|
|
71
102
|
- Expecting `update()` or `--fy-*` frame-state variables to run. A static composition only runs `mount()` and prop validation; use CSS, markup, or mount-time canvas drawing.
|
|
@@ -74,6 +105,26 @@
|
|
|
74
105
|
- Passing `--container`, `--codec`, `--encoder`, or `--concurrency` to `render` for a static composition. Use a timeline image clip if the deliverable must be video.
|
|
75
106
|
- Requesting a still frame other than 0 from a static composition.
|
|
76
107
|
|
|
108
|
+
## Context, inspection, and templates
|
|
109
|
+
|
|
110
|
+
- Reading the whole repository before a small edit. Run `npx frameyard context --json` first; it returns the format, props, timelines, assets, policy, and next commands.
|
|
111
|
+
- Treating `inspect`/`compare` as a vision model. They return facts such as blank, alpha coverage, bbox, and changed pixels; they do not judge whether a design looks right.
|
|
112
|
+
- Expecting `compare` to exit 0 when the images differ. It exits 1 and still returns `ok: true` with `identical: false`.
|
|
113
|
+
- Forgetting that `add` does not edit `fy.config.js`. Merge the returned `register` object yourself, then run `lint`.
|
|
114
|
+
- Adding a composition template to a project without `format`. Composition templates inherit the project format; add `format { width, height, fps }` first.
|
|
115
|
+
- Adding the slideshow timeline before `assets/slide-1.png` through `assets/slide-3.png` exist. `check` will report the missing assets.
|
|
116
|
+
|
|
117
|
+
## Resource policy and lint
|
|
118
|
+
|
|
119
|
+
- Using `check` as the edit loop. Use `lint`; it resolves the timeline, projects every scene and keyframe channel, and scans the controller without Chromium.
|
|
120
|
+
- Treating a clean `lint` as a successful `check`. Lint does not run `validateProps` or composition runtime.
|
|
121
|
+
- Running `tts`, `transcribe`, or `render` while the workspace policy is `authoring` or `light`. Use `--dry-run` and hand off the command.
|
|
122
|
+
- Editing `fy.policy.json`, `.opencode/`, `.pi/`, or `skills/frameyard/` to bypass the policy. Those files are protected by host permissions and are not part of the authoring task.
|
|
123
|
+
- Passing `--force` or `--overwrite` outside `full`. Ask the user before replacing an output; the CLI will fail with `RESOURCE_POLICY_BLOCKED`.
|
|
124
|
+
- Requesting more still frames than the tier allows. The caps are 1 for `light`, 4 for `authoring`, and 32 for `full`; split larger ranges.
|
|
125
|
+
- Passing `--workers 8` with `--profile shared-cpu`. The profile caps workers at 2 and fails with `RESOURCE_PROFILE_CONFLICT` instead of clamping.
|
|
126
|
+
- Forgetting that the policy is discovered from the working directory upward, independent of `fy.config.js`. Running from a nested directory still finds the workspace file.
|
|
127
|
+
|
|
77
128
|
## Validation
|
|
78
129
|
|
|
79
130
|
- Replacing `check` with a single `still`.
|
|
@@ -4,7 +4,7 @@ Frameyard compositions are partial HTML fragments. The preview server wraps them
|
|
|
4
4
|
|
|
5
5
|
## Project configuration
|
|
6
6
|
|
|
7
|
-
`
|
|
7
|
+
`fy.config.js` lists every composition explicitly:
|
|
8
8
|
|
|
9
9
|
```js
|
|
10
10
|
export default {
|
|
@@ -99,6 +99,12 @@ CSS `@keyframes` and the Web Animations API are paused and seeked by the runtime
|
|
|
99
99
|
- The root exposes `--fy-frame`, `--fy-time`, `--fy-progress`, `--fy-fps`, `--fy-duration`, and `--fy-duration-frames`. Use them in `calc()` for simple computed motion and progress bars.
|
|
100
100
|
- Do not use CSS transitions. They are diagnosed as non-deterministic.
|
|
101
101
|
|
|
102
|
+
## Timeline clip keyframes
|
|
103
|
+
|
|
104
|
+
CSS `@keyframes` animate elements *inside* a composition. A timeline clip's `keyframes` animate the clip's placement in the edit: `x`, `y`, `scale`, `rotation`, and `opacity`. The two mechanisms compose: a composition may animate internally while its timeline clip also fades or moves.
|
|
105
|
+
|
|
106
|
+
Timeline keyframes use clip-local integer frames, not composition frames or seconds. Keyframe values are derived only from the integer frame and the declared easing, so they are seek-safe. Preview evaluates the exact CSS timing function; production generates per-frame FFmpeg expressions and approximates the curve. See [timelines](timelines.md) for the contract.
|
|
107
|
+
|
|
102
108
|
## Controllers
|
|
103
109
|
|
|
104
110
|
An optional controller handles Canvas 2D, WebGL, karaoke, and per-frame computation CSS cannot express.
|
|
@@ -215,17 +221,18 @@ Declare canonical transcript requirements with:
|
|
|
215
221
|
|
|
216
222
|
Remote data, images, fonts, and other assets must resolve during the loading phase before frame production. The production browser blocks network requests after loading begins. A fetch from `update()` is a bug.
|
|
217
223
|
|
|
218
|
-
Images declared in the fragment are awaited by the host.
|
|
224
|
+
Images declared in the fragment are awaited by the host. Fonts should be vendored into the project with `npx frameyard font search`/`font add <id>` or `font add --file`; the server injects the generated `@font-face` sheet and the host awaits `document.fonts.ready`. A used face that fails to load fails the capture with `FONT_LOAD_FAILED`. A remote font URL produces a `FONT_REMOTE` warning and is not reproducible. See [fonts](fonts.md).
|
|
219
225
|
|
|
220
226
|
## Validation and inspection
|
|
221
227
|
|
|
222
228
|
```sh
|
|
229
|
+
npx frameyard lint hello --json
|
|
223
230
|
npx frameyard check hello --json
|
|
224
231
|
npx frameyard still hello --frame 42 --out hello-42.png --json
|
|
225
232
|
npx frameyard still hello --frame 0,118-126 --out out/stills --json
|
|
226
233
|
```
|
|
227
234
|
|
|
228
|
-
`check` runs static validation before Chromium, then evaluates every animated frame and validates audio and declared transcripts without capturing or encoding. A static composition is inspected once. Read the structured `diagnostics` array. Do not replace `check` with a single still. Inspect several representative frames in one `still` call with a range or list; the batch seeks sequentially in one session and is limited to 32 unique frames. A static composition exposes only frame 0.
|
|
235
|
+
`lint` runs static validation, timeline resolution, scene and keyframe projection for every timeline frame, and a controller determinism scan without Chromium. Use it while editing; it does not run `validateProps` or composition runtime. `check` runs static validation before Chromium, then evaluates every animated frame and validates audio and declared transcripts without capturing or encoding. A static composition is inspected once. Read the structured `diagnostics` array. Do not replace `check` with a single still. Inspect several representative frames in one `still` call with a range or list; the batch seeks sequentially in one session and is limited to 32 unique frames in `full` (4 in `authoring`, 1 in `light`). A static composition exposes only frame 0.
|
|
229
236
|
|
|
230
237
|
## Metadata is fixed
|
|
231
238
|
|
|
@@ -35,7 +35,7 @@ Static validation covers:
|
|
|
35
35
|
}
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
`details` may include `path`, `line`, `column`, `endLine`, `endColumn`, `element`, `attribute`, `value`, `suggestion`, `frame`, and `controller`.
|
|
38
|
+
`details` may include `path`, `line`, `column`, `endLine`, `endColumn`, `element`, `attribute`, `value`, `suggestion`, `frame`, and `controller`. Timeline diagnostics may also include `timeline`, `clip`, `track`, `channel`, `keyframe`, `jsonPath`, `timelineFrame`, `sourceFrame`, `from`, `to`, and `nesting`; keyframe errors use `channel`, the zero-based `keyframe` index, and the clip-local `frame`, and transition errors use `from`/`to`.
|
|
39
39
|
|
|
40
40
|
## Common codes
|
|
41
41
|
|
|
@@ -66,14 +66,38 @@ Static validation covers:
|
|
|
66
66
|
| `ELEMENT_NOT_SUPPORTED` | error | `iframe`, `object`, `embed`, `base`, refresh/CSP meta, `source`, `track`, or declarative shadow DOM |
|
|
67
67
|
| `ELEMENT_UNKNOWN` | warning | Unknown `fy-*` element |
|
|
68
68
|
| `CONTENT_OUTSIDE_ROOT` | warning | Visual content outside `<fy-composition>` |
|
|
69
|
+
| `TIMELINE_TRANSITION_INVALID` | error | Unknown transition field, invalid clip id, or unsupported easing |
|
|
70
|
+
| `TIMELINE_TRANSITION_UNKNOWN_CLIP` | error | Transition names a missing or self clip |
|
|
71
|
+
| `TIMELINE_TRANSITION_WRONG_TRACK` | error | Transition clips are on different tracks |
|
|
72
|
+
| `TIMELINE_TRANSITION_AUDIO_TRACK` | error | A `dissolve` is declared on an audio track; use `crossfade` |
|
|
73
|
+
| `TIMELINE_TRANSITION_VIDEO_TRACK` | error | A `crossfade` is declared on a video track; use `dissolve` |
|
|
74
|
+
| `TIMELINE_TRANSITION_NOT_OVERLAPPING` | error | Declared transition has no overlap |
|
|
75
|
+
| `TIMELINE_TRANSITION_GEOMETRY` | error | Reversed order or the incoming clip ends before the outgoing clip |
|
|
76
|
+
| `TIMELINE_TRANSITION_DUPLICATE` | error | Repeated pair or a clip used twice on one side |
|
|
77
|
+
| `TIMELINE_TRANSITION_KIND` | error | Unsupported or missing transition `kind` |
|
|
69
78
|
| `MEDIA_INVALID` | error | Invalid or missing audio/video source or attributes |
|
|
70
79
|
| `TRANSCRIPT_INVALID` | error | Invalid or missing transcript source |
|
|
71
80
|
| `ASSET_NOT_FOUND` | error | Missing image, stylesheet, media, or transcript file |
|
|
81
|
+
| `FONT_MANIFEST_INVALID` | error | Invalid font manifest schema, path, face, weight, or duplicate family |
|
|
82
|
+
| `FONT_FILE_MISSING` | error | A manifest font file is missing |
|
|
83
|
+
| `FONT_FILE_INVALID` | error | Wrong extension, signature, or non-file font path |
|
|
84
|
+
| `FONT_HASH_MISMATCH` | error | A vendored font file changed size or SHA-256 |
|
|
85
|
+
| `FONT_LICENSE_MISSING` | warning | Font license type or file is unknown or absent |
|
|
86
|
+
| `FONT_LOAD_FAILED` | error | Chromium failed to load a used `@font-face` |
|
|
87
|
+
| `FONT_PROVIDER_UNAVAILABLE` | error | Fontsource API/CDN unreachable or invalid response |
|
|
88
|
+
| `FONT_DOWNLOAD_FAILED` | error | A Fontsource or URL response was not a valid font file |
|
|
89
|
+
| `FONT_REMOTE` | warning | CSS references a remote font or stylesheet |
|
|
90
|
+
| `FONT_NOT_FOUND` | error | Fontsource id or `font remove` id is not installed |
|
|
72
91
|
| `AUDIO_WARNING` | warning | Audio timeline overrun or silent embedded media |
|
|
73
92
|
| `TRANSCRIPT_WARNING` | warning | Empty transcript text or no timed segments |
|
|
74
93
|
| `DETERMINISM_WARNING` | warning | Forbidden clock/randomness, CSS transition, or runtime diagnostic |
|
|
75
94
|
| `COMPOSITION_NOT_READY` | error | `seek`/`settle` before a successful load |
|
|
76
95
|
| `COMPOSITION_DISPOSED` | error | Operation after the host was disposed |
|
|
96
|
+
| `IMAGE_INVALID` | error | An inspect/compare input is missing, unreadable, not an image, or has different dimensions in a comparison |
|
|
97
|
+
| `POLICY_INVALID` | error | `fy.policy.json` or `FRAMEYARD_POLICY` is missing a required field, has an unknown field, or uses an invalid value |
|
|
98
|
+
| `RESOURCE_POLICY_BLOCKED` | error | The active tier blocks the operation, `--force`, or `--overwrite`; use `--dry-run` or ask the user for a `full` run |
|
|
99
|
+
| `RESOURCE_POLICY_LIMIT` | error | A still request exceeds the active tier's frame cap |
|
|
100
|
+
| `RESOURCE_PROFILE_CONFLICT` | error | `--workers` or `--concurrency` exceeds the `shared-cpu` profile cap |
|
|
77
101
|
|
|
78
102
|
## Results
|
|
79
103
|
|
|
@@ -87,6 +111,12 @@ Static validation covers:
|
|
|
87
111
|
|
|
88
112
|
Failures use the same envelope with `error.code`, `error.message`, optional `error.details`, and top-level `diagnostics`.
|
|
89
113
|
|
|
114
|
+
## Lint and resource policy
|
|
115
|
+
|
|
116
|
+
`lint` returns the same diagnostic shape as `check` but does not launch Chromium. It reports static composition and timeline errors, scene and keyframe projection errors for every timeline frame, media metadata errors, and `DETERMINISM_WARNING` for controller source that calls wall-clock or randomness APIs. It does not run `validateProps` or composition runtime.
|
|
117
|
+
|
|
118
|
+
Policy failures use the normal error envelope. `RESOURCE_POLICY_BLOCKED` details include `policy`, `operation`, `allowed`, and a `suggestion`; `RESOURCE_POLICY_LIMIT` includes `limit` and `requested`; `RESOURCE_PROFILE_CONFLICT` includes `profile`, `option`, `limit`, and `requested`. Run the blocked `tts`, `transcribe`, or `render` command with `--dry-run` to get the handoff plan instead of treating the block as a render failure. `font add` requires `authoring` or `full`; `font remove` and `font add --force` require `full`.
|
|
119
|
+
|
|
90
120
|
## Working with diagnostics
|
|
91
121
|
|
|
92
122
|
1. Run `npx frameyard check <id> --json`.
|
|
@@ -7,13 +7,13 @@ Do not claim success after a nonzero exit. Fix the cause, rerun `check`, and onl
|
|
|
7
7
|
| Code | Meaning | Action |
|
|
8
8
|
| --- | --- | --- |
|
|
9
9
|
| `USAGE` | Invalid flags or missing required arguments | Read `npx frameyard <command> --help`. |
|
|
10
|
-
| `CONFIG_NOT_FOUND` | No `
|
|
11
|
-
| `CONFIG_INVALID` | Invalid config or composition entry | Fix `
|
|
12
|
-
| `COMPOSITION_NOT_FOUND` | ID is not listed | Add the entry to `
|
|
10
|
+
| `CONFIG_NOT_FOUND` | No `fy.config.js` found | Run from the project root or pass `--project`. |
|
|
11
|
+
| `CONFIG_INVALID` | Invalid config or composition entry | Fix `fy.config.js`; entries must be explicit, local, `.html`, and exist. |
|
|
12
|
+
| `COMPOSITION_NOT_FOUND` | ID is not listed | Add the entry to `fy.config.js`. |
|
|
13
13
|
| `COMPOSITION_INVALID` | Invalid root, metadata, fragment structure, or reserved attribute | Fix the composition fragment; check `details.line` and `details.attribute`. |
|
|
14
14
|
| `COMPOSITION_DOCUMENT_SHELL` | A full document shell is present | Remove `<!doctype>`, `<html>`, `<head>`, and `<body>`; keep the fragment body and top-level metadata. See the 0.8 migration guide. |
|
|
15
15
|
| `IMPORT_MAP_INVALID` | Invalid import map placement, JSON, attributes, or reserved specifier | Keep one or more top-level import maps; remove `frameyard`/`frameyard/` keys and forbidden attributes. |
|
|
16
|
-
| `COMPOSITION_FORMAT_UNRESOLVED` | The composition omits `width`/`height` and the project has no `format` | Declare `width`/`height` on `<fy-composition>`, add `format { width, height, fps }` to `
|
|
16
|
+
| `COMPOSITION_FORMAT_UNRESOLVED` | The composition omits `width`/`height` and the project has no `format` | Declare `width`/`height` on `<fy-composition>`, add `format { width, height, fps }` to `fy.config.js`, or render through a timeline. |
|
|
17
17
|
| `COMPOSITION_ERROR` | Mount, update, or browser page error | Read the frame location; remove forbidden clocks/randomness or fix the controller. |
|
|
18
18
|
| `COMPOSITION_UPDATE_ASYNC` | `update()` returned a promise | Move async work to `mount()`. |
|
|
19
19
|
| `COMPOSITION_NOT_READY` | `seek`/`settle` before a successful load | Await `load`/`ready`. |
|
|
@@ -42,17 +42,41 @@ Do not claim success after a nonzero exit. Fix the cause, rerun `check`, and onl
|
|
|
42
42
|
| `FRAME_OUT_OF_RANGE` | Frame outside `0..durationInFrames - 1` | Choose valid frames; a batch error lists every invalid frame and the valid range. |
|
|
43
43
|
| `AUDIO_INVALID` | Bad clip or unreadable/unsupported audio | Use a local project-root-relative file with a decodable audio stream. |
|
|
44
44
|
| `AUDIO_WARNING` | Audio timeline overrun or silent embedded media | Adjust timing/trim or remove the silent source. |
|
|
45
|
+
| `TIMELINE_TRANSITION_UNKNOWN_CLIP` | A transition names a missing or self clip | Use two distinct resolved clip IDs in this timeline. |
|
|
46
|
+
| `TIMELINE_TRANSITION_WRONG_TRACK` | The pair is on different tracks | Keep both clips on one video track. |
|
|
47
|
+
| `TIMELINE_TRANSITION_AUDIO_TRACK` | A `dissolve` is on an audio track | Use `"kind": "crossfade"`. |
|
|
48
|
+
| `TIMELINE_TRANSITION_VIDEO_TRACK` | A `crossfade` is on a video track | Use `"kind": "dissolve"`. |
|
|
49
|
+
| `TIMELINE_TRANSITION_NOT_OVERLAPPING` | The incoming clip starts at or after the outgoing clip ends | Move `to.start` earlier; touching endpoints are not a transition. |
|
|
50
|
+
| `TIMELINE_TRANSITION_GEOMETRY` | Reversed order or the incoming clip ends before the outgoing clip | Use `from.start < to.start < from.end < to.end`. |
|
|
51
|
+
| `TIMELINE_TRANSITION_DUPLICATE` | Repeated pair or a clip used twice on one side | Declare each pair once; a clip may chain `from → to → from`. |
|
|
52
|
+
| `TIMELINE_TRANSITION_KIND` | Unsupported or missing `kind` | Use `"kind": "dissolve"` on a video track or `"kind": "crossfade"` on an audio track. |
|
|
53
|
+
| `TIMELINE_TRANSITION_INVALID` | Unknown transition field, invalid id, or unsupported easing | Fix the entry; easing uses the keyframe timing-function vocabulary. |
|
|
45
54
|
| `MEDIA_INVALID` | Invalid media source or attributes | Use local, seekable files and valid `data-fy-*` values. |
|
|
46
55
|
| `MEDIA_SEEK_FAILED` | Embedded video did not reach the requested time | Re-encode with a regular keyframe interval or replace the source. |
|
|
47
56
|
| `TRANSCRIPT_INVALID` | Invalid/missing transcript declaration or JSON | Regenerate with `npx frameyard transcribe`; do not hand-edit timestamps. |
|
|
48
57
|
| `TRANSCRIPT_WORDS_UNAVAILABLE` | Karaoke requested without measured word timings | Re-run transcription with `--words` or remove `words`. |
|
|
49
58
|
| `TRANSCRIPT_WARNING` | Empty transcript text or no timed segments | Regenerate the transcript. |
|
|
50
59
|
| `ASSET_NOT_FOUND` | Required file is missing or unreadable | Restore the asset, model, or text file. |
|
|
60
|
+
| `FONT_MANIFEST_INVALID` | Invalid or unknown manifest field, version, id, family, path, face, or weight | Fix `fy.fonts.json`; reinstall with `npx frameyard font add --file` instead of hand-editing paths. |
|
|
61
|
+
| `FONT_FILE_MISSING` | A font file in the manifest does not exist | Restore the file or remove the face/family. |
|
|
62
|
+
| `FONT_FILE_INVALID` | Wrong extension, signature, or non-file path | Reinstall the font from a valid WOFF2/WOFF/TTF/OTF source. |
|
|
63
|
+
| `FONT_HASH_MISMATCH` | A vendored font file changed size or hash | Restore the file, or reinstall with `npx frameyard font add --force` after approval. |
|
|
64
|
+
| `FONT_LICENSE_MISSING` | License type/file is unknown or absent | Add `--license`/`--license-file`; do not redistribute the project until resolved. |
|
|
65
|
+
| `FONT_LOAD_FAILED` | Chromium could not load a used `@font-face` | Run `npx frameyard font verify`; fix the manifest or file. |
|
|
66
|
+
| `FONT_PROVIDER_UNAVAILABLE` | Fontsource API/CDN is unreachable or returned an invalid response | Retry, check the network, or install the file with `font add --file`/`--url`. |
|
|
67
|
+
| `FONT_DOWNLOAD_FAILED` | A Fontsource or URL response was not a valid font file | Retry the install; verify the URL and family metadata. |
|
|
68
|
+
| `FONT_REMOTE` | CSS still references a remote font/stylesheet | Vendor the font with `font add`; remote fonts are not reproducible. |
|
|
69
|
+
| `FONT_NOT_FOUND` | Fontsource id or `font remove` id is not installed | Search with `npx frameyard font search --json`, or list installed ids. |
|
|
51
70
|
| `DETERMINISM_WARNING` | Forbidden clock/randomness, CSS transition, or runtime warning | Derive state from `frame`/`props`; use `random("stable:key")`. |
|
|
52
71
|
| `OUTPUT_EXISTS` | Output exists and replacement was not approved | Reuse it, choose another path, or pass `--overwrite`/`--force` with permission. |
|
|
72
|
+
| `IMAGE_INVALID` | An inspected or compared image is missing, unreadable, or has different dimensions | Check the paths; for `compare`, use images with the same width and height. |
|
|
73
|
+
| `POLICY_INVALID` | The workspace policy file or `FRAMEYARD_POLICY` is malformed | Read the `details.path`/`jsonPath`, fix the field, or ask the user to remove the override. |
|
|
74
|
+
| `RESOURCE_POLICY_BLOCKED` | The active tier blocks the operation, `--force`, or `--overwrite` | Use `--dry-run` for a handoff plan, or ask the user to run the heavy command with `FRAMEYARD_POLICY=full`. |
|
|
75
|
+
| `RESOURCE_POLICY_LIMIT` | The still request exceeds the tier frame cap | Split the frames into smaller calls; do not assume the 32-frame library cap is available. |
|
|
76
|
+
| `RESOURCE_PROFILE_CONFLICT` | `--workers` or `--concurrency` exceeds the `shared-cpu` cap | Lower the value or omit `--profile`; the CLI does not clamp it. |
|
|
53
77
|
| `OUTPUT_FAILED` | An output or manifest could not be written atomically | Check directory permissions and free space; rerun. |
|
|
54
|
-
| `EXECUTABLE_NOT_FOUND` | `qwen-tts` or `whisper-cli` could not be resolved | Pass `--executable`, set
|
|
55
|
-
| `TTS_FAILED` | Speech synthesis failed or produced an invalid WAV | Read stderr; verify the talker
|
|
78
|
+
| `EXECUTABLE_NOT_FOUND` | `qwen-tts`, `sherpa-onnx-offline-tts`, or `whisper-cli` could not be resolved | Pass `--executable`, set `FRAMEYARD_QWEN_TTS`, `FRAMEYARD_SHERPA_ONNX_TTS`, or `FRAMEYARD_WHISPER_CLI`, or fix `PATH`. |
|
|
79
|
+
| `TTS_FAILED` | Speech synthesis failed or produced an invalid WAV | Read stderr; verify the engine-specific models (`--talker-model`/`--codec-model` for qwen, `--model-dir` for Supertonic) and CLI compatibility. |
|
|
56
80
|
| `TRANSCRIBE_FAILED` | Normalization or `whisper-cli` failed | Verify FFmpeg, the source media, and the Whisper model. |
|
|
57
81
|
| `MANIFEST_INVALID` | Provenance manifest is corrupt or unsupported | Restore or regenerate the manifest; do not invent hashes. |
|
|
58
82
|
| `FFMPEG_FAILED` | FFmpeg is missing, lacks encoders, or failed | Run `npx frameyard doctor --json`; install FFmpeg with libx264 and AAC. |
|
|
@@ -62,6 +86,8 @@ Do not claim success after a nonzero exit. Fix the cause, rerun `check`, and onl
|
|
|
62
86
|
| `CANCELLED` | The operation was interrupted | Rerun when ready. Partial outputs are removed. |
|
|
63
87
|
| `INTERNAL` | Unexpected internal failure | Rerun with `FRAMEYARD_DEBUG=1` and report the stack. |
|
|
64
88
|
|
|
89
|
+
When a heavy command is blocked, run it once with `--dry-run`. The result contains `commandLine`, `prerequisites`, and `blockers`; report `deferred: true` and the command instead of claiming the render or asset exists. The command line already includes `FRAMEYARD_POLICY=full` when the active tier is not `full`.
|
|
90
|
+
|
|
65
91
|
## Behavior to rely on
|
|
66
92
|
|
|
67
93
|
- A failed render writes to a temporary file and does not publish a partial output.
|