hyperframes 0.8.37 → 0.8.38

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 (30) hide show
  1. package/dist/cli.js +118 -142
  2. package/dist/docs/examples.md +1 -1
  3. package/dist/fontLocalizeCli.js +3 -92
  4. package/dist/hyperframes-player.global.js +1 -1
  5. package/dist/skills/hyperframes/SKILL.md +1 -1
  6. package/dist/skills/hyperframes/references/brief-contract.md +108 -0
  7. package/dist/skills/hyperframes/references/brief-format.md +72 -0
  8. package/dist/skills/hyperframes/references/capability-menu.md +1 -1
  9. package/dist/skills/hyperframes/references/frame-worker-core.md +80 -0
  10. package/dist/skills/hyperframes/references/intent-interview.md +8 -8
  11. package/dist/skills/hyperframes/references/production-loop.md +26 -0
  12. package/dist/skills/hyperframes/references/review-loop.md +41 -0
  13. package/dist/skills/hyperframes/references/route-briefs.md +1 -1
  14. package/dist/skills/hyperframes/references/script-format.md +52 -0
  15. package/dist/skills/hyperframes/references/storyboard-format.md +130 -0
  16. package/dist/skills/hyperframes/references/subagent-dispatch.md +35 -0
  17. package/dist/skills/hyperframes/scripts/lib/frame-packets-core.mjs +212 -0
  18. package/dist/skills/hyperframes-cli/SKILL.md +9 -8
  19. package/dist/skills/hyperframes-cli/references/init-and-scaffold.md +6 -6
  20. package/dist/skills/hyperframes-cli/references/preview-render.md +6 -3
  21. package/dist/studio/assets/{hyperframes-player-DrGk-lf5.js → hyperframes-player-otov4oIw.js} +1 -1
  22. package/dist/studio/assets/{index-xK1YGiPD.js → index-B-FzDhAR.js} +1 -1
  23. package/dist/studio/assets/{index-BtGF7Kl-.js → index-Bw6cyxOm.js} +1 -1
  24. package/dist/studio/assets/{index-DMsdtB-j.js → index-DWFEyVJZ.js} +4 -4
  25. package/dist/studio/index.html +1 -1
  26. package/dist/studio/index.js +1 -1
  27. package/dist/studio/index.js.map +1 -1
  28. package/dist/templates/blank/index.html +17 -37
  29. package/dist/templates/from-file/index.html +74 -0
  30. package/package.json +1 -1
@@ -0,0 +1,130 @@
1
+ # Storyboard format — `STORYBOARD.md` + parsed manifest
2
+
3
+ Defines the storyboard's **base data format** only: the `STORYBOARD.md` file shape and the `StoryboardManifest` it parses into. How a workflow _generates_ a storyboard lives in that workflow; the optional narration/TTS file (`SCRIPT.md`) is a separate concern owned by the TTS step, not here.
4
+
5
+ A storyboard is the **plan layer** for a video — an ordered set of **frames** (key moments) in one markdown file. HyperFrames Studio renders it as a contact sheet (the Storyboard view, available by default in every Studio session). Parser: `@hyperframes/core/storyboard` → `StoryboardManifest`; read API: `GET /api/projects/<id>/storyboard`.
6
+
7
+ ## Frontmatter (global direction)
8
+
9
+ YAML block at the top. Unknown keys are kept under `globals.extra`.
10
+
11
+ | Key | Meaning | Example |
12
+ | ---------- | ----------------------------------------------------------------- | ----------------------------------------- |
13
+ | `format` | Canvas size | `1920x1080` |
14
+ | `duration` | The brief's rough length expectation (advisory, not a hard limit) | `22s` |
15
+ | `message` | One-line thesis | `Ship a launch video in an afternoon` |
16
+ | `arc` | Narrative arc | `Hook → Problem → Solution → Proof → CTA` |
17
+ | `audience` | Who it's for | `indie devs on X` |
18
+ | `mode` | Interaction mode (see `brief-contract.md`; default collaborative) | `autonomous` |
19
+
20
+ Set `duration` from the brief's `length` when the storyboard is first written. It is an expectation, not a gate: assembly reports where the cut actually lands against it and flags a large gap — judge whether the drift serves the piece, and update the value when the intended length genuinely changes.
21
+
22
+ ## Per-frame sections
23
+
24
+ One `## Frame N — Title` heading per frame (`Frame` / `Beat` / `Scene` accepted at H2/H3). Metadata as `- key: value` bullets; everything below them until the next heading is the free-form **narrative**.
25
+
26
+ | Key | Meaning |
27
+ | --------------- | ------------------------------------------------------------------------------------------------------------- |
28
+ | `status` | `outline` → `built` → `animated` (defaults `outline`) |
29
+ | `src` | project-relative path to the frame's HTML sub-composition (the tile poster renders from it) |
30
+ | `duration` | e.g. `4s` |
31
+ | `transition_in` | `crossfade` / `cut` / `wipe` … (alias `transition`) |
32
+ | `scene` | one-line contact-sheet caption (aliases `description` / `summary` / `caption`) |
33
+ | `voiceover` | the frame's narration _guide_ (aliases `vo` / `voice_over` / `narration`) |
34
+ | `poster` | seconds to seek for the tile poster (past the intro animation) |
35
+ | _any other key_ | kept verbatim under the frame's `extra` — a workflow carries its own per-frame data (effects, assets, …) here |
36
+
37
+ ## Parsed manifest
38
+
39
+ The parser is **lenient**: it never throws and records anything surprising as a `warning`.
40
+
41
+ ```
42
+ StoryboardManifest {
43
+ globals: { format?, message?, arc?, audience?, extra: {…} }
44
+ frames: Array<{
45
+ index, number?, title?,
46
+ status, // "outline" | "built" | "animated"
47
+ src?, duration? / durationSeconds?, transitionIn?,
48
+ scene?, voiceover?, poster?,
49
+ narrative, // markdown below the metadata
50
+ extra: {…} // unknown keys, preserved
51
+ }>
52
+ warnings: Array<{ message, line?, frameIndex? }>
53
+ }
54
+ ```
55
+
56
+ The read API also adds `srcExists` per frame and attaches the optional `SCRIPT.md` payload when present.
57
+
58
+ ## `SCRIPT.md` (out of scope here)
59
+
60
+ Optional, free-form, **not parsed into the manifest** — the locked-narration file that drives TTS. Its format is defined in `references/script-format.md`, and it is absent for videos with no narration/TTS. The per-frame `voiceover` above is the storyboard's own narration guide.
61
+
62
+ ## Frame comments — `.hyperframes/frame-comments.json`
63
+
64
+ The storyboard review's **structured feedback channel** — the file Studio's per-frame comment boxes write on submit (chat feedback follows the same rule — `brief-contract.md` § 1, the comments channel). Like `SCRIPT.md`, it is a sibling of the storyboard, not parsed into the manifest.
65
+
66
+ ```json
67
+ {
68
+ "version": 1,
69
+ "pass": "sketch",
70
+ "submitted_at": "2026-07-09T12:04:00Z",
71
+ "comments": [
72
+ {
73
+ "frame": 3,
74
+ "src": "compositions/frames/03-mechanism.html",
75
+ "title": "Mechanism",
76
+ "text": "Swap the bar chart for a before/after slider."
77
+ }
78
+ ]
79
+ }
80
+ ```
81
+
82
+ | Field | Meaning |
83
+ | ------------------------ | ------------------------------------------------------------------------------------------------------------------- |
84
+ | `pass` | which review the batch belongs to: `storyboard` (text layer) / `sketch` (static frames) / `final` (assembled video) |
85
+ | `comments[].frame` | the frame's 1-based `index` in the manifest — the key |
86
+ | `comments[].src` `title` | copied from the frame at submit time — if frames get reordered after submit, the mismatch shows |
87
+ | `comments[].text` | the feedback, verbatim |
88
+
89
+ Lifecycle — the whole contract: a workflow finding this file at a checkpoint treats it as the revision feedback — **revise exactly the frames named, delete the file, re-present**. Writers create it only on submit; it never lingers across rounds.
90
+
91
+ ## Example
92
+
93
+ ```markdown
94
+ ---
95
+ format: 1920x1080
96
+ message: "Ship a launch video in an afternoon"
97
+ arc: Hook → Problem → Solution → Proof → CTA
98
+ audience: indie devs on X
99
+ ---
100
+
101
+ ## Frame 1 — Hook
102
+
103
+ - scene: Big type punches in on the beat
104
+ - duration: 3s
105
+ - poster: 2s
106
+ - transition_in: cut
107
+ - status: animated
108
+ - voiceover: "Ship a launch video in an afternoon."
109
+ - src: compositions/frames/01-hook.html
110
+
111
+ Open cold on the promise. This is the thesis — everything after pays it off.
112
+
113
+ ## Frame 2 — The problem
114
+
115
+ - scene: A 20-minute timer spins on a stack of rejected takes
116
+ - duration: 4s
117
+ - transition_in: crossfade
118
+ - status: built
119
+ - voiceover: "The old way? Prompt, wait twenty minutes, get something that misses."
120
+ - src: compositions/frames/02-problem.html
121
+
122
+ The old way: prompt, wait, get something that misses. Establish the pain we remove.
123
+ ```
124
+
125
+ ## Notes
126
+
127
+ - A frame with `status: outline` and no built `src` renders as an outline placeholder.
128
+ - `built` is the middle rung: the frame's HTML exists and its **layout is confirmed** (a wireframe sketch or better) — motion not yet added. Studio chips it blue.
129
+ - The process that walks these statuses — plan, sketch, build, each pass reviewed on the board — is `review-loop.md`.
130
+ - Multi-line `voiceover` values collapse to one line on save.
@@ -0,0 +1,35 @@
1
+ # Subagent dispatch — harness adapter
2
+
3
+ The video workflows (`product-launch-video` / `faceless-explainer` / `pr-to-video` / `motion-graphics` / `general-video`) describe subagent dispatch in harness-neutral verbs. This file maps those verbs to the primitives of whatever agent harness you are running on. Read it once per run, before the first dispatch; everything else in the workflows (dispatch packets, file artifacts, exit-code gates, Resume tables) is harness-independent and needs no translation.
4
+
5
+ ## The contract (identical on every harness)
6
+
7
+ - **DISPATCH(role_file, dispatch_context)** — start one child agent whose prompt is the **full contents of the named role file** (a builder-assembled payload like `.hyperframes/frame-packets/_role.md`, or a workflow's `sub-agents/<role>.md`) followed by the `## Dispatch context` block from the workflow, copied **verbatim** (never digested or paraphrased). Every harness below accepts arbitrary task text, so this works everywhere; never rely on the child seeing your conversation, memory, or skills — the prompt and the files on disk are its entire world.
8
+ - **Parallel fan-out** — when a step says "start N workers in parallel", the workers are mutually independent (no ordering, no shared state beyond the filesystem). Run as many concurrently as your harness allows.
9
+ - **WAIT** — a step's completion criterion is always **the expected artifact existing on disk** (e.g. `compositions/<scene-id>.html`), never the harness's completion notification (some harnesses deliver results best-effort). After waiting, verify the artifacts; a missing artifact means that child failed — re-dispatch it once with the same prompt before surfacing an error.
10
+
11
+ ## Concurrency cap → batching rule (cap never changes scope)
12
+
13
+ A harness concurrency limit **reduces parallelism, not work**: every scene still gets built, one scene per dispatch, with the available slots chewing through the full list.
14
+
15
+ - When the harness queues excess children internally, submit **all N at once** and let the queue drain.
16
+ - Harness hard-caps active children (e.g. OpenClaw `maxChildrenPerAgent`) → dispatch in **waves of the cap size**: start `cap` workers, wait for their artifacts, start the next wave, until all N scenes exist. Example: 9 scenes on a cap-3 harness = 3 waves of 3 — never drop scenes, never merge scenes into one worker to fit the cap.
17
+
18
+ ## Harness mapping
19
+
20
+ Use the current harness's native delegation and waiting tools when they are available. The workflow contract stays the same:
21
+
22
+ - **DISPATCH** sends the complete role file and dispatch context to one worker.
23
+ - **Parallel fan-out** starts independent workers concurrently up to the harness limit.
24
+ - **WAIT** verifies the expected artifacts on disk, not only a completion notification.
25
+ - **Re-dispatch** starts a fresh worker with the same context plus the gate failure.
26
+
27
+ When native delegation is unavailable, use the existing fallback ladder: launch headless CLI workers that share the project filesystem, then fall back to inline serial execution.
28
+
29
+ On Codex, native delegation requires the user's explicit permission. Fold a one-line request into the workflow's first existing user pause before dispatch; a standing grant in `AGENTS.md` or the kickoff prompt also counts. Without it, use the fallback ladder rather than silently skipping work.
30
+
31
+ ## Vocabulary mapping
32
+
33
+ - A request to work "in the background" means dispatch concurrently when the harness supports it.
34
+ - Load a named skill through the harness's skill mechanism, or read `<skills-root>/<skill>/SKILL.md` directly.
35
+ - Map generic read, write, edit, and shell verbs to the current harness's equivalent tools.
@@ -0,0 +1,212 @@
1
+ // Shared frame-packet builder — the script half of the frame-worker core/delta split.
2
+ //
3
+ // Each narrative workflow ships a thin `scripts/frame-packets.mjs` wrapper that pins
4
+ // its own paths (animation skill, role delta, design-truth resolution, extra packet
5
+ // sections) and delegates everything else here, exactly as the markdown half already
6
+ // does with `references/frame-worker-core.md` + each workflow's delta. One owner for
7
+ // the packet-building logic; the wrappers own only what genuinely differs per workflow.
8
+ //
9
+ // Packet (<frame_id>.md) = project inputs + the frame's exact `## Frame N` block
10
+ // + the blueprint body + every cited rule recipe, inlined — so a worker never opens
11
+ // the shared STORYBOARD.md or any skill document. Cited motions are found
12
+ // mechanically: the explicit `- rules:` field when present, plus every valid rule id
13
+ // (a filename under the animation skill's rules/) mentioned in the block.
14
+ //
15
+ // _role.md = frame-worker-core.md + the workflow's sub-agents/frame-worker.md,
16
+ // concatenated verbatim — the complete worker role, assembled from the two source
17
+ // documents so nothing is hand-maintained twice.
18
+
19
+ import {
20
+ existsSync,
21
+ mkdirSync,
22
+ readFileSync,
23
+ readdirSync,
24
+ realpathSync,
25
+ writeFileSync,
26
+ } from "node:fs";
27
+ import { basename, join, resolve } from "node:path";
28
+ import { pathToFileURL } from "node:url";
29
+
30
+ export function field(block, name) {
31
+ const match = block.match(new RegExp(`^-\\s+${name}:\\s*(.+)$`, "im"));
32
+ return match?.[1]?.trim() ?? null;
33
+ }
34
+
35
+ export function splitFrames(storyboard) {
36
+ const matches = [...storyboard.matchAll(/^## Frame\s+([^\n]+)$/gm)];
37
+ return matches.map((match, index) => {
38
+ const start = match.index;
39
+ const end = matches[index + 1]?.index ?? storyboard.length;
40
+ return {
41
+ heading: match[1].trim(),
42
+ block: storyboard.slice(start, end).trim(),
43
+ };
44
+ });
45
+ }
46
+
47
+ export function frameId(frame) {
48
+ const src = field(frame.block, "src");
49
+ if (!src) throw new Error(`${frame.heading}: missing src`);
50
+ return basename(src).replace(/\.html?$/i, "");
51
+ }
52
+
53
+ export function selectedFile(path, heading) {
54
+ if (!path || !existsSync(path)) return "";
55
+ return `\n## ${heading}\n\n${readFileSync(path, "utf8").trim()}\n`;
56
+ }
57
+
58
+ function escapeRegExp(id) {
59
+ return id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
60
+ }
61
+
62
+ export function knownRuleIds(animationDir) {
63
+ const rulesDir = join(animationDir, "rules");
64
+ if (!existsSync(rulesDir)) {
65
+ console.warn(
66
+ `frame-packets: no rules dir at ${rulesDir} — packets will inline no motion recipes`,
67
+ );
68
+ return [];
69
+ }
70
+ return readdirSync(rulesDir)
71
+ .filter((name) => name.endsWith(".md"))
72
+ .map((name) => name.replace(/\.md$/, ""));
73
+ }
74
+
75
+ export function citedRules(block, ruleIds) {
76
+ const explicit = (field(block, "rules") ?? "")
77
+ .split(/[,\s]+/)
78
+ .map((rule) => rule.trim())
79
+ .filter(Boolean);
80
+ const mentioned = ruleIds.filter((id) =>
81
+ new RegExp(`(?<![\\w-])${escapeRegExp(id)}(?![\\w-])`, "i").test(block),
82
+ );
83
+ return [...new Set([...explicit, ...mentioned])].filter((id) => ruleIds.includes(id));
84
+ }
85
+
86
+ // visual-design.md tells the author to write the blueprint as `<id> (Reproduce)`
87
+ // or `<id> (Adapt)` — the qualifier is direction for the frame worker, not part of
88
+ // the filename. Parse the field into the id it names (or null for `compose`), so
89
+ // no caller ever resolves a raw field value against the blueprints directory.
90
+ export function blueprintId(block) {
91
+ const raw = field(block, "blueprint");
92
+ if (!raw) return null;
93
+ const id = raw.replace(/\s*\([^)]*\)\s*$/, "").trim();
94
+ return id && id.toLowerCase() !== "compose" ? id : null;
95
+ }
96
+
97
+ export function resourceSections(block, { animationDir, ruleIds, frameId }) {
98
+ let sections = "";
99
+ const blueprint = blueprintId(block);
100
+ if (blueprint) {
101
+ const blueprintsDir = join(animationDir, "blueprints");
102
+ const path = join(blueprintsDir, `${blueprint}.md`);
103
+ // A blueprint that resolved to nothing used to inline an empty string, so the
104
+ // packet shipped without the one document the frame was designed against and
105
+ // the run still reported success. Name it instead — but only when the library
106
+ // is actually there to be named against. The animation skill installs on
107
+ // demand, so an absent blueprints/ is a missing install, not a bad id, and it
108
+ // degrades with a warning exactly like an absent rules/ (see knownRuleIds).
109
+ if (!existsSync(blueprintsDir)) {
110
+ console.warn(
111
+ `frame-packets: no blueprints dir at ${blueprintsDir} — packets will inline no blueprint`,
112
+ );
113
+ } else if (!existsSync(path)) {
114
+ throw new Error(`${frameId ?? "frame"}: blueprint "${blueprint}" has no file at ${path}`);
115
+ } else {
116
+ sections += selectedFile(path, `Selected blueprint: ${blueprint}`);
117
+ }
118
+ }
119
+ for (const rule of citedRules(block, ruleIds)) {
120
+ sections += selectedFile(
121
+ join(animationDir, "rules", `${rule}.md`),
122
+ `Selected motion rule: ${rule}`,
123
+ );
124
+ }
125
+ return sections;
126
+ }
127
+
128
+ export function buildRolePayload({ corePath, deltaPath, outDir }) {
129
+ const core = readFileSync(corePath, "utf8").trim();
130
+ const delta = readFileSync(deltaPath, "utf8").trim();
131
+ const role = `${core}\n\n---\n\n${delta}\n`;
132
+ mkdirSync(outDir, { recursive: true });
133
+ const path = join(outDir, "_role.md");
134
+ writeFileSync(path, role);
135
+ return { path, bytes: Buffer.byteLength(role) };
136
+ }
137
+
138
+ export function buildFramePackets({
139
+ projectDir,
140
+ storyboardPath = join(projectDir, "STORYBOARD.md"),
141
+ outDir = join(projectDir, ".hyperframes", "frame-packets"),
142
+ maxPacketBytes = 48_000,
143
+ animationDir,
144
+ corePath,
145
+ deltaPath,
146
+ // Per-workflow hooks (all optional):
147
+ // designTruthLine(projectDir) -> the packet's design-truth input line
148
+ // validateFrame(frame, id) -> throw to reject a frame before packing
149
+ // extraSections(block) -> extra packet sections appended after the rule recipes
150
+ designTruthLine = (dir) => `- Design tokens: ${join(resolve(dir), "frame.md")}`,
151
+ validateFrame,
152
+ extraSections,
153
+ }) {
154
+ const storyboard = readFileSync(storyboardPath, "utf8");
155
+ const frames = splitFrames(storyboard);
156
+ if (frames.length === 0) throw new Error("STORYBOARD.md has no frame blocks");
157
+ const ruleIds = knownRuleIds(animationDir);
158
+
159
+ const packets = frames.map((frame) => {
160
+ const id = frameId(frame);
161
+ if (validateFrame) validateFrame(frame, id);
162
+ const packet = `# Frame packet: ${id}\n\n## Project inputs\n\n- Project: ${resolve(projectDir)}\n${designTruthLine(projectDir)}\n- RULES_DIR: ${join(animationDir, "rules")}\n\n## Assigned storyboard block\n\n${frame.block}\n${resourceSections(frame.block, { animationDir, ruleIds, frameId: id })}${extraSections ? extraSections(frame.block) : ""}`;
163
+ const bytes = Buffer.byteLength(packet);
164
+ if (bytes > maxPacketBytes) {
165
+ throw new Error(`${id}: frame packet is ${bytes} bytes (limit ${maxPacketBytes})`);
166
+ }
167
+ return { frameId: id, path: join(outDir, `${id}.md`), bytes, packet };
168
+ });
169
+
170
+ mkdirSync(outDir, { recursive: true });
171
+ for (const { path, packet } of packets) writeFileSync(path, packet);
172
+ buildRolePayload({ corePath, deltaPath, outDir });
173
+ return packets.map(({ packet: _packet, ...result }) => result);
174
+ }
175
+
176
+ export function flag(argv, name, fallback) {
177
+ const index = argv.indexOf(`--${name}`);
178
+ return index >= 0 && argv[index + 1] ? argv[index + 1] : fallback;
179
+ }
180
+
181
+ // realpath both sides: on macOS /tmp → /private/tmp, and node resolves the main
182
+ // module's symlinks in import.meta.url while argv[1] keeps the invoked spelling —
183
+ // a raw compare silently skips main() when invoked through any symlinked path.
184
+ export function isMainModule(importMetaUrl) {
185
+ if (!process.argv[1]) return false;
186
+ try {
187
+ return pathToFileURL(realpathSync(process.argv[1])).href === importMetaUrl;
188
+ } catch {
189
+ return false;
190
+ }
191
+ }
192
+
193
+ export function runCli({ buildFramePackets: build, buildRolePayload: buildRole }) {
194
+ const argv = process.argv.slice(2);
195
+ const projectDir = resolve(flag(argv, "project", "."));
196
+ const outDir = resolve(flag(argv, "out-dir", join(projectDir, ".hyperframes", "frame-packets")));
197
+ try {
198
+ const packets = build({
199
+ projectDir,
200
+ storyboardPath: resolve(flag(argv, "storyboard", join(projectDir, "STORYBOARD.md"))),
201
+ outDir,
202
+ });
203
+ const role = buildRole({ outDir });
204
+ console.log(`✓ frame packets: ${packets.length} bounded packet(s)`);
205
+ for (const packet of packets)
206
+ console.log(` ${packet.frameId}: ${packet.bytes} bytes → ${packet.path}`);
207
+ console.log(` worker role: ${role.bytes} bytes → ${role.path}`);
208
+ } catch (error) {
209
+ console.error(`✗ frame packets: ${error.message}`);
210
+ process.exit(1);
211
+ }
212
+ }
@@ -15,15 +15,15 @@ Run commands as `npx hyperframes ...` unless project instructions provide a wrap
15
15
 
16
16
  ## Development loop
17
17
 
18
- 1. **Scaffold:** `npx hyperframes init <project>` or capture a site. In non-TTY mode, pass `--non-interactive --example=<name>`.
18
+ 1. **Scaffold:** `npx hyperframes init <project>` (centered blank). Or capture a site. Pass `--example=<name>` only to start from a named example.
19
19
  2. **Find the move:** before authoring motion by hand, search for a primitive that already does it: `npx hyperframes catalog --query "reveal a headline one line at a time"`. Ask for the effect you want rather than the mechanism you have in mind. Install with `npx hyperframes add <name>` (see `/hyperframes-registry`). Author by hand only once nothing fits.
20
20
  3. **Author:** write the composition using `/hyperframes-core`.
21
21
  4. **Get fast feedback while editing:** run `npx hyperframes lint` after the first HTML pass and after structural changes.
22
22
  5. **Run the final gate:** run `npx hyperframes check`; it reruns lint before opening the browser. Do not prepend a redundant standalone lint invocation. Add `--snapshots` for annotated overview frames and finding crops.
23
23
  6. **Inspect sub-compositions:** when `index.html` mounts `data-composition-src`, capture midpoint snapshots and inspect each mounted scene.
24
24
  7. **Open the final Studio preview:** run `npx hyperframes preview --background`, verify the URL returns HTTP 200, hand the timeline project URL to the user, and ask whether to revise or render. Keep it alive until review ends.
25
- 8. **Render only after approval:** use draft quality for iteration and high quality for delivery.
26
- 9. **Verify the output:** confirm the file exists, is non-empty, and has a plausible duration.
25
+ 8. **Render only after approval:** use `--quality draft` while iterating, `--quality looks` for the first real encode (the CLI default), and `--quality delivery` for final delivery.
26
+ 9. **Verify the output:** confirm the file exists and is non-empty. Read the render summary's second line (`beginframe` vs `screenshot`, GPU, stage timings). `screenshot` + `software gpu` on Linux is the slow path. `ffprobe -v error -show_format -show_streams` and compare duration (and fps if the brief set it) to the root `data-duration`.
27
27
 
28
28
  ## Mandatory creator-edit cross-references
29
29
 
@@ -46,9 +46,9 @@ npx hyperframes lint
46
46
  # Required final gate; includes lint.
47
47
  npx hyperframes check
48
48
  npx hyperframes preview --background
49
- npx hyperframes render --quality high --output out.mp4
49
+ npx hyperframes render --quality looks --output out.mp4
50
50
  test -s out.mp4
51
- ffprobe -v error -show_format out.mp4
51
+ ffprobe -v error -show_format -show_streams out.mp4
52
52
  ```
53
53
 
54
54
  `check` runs lint first, then uses one browser session and one seek pass to audit runtime errors, failed requests, layout, `*.motion.json` assertions, and WCAG contrast. Persistent findings gate the exit code; transient entrance or exit findings are informational. Use `--strict` to gate warnings. `validate`, `inspect`, and `layout` remain aliases for compatibility but must not appear in new instructions or scripts.
@@ -62,7 +62,7 @@ Do not confuse these states:
62
62
  | Storyboard board | Before composition checks, only when `storyboard: yes` | Review plan cards and wireframe sketches. Open `?view=storyboard#project/<name>`. |
63
63
  | Final composition preview | After `check` passes | Review the assembled timeline before render. Open `#project/<name>`. |
64
64
 
65
- The early board is not approval of the final video. Rendering always requires the final approval defined by `hyperframes-core/references/review-loop.md`.
65
+ The early board is not approval of the final video. Rendering always requires the final approval defined by `hyperframes/references/review-loop.md`.
66
66
 
67
67
  ## Sub-composition smoke test
68
68
 
@@ -90,7 +90,7 @@ Treat tiny unstyled content, canvas-sized icons, missing hero elements, or timel
90
90
  npx hyperframes doctor --json | jq -e '.ok' >/dev/null
91
91
  ```
92
92
 
93
- - Non-TTY mode is automatic. `init` requires `--example` there; use `--non-interactive` to force deterministic behavior on a TTY.
93
+ - Non-TTY mode is automatic and scaffolds the centered blank. Pass `--example` only to start from a named example. Use `--non-interactive` to force flag-only mode on a TTY.
94
94
  - Use one `HYPERFRAMES_RUN_ID` for all commands in the same verification loop.
95
95
  - Use `--strict`, `--strict-all`, and `--strict-variables` when the corresponding warnings, variables, or CI conditions must gate the render.
96
96
  - JSON paths redact the home directory as `$HOME`; do not try to reverse the redaction.
@@ -112,7 +112,8 @@ Use `selection.target.hfId` when available, otherwise its selector and source fi
112
112
  | Need | Command |
113
113
  | ---------------------------------------- | ----------------------------------------------------------------------------- |
114
114
  | Fast local iteration | `npx hyperframes render --quality draft` |
115
- | Final local delivery | `npx hyperframes render --quality high --output out.mp4` |
115
+ | First real encode | `npx hyperframes render --quality looks --output out.mp4` |
116
+ | Final local delivery | `npx hyperframes render --quality delivery --output out.mp4` |
116
117
  | Reproducible container render | `npx hyperframes render --docker --strict --output out.mp4` |
117
118
  | Local variable-driven batch render | `npx hyperframes render --batch rows.json --output "renders/{name}.mp4"` |
118
119
  | HeyGen-hosted zero-infrastructure render | `npx hyperframes cloud render` |
@@ -7,18 +7,18 @@ Scaffolding commands. Use these instead of creating files by hand — they set u
7
7
  ## init
8
8
 
9
9
  ```bash
10
- npx hyperframes init my-video # TTY: interactive wizard
10
+ npx hyperframes init my-video # centered blank (TTY: wizard)
11
11
  npx hyperframes init my-video --example warm-grain # pick an example
12
- npx hyperframes init my-video --example blank --resolution portrait
12
+ npx hyperframes init my-video --resolution portrait
13
13
  npx hyperframes init my-video --video clip.mp4 # with video file
14
14
  npx hyperframes init my-video --audio track.mp3 # with audio file
15
- npx hyperframes init my-video --example blank --tailwind # Tailwind v4 browser runtime
16
- npx hyperframes init my-video --non-interactive --example blank # CI/agents — flag-only
15
+ npx hyperframes init my-video --tailwind # Tailwind v4 browser runtime
16
+ npx hyperframes init my-video --non-interactive # CI — flag-only, same blank
17
17
  ```
18
18
 
19
- **Default depends on TTY**: in a terminal, the CLI prompts for example/options. Outside a TTY (CI, agents, piped output) it auto-switches to non-interactive and **requires `--example`** (the CLI errors with a usage example if missing). Pass `--non-interactive` to force flag-only mode even on a TTY.
19
+ **Default depends on TTY**: in a terminal, the CLI prompts for example/options (default: centered blank). Outside a TTY (CI, agents, piped output) it auto-switches to non-interactive and scaffolds that blank. Pass `--example` only to start from a named example. Pass `--non-interactive` to force flag-only mode on a TTY.
20
20
 
21
- Templates: `blank`, `warm-grain`, `play-mode`, `swiss-grid`, `vignelli`, `decision-tree`, `kinetic-type`, `product-promo`, `nyt-graph`, `vscode-theme-visualizer`. (The closed set of `hyperframes:example` items in `registry/registry.json`. `hyperframes catalog` does not list examples — its `--type` takes only `block` or `component` — so this list has no live equivalent and is checked by `bun run lint:skills`.)
21
+ Templates: `blank`, `warm-grain`, `play-mode`, `swiss-grid`, `vignelli`, `decision-tree`, `kinetic-type`, `product-promo`, `nyt-graph`, `vscode-theme-visualizer`. (The closed set of `hyperframes:example` items in `registry/registry.json` plus the bundled `blank` template. `hyperframes catalog` does not list examples — its `--type` takes only `block` or `component` — so this list has no live equivalent and is checked by `bun run lint:skills`.)
22
22
 
23
23
  Other useful flags:
24
24
 
@@ -109,7 +109,8 @@ npx hyperframes render ./my-video --output ./out.mp4 # render from outside the
109
109
  npx hyperframes render --output final.mp4 # named output (no timestamp)
110
110
  npx hyperframes render -c compositions/intro.html -o intro.mp4 # render a specific sub-composition file
111
111
  npx hyperframes render --quality draft # fast iteration
112
- npx hyperframes render --fps 60 --quality high # final delivery
112
+ npx hyperframes render --quality looks # first real encode (default)
113
+ npx hyperframes render --fps 60 --quality delivery # final delivery
113
114
  npx hyperframes render --format webm # transparent WebM
114
115
  npx hyperframes render --docker # byte-identical
115
116
  ```
@@ -122,7 +123,7 @@ npx hyperframes render --docker # byte-identical
122
123
  | `--composition`, `-c` | path to composition file | `index.html` | Render a specific composition file (e.g. `compositions/intro.html`) instead of the project's `index.html`. |
123
124
  | `--output`, `-o` | path | `renders/<project>_<ts>.<ext>` | Output path. Default is timestamped (`<project-name>_YYYY-MM-DD_HH-MM-SS.<ext>`). |
124
125
  | `--fps` | 24, 30, 60 | 30 | 60fps doubles render time |
125
- | `--quality` | draft, standard, high | standard | draft for iterating |
126
+ | `--quality` | draft, looks, delivery, standard, high | looks | `looks` is CRF 16 on the standard preset. `delivery` is `high`. draft for iterating |
126
127
  | `--format` | mp4, webm, mov, gif, png-sequence | mp4 | WebM/MOV render with transparency; gif for inline autoplay in GitHub PRs/READMEs/docs (two-pass palette encode, fps capped at 30 — prefer `--fps 15` — no audio, 1-bit transparency only, HDR falls back to SDR); png-sequence writes RGBA frames to a directory (AE/Nuke/Fusion ingest) |
127
128
  | `--gif-loop` | 0-65535 | 0 | GIF loop count; `0` loops forever. Only with `--format gif`. |
128
129
  | `--resolution` | landscape, portrait, landscape-4k, portrait-4k, square, square-4k (+ aliases `1080p`, `4k`, `uhd`) | — | Supersample via Chrome `deviceScaleFactor`. Aspect ratio must match composition; scale must be an integer. Not with `--hdr`. |
@@ -142,7 +143,9 @@ npx hyperframes render --docker # byte-identical
142
143
  | `--variables-file` | path | — | JSON file with variable values (alternative to `--variables`) |
143
144
  | `--strict-variables` | flag | off | Fail render on undeclared keys or type mismatches in `--variables` |
144
145
 
145
- **Quality guidance:** `draft` while iterating, `standard` for review, `high` for final delivery.
146
+ **Quality guidance:** `draft` while iterating, `looks` (the default) for the first real encode, `delivery` for final delivery. `standard` and `high` still work.
147
+
148
+ **Read the summary's second line.** After the output path and size, `render` prints the capture path, GPU mode, and per-stage timings, e.g. `beginframe capture · software gpu · compile 0.7s · setup 1.1s · capture 8.1s · encode 5.8s`. `capture` is the frame loop and `setup` is calibration plus session init; the capture path reads `a|b` when parallel workers diverged, and `encode (during capture)` means the streaming encoder overlapped the loop, so do not add it to the wall time. Local GPU `auto` (the default) requests BeginFrame. If the line still says `screenshot capture` with `software gpu` on Linux, a hint under it says BeginFrame did not run (needs chrome-headless-shell, no `--resolution` upscale). It only appears when the GPU was auto-probed, never under `--docker` or `--no-browser-gpu`, where software GL is the point. Heavy compositions can stall on software GL. Do not reach for `--debug` just to learn which path ran.
146
149
 
147
150
  **Parametrized renders:** the composition declares its variables on the `<html>` root with **`data-composition-variables`** — a JSON **array of declarations** (`{id, type, label, default}` per entry) that defines the schema. Scripts inside read the resolved values via `window.__hyperframes.getVariables()`. The CLI `--variables '{"title":"Q4 Report"}'` is a JSON **object keyed by id** that overrides those declared defaults for one render; missing keys fall through, so the same composition runs unchanged in dev preview and in production. Sub-comp hosts can also override per-instance with `data-variable-values`. See the `hyperframes-core` skill for the full pattern.
148
151
 
@@ -1,4 +1,4 @@
1
- var ve=Object.defineProperty;var be=(n,i,e)=>i in n?ve(n,i,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[i]=e;var u=(n,i,e)=>be(n,typeof i!="symbol"?i+"":i,e);import{r as le,i as we,a as Ae}from"./index-DMsdtB-j.js";function Ee(n){return n.hasRuntime||n.runtimeInjected?!1:!!(n.hasNestedCompositions||n.hasTimelines&&n.attempts>=5)}function V(n){return typeof n=="object"&&n!==null}function Ce(n){return V(n)&&typeof n.getDuration=="function"}function Te(n){return V(n)&&typeof n.duration=="function"&&typeof n.time=="function"&&typeof n.seek=="function"&&typeof n.play=="function"&&typeof n.pause=="function"}function Se(n){if(!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(n))throw new Error(`Invalid HyperFrames runtime version: ${n}`);return`https://cdn.jsdelivr.net/npm/@hyperframes/core@${n}/dist/hyperframe.runtime.iife.js`}const H=typeof __HYPERFRAMES_RUNTIME_CDN_URL__=="string"?__HYPERFRAMES_RUNTIME_CDN_URL__:Se("0.0.0-dev");function $(n){if(n===null)return null;const i=Number.parseInt(n,10);return Number.isFinite(i)&&i>0?i:null}function Re(n){const i=(n==null?void 0:n.querySelector("[data-composition-id][data-width][data-height]"))??(n==null?void 0:n.querySelector("[data-width][data-height]"));if(!i)return null;const e=$(i.getAttribute("data-width")),t=$(i.getAttribute("data-height"));return e!==null&&t!==null?{width:e,height:t}:null}class xe{constructor(i,e){u(this,"_interval",null);u(this,"_runtimeInjected",!1);this._iframe=i,this._callbacks=e}get runtimeInjected(){return this._runtimeInjected}start(){this.stop(),this._runtimeInjected=!1;let i=0;this._interval=setInterval(()=>{var e;i++;try{const t=this._iframe.contentWindow;if(!t)return;const r=!!(t.__hf||t.__player),s=!!(t.__timelines&&Object.keys(t.__timelines).length>0),o=!!((e=this._iframe.contentDocument)!=null&&e.querySelector("[data-composition-src]"));if(Ee({hasRuntime:r,hasTimelines:s,hasNestedCompositions:o,runtimeInjected:this._runtimeInjected,attempts:i})){this._injectRuntime();return}if(this._runtimeInjected&&!r)return;const d=this._resolvePlaybackDurationAdapter(t);if(d&&d.getDuration()>0){this.stop();const p=Re(this._iframe.contentDocument);this._callbacks.onReady({duration:d.getDuration(),adapter:d,compositionSize:p});return}}catch{}i>=40&&(this.stop(),this._callbacks.onError("Composition timeline not found after 8s"))},200)}stop(){this._interval!==null&&(clearInterval(this._interval),this._interval=null)}resolveDirectTimelineAdapter(){try{const i=this._iframe.contentWindow;return i?this._resolveDirectTimelineAdapterFromWindow(i):null}catch{return null}}resolveDirectTimelineAdapterFromWindow(i){return this._resolveDirectTimelineAdapterFromWindow(i)}hasRuntimeBridge(i){return Reflect.get(i,"__hf")!==void 0||V(Reflect.get(i,"__player"))}_injectRuntime(){var i,e;this._runtimeInjected=!0;try{const t=this._iframe.contentDocument;if(!t)return;const r=t.createElement("script");r.src=H,(t.head||t.documentElement).appendChild(r),(e=(i=this._callbacks).onRuntimeInjected)==null||e.call(i)}catch{}}_resolveDirectTimelineAdapterFromWindow(i){var d,p;if(this.hasRuntimeBridge(i))return null;const e=Reflect.get(i,"__timelines");if(!V(e))return null;const t=Object.keys(e);if(t.length===0)return null;const r=(p=(d=this._iframe.contentDocument)==null?void 0:d.querySelector("[data-composition-id]"))==null?void 0:p.getAttribute("data-composition-id"),s=r&&r in e?r:t[t.length-1],o=e[s];return Te(o)?o:null}_resolvePlaybackDurationAdapter(i){const e=Reflect.get(i,"__player");if(Ce(e))return{kind:"runtime",getDuration:()=>e.getDuration()};const t=this._resolveDirectTimelineAdapterFromWindow(i);return t?{kind:"direct-timeline",timeline:t,getDuration:()=>t.duration()}:null}}const Me=`
1
+ var ve=Object.defineProperty;var be=(n,i,e)=>i in n?ve(n,i,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[i]=e;var u=(n,i,e)=>be(n,typeof i!="symbol"?i+"":i,e);import{r as le,i as we,a as Ae}from"./index-DWFEyVJZ.js";function Ee(n){return n.hasRuntime||n.runtimeInjected?!1:!!(n.hasNestedCompositions||n.hasTimelines&&n.attempts>=5)}function V(n){return typeof n=="object"&&n!==null}function Ce(n){return V(n)&&typeof n.getDuration=="function"}function Te(n){return V(n)&&typeof n.duration=="function"&&typeof n.time=="function"&&typeof n.seek=="function"&&typeof n.play=="function"&&typeof n.pause=="function"}function Se(n){if(!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(n))throw new Error(`Invalid HyperFrames runtime version: ${n}`);return`https://cdn.jsdelivr.net/npm/@hyperframes/core@${n}/dist/hyperframe.runtime.iife.js`}const H=typeof __HYPERFRAMES_RUNTIME_CDN_URL__=="string"?__HYPERFRAMES_RUNTIME_CDN_URL__:Se("0.0.0-dev");function $(n){if(n===null)return null;const i=Number.parseInt(n,10);return Number.isFinite(i)&&i>0?i:null}function Re(n){const i=(n==null?void 0:n.querySelector("[data-composition-id][data-width][data-height]"))??(n==null?void 0:n.querySelector("[data-width][data-height]"));if(!i)return null;const e=$(i.getAttribute("data-width")),t=$(i.getAttribute("data-height"));return e!==null&&t!==null?{width:e,height:t}:null}class xe{constructor(i,e){u(this,"_interval",null);u(this,"_runtimeInjected",!1);this._iframe=i,this._callbacks=e}get runtimeInjected(){return this._runtimeInjected}start(){this.stop(),this._runtimeInjected=!1;let i=0;this._interval=setInterval(()=>{var e;i++;try{const t=this._iframe.contentWindow;if(!t)return;const r=!!(t.__hf||t.__player),s=!!(t.__timelines&&Object.keys(t.__timelines).length>0),o=!!((e=this._iframe.contentDocument)!=null&&e.querySelector("[data-composition-src]"));if(Ee({hasRuntime:r,hasTimelines:s,hasNestedCompositions:o,runtimeInjected:this._runtimeInjected,attempts:i})){this._injectRuntime();return}if(this._runtimeInjected&&!r)return;const d=this._resolvePlaybackDurationAdapter(t);if(d&&d.getDuration()>0){this.stop();const p=Re(this._iframe.contentDocument);this._callbacks.onReady({duration:d.getDuration(),adapter:d,compositionSize:p});return}}catch{}i>=40&&(this.stop(),this._callbacks.onError("Composition timeline not found after 8s"))},200)}stop(){this._interval!==null&&(clearInterval(this._interval),this._interval=null)}resolveDirectTimelineAdapter(){try{const i=this._iframe.contentWindow;return i?this._resolveDirectTimelineAdapterFromWindow(i):null}catch{return null}}resolveDirectTimelineAdapterFromWindow(i){return this._resolveDirectTimelineAdapterFromWindow(i)}hasRuntimeBridge(i){return Reflect.get(i,"__hf")!==void 0||V(Reflect.get(i,"__player"))}_injectRuntime(){var i,e;this._runtimeInjected=!0;try{const t=this._iframe.contentDocument;if(!t)return;const r=t.createElement("script");r.src=H,(t.head||t.documentElement).appendChild(r),(e=(i=this._callbacks).onRuntimeInjected)==null||e.call(i)}catch{}}_resolveDirectTimelineAdapterFromWindow(i){var d,p;if(this.hasRuntimeBridge(i))return null;const e=Reflect.get(i,"__timelines");if(!V(e))return null;const t=Object.keys(e);if(t.length===0)return null;const r=(p=(d=this._iframe.contentDocument)==null?void 0:d.querySelector("[data-composition-id]"))==null?void 0:p.getAttribute("data-composition-id"),s=r&&r in e?r:t[t.length-1],o=e[s];return Te(o)?o:null}_resolvePlaybackDurationAdapter(i){const e=Reflect.get(i,"__player");if(Ce(e))return{kind:"runtime",getDuration:()=>e.getDuration()};const t=this._resolveDirectTimelineAdapterFromWindow(i);return t?{kind:"direct-timeline",timeline:t,getDuration:()=>t.duration()}:null}}const Me=`
2
2
  :host {
3
3
  display: block;
4
4
  position: relative;
@@ -1 +1 @@
1
- import{g as P}from"./index-DMsdtB-j.js";function j(c,d){for(var s=0;s<d.length;s++){const a=d[s];if(typeof a!="string"&&!Array.isArray(a)){for(const i in a)if(i!=="default"&&!(i in c)){const l=Object.getOwnPropertyDescriptor(a,i);l&&Object.defineProperty(c,i,l.get?l:{enumerable:!0,get:()=>a[i]})}}}return Object.freeze(Object.defineProperty(c,Symbol.toStringTag,{value:"Module"}))}var v={},w;function k(){if(w)return v;w=1,Object.defineProperty(v,"__esModule",{value:!0}),v.default=d;var c=window.OfflineAudioContext||window.webkitOfflineAudioContext;function d(e){var r=a(e);return r.start(0),[i,y,O(e.sampleRate),s].reduce(function(t,o){return o(t)},r.buffer.getChannelData(0))}function s(e){return e.sort(function(r,t){return t.count-r.count}).splice(0,5)[0].tempo}function a(e){var r=e.length,t=e.numberOfChannels,o=e.sampleRate,n=new c(t,r,o),u=n.createBufferSource();u.buffer=e;var f=n.createBiquadFilter();return f.type="lowpass",u.connect(f),f.connect(n.destination),u}function i(e){for(var r=[],t=.9,o=.3,n=15;r.length<n&&t>=o;)r=l(e,t),t-=.05;if(r.length<n)throw new Error("Could not find enough samples for a reliable detection.");return r}function l(e,r){for(var t=[],o=0,n=e.length;o<n;o+=1)e[o]>r&&(t.push(o),o+=1e4);return t}function y(e){var r=[];return e.forEach(function(t,o){for(var n=function(x){var g=e[o+x]-t,_=r.some(function(h){if(h.interval===g)return h.count+=1});_||r.push({interval:g,count:1})},u=0;u<10;u+=1)n(u)}),r}function O(e){return function(r){var t=[];return r.forEach(function(o){if(o.interval!==0){for(var n=60/(o.interval/e);n<90;)n*=2;for(;n>180;)n/=2;n=Math.round(n);var u=t.some(function(f){if(f.tempo===n)return f.count+=o.count});u||t.push({tempo:n,count:o.count})}}),t}}return v}var p,b;function q(){return b||(b=1,p=k().default),p}var m=q();const A=P(m),D=j({__proto__:null,default:A},[m]);export{D as i};
1
+ import{g as P}from"./index-DWFEyVJZ.js";function j(c,d){for(var s=0;s<d.length;s++){const a=d[s];if(typeof a!="string"&&!Array.isArray(a)){for(const i in a)if(i!=="default"&&!(i in c)){const l=Object.getOwnPropertyDescriptor(a,i);l&&Object.defineProperty(c,i,l.get?l:{enumerable:!0,get:()=>a[i]})}}}return Object.freeze(Object.defineProperty(c,Symbol.toStringTag,{value:"Module"}))}var v={},w;function k(){if(w)return v;w=1,Object.defineProperty(v,"__esModule",{value:!0}),v.default=d;var c=window.OfflineAudioContext||window.webkitOfflineAudioContext;function d(e){var r=a(e);return r.start(0),[i,y,O(e.sampleRate),s].reduce(function(t,o){return o(t)},r.buffer.getChannelData(0))}function s(e){return e.sort(function(r,t){return t.count-r.count}).splice(0,5)[0].tempo}function a(e){var r=e.length,t=e.numberOfChannels,o=e.sampleRate,n=new c(t,r,o),u=n.createBufferSource();u.buffer=e;var f=n.createBiquadFilter();return f.type="lowpass",u.connect(f),f.connect(n.destination),u}function i(e){for(var r=[],t=.9,o=.3,n=15;r.length<n&&t>=o;)r=l(e,t),t-=.05;if(r.length<n)throw new Error("Could not find enough samples for a reliable detection.");return r}function l(e,r){for(var t=[],o=0,n=e.length;o<n;o+=1)e[o]>r&&(t.push(o),o+=1e4);return t}function y(e){var r=[];return e.forEach(function(t,o){for(var n=function(x){var g=e[o+x]-t,_=r.some(function(h){if(h.interval===g)return h.count+=1});_||r.push({interval:g,count:1})},u=0;u<10;u+=1)n(u)}),r}function O(e){return function(r){var t=[];return r.forEach(function(o){if(o.interval!==0){for(var n=60/(o.interval/e);n<90;)n*=2;for(;n>180;)n/=2;n=Math.round(n);var u=t.some(function(f){if(f.tempo===n)return f.count+=o.count});u||t.push({tempo:n,count:o.count})}}),t}}return v}var p,b;function q(){return b||(b=1,p=k().default),p}var m=q();const A=P(m),D=j({__proto__:null,default:A},[m]);export{D as i};
@@ -1,4 +1,4 @@
1
- import{n as Qi}from"./index-DMsdtB-j.js";/*!
1
+ import{n as Qi}from"./index-DWFEyVJZ.js";/*!
2
2
  * Copyright (c) 2026-present, Vanilagy and contributors
3
3
  *
4
4
  * This Source Code Form is subject to the terms of the Mozilla Public