frameyard 0.7.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 +164 -7
- package/GLOSSARY.md +152 -18
- package/README.md +328 -285
- 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 +1705 -106
- package/dist/cli.js.map +1 -1
- package/dist/composition/descriptor.d.ts +17 -3
- package/dist/composition/descriptor.d.ts.map +1 -1
- package/dist/composition/descriptor.js +51 -28
- package/dist/composition/descriptor.js.map +1 -1
- package/dist/composition/format.d.ts +12 -0
- package/dist/composition/format.d.ts.map +1 -0
- package/dist/composition/format.js +28 -0
- package/dist/composition/format.js.map +1 -0
- package/dist/composition/index.d.ts +2 -1
- package/dist/composition/index.d.ts.map +1 -1
- package/dist/composition/index.js +1 -0
- package/dist/composition/index.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 +80 -6
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +3 -0
- 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/document-adapter.d.ts +3 -3
- package/dist/html/document-adapter.d.ts.map +1 -1
- package/dist/html/document-adapter.js +1 -1
- package/dist/html/document-adapter.js.map +1 -1
- package/dist/html/index.d.ts +1 -1
- package/dist/html/index.d.ts.map +1 -1
- package/dist/html/parse.d.ts +10 -2
- package/dist/html/parse.d.ts.map +1 -1
- package/dist/html/parse.js +14 -5
- package/dist/html/parse.js.map +1 -1
- package/dist/html/validate.d.ts +12 -1
- package/dist/html/validate.d.ts.map +1 -1
- package/dist/html/validate.js +292 -61
- package/dist/html/validate.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- 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/main.d.ts +2 -2
- package/dist/preview/main.d.ts.map +1 -1
- package/dist/preview/main.js +35 -14
- package/dist/preview/main.js.map +1 -1
- package/dist/preview/timeline-data.d.ts +17 -65
- 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 +73 -38
- 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 -3
- package/dist/preview/timeline-main.js.map +1 -1
- package/dist/project.d.ts +13 -8
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +20 -17
- package/dist/project.js.map +1 -1
- package/dist/renderer/browser.d.ts +2 -2
- package/dist/renderer/browser.d.ts.map +1 -1
- package/dist/renderer/browser.js +10 -4
- package/dist/renderer/browser.js.map +1 -1
- package/dist/renderer/ffmpeg.d.ts +44 -1
- package/dist/renderer/ffmpeg.d.ts.map +1 -1
- package/dist/renderer/ffmpeg.js +371 -73
- package/dist/renderer/ffmpeg.js.map +1 -1
- package/dist/renderer/index.d.ts +40 -4
- package/dist/renderer/index.d.ts.map +1 -1
- package/dist/renderer/index.js +100 -25
- 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 +168 -106
- package/dist/renderer/timeline.js.map +1 -1
- package/dist/runtime/host.d.ts.map +1 -1
- package/dist/runtime/host.js +159 -31
- 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 -317
- package/dist/scaffold.js.map +1 -1
- package/dist/server/composition-html.d.ts +15 -7
- package/dist/server/composition-html.d.ts.map +1 -1
- package/dist/server/composition-html.js +72 -16
- package/dist/server/composition-html.js.map +1 -1
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +56 -6
- 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 -23
- 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 +20 -3
- 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 +16 -3
- package/dist/timeline/resolve.d.ts.map +1 -1
- package/dist/timeline/resolve.js +119 -13
- 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 +38 -0
- package/dist/timeline/schema.d.ts.map +1 -1
- package/dist/timeline/schema.js +290 -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 +2 -2
- package/docs/adr/0008-share-the-composition-document-grammar.md +1 -1
- package/docs/adr/0010-make-timelines-the-render-unit.md +3 -2
- package/docs/adr/0011-compose-timeline-frames-in-a-live-browser-host.md +1 -1
- 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 +38 -0
- package/docs/adr/0015-inherit-composition-format-from-the-project.md +28 -0
- package/docs/adr/0016-author-compositions-as-html-fragments.md +32 -0
- package/docs/adr/0017-render-timeless-compositions-as-stills.md +99 -0
- 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 +5 -3
- package/docs/migration-0.8.md +163 -0
- package/docs/preview-testing.md +23 -3
- package/docs/{v0.1-design.md → v0.x-design.md} +86 -53
- package/examples/blend/README.md +106 -0
- package/examples/blend/assets/ink.mp4 +0 -0
- package/examples/blend/compositions/aurora.html +55 -0
- package/examples/blend/compositions/backdrop.html +74 -0
- package/examples/blend/compositions/labels.html +149 -0
- package/examples/blend/fy.config.js +11 -0
- package/examples/blend/scripts/make-ink.sh +26 -0
- package/examples/blend/timelines/blend.timeline.json +155 -0
- package/examples/demo/README.md +2 -2
- package/examples/demo/compositions/demo.html +243 -251
- 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 +17 -0
- package/examples/static/compositions/still.html +37 -0
- package/examples/static/fy.config.js +9 -0
- package/examples/static/timelines/still.timeline.json +13 -0
- 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/video/compositions/video-mp4.html +349 -357
- package/examples/video/compositions/video-overlay.html +337 -345
- package/examples/video/compositions/video.html +349 -357
- package/examples/webgl/README.md +1 -1
- package/examples/webgl/compositions/webgl.html +205 -213
- package/package.json +13 -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 -59
- package/skills/frameyard/references/audio-assets.md +32 -3
- package/skills/frameyard/references/common-mistakes.md +81 -7
- package/skills/frameyard/references/controllers.md +5 -4
- package/skills/frameyard/references/deterministic-authoring.md +49 -37
- package/skills/frameyard/references/diagnostics.md +42 -4
- package/skills/frameyard/references/failure-recovery.md +56 -10
- package/skills/frameyard/references/fonts.md +132 -0
- package/skills/frameyard/references/json-results.md +165 -10
- package/skills/frameyard/references/quick-reference.md +222 -0
- package/skills/frameyard/references/timelines.md +109 -15
- /package/examples/demo/{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,14 +2,19 @@
|
|
|
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`
|
|
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
|
|
9
9
|
|
|
10
|
-
-
|
|
11
|
-
- Missing `width`, `height`, `fps`,
|
|
12
|
-
-
|
|
10
|
+
- Adding `<!doctype>`, `<html>`, `<head>`, `<body>`, `<title>`, or `<meta>`; compositions are fragments and the framework owns the shell (`COMPOSITION_DOCUMENT_SHELL`).
|
|
11
|
+
- Missing/multiple `<fy-composition>` roots, a root nested inside another element, or missing `duration-in-frames` on an animated composition. `width`, `height`, and `fps` may be omitted when the project declares `format`, but declaring only one of `width` and `height` is an error.
|
|
12
|
+
- Forgetting both `duration-in-frames` and `fps` on a composition that should animate; that composition is now static and renders to PNG. Declaring `fps` without `duration-in-frames` is an error.
|
|
13
|
+
- Rendering a composition that declares no dimensions in a project without `format`; add `width`/`height` or the project `format` (`COMPOSITION_FORMAT_UNRESOLVED`).
|
|
14
|
+
- Missing the top-level `data-fy="props"` block when the composition relies on default props; a nested props block is rejected.
|
|
15
|
+
- Putting props, the controller, or an import map inside `<fy-composition>` instead of at the top level.
|
|
16
|
+
- Using the reserved `data-fy-host` attribute or `data-fy="format"` block.
|
|
17
|
+
- Declaring an import map with `frameyard` or `frameyard/` specifiers; those are reserved (`IMPORT_MAP_INVALID`).
|
|
13
18
|
- Visual content outside `<fy-composition>`; it is not captured.
|
|
14
19
|
- Using `<base>`, refresh/CSP meta tags, `<iframe>`, `<object>`, or `<embed>`.
|
|
15
20
|
|
|
@@ -42,14 +47,83 @@
|
|
|
42
47
|
- Importing libraries from executable `<script>` tags; import them from the controller instead.
|
|
43
48
|
- Using `Math.random()`, `Date.now()`, `performance.now()`, timers, independent `requestAnimationFrame` loops, or CSS transitions.
|
|
44
49
|
|
|
45
|
-
##
|
|
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`.
|
|
46
60
|
|
|
61
|
+
## Media and transcripts
|
|
47
62
|
- Using remote, live, or unseekable media sources.
|
|
48
63
|
- Using `<source>` or `<track>` children; use a local `src` and `<fy-transcript>`.
|
|
49
|
-
- Adding or changing media in `update()`; prepare it in the
|
|
64
|
+
- Adding or changing media in `update()`; prepare it in the fragment or in `mount()`.
|
|
50
65
|
- Calling `play()`, `pause()`, or setting `currentTime` on a video.
|
|
51
66
|
- Assuming media/transcript `src` is document-relative. It is project-root-relative.
|
|
52
|
-
- Assuming the controller `src` is project-root-relative. It is
|
|
67
|
+
- Assuming the controller `src` is project-root-relative. It is relative to the composition URL.
|
|
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
|
+
|
|
78
|
+
## Timelines
|
|
79
|
+
|
|
80
|
+
- Putting `blend` on an audio clip. It is only valid on visual clips.
|
|
81
|
+
- Using `hue`, `saturation`, `color`, or `luminosity`. They need cross-channel math and are rejected; use one of the separable CSS modes.
|
|
82
|
+
- Expecting a blend on a transparent nested timeline to see the parent's layers. A nested timeline is its own document and blends only against its own backdrop.
|
|
83
|
+
- Expecting a blend on a composition with an opaque background to reach lower tracks. The composition's alpha is opaque; leave its background transparent.
|
|
84
|
+
- Expecting preview and final render blend pixels to match exactly. CSS is the reference and FFmpeg approximates it; the guarantee is at clip capture.
|
|
85
|
+
- Omitting `duration` on a static composition clip, or setting `sourceIn` above 0. A static source has no intrinsic duration or frames.
|
|
86
|
+
- Expecting a static composition clip to contribute audio or transcripts. It is visual-only.
|
|
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
|
+
|
|
100
|
+
## Static compositions
|
|
101
|
+
|
|
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.
|
|
103
|
+
- Adding `data-fy-anim-*`, `<video>`, `<audio>`, or `<fy-transcript>` to a static composition. These are `STATIC_ANIMATION_UNSUPPORTED` or `STATIC_MEDIA_UNSUPPORTED`.
|
|
104
|
+
- Relying on CSS animations or transitions at capture time; they are paused at 0 with `STATIC_ANIMATION_PAUSED`.
|
|
105
|
+
- Passing `--container`, `--codec`, `--encoder`, or `--concurrency` to `render` for a static composition. Use a timeline image clip if the deliverable must be video.
|
|
106
|
+
- Requesting a still frame other than 0 from a static composition.
|
|
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.
|
|
53
127
|
|
|
54
128
|
## Validation
|
|
55
129
|
|
|
@@ -36,17 +36,18 @@ export default {
|
|
|
36
36
|
};
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
Exactly one controller is allowed. Inline source is served through a virtual module URL, so relative imports and `import.meta.url` work like an external module. A literal `</script>` inside inline source must be escaped.
|
|
39
|
+
Exactly one controller is allowed, and it must be a top-level sibling of `<fy-composition>` in the fragment. Inline source is served through a virtual module URL, so relative imports and `import.meta.url` work like an external module. A literal `</script>` inside inline source must be escaped.
|
|
40
40
|
|
|
41
41
|
## Lifecycle
|
|
42
42
|
|
|
43
43
|
- `mount({ root, props })` runs once per load and may be asynchronous. Use it to query the root, create canvases, and start loading local data.
|
|
44
44
|
- `update({ frame, props, root })` runs on every seek. It must be synchronous and seek-safe: derive the frame from `frame` and `props` only.
|
|
45
|
-
- `dispose()` releases resources and resets module-scope state. The host calls it before remounting or disposing the
|
|
45
|
+
- `dispose()` releases resources and resets module-scope state. The host calls it before remounting or disposing the composition host.
|
|
46
|
+
- A static composition runs `mount()` and `validateProps()` only. `update()` is not called and frame-state variables are not set; the host reports `STATIC_UPDATE_IGNORED`. Do not create media in a static controller: the host rejects it with `STATIC_MEDIA_UNSUPPORTED`. Draw canvas/WebGL content once from `mount()`.
|
|
46
47
|
- `validateProps(props)` is optional and synchronous. Return an array of human-readable errors, or `void`.
|
|
47
48
|
- A validation-only controller may export just `validateProps`.
|
|
48
49
|
|
|
49
|
-
Keep mutable state in module scope, never on `this`. The controller module is imported once per
|
|
50
|
+
Keep mutable state in module scope, never on `this`. The controller module is imported once per page; `load` remounts it without re-importing, so `dispose` must reset module state.
|
|
50
51
|
|
|
51
52
|
## Props validation
|
|
52
53
|
|
|
@@ -105,7 +106,7 @@ Never use `Math.random()`, `Date.now()`, `performance.now()`, timers, independen
|
|
|
105
106
|
|
|
106
107
|
Prefer declarative `<video>`/`<audio>` elements with `data-fy-*` attributes. The host pauses and seeks them; do not call `play()`, `pause()`, or set `currentTime` in `update()`. Media created by the controller is allowed in `mount`; media added or changed in `update` cannot be prepared without network access.
|
|
107
108
|
|
|
108
|
-
Media and transcript `src` values resolve from the project root. The controller `src` resolves relative to the composition
|
|
109
|
+
Media and transcript `src` values resolve from the project root. The controller `src` resolves relative to the composition URL.
|
|
109
110
|
|
|
110
111
|
## Karaoke
|
|
111
112
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Deterministic HTML authoring
|
|
2
2
|
|
|
3
|
-
Frameyard compositions are
|
|
3
|
+
Frameyard compositions are partial HTML fragments. The preview server wraps them in the document shell and injects the framework import map and composition host; do not add a bundling step. Optional controllers are native JavaScript modules that import bare `frameyard/runtime`.
|
|
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 {
|
|
@@ -14,42 +14,47 @@ export default {
|
|
|
14
14
|
};
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
There is no directory scanning. A new composition
|
|
17
|
+
There is no directory scanning. A new composition fragment is invisible until it is listed. Entries must be `.html` files; JavaScript composition modules are rejected.
|
|
18
18
|
|
|
19
19
|
## Composition shape
|
|
20
20
|
|
|
21
21
|
```html
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
{
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
<h1 id="title"
|
|
40
|
-
data-fy-text="title"
|
|
41
|
-
data-fy-anim-start="0"
|
|
42
|
-
data-fy-anim-duration="30"></h1>
|
|
43
|
-
<div style="width: calc(var(--fy-progress) * 100%)"></div>
|
|
44
|
-
</fy-composition>
|
|
45
|
-
</body>
|
|
46
|
-
</html>
|
|
22
|
+
<script type="application/json" data-fy="props">
|
|
23
|
+
{ "title": "Hello", "accent": "#b8f36b" }
|
|
24
|
+
</script>
|
|
25
|
+
<style>
|
|
26
|
+
@keyframes fade-up {
|
|
27
|
+
from { opacity: 0; transform: translateY(24px); }
|
|
28
|
+
to { opacity: 1; transform: translateY(0); }
|
|
29
|
+
}
|
|
30
|
+
#title { animation: fade-up 1s ease-out both; }
|
|
31
|
+
</style>
|
|
32
|
+
<fy-composition width="1920" height="1080" fps="30" duration-in-frames="900">
|
|
33
|
+
<h1 id="title"
|
|
34
|
+
data-fy-text="title"
|
|
35
|
+
data-fy-anim-start="0"
|
|
36
|
+
data-fy-anim-duration="30"></h1>
|
|
37
|
+
<div style="width: calc(var(--fy-progress) * 100%)"></div>
|
|
38
|
+
</fy-composition>
|
|
47
39
|
```
|
|
48
40
|
|
|
49
|
-
- `width`, `height`, `fps`, and `duration-in-frames`
|
|
50
|
-
- The `data-fy="props"` block is a JSON object. External props merge shallowly over it and are deep-frozen.
|
|
51
|
-
- There is exactly one `<fy-composition>` root,
|
|
52
|
-
-
|
|
41
|
+
- `duration-in-frames` is required for an animated composition. `width`, `height`, and `fps` are optional: a missing value inherits the project `format`, and a declared value wins. Declare `width` and `height` together or not at all. A composition that declares neither `duration-in-frames` nor `fps` is static.
|
|
42
|
+
- The `data-fy="props"` block is a top-level JSON object. External props merge shallowly over it and are deep-frozen.
|
|
43
|
+
- There is exactly one `<fy-composition>` root, and it must be a top-level element in the fragment.
|
|
44
|
+
- Do not add `<!doctype>`, `<html>`, `<head>`, `<body>`, `<title>`, or `<meta>`; Frameyard owns the shell. 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; styles inside the root stay in the body.
|
|
45
|
+
- An optional top-level `<script type="importmap">` adds bare specifiers for controllers. `frameyard` and `frameyard/` specifiers are reserved.
|
|
46
|
+
- The reserved `data-fy-host` attribute and `data-fy="format"` block are framework-only. Unsupported features such as `<base>`, refresh/CSP meta tags, `<iframe>`, `<object>`, and `<embed>` are rejected.
|
|
47
|
+
|
|
48
|
+
## Static compositions
|
|
49
|
+
|
|
50
|
+
A static composition declares neither `duration-in-frames` nor `fps`. It has no frame clock: Frameyard loads it, applies bindings, runs `mount()`, pauses CSS animations at time 0, and captures one PNG. `update()` is not called and `--fy-frame`, `--fy-time`, `--fy-progress`, `--fy-fps`, `--fy-duration`, and `--fy-duration-frames` are not set.
|
|
51
|
+
|
|
52
|
+
- Use `<img>`, CSS backgrounds, SVG, and canvas drawn once from `mount()`.
|
|
53
|
+
- Do not use `data-fy-anim-*`, `<video>`, `<audio>`, or `<fy-transcript>`; these are validation errors. Put media and captions on a timeline.
|
|
54
|
+
- Do not rely on `update()` or frame-state variables. A controller that exports `update()` loads with a `STATIC_UPDATE_IGNORED` warning.
|
|
55
|
+
- CSS animations and transitions are paused at 0 with a `STATIC_ANIMATION_PAUSED` warning; remove them for a predictable still.
|
|
56
|
+
- In a timeline, a static composition clip needs an explicit `duration` and `sourceIn: 0`.
|
|
57
|
+
- `render` writes a PNG and rejects video flags. Use a timeline image clip when the deliverable must be video.
|
|
53
58
|
|
|
54
59
|
## Bindings
|
|
55
60
|
|
|
@@ -94,6 +99,12 @@ CSS `@keyframes` and the Web Animations API are paused and seeked by the runtime
|
|
|
94
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.
|
|
95
100
|
- Do not use CSS transitions. They are diagnosed as non-deterministic.
|
|
96
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
|
+
|
|
97
108
|
## Controllers
|
|
98
109
|
|
|
99
110
|
An optional controller handles Canvas 2D, WebGL, karaoke, and per-frame computation CSS cannot express.
|
|
@@ -126,7 +137,7 @@ External for a reusable or complex controller:
|
|
|
126
137
|
- A validation-only controller may export just `validateProps(props)`.
|
|
127
138
|
- Inline source is served through a virtual module URL, so relative imports and `import.meta.url` work like an external module. A literal `</script>` inside inline source must be escaped.
|
|
128
139
|
- The controller is the only executable code. Other executable `<script>` elements and `on*` handlers are rejected.
|
|
129
|
-
- Controller `src` resolves relative to the composition
|
|
140
|
+
- Controller `src` resolves relative to the composition URL. Media and transcript `src` values resolve from the project root.
|
|
130
141
|
- Keep mutable state in module scope, never on `this`. Reset it in `dispose()`.
|
|
131
142
|
|
|
132
143
|
## Forbidden animation inputs
|
|
@@ -167,7 +178,7 @@ Do not preserve and accumulate WebGL drawing across frames unless that accumulat
|
|
|
167
178
|
|
|
168
179
|
## Embedded video and audio
|
|
169
180
|
|
|
170
|
-
Declare local media in the composition
|
|
181
|
+
Declare local media in the composition fragment:
|
|
171
182
|
|
|
172
183
|
```html
|
|
173
184
|
<video
|
|
@@ -210,18 +221,19 @@ Declare canonical transcript requirements with:
|
|
|
210
221
|
|
|
211
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.
|
|
212
223
|
|
|
213
|
-
Images declared in the
|
|
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).
|
|
214
225
|
|
|
215
226
|
## Validation and inspection
|
|
216
227
|
|
|
217
228
|
```sh
|
|
229
|
+
npx frameyard lint hello --json
|
|
218
230
|
npx frameyard check hello --json
|
|
219
231
|
npx frameyard still hello --frame 42 --out hello-42.png --json
|
|
220
232
|
npx frameyard still hello --frame 0,118-126 --out out/stills --json
|
|
221
233
|
```
|
|
222
234
|
|
|
223
|
-
`check` runs static validation before Chromium, then evaluates every frame and validates audio and declared transcripts without capturing or encoding. 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.
|
|
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.
|
|
224
236
|
|
|
225
237
|
## Metadata is fixed
|
|
226
238
|
|
|
227
|
-
`width`, `height`, `fps`, and `duration-in-frames` cannot be derived from props.
|
|
239
|
+
`width`, `height`, `fps`, and `duration-in-frames` cannot be derived from props. An animated composition always declares `duration-in-frames`; a static composition declares neither time attribute. `width`, `height`, and `fps` are either declared or inherited from the project `format`, and the resolved values are fixed for the load. A composition that declares no dimensions in a project without `format` fails with `COMPOSITION_FORMAT_UNRESOLVED`. The resolved format is part of the immutable frame environment, so viewport units and percentage layout are valid but depend on it. Put variation in props. Frameyard does not change duration to fit audio.
|
|
@@ -8,12 +8,13 @@ Frameyard reports composition problems as structured diagnostics before and duri
|
|
|
8
8
|
|
|
9
9
|
Static validation covers:
|
|
10
10
|
|
|
11
|
-
-
|
|
11
|
+
- fragment structure, root count and placement, metadata (including `width`/`height` pairing), default-props JSON, and import maps
|
|
12
12
|
- controller declaration and file existence
|
|
13
13
|
- executable scripts, `on*` handlers, `javascript:` URLs, and unsupported visual elements
|
|
14
14
|
- unknown `data-fy-*` attributes, binding paths, `if`/`repeat` placement, and animation attributes
|
|
15
15
|
- audio/video/transcript references and ordinary image/stylesheet assets
|
|
16
16
|
- visual content outside `<fy-composition>`
|
|
17
|
+
- document-level `<title>`/`<meta>` elements, the reserved `data-fy-host` attribute, and non-top-level props, controllers, or import maps
|
|
17
18
|
|
|
18
19
|
## Diagnostic shape
|
|
19
20
|
|
|
@@ -34,13 +35,16 @@ Static validation covers:
|
|
|
34
35
|
}
|
|
35
36
|
```
|
|
36
37
|
|
|
37
|
-
`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`.
|
|
38
39
|
|
|
39
40
|
## Common codes
|
|
40
41
|
|
|
41
42
|
| Code | Severity | Meaning |
|
|
42
43
|
| --- | --- | --- |
|
|
43
|
-
| `COMPOSITION_INVALID` | error | Missing/invalid
|
|
44
|
+
| `COMPOSITION_INVALID` | error | Missing/invalid root, metadata, fragment structure, or reserved attributes |
|
|
45
|
+
| `COMPOSITION_DOCUMENT_SHELL` | error | A full document shell (`<!doctype>`, `<html>`, `<head>`, or `<body>`) is present; compositions are fragments |
|
|
46
|
+
| `IMPORT_MAP_INVALID` | error | Invalid import map placement, JSON, attributes, or reserved `frameyard` specifiers |
|
|
47
|
+
| `COMPOSITION_FORMAT_UNRESOLVED` | error | The composition omits `width`/`height` and the project has no `format`; this is a command error, not a document diagnostic |
|
|
44
48
|
| `PROPS_INVALID` | error | Invalid default props JSON, invalid overrides, or `validateProps` failure |
|
|
45
49
|
| `CONTROLLER_INVALID` | error | Missing/duplicate/both-inline-and-external controller, missing file, or invalid exports |
|
|
46
50
|
| `BINDING_INVALID` | error | Unknown `data-fy-*` attribute or malformed binding path |
|
|
@@ -52,20 +56,48 @@ Static validation covers:
|
|
|
52
56
|
| `ANIMATION_INVALID` | error | Invalid `data-fy-anim-*` value |
|
|
53
57
|
| `ANIMATION_NOT_FOUND` | warning | `data-fy-anim-*` is present but the element has no CSS animation |
|
|
54
58
|
| `ANIMATION_MULTIPLE` | warning | Multiple animations on one declaratively scheduled element |
|
|
59
|
+
| `STATIC_ANIMATION_UNSUPPORTED` | error | A static composition uses `data-fy-anim-*`; remove it or declare `duration-in-frames` and `fps` |
|
|
60
|
+
| `STATIC_MEDIA_UNSUPPORTED` | error | A static composition declares `<video>`, `<audio>`, or `<fy-transcript>`; move media to a timeline |
|
|
61
|
+
| `STATIC_UPDATE_IGNORED` | warning | A static composition controller exports `update()`; move initialization into `mount()` |
|
|
62
|
+
| `STATIC_ANIMATION_PAUSED` | warning | A static composition has CSS animations/transitions; they are paused at time 0 |
|
|
55
63
|
| `SCRIPT_NOT_ALLOWED` | error | Executable `<script>` other than the controller |
|
|
56
64
|
| `EVENT_HANDLER_NOT_ALLOWED` | error | Inline `on*` handler or `data-fy-attr-on*` |
|
|
57
65
|
| `URL_NOT_ALLOWED` | error | `javascript:` URL |
|
|
58
66
|
| `ELEMENT_NOT_SUPPORTED` | error | `iframe`, `object`, `embed`, `base`, refresh/CSP meta, `source`, `track`, or declarative shadow DOM |
|
|
59
67
|
| `ELEMENT_UNKNOWN` | warning | Unknown `fy-*` element |
|
|
60
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` |
|
|
61
78
|
| `MEDIA_INVALID` | error | Invalid or missing audio/video source or attributes |
|
|
62
79
|
| `TRANSCRIPT_INVALID` | error | Invalid or missing transcript source |
|
|
63
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 |
|
|
64
91
|
| `AUDIO_WARNING` | warning | Audio timeline overrun or silent embedded media |
|
|
65
92
|
| `TRANSCRIPT_WARNING` | warning | Empty transcript text or no timed segments |
|
|
66
93
|
| `DETERMINISM_WARNING` | warning | Forbidden clock/randomness, CSS transition, or runtime diagnostic |
|
|
67
94
|
| `COMPOSITION_NOT_READY` | error | `seek`/`settle` before a successful load |
|
|
68
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 |
|
|
69
101
|
|
|
70
102
|
## Results
|
|
71
103
|
|
|
@@ -75,10 +107,16 @@ Static validation covers:
|
|
|
75
107
|
- `controller`: `{ kind: "inline" }`, `{ kind: "external", src }`, or `null`
|
|
76
108
|
- `diagnostics`: all static and runtime diagnostics, deduplicated
|
|
77
109
|
|
|
78
|
-
`check`
|
|
110
|
+
`check` and `render` are discriminated unions. Animated/video results report `kind: "animated"` or `kind: "video"`; static composition results report `kind: "static"` or `kind: "image"`. A static check exposes `metadata: { width, height, static: true }` and `framesChecked: 1` with no audio/transcript fields. An image render exposes the PNG path, dimensions, bytes, diagnostics, and elapsed time with no fps, duration, frame count, codec, container, encoder, audio, or transcript fields. `still` exposes `frames`, `width`, and `height`, plus `frame` and `path` when exactly one frame was captured; a static composition only accepts frame 0.
|
|
79
111
|
|
|
80
112
|
Failures use the same envelope with `error.code`, `error.message`, optional `error.details`, and top-level `diagnostics`.
|
|
81
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
|
+
|
|
82
120
|
## Working with diagnostics
|
|
83
121
|
|
|
84
122
|
1. Run `npx frameyard check <id> --json`.
|
|
@@ -7,15 +7,18 @@ 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 `
|
|
13
|
-
| `COMPOSITION_INVALID` | Invalid
|
|
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
|
+
| `COMPOSITION_INVALID` | Invalid root, metadata, fragment structure, or reserved attribute | Fix the composition fragment; check `details.line` and `details.attribute`. |
|
|
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
|
+
| `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 `fy.config.js`, or render through a timeline. |
|
|
14
17
|
| `COMPOSITION_ERROR` | Mount, update, or browser page error | Read the frame location; remove forbidden clocks/randomness or fix the controller. |
|
|
15
18
|
| `COMPOSITION_UPDATE_ASYNC` | `update()` returned a promise | Move async work to `mount()`. |
|
|
16
19
|
| `COMPOSITION_NOT_READY` | `seek`/`settle` before a successful load | Await `load`/`ready`. |
|
|
17
20
|
| `COMPOSITION_DISPOSED` | Operation after host disposal | Reload the composition. |
|
|
18
|
-
| `CONTROLLER_INVALID` | Missing, duplicate, or invalid controller declaration/file/exports | Keep exactly one inline or external controller; verify its exports and path. |
|
|
21
|
+
| `CONTROLLER_INVALID` | Missing, duplicate, non-top-level, or invalid controller declaration/file/exports | Keep exactly one inline or external top-level controller; verify its exports and path. |
|
|
19
22
|
| `BINDING_INVALID` | Unknown `data-fy-*` attribute or malformed path | Fix the attribute name or path. |
|
|
20
23
|
| `BINDING_PATH_MISSING` | Binding path absent from resolved props | Fix the prop or path; use `details.suggestion`. |
|
|
21
24
|
| `BINDING_VALUE_INVALID` | Object/array used where a primitive is required | Use a string/number/boolean or move the logic to a controller. |
|
|
@@ -25,27 +28,55 @@ Do not claim success after a nonzero exit. Fix the cause, rerun `check`, and onl
|
|
|
25
28
|
| `ANIMATION_INVALID` | Invalid `data-fy-anim-*` value | Use non-negative integer frames and a positive duration. |
|
|
26
29
|
| `ANIMATION_NOT_FOUND` | `data-fy-anim-*` without a CSS animation | Add an `animation` shorthand or remove the attributes. |
|
|
27
30
|
| `ANIMATION_MULTIPLE` | Multiple animations on one scheduled element | Use one animation or move scheduling to CSS/the controller. |
|
|
31
|
+
| `STATIC_ANIMATION_UNSUPPORTED` | A static composition uses `data-fy-anim-*` | Remove the attributes or declare `duration-in-frames` and `fps` to make the composition animated. |
|
|
32
|
+
| `STATIC_MEDIA_UNSUPPORTED` | A static composition declares video, audio, or a transcript | Move media and captions to a timeline, or declare `duration-in-frames` and `fps`. |
|
|
33
|
+
| `STATIC_UPDATE_IGNORED` | A static composition exports `update()` | Move initialization into `mount()`; static compositions do not run `update()`. |
|
|
34
|
+
| `STATIC_ANIMATION_PAUSED` | A static composition has CSS animations/transitions | Remove them; the capture uses time 0. |
|
|
28
35
|
| `SCRIPT_NOT_ALLOWED` | Executable script other than the controller | Move code into the controller. |
|
|
29
36
|
| `EVENT_HANDLER_NOT_ALLOWED` | Inline `on*` handler or `data-fy-attr-on*` | Remove the handler; use a controller. |
|
|
30
37
|
| `URL_NOT_ALLOWED` | `javascript:` URL | Use a safe URL. |
|
|
31
|
-
| `ELEMENT_NOT_SUPPORTED` | `iframe`, `object`, `embed`, `base`, refresh/CSP meta, `source`, `track`, or declarative shadow DOM | Use supported DOM/SVG/canvas/video sources. |
|
|
38
|
+
| `ELEMENT_NOT_SUPPORTED` | `iframe`, `object`, `embed`, `base`, document-level `<title>`/`<meta>`, refresh/CSP meta, `source`, `track`, or declarative shadow DOM | Use supported DOM/SVG/canvas/video sources. |
|
|
32
39
|
| `ELEMENT_UNKNOWN` | Unknown `fy-*` element | Use `fy-composition`/`fy-transcript` or a normal element. |
|
|
33
40
|
| `CONTENT_OUTSIDE_ROOT` | Visual content outside `<fy-composition>` | Move it inside the root. |
|
|
34
|
-
| `PROPS_INVALID` | Invalid props JSON or `validateProps()` failure |
|
|
41
|
+
| `PROPS_INVALID` | Invalid/non-top-level props JSON or `validateProps()` failure | Put the props block at the top level of the fragment; fix the props file or validator. |
|
|
35
42
|
| `FRAME_OUT_OF_RANGE` | Frame outside `0..durationInFrames - 1` | Choose valid frames; a batch error lists every invalid frame and the valid range. |
|
|
36
43
|
| `AUDIO_INVALID` | Bad clip or unreadable/unsupported audio | Use a local project-root-relative file with a decodable audio stream. |
|
|
37
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. |
|
|
38
54
|
| `MEDIA_INVALID` | Invalid media source or attributes | Use local, seekable files and valid `data-fy-*` values. |
|
|
39
55
|
| `MEDIA_SEEK_FAILED` | Embedded video did not reach the requested time | Re-encode with a regular keyframe interval or replace the source. |
|
|
40
56
|
| `TRANSCRIPT_INVALID` | Invalid/missing transcript declaration or JSON | Regenerate with `npx frameyard transcribe`; do not hand-edit timestamps. |
|
|
41
57
|
| `TRANSCRIPT_WORDS_UNAVAILABLE` | Karaoke requested without measured word timings | Re-run transcription with `--words` or remove `words`. |
|
|
42
58
|
| `TRANSCRIPT_WARNING` | Empty transcript text or no timed segments | Regenerate the transcript. |
|
|
43
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. |
|
|
44
70
|
| `DETERMINISM_WARNING` | Forbidden clock/randomness, CSS transition, or runtime warning | Derive state from `frame`/`props`; use `random("stable:key")`. |
|
|
45
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. |
|
|
46
77
|
| `OUTPUT_FAILED` | An output or manifest could not be written atomically | Check directory permissions and free space; rerun. |
|
|
47
|
-
| `EXECUTABLE_NOT_FOUND` | `qwen-tts` or `whisper-cli` could not be resolved | Pass `--executable`, set
|
|
48
|
-
| `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. |
|
|
49
80
|
| `TRANSCRIBE_FAILED` | Normalization or `whisper-cli` failed | Verify FFmpeg, the source media, and the Whisper model. |
|
|
50
81
|
| `MANIFEST_INVALID` | Provenance manifest is corrupt or unsupported | Restore or regenerate the manifest; do not invent hashes. |
|
|
51
82
|
| `FFMPEG_FAILED` | FFmpeg is missing, lacks encoders, or failed | Run `npx frameyard doctor --json`; install FFmpeg with libx264 and AAC. |
|
|
@@ -55,6 +86,8 @@ Do not claim success after a nonzero exit. Fix the cause, rerun `check`, and onl
|
|
|
55
86
|
| `CANCELLED` | The operation was interrupted | Rerun when ready. Partial outputs are removed. |
|
|
56
87
|
| `INTERNAL` | Unexpected internal failure | Rerun with `FRAMEYARD_DEBUG=1` and report the stack. |
|
|
57
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
|
+
|
|
58
91
|
## Behavior to rely on
|
|
59
92
|
|
|
60
93
|
- A failed render writes to a temporary file and does not publish a partial output.
|
|
@@ -62,7 +95,7 @@ Do not claim success after a nonzero exit. Fix the cause, rerun `check`, and onl
|
|
|
62
95
|
- Failed TTS/transcription operations leave no artifact and no manifest entry.
|
|
63
96
|
- Existing outputs are replaced only with `--overwrite` (renders/stills) or `--force` (generated assets).
|
|
64
97
|
- Cancellation terminates the child inference process and cleans temporary work.
|
|
65
|
-
- `check` validates every frame and declared transcripts without encoding.
|
|
98
|
+
- `check` validates every animated frame, or a static composition's single state, and declared transcripts without encoding.
|
|
66
99
|
- Static validation failures exit before Chromium starts.
|
|
67
100
|
- `check`, `still`, and `render` seek embedded videos and wait for decode before evaluating or capturing a frame.
|
|
68
101
|
- `doctor` inspects but never repairs, downloads, or modifies the environment.
|
|
@@ -82,3 +115,16 @@ Report all of:
|
|
|
82
115
|
- still paths and whether they were viewed;
|
|
83
116
|
- final video path, frame count, and audio/transcript summary;
|
|
84
117
|
- any skipped step or unresolved diagnostic.
|
|
118
|
+
|
|
119
|
+
## 0.8 fragment migration
|
|
120
|
+
|
|
121
|
+
0.8 changed compositions from full documents to partial HTML fragments. If `check` reports `COMPOSITION_DOCUMENT_SHELL`:
|
|
122
|
+
|
|
123
|
+
1. Remove `<!doctype html>`, `<html>`, `<head>`, `<body>`, `<title>`, and `<meta>`.
|
|
124
|
+
2. Keep one top-level `<fy-composition>` root.
|
|
125
|
+
3. Move the props block, the controller, and import maps to the top level.
|
|
126
|
+
4. Keep top-level `<style>` and `<link rel="stylesheet">` as siblings; Frameyard hoists them into the generated head. Styles inside the root stay in the body.
|
|
127
|
+
5. Remove the reserved `data-fy-host` attribute and `data-fy="format"` block.
|
|
128
|
+
6. Run `npx frameyard check <id> --json` again.
|
|
129
|
+
|
|
130
|
+
See `docs/migration-0.8.md` in the Frameyard repository for before/after examples.
|