frameyard 0.8.0 → 0.9.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 +110 -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/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 +47 -4
- 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/ffmpeg.d.ts +42 -2
- package/dist/renderer/ffmpeg.d.ts.map +1 -1
- package/dist/renderer/ffmpeg.js +254 -22
- 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/timeline.d.ts +2 -0
- package/dist/renderer/timeline.d.ts.map +1 -1
- package/dist/renderer/timeline.js +89 -116
- package/dist/renderer/timeline.js.map +1 -1
- 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/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 +18 -2
- 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.js +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/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/migration-0.4.md +2 -2
- package/docs/preview-testing.md +19 -6
- package/docs/{v0.1-design.md → v0.x-design.md} +55 -30
- 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 +12 -5
- 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
|
@@ -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.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Fonts
|
|
2
|
+
|
|
3
|
+
Frameyard vendors font files into the project. A composition uses a font by its
|
|
4
|
+
CSS family name; the preview server injects the generated `@font-face` sheet
|
|
5
|
+
into every composition before author styles. There is no runtime font CDN
|
|
6
|
+
dependency.
|
|
7
|
+
|
|
8
|
+
## Search Fontsource
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
npx frameyard font search "space grotesk" --subset latin --json
|
|
12
|
+
npx frameyard font search --category sans-serif --variable --limit 10 --json
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Filters are `--category`, `--subset`, `--style`, `--variable`, and `--limit`
|
|
16
|
+
(1–100). Search returns `id`, `family`, `category`, `variable`, `weights`,
|
|
17
|
+
`styles`, `subsets`, `license`, and `type`. Fontsource is the default catalog;
|
|
18
|
+
Google Fonts is covered through Fontsource and does not need a separate API key.
|
|
19
|
+
|
|
20
|
+
## Install a Fontsource family
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
npx frameyard font add space-grotesk --subset latin
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The command fetches the family metadata, pins the Fontsource `npmVersion`,
|
|
27
|
+
downloads only the requested WOFF2 files, downloads the package `LICENSE`, and
|
|
28
|
+
records the exact CDN URL and SHA-256 in `fy.fonts.json`.
|
|
29
|
+
|
|
30
|
+
- A variable family installs the `wght` axis by default. Use multiple
|
|
31
|
+
`--subset` and `--style` values, for example
|
|
32
|
+
`--subset latin,latin-ext --style normal,italic`.
|
|
33
|
+
- `--static` installs static files. `--weight` accepts a comma-separated list,
|
|
34
|
+
for example `--static --weight 400,700`.
|
|
35
|
+
- `--family` overrides the CSS family name when a project needs an alias.
|
|
36
|
+
- Fontsource supplies the license; do not pass `--license` for a Fontsource id.
|
|
37
|
+
- A variable family and a static install of the same family cannot be mixed
|
|
38
|
+
without `--force`, which replaces the family record.
|
|
39
|
+
|
|
40
|
+
`FONT_PROVIDER_UNAVAILABLE` means the Fontsource API or CDN was unreachable.
|
|
41
|
+
`FONT_DOWNLOAD_FAILED` means a response was not a valid WOFF2 file.
|
|
42
|
+
|
|
43
|
+
## Install a local or URL font
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
npx frameyard font add --file ./fonts/SpaceGrotesk.woff2 \
|
|
47
|
+
--family "Space Grotesk" --weight 300-700 --subset latin \
|
|
48
|
+
--license OFL-1.1 --license-file ./fonts/OFL.txt
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`--weight` is a number (`400`) or a variable range (`300-700`). `--style` is
|
|
52
|
+
`normal` or `italic`. `--subset`, `--unicode-range`, `--license`, and
|
|
53
|
+
`--license-file` are optional; a missing license is a warning. The command
|
|
54
|
+
copies the font and license under `assets/fonts/<id>/`, records the SHA-256 and
|
|
55
|
+
byte size, and prints the CSS family to use.
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
npx frameyard font add --url https://example.com/Inter.woff2 \
|
|
59
|
+
--family "Inter" --license OFL-1.1 --license-file ./fonts/OFL.txt \
|
|
60
|
+
--sha256 <hash>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`--url` requires HTTPS; loopback HTTP is allowed for local tests. `--sha256` is
|
|
64
|
+
optional but recommended: the computed hash is always recorded. A URL import
|
|
65
|
+
without `--license`/`--license-file` records `license: { "type": "unknown" }`,
|
|
66
|
+
warns in
|
|
67
|
+
`list`/`verify`/`doctor`, requires the `full` policy, and does not use a shared
|
|
68
|
+
cache. Do not point `--url` at a font whose license you cannot redistribute.
|
|
69
|
+
|
|
70
|
+
Fontsource is the primary catalog. Google Fonts is covered by Fontsource.
|
|
71
|
+
Bunny Fonts is not a vendoring source: its catalog is undocumented, it exposes
|
|
72
|
+
no license metadata, and its files are not frozen. Fontshare is not a provider:
|
|
73
|
+
its Free Font License prohibits redistribution, subsetting, and format
|
|
74
|
+
conversion for most of its catalog. Only OFL-licensed Fontshare files may be
|
|
75
|
+
vendored, through `font add --url`, with their license text and an explicit
|
|
76
|
+
decision by the user.
|
|
77
|
+
|
|
78
|
+
## Manage the manifest
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
npx frameyard font list --json
|
|
82
|
+
npx frameyard font verify --json
|
|
83
|
+
npx frameyard font remove space-grotesk --json
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`font list` reports installed families, subset/style labels, license, file
|
|
87
|
+
count, bytes, and status. `font verify` checks paths, formats, byte sizes,
|
|
88
|
+
SHA-256 values, license files, and orphaned font directories. `font remove`
|
|
89
|
+
deletes the manifest entry and `assets/fonts/<id>/`; add `--keep-files` to leave
|
|
90
|
+
the binaries in place. `font remove`, `font add --force`, and an unlicensed
|
|
91
|
+
`--url` import require the `full` policy.
|
|
92
|
+
|
|
93
|
+
Adding another subset, style, or weight to the same family merges into the
|
|
94
|
+
existing entry. Adding the same face again fails with `OUTPUT_EXISTS`; pass
|
|
95
|
+
`--force` to replace it. `fy.fonts.json` has `version: 1` and one entry
|
|
96
|
+
per family. Commit it with `assets/fonts/` and the license files.
|
|
97
|
+
|
|
98
|
+
## Use the font
|
|
99
|
+
|
|
100
|
+
```css
|
|
101
|
+
.title {
|
|
102
|
+
font-family: "Space Grotesk", system-ui, sans-serif;
|
|
103
|
+
font-weight: 700;
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The generated sheet uses `font-display: block`. The host waits for
|
|
108
|
+
`document.fonts.ready` after `mount()`, then fails with `FONT_LOAD_FAILED` if a
|
|
109
|
+
used face reports an error instead of silently capturing fallback text.
|
|
110
|
+
`check`, `still`, and `render` verify sizes and hashes before launching
|
|
111
|
+
Chromium; `lint` verifies existence and size. A changed file fails with
|
|
112
|
+
`FONT_HASH_MISMATCH`.
|
|
113
|
+
|
|
114
|
+
## Legacy fonts and remote URLs
|
|
115
|
+
|
|
116
|
+
Existing local `@font-face` rules and `<link rel="stylesheet">` elements keep
|
|
117
|
+
working. A remote font URL in inline CSS, linked local CSS, or a remote
|
|
118
|
+
stylesheet link produces a `FONT_REMOTE` warning. It may work during the loading
|
|
119
|
+
phase, but it is not reproducible and is not covered by the manifest.
|
|
120
|
+
|
|
121
|
+
## Agent workflow
|
|
122
|
+
|
|
123
|
+
1. Search Fontsource with `font search` or choose a local/URL source and verify
|
|
124
|
+
its license.
|
|
125
|
+
2. Install with `font add`, preferring the variable face or the exact static
|
|
126
|
+
weights the composition uses.
|
|
127
|
+
3. Use the returned family name in the composition CSS.
|
|
128
|
+
4. Run `font verify`, `context --json`, `lint`, and `still`.
|
|
129
|
+
5. Inspect the still when typography or line breaks change.
|
|
130
|
+
|
|
131
|
+
Do not download fonts from `update()` or `mount()`. Font acquisition is an
|
|
132
|
+
authoring operation, not frame production.
|
|
@@ -51,7 +51,107 @@ Additional fields are command-specific and stable within the 0.8 contract. `chec
|
|
|
51
51
|
}
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
Static validation diagnostics include source locations. Runtime diagnostics may include a `frame`. Audio and transcript warnings use `AUDIO_WARNING` and `TRANSCRIPT_WARNING`; controller clock/randomness and CSS transition warnings use `DETERMINISM_WARNING`. See [diagnostics](diagnostics.md) for the code table.
|
|
54
|
+
Static validation diagnostics include source locations. Runtime diagnostics may include a `frame`. Timeline diagnostics add `timeline`, `clip`, `track`, `jsonPath`, and, for keyframe errors, `channel` and the zero-based `keyframe` index; transition diagnostics add `from` and `to`. Audio and transcript warnings use `AUDIO_WARNING` and `TRANSCRIPT_WARNING`; controller clock/randomness and CSS transition warnings use `DETERMINISM_WARNING`. See [diagnostics](diagnostics.md) for the code table.
|
|
55
|
+
|
|
56
|
+
## lint
|
|
57
|
+
|
|
58
|
+
`lint` reports `kind: "composition"` or `kind: "timeline"` and the same diagnostic shape. A timeline result includes `framesProjected`, `segments`, `tracks`, `clips`, and `transitions`; a composition result includes `metadata` and reports `framesProjected: 0` because lint does not seek frames. The result also includes the active `policy` details.
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"ok": true,
|
|
63
|
+
"command": "lint",
|
|
64
|
+
"policy": { "policy": "authoring", "maxStillFrames": 4, "allowOverwrite": false, "source": "env" },
|
|
65
|
+
"kind": "timeline",
|
|
66
|
+
"timeline": "main",
|
|
67
|
+
"entry": "timelines/main.timeline.json",
|
|
68
|
+
"format": { "width": 1920, "height": 1080, "fps": 30 },
|
|
69
|
+
"durationInFrames": 900,
|
|
70
|
+
"framesProjected": 900,
|
|
71
|
+
"segments": 3,
|
|
72
|
+
"diagnostics": [],
|
|
73
|
+
"elapsedMs": 2
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`lint` does not run `validateProps` or composition runtime. A clean lint is not a successful `check`.
|
|
78
|
+
|
|
79
|
+
## context
|
|
80
|
+
|
|
81
|
+
`context` returns a read-only project digest. It includes `root`, `format`, `policy`, `compositions`, `timelines`, `assets`, `fonts`, `next`, and `truncated`. Each composition carries its resolved `metadata`, prop `path`/`type` pairs, controller, and diagnostic counts; each timeline carries duration, tracks, clip summaries, transitions, audio/transcript counts, and diagnostic counts. Each font carries `id`, `family`, `provider`, optional `version`, `variable`, `subsets`, `styles`, `license`, `files`, `bytes`, and `status`. `truncated.fonts` reports a clipped font list.
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"ok": true,
|
|
86
|
+
"command": "context",
|
|
87
|
+
"root": "/path/to/project",
|
|
88
|
+
"format": { "width": 1920, "height": 1080, "fps": 30 },
|
|
89
|
+
"policy": { "policy": "authoring", "profile": "shared-cpu", "maxStillFrames": 4, "allowOverwrite": false, "source": "file" },
|
|
90
|
+
"compositions": [
|
|
91
|
+
{ "id": "main", "entry": "compositions/main.html", "kind": "animated", "metadata": { "width": 1920, "height": 1080, "fps": 30, "durationInFrames": 900 }, "controller": { "kind": "external", "src": "compositions/main.controller.js" }, "props": [{ "path": "title", "type": "string" }], "diagnostics": { "errors": 0, "warnings": 0, "codes": [] } }
|
|
92
|
+
],
|
|
93
|
+
"timelines": [],
|
|
94
|
+
"assets": [],
|
|
95
|
+
"next": ["npx frameyard lint main --json"],
|
|
96
|
+
"truncated": { "clips": false, "assets": false, "props": false }
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## inspect
|
|
101
|
+
|
|
102
|
+
`inspect` decodes the first frame and reports `width`, `height`, `bytes`, `alpha`, `blank`, `background`, `content`, `colors`, `luminanceGrid`, and `edgeDensity`. `alpha.coverage` is the opaque fraction; `content.bbox` is `null` when no pixels differ from the background. It never launches Chromium.
|
|
103
|
+
|
|
104
|
+
## compare
|
|
105
|
+
|
|
106
|
+
`compare` reports `identical`, `differingPixels`, `totalPixels`, `percent`, `maxChannelDelta`, `meanChannelDelta`, `bbox`, and 3×3 `regions`. It exits 1 when the images differ; the JSON body still has `ok: true` and `identical: false`. Different dimensions fail with `IMAGE_INVALID`.
|
|
107
|
+
|
|
108
|
+
## add
|
|
109
|
+
|
|
110
|
+
`add --list` returns `templates` with `name`, `kind`, `description`, and optional `prerequisites`. Adding an entry returns `kind`, `id`, `template`, `entry`, `path`, `register`, `prerequisites`, and `next`. `register` is the exact object to merge into `fy.config.js`; `add` does not edit the config itself.
|
|
111
|
+
|
|
112
|
+
## font
|
|
113
|
+
|
|
114
|
+
`font search` returns `provider: "fontsource"`, the `query`, applied `filters`, `results`, and `truncated`. Each result has `id`, `family`, `category`, `variable`, `weights`, `styles`, `subsets`, `license`, optional `version`, and `type`.
|
|
115
|
+
|
|
116
|
+
`font add` (Fontsource id, `--file`, or `--url`) returns `action: "add"`, a `font` summary (`id`, `family`, `provider`, optional `version`, `variable`, `subsets`, `styles`, `license`, `files`, `bytes`, `status`), the `manifest` path, installed `files`, `warnings`, and `next` commands. Fontsource results identify `provider: "fontsource"` and the pinned `version`; URL results identify `provider: "url"`. `font list` returns `manifest` and a `fonts` array of the same summaries. `font verify` returns `manifest`, `fonts`, `files`, `bytes`, `valid`, and `diagnostics`; errors exit 1, warnings exit 0. `font remove` returns `id`, `family`, `manifest`, `files`, and `keptFiles`.
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"ok": true,
|
|
121
|
+
"command": "font",
|
|
122
|
+
"action": "verify",
|
|
123
|
+
"manifest": "/project/fy.fonts.json",
|
|
124
|
+
"fonts": 1,
|
|
125
|
+
"files": 1,
|
|
126
|
+
"bytes": 22288,
|
|
127
|
+
"valid": true,
|
|
128
|
+
"diagnostics": []
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
A present-but-invalid manifest fails with `FONT_MANIFEST_INVALID`; missing files use `FONT_FILE_MISSING`, changed content uses `FONT_HASH_MISMATCH`, and a failed browser load uses `FONT_LOAD_FAILED`. Remote font CSS is a `FONT_REMOTE` warning. Fontsource network or API failures use `FONT_PROVIDER_UNAVAILABLE`; invalid downloaded WOFF2 content uses `FONT_DOWNLOAD_FAILED`; a missing Fontsource family or `font remove` id uses `FONT_NOT_FOUND`.
|
|
133
|
+
|
|
134
|
+
## dry run
|
|
135
|
+
|
|
136
|
+
`tts`, `transcribe`, and `render` accept `--dry-run`. The result has `dryRun: true`, the active `policy`, a command-specific `plan`, a shell-ready `commandLine`, `prerequisites`, `blockers`, and the output path. It does not create the output directory, write the manifest, load a model, launch Chromium, or encode.
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"ok": true,
|
|
141
|
+
"command": "render",
|
|
142
|
+
"dryRun": true,
|
|
143
|
+
"deferred": true,
|
|
144
|
+
"policy": { "policy": "authoring", "maxStillFrames": 4, "allowOverwrite": false, "source": "env" },
|
|
145
|
+
"plan": { "kind": "video", "frames": 900, "segments": 3, "workers": 2, "encoder": "auto", "codec": "h264", "container": "mp4", "probed": false },
|
|
146
|
+
"commandLine": "FRAMEYARD_POLICY=full npx frameyard render main --workers 2 --codec h264 --container mp4",
|
|
147
|
+
"prerequisites": ["assets/voice.wav"],
|
|
148
|
+
"blockers": [],
|
|
149
|
+
"diagnostics": [],
|
|
150
|
+
"output": { "path": "out/main.mp4", "exists": false }
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
An existing output appears in `blockers` with `OUTPUT_EXISTS`; the dry run still exits 0. The handoff command includes `FRAMEYARD_POLICY=full` when the active tier is not `full`. A dry run is not a render; `deferred: true` means the heavy operation was not executed and the `commandLine` is the handoff.
|
|
55
155
|
|
|
56
156
|
## check
|
|
57
157
|
|
|
@@ -111,6 +211,10 @@ A static composition result reports `kind: "static"`, dimensions only, `framesCh
|
|
|
111
211
|
}
|
|
112
212
|
```
|
|
113
213
|
|
|
214
|
+
### Timeline check
|
|
215
|
+
|
|
216
|
+
A timeline check reports `timeline`, `entry`, `format`, `durationInFrames`, `framesChecked`, `tracks`, `clips`, `transitions`, `audio`, `transcripts`, `diagnostics`, and `elapsedMs`. `framesChecked` equals the timeline duration; `transitions` is the resolved count, and transition diagnostics carry `from`/`to` and `jsonPath` in `details`.
|
|
217
|
+
|
|
114
218
|
## still
|
|
115
219
|
|
|
116
220
|
```json
|
|
@@ -191,6 +295,7 @@ A successful render means the final path exists and was published atomically. A
|
|
|
191
295
|
{
|
|
192
296
|
"ok": true,
|
|
193
297
|
"command": "tts",
|
|
298
|
+
"engine": "qwen",
|
|
194
299
|
"path": "/absolute/path/assets/voice.wav",
|
|
195
300
|
"bytes": 480000,
|
|
196
301
|
"sha256": "64-character hex digest",
|
|
@@ -198,12 +303,12 @@ A successful render means the final path exists and was published atomically. A
|
|
|
198
303
|
"channels": 1,
|
|
199
304
|
"speaker": "vivian",
|
|
200
305
|
"language": "English",
|
|
201
|
-
"manifest": "/absolute/path/
|
|
306
|
+
"manifest": "/absolute/path/fy.assets.json",
|
|
202
307
|
"elapsedMs": 12000
|
|
203
308
|
}
|
|
204
309
|
```
|
|
205
310
|
|
|
206
|
-
`language` is omitted when it was not supplied.
|
|
311
|
+
`engine` is `qwen` or `supertonic`. `language` is omitted when it was not supplied; Supertonic always supplies it. `device` is present only for qwen.
|
|
207
312
|
|
|
208
313
|
## transcribe
|
|
209
314
|
|
|
@@ -230,7 +335,7 @@ A successful render means the final path exists and was published atomically. A
|
|
|
230
335
|
"sha256": "64-character hex digest"
|
|
231
336
|
}
|
|
232
337
|
],
|
|
233
|
-
"manifest": "/absolute/path/
|
|
338
|
+
"manifest": "/absolute/path/fy.assets.json",
|
|
234
339
|
"elapsedMs": 30000
|
|
235
340
|
}
|
|
236
341
|
```
|
|
@@ -239,6 +344,8 @@ A successful render means the final path exists and was published atomically. A
|
|
|
239
344
|
|
|
240
345
|
## doctor
|
|
241
346
|
|
|
347
|
+
The doctor result adds `policy` and `resources` fields. `policy.status` is `ok` or `error`; an invalid policy is reported as a finding without preventing the environment checks. `resources` reports platform, architecture, CPU count, load average, and total/free memory. `fonts` reports the project font manifest path, whether it exists, `valid`, installed `fonts`/`files`/`bytes`, and an optional `detail` for the first integrity problem.
|
|
348
|
+
|
|
242
349
|
Doctor reports findings rather than throwing for environment problems. `ok: true` means the inspection completed; `healthy` says whether required checks, manifest integrity, explicitly supplied models, and integration compatibility passed. `ffmpeg-opus-encoder` and `ffmpeg-video-encoders` are optional: the video check probes each supported encoder with a one-frame encode and lists any that FFmpeg advertises but cannot open (an old driver, for example), and neither check affects `healthy`.
|
|
243
350
|
|
|
244
351
|
```json
|
|
@@ -271,6 +378,12 @@ Doctor reports findings rather than throwing for environment problems. `ok: true
|
|
|
271
378
|
"status": "missing",
|
|
272
379
|
"requested": false,
|
|
273
380
|
"referenceRevision": "..."
|
|
381
|
+
},
|
|
382
|
+
"sherpaOnnxTts": {
|
|
383
|
+
"name": "sherpa-onnx-offline-tts",
|
|
384
|
+
"status": "missing",
|
|
385
|
+
"requested": false,
|
|
386
|
+
"referenceRevision": "..."
|
|
274
387
|
}
|
|
275
388
|
},
|
|
276
389
|
"models": [
|
|
@@ -282,8 +395,8 @@ Doctor reports findings rather than throwing for environment problems. `ok: true
|
|
|
282
395
|
"sha256": "64-character hex digest"
|
|
283
396
|
}
|
|
284
397
|
],
|
|
285
|
-
"manifest": { "path": "/absolute/path/
|
|
398
|
+
"manifest": { "path": "/absolute/path/fy.assets.json", "exists": true, "valid": true, "artifacts": 2 }
|
|
286
399
|
}
|
|
287
400
|
```
|
|
288
401
|
|
|
289
|
-
`models` is populated only for model paths passed explicitly with `--talker-model`, `--codec-model`, or `--model`. `healthy: false` exits `1`. Missing optional integrations do not fail, but a found executable with incompatible capabilities does.
|
|
402
|
+
`models` is populated only for model paths passed explicitly with `--talker-model`, `--codec-model`, `--model-dir`, or `--model`. A Supertonic `--model-dir` reports one entry with role `supertonic`; `tts` provenance records the seven individual model files. `healthy: false` exits `1`. Missing optional integrations do not fail, but a found executable with incompatible capabilities does.
|