frameyard 0.1.0 → 0.3.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.
Files changed (87) hide show
  1. package/CHANGELOG.md +60 -2
  2. package/GLOSSARY.md +16 -0
  3. package/README.md +89 -51
  4. package/dist/assets/integrations.d.ts.map +1 -1
  5. package/dist/assets/integrations.js.map +1 -1
  6. package/dist/assets/process.d.ts.map +1 -1
  7. package/dist/assets/process.js +2 -2
  8. package/dist/assets/process.js.map +1 -1
  9. package/dist/assets/provenance.d.ts.map +1 -1
  10. package/dist/assets/provenance.js.map +1 -1
  11. package/dist/assets/transcribe.d.ts.map +1 -1
  12. package/dist/assets/transcribe.js +4 -1
  13. package/dist/assets/transcribe.js.map +1 -1
  14. package/dist/assets/tts.d.ts.map +1 -1
  15. package/dist/assets/tts.js +2 -4
  16. package/dist/assets/tts.js.map +1 -1
  17. package/dist/cli.js +19 -94
  18. package/dist/cli.js.map +1 -1
  19. package/dist/contracts.d.ts.map +1 -1
  20. package/dist/contracts.js +5 -6
  21. package/dist/contracts.js.map +1 -1
  22. package/dist/doctor.d.ts.map +1 -1
  23. package/dist/doctor.js +27 -22
  24. package/dist/doctor.js.map +1 -1
  25. package/dist/errors.d.ts +1 -1
  26. package/dist/errors.d.ts.map +1 -1
  27. package/dist/errors.js.map +1 -1
  28. package/dist/index.d.ts +1 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +1 -1
  31. package/dist/index.js.map +1 -1
  32. package/dist/preview/main.d.ts +2 -0
  33. package/dist/preview/main.d.ts.map +1 -1
  34. package/dist/preview/main.js +28 -3
  35. package/dist/preview/main.js.map +1 -1
  36. package/dist/renderer/audio.d.ts +5 -1
  37. package/dist/renderer/audio.d.ts.map +1 -1
  38. package/dist/renderer/audio.js +5 -2
  39. package/dist/renderer/audio.js.map +1 -1
  40. package/dist/renderer/browser.d.ts +1 -0
  41. package/dist/renderer/browser.d.ts.map +1 -1
  42. package/dist/renderer/browser.js +8 -2
  43. package/dist/renderer/browser.js.map +1 -1
  44. package/dist/renderer/ffmpeg.d.ts.map +1 -1
  45. package/dist/renderer/ffmpeg.js +3 -1
  46. package/dist/renderer/ffmpeg.js.map +1 -1
  47. package/dist/renderer/index.d.ts +5 -0
  48. package/dist/renderer/index.d.ts.map +1 -1
  49. package/dist/renderer/index.js +144 -19
  50. package/dist/renderer/index.js.map +1 -1
  51. package/dist/renderer/transcripts.d.ts.map +1 -1
  52. package/dist/renderer/transcripts.js +1 -3
  53. package/dist/renderer/transcripts.js.map +1 -1
  54. package/dist/runtime/index.d.ts +3 -0
  55. package/dist/runtime/index.d.ts.map +1 -1
  56. package/dist/runtime/index.js +85 -9
  57. package/dist/runtime/index.js.map +1 -1
  58. package/dist/runtime/media.d.ts +16 -0
  59. package/dist/runtime/media.d.ts.map +1 -0
  60. package/dist/runtime/media.js +307 -0
  61. package/dist/runtime/media.js.map +1 -0
  62. package/dist/scaffold.d.ts +3 -0
  63. package/dist/scaffold.d.ts.map +1 -0
  64. package/dist/scaffold.js +179 -0
  65. package/dist/scaffold.js.map +1 -0
  66. package/dist/server/server.d.ts.map +1 -1
  67. package/dist/server/server.js.map +1 -1
  68. package/dist/transcript.d.ts.map +1 -1
  69. package/dist/transcript.js.map +1 -1
  70. package/dist/version.d.ts +1 -1
  71. package/dist/version.js +1 -1
  72. package/docs/adr/0003-render-browser-frames-offline.md +2 -0
  73. package/docs/adr/0005-keep-parallel-rendering-opt-in-and-frame-ordered.md +3 -0
  74. package/docs/adr/0006-seek-embedded-media-from-the-frame-clock.md +9 -0
  75. package/docs/v0.1-design.md +16 -12
  76. package/examples/demo/compositions/demo.js +2 -5
  77. package/package.json +13 -4
  78. package/skills/frameyard/README.md +35 -0
  79. package/skills/frameyard/SKILL.md +63 -0
  80. package/{skill → skills/frameyard}/references/audio-assets.md +3 -3
  81. package/{skill → skills/frameyard}/references/deterministic-authoring.md +32 -4
  82. package/{skill → skills/frameyard}/references/failure-recovery.md +6 -3
  83. package/{skill → skills/frameyard}/references/json-results.md +2 -1
  84. package/{skill → skills/frameyard}/references/subtitles-karaoke.md +3 -3
  85. package/{skill → skills/frameyard}/scripts/frameyard.mjs +2 -2
  86. package/skill/README.md +0 -29
  87. package/skill/SKILL.md +0 -57
@@ -73,20 +73,48 @@ import { random } from "frameyard/runtime";
73
73
  const x = random("star:17:x") * width;
74
74
  ```
75
75
 
76
- ## CSS and Canvas
76
+ ## Canvas and WebGL
77
77
 
78
78
  CSS `@keyframes` and the Web Animations API are paused and seeked by the runtime inside the composition container. They are valid animation drivers. CSS transitions are diagnosed as non-deterministic and should not be used.
79
79
 
80
- Canvas 2D must be redrawn from the current frame:
80
+ Canvas 2D and WebGL must be redrawn from the current frame:
81
81
 
82
82
  ```js
83
83
  update({ frame }) {
84
84
  context.clearRect(0, 0, canvas.width, canvas.height);
85
85
  context.fillStyle = frame % 2 === 0 ? "#101820" : "#203040";
86
86
  context.fillRect(0, 0, canvas.width, canvas.height);
87
+
88
+ gl.clearColor(frame % 2 === 0 ? 0.1 : 0.2, 0.1, 0.13, 1);
89
+ gl.clear(gl.COLOR_BUFFER_BIT);
87
90
  }
88
91
  ```
89
92
 
93
+ Do not preserve and accumulate WebGL drawing across frames unless that accumulation is itself deterministic. Production sessions use software WebGL so output does not depend on the host GPU.
94
+
95
+ ## Embedded video
96
+
97
+ Place local `<video>` elements in `mount()` and let the runtime seek them:
98
+
99
+ ```html
100
+ <video
101
+ src="assets/clip.webm"
102
+ data-frameyard-start="30"
103
+ data-frameyard-trim="0.5"
104
+ data-frameyard-duration="4"
105
+ data-frameyard-volume="0.8"
106
+ ></video>
107
+ ```
108
+
109
+ - `data-frameyard-start` is the composition frame where the source starts. Default `0`.
110
+ - `data-frameyard-trim` is seconds trimmed from the source start. Default `0`.
111
+ - `data-frameyard-duration` optionally limits how many source seconds are used.
112
+ - `data-frameyard-volume` sets embedded-audio volume from `0` to `1`. Default `1`.
113
+
114
+ Do not call `play()`, `pause()`, or set `currentTime` in `update()`. Create video elements and set the `data-frameyard-*` attributes in `mount()`. Videos must be in the composition container's light DOM; shadow roots are not scanned. The runtime pauses every video, loads the complete local file during the loading phase, and seeks it after each `update()` before capture. Video sources must be local, seekable project files; live streams, remote URLs, and videos added or changed during `update()` are not supported.
115
+
116
+ An unmuted video's audio is extracted and mixed into the output timeline. Set `muted` or `data-frameyard-audio="off"` for a silent visual asset. The same start, trim, duration, and volume attributes place its audio.
117
+
90
118
  ## Loading assets
91
119
 
92
120
  Remote data, images, fonts, and other assets must resolve during `mount()` or before the first frame. The production browser blocks network requests after loading begins. A fetch from `update()` is a bug.
@@ -96,8 +124,8 @@ Images inserted in `mount()` are awaited by the runtime. Static fonts should be
96
124
  ## Validation and inspection
97
125
 
98
126
  ```sh
99
- frameyard check hello --json
100
- frameyard still hello --frame 42 --out hello-42.png --json
127
+ npx frameyard check hello --json
128
+ npx frameyard still hello --frame 42 --out hello-42.png --json
101
129
  ```
102
130
 
103
131
  `check` mounts the composition, evaluates every frame, and validates audio and declared transcripts without capturing or encoding. Do not replace it with a single still.
@@ -6,7 +6,7 @@ Do not claim success after a nonzero exit. Fix the cause, rerun `check`, and onl
6
6
 
7
7
  | Code | Meaning | Action |
8
8
  | --- | --- | --- |
9
- | `USAGE` | Invalid flags or missing required arguments | Read `frameyard <command> --help`. |
9
+ | `USAGE` | Invalid flags or missing required arguments | Read `npx frameyard <command> --help`. |
10
10
  | `CONFIG_NOT_FOUND` | No `video.config.js` found | Run from the project root or pass `--project`. |
11
11
  | `CONFIG_INVALID` | Invalid config or composition entry | Fix `video.config.js`; entries must be explicit, local, and exist. |
12
12
  | `COMPOSITION_NOT_FOUND` | ID is not listed | Add the entry to `video.config.js`. |
@@ -16,16 +16,18 @@ Do not claim success after a nonzero exit. Fix the cause, rerun `check`, and onl
16
16
  | `PROPS_INVALID` | Props are not JSON or fail `validateProps()` | Fix the props file or validator. |
17
17
  | `FRAME_OUT_OF_RANGE` | Frame is outside `0..durationInFrames - 1` | Choose a valid frame. |
18
18
  | `AUDIO_INVALID` | Bad clip or unreadable/unsupported audio | Use a local project-relative file with a decodable audio stream. |
19
+ | `MEDIA_INVALID` | Embedded video has invalid attributes, is remote, or cannot be decoded | Use a local seekable video file and valid `data-frameyard-*` values. |
20
+ | `MEDIA_SEEK_FAILED` | Embedded video did not reach the requested frame time | Re-encode the source with a regular keyframe interval or remove the bad source. |
19
21
  | `ASSET_NOT_FOUND` | Required file is missing or unreadable | Restore the asset, model, or text file. |
20
22
  | `OUTPUT_EXISTS` | Output exists and replacement was not approved | Reuse it, choose another path, or pass `--overwrite`/`--force` with permission. |
21
23
  | `OUTPUT_FAILED` | An output or manifest could not be written atomically | Check directory permissions and free space; rerun. |
22
24
  | `EXECUTABLE_NOT_FOUND` | `qwen-tts` or `whisper-cli` could not be resolved | Pass `--executable`, set the environment variable, or fix `PATH`. |
23
25
  | `TTS_FAILED` | Speech synthesis failed or produced an invalid WAV | Read stderr; verify the talker/codec models and CLI compatibility. |
24
26
  | `TRANSCRIBE_FAILED` | Normalization or `whisper-cli` failed | Verify FFmpeg, the source media, and the Whisper model. |
25
- | `TRANSCRIPT_INVALID` | Canonical transcript JSON is malformed | Regenerate it with `frameyard transcribe`; do not hand-edit timestamps. |
27
+ | `TRANSCRIPT_INVALID` | Canonical transcript JSON is malformed | Regenerate it with `npx frameyard transcribe`; do not hand-edit timestamps. |
26
28
  | `TRANSCRIPT_WORDS_UNAVAILABLE` | Karaoke requested without measured word timings | Re-run transcription with `--words` or remove `words: true`. |
27
29
  | `MANIFEST_INVALID` | Provenance manifest is corrupt or unsupported | Restore or regenerate the manifest; do not invent hashes. |
28
- | `FFMPEG_FAILED` | FFmpeg is missing, lacks encoders, or failed | Run `frameyard doctor --json`; install FFmpeg with libx264 and AAC. |
30
+ | `FFMPEG_FAILED` | FFmpeg is missing, lacks encoders, or failed | Run `npx frameyard doctor --json`; install FFmpeg with libx264 and AAC. |
29
31
  | `FFPROBE_FAILED` | FFprobe is missing or failed | Install FFmpeg/FFprobe together. |
30
32
  | `BROWSER_FAILED` | Chromium could not start | Install the Playwright Chromium build. |
31
33
  | `RENDER_FAILED` | Capture or encoding failed | Read stderr; rerun `check` and a representative `still`. |
@@ -38,6 +40,7 @@ Do not claim success after a nonzero exit. Fix the cause, rerun `check`, and onl
38
40
  - Existing outputs are replaced only with `--overwrite` (renders/stills) or `--force` (generated assets).
39
41
  - Cancellation terminates the child inference process and cleans temporary work.
40
42
  - `check` validates every frame and declared transcripts without encoding.
43
+ - `check`, `still`, and `render` seek embedded videos and wait for decode before evaluating or capturing a frame.
41
44
  - `doctor` inspects but never repairs, downloads, or modifies the environment.
42
45
 
43
46
  ## When visual inspection is impossible
@@ -92,6 +92,7 @@ Use the error `code` for decisions, not message text. `command` is `null` only f
92
92
  "durationInFrames": 900,
93
93
  "durationSeconds": 30,
94
94
  "framesRendered": 900,
95
+ "concurrency": 1,
95
96
  "bytes": 1234567,
96
97
  "audio": { "clips": 1, "warnings": [] },
97
98
  "transcripts": { "clips": 1, "warnings": [] },
@@ -99,7 +100,7 @@ Use the error `code` for decisions, not message text. `command` is `null` only f
99
100
  }
100
101
  ```
101
102
 
102
- A successful render means the final path exists and was published atomically. A nonzero exit means no new partial video was published.
103
+ A successful render means the final path exists and was published atomically. A nonzero exit means no new partial video was published. `concurrency` is the number of browser sessions actually used; it is capped by `durationInFrames` and by the CLI maximum of 16.
103
104
 
104
105
  ## tts
105
106
 
@@ -79,8 +79,8 @@ Apply that value to text styling inside `update({ frame })`. Do not use timers,
79
79
  ## Check and inspect
80
80
 
81
81
  ```sh
82
- frameyard check karaoke --json
83
- frameyard still karaoke --frame 120 --out karaoke-120.png --json
82
+ npx frameyard check karaoke --json
83
+ npx frameyard still karaoke --frame 120 --out karaoke-120.png --json
84
84
  ```
85
85
 
86
86
  Inspect several frames across a word boundary. A single still cannot prove that highlighting is synchronized.
@@ -88,7 +88,7 @@ Inspect several frames across a word boundary. A single still cannot prove that
88
88
  ## Derived exports
89
89
 
90
90
  ```sh
91
- frameyard transcribe assets/voice.wav --model models/ggml-base.en.bin \
91
+ npx frameyard transcribe assets/voice.wav --model models/ggml-base.en.bin \
92
92
  --out assets/voice.transcript.json --export srt,vtt --json
93
93
  ```
94
94
 
@@ -5,7 +5,7 @@ import path from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
 
7
7
  const scriptDir = path.dirname(fileURLToPath(import.meta.url));
8
- const localCli = path.resolve(scriptDir, "../../dist/cli.js");
8
+ const localCli = path.resolve(scriptDir, "../../../dist/cli.js");
9
9
  const configured = process.env.FRAMEYARD_CLI;
10
10
 
11
11
  let command;
@@ -43,7 +43,7 @@ for (const signal of ["SIGINT", "SIGTERM"]) {
43
43
  child.once("error", (error) => {
44
44
  if (error.code === "ENOENT") {
45
45
  process.stderr.write(
46
- "The frameyard CLI was not found. Add frameyard to PATH or set FRAMEYARD_CLI to dist/cli.js.\n",
46
+ "The frameyard CLI was not found. Run `npx frameyard`, add frameyard to PATH, or set FRAMEYARD_CLI to an installed CLI path.\n",
47
47
  );
48
48
  } else {
49
49
  process.stderr.write(`Could not start the Frameyard CLI: ${error.message}\n`);
package/skill/README.md DELETED
@@ -1,29 +0,0 @@
1
- # Frameyard agent skill
2
-
3
- This directory is the optional, host-neutral Frameyard agent skill. Its API version matches the npm package version. The npm package includes this directory.
4
-
5
- ## Install or reference
6
-
7
- Copy or symlink the whole `skill/` directory into the skill location your agent host uses. Examples:
8
-
9
- - Claude Code project skills: `.claude/skills/frameyard`
10
- - Agent hosts that read `.agents/skills`: `.agents/skills/frameyard`
11
- - Global installations: place it under your host's documented personal skills directory
12
-
13
- The skill is host-neutral: it uses only the normal `frameyard` project format and CLI. There is no installer command and no agent-only runtime.
14
-
15
- When the skill is copied outside the npm package, `scripts/frameyard.mjs` uses the `frameyard` command on `PATH`. If that command is unavailable, set `FRAMEYARD_CLI` to the installed `dist/cli.js` path.
16
-
17
- ## Contents
18
-
19
- - `SKILL.md`: trigger description and operating workflow
20
- - `references/deterministic-authoring.md`
21
- - `references/audio-assets.md`
22
- - `references/subtitles-karaoke.md`
23
- - `references/json-results.md`
24
- - `references/failure-recovery.md`
25
- - `scripts/frameyard.mjs`: thin fallback wrapper for the installed CLI
26
-
27
- ## Version matching
28
-
29
- `SKILL.md` records `frameyard-api-version`. If it does not match the installed package's version, use the skill files shipped inside that package. Contract changes are documented in the repository README and design baseline.
package/skill/SKILL.md DELETED
@@ -1,57 +0,0 @@
1
- ---
2
- name: frameyard
3
- description: Create, validate, inspect, and render deterministic Frameyard videos authored with HTML, CSS, and native JavaScript. Use when a user asks to build or operate a Frameyard project, produce parameterized video, add a local TTS or transcription asset through Frameyard's wrappers, or diagnose a Frameyard render. Do not use for general-purpose media generation outside a Frameyard project.
4
- frameyard-api-version: 0.1.0
5
- ---
6
-
7
- # Frameyard
8
-
9
- Use Frameyard's public project format and CLI. Do not introduce an agent-only runtime, a hosted service, or direct calls to `qwen-tts` or `whisper-cli`.
10
-
11
- ## First decisions
12
-
13
- 1. Find the project: look for `video.config.js` from the working directory upward, or run `frameyard init <directory>` to scaffold one.
14
- 2. Read the composition entry points listed in `video.config.js`. There is no directory scanning.
15
- 3. Read the existing props file and composition source before changing behavior.
16
- 4. If the task needs speech or transcription, confirm that inference is requested or explicitly approved before producing assets.
17
-
18
- ## Workflow
19
-
20
- 1. **Author** the composition with HTML, CSS, SVG, Canvas 2D, and native JavaScript ES modules. Keep `width`, `height`, `fps`, and `durationInFrames` fixed. Read [deterministic authoring](references/deterministic-authoring.md).
21
- 2. **Provide props** as JSON. Validate domain-specific requirements with `validateProps()`.
22
- 3. **Produce optional assets** with `frameyard tts` and `frameyard transcribe`, never with the upstream executables. Read [audio assets](references/audio-assets.md). Do not overwrite or regenerate existing generated assets without permission.
23
- 4. **Validate** with `frameyard check <id> --json`. It evaluates every frame, checks audio and declared transcripts, and does not encode.
24
- 5. **Inspect** representative frames with `frameyard still <id> --frame <n> --out <file.png> --json`. View the PNGs when the host supports image viewing. If visual inspection is impossible, say so explicitly.
25
- 6. **Render** with `frameyard render <id> --out <file.mp4> [--props <file>] --json`.
26
- 7. **Report** the project path, composition entry, props, generated assets, provenance manifest, validation results, still paths and inspection status, final video path, and any failures.
27
-
28
- Never claim success when `check` fails, a still is missing, a render exits nonzero, or visual inspection was skipped. A partial or unpublished render is not a successful result.
29
-
30
- ## Command rules
31
-
32
- - Use `--json` for every non-interactive command. Parse stdout as the structured result; treat stderr as progress and diagnostics.
33
- - Keep the current output files unless the user explicitly approves replacement. Use `--force` only with that approval.
34
- - Use `--project <dir>` when not running from the project root.
35
- - Use `frameyard doctor --json` before reporting an environment problem. It inspects without repairing or downloading.
36
- - Read [JSON results](references/json-results.md) for result and error shapes, and [failure recovery](references/failure-recovery.md) for actionable error codes.
37
-
38
- ## Determinism guardrails
39
-
40
- - A frame may depend only on its integer `frame`, immutable `props`, and assets loaded before frame production.
41
- - Use `random("stable:key")` from `frameyard/runtime`; never use `Math.random()`.
42
- - Never use `Date.now()`, `performance.now()`, timers, independent `requestAnimationFrame` loops, or CSS transitions as animation inputs.
43
- - Finish remote data, fonts, and image loading during `mount()` or before the first frame. Network requests are blocked during frame capture.
44
- - Keep `update({ frame, props })` synchronous and seek-safe. Do not carry state between frames.
45
- - Do not run model inference inside `mount()`, `update()`, or frame production.
46
-
47
- ## Subtitle and karaoke rule
48
-
49
- Use canonical transcripts produced by `frameyard transcribe`. Convert integer source-relative milliseconds to composition frames with the audio clip's `startFrame`, `trimStart`, and the composition `fps`. Karaoke highlighting requires actual `words` timings; never interpolate segment timings. Read [subtitles and karaoke](references/subtitles-karaoke.md).
50
-
51
- ## Host script
52
-
53
- `scripts/frameyard.mjs` runs the installed package CLI with inherited stdio. It prefers the package-local `dist/cli.js`, then the `frameyard` command on `PATH`. Prefer the `frameyard` command directly; the script is a fallback for hosts that cannot resolve package binaries. When the skill is copied out of the package, set `FRAMEYARD_CLI` to the installed CLI if `frameyard` is not on `PATH`.
54
-
55
- ```sh
56
- node scripts/frameyard.mjs check <id> --json
57
- ```