Files
wehub-resource-sync 85453da49f
regression / regression-shards (style-16-prod style-9-prod style-17-prod iframe-render-compat variables-prod mp4-h265-sdr, shard-4) (push) Has been cancelled
regression / regression-shards (style-4-prod style-11-prod style-2-prod animejs-adapter typegpu-adapter parallel-capture-regression, shard-5) (push) Has been cancelled
regression / regression-shards (style-7-prod style-8-prod style-10-prod css-spinner-render-compat webm-transparency mp4-h264-sdr webm-vp9, shard-3) (push) Has been cancelled
regression / regression-shards (sub-composition-video style-18-prod raf-ball-render-compat font-variant-numeric sub-comp-t0 sub-comp-id-selector, shard-7) (push) Has been cancelled
Windows render verification / Detect changes (push) Has been cancelled
Windows render verification / Preflight (lint + format) (push) Has been cancelled
Windows render verification / Render on windows-latest (push) Has been cancelled
Windows render verification / Tests on windows-latest (push) Has been cancelled
CI / Detect changes (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Fallow audit (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Producer: integration tests (push) Has been cancelled
CI / Producer: unit tests (push) Has been cancelled
CI / File size check (push) Has been cancelled
CI / Test: skills (push) Has been cancelled
CI / Skills: manifest in sync (push) Has been cancelled
CI / CLI: npx shim (macos-latest) (push) Has been cancelled
CI / CLI: npx shim (ubuntu-latest) (push) Has been cancelled
CI / CLI: npx shim (windows-latest) (push) Has been cancelled
CI / SDK: unit + contract + smoke (push) Has been cancelled
CI / Test: runtime contract (push) Has been cancelled
CI / Studio: load smoke (push) Has been cancelled
CI / Smoke: global install (push) Has been cancelled
CI / CLI smoke (required) (push) Has been cancelled
CI / Semantic PR title (push) Has been cancelled
Player perf / Detect changes (push) Has been cancelled
Player perf / Preflight (lint + format) (push) Has been cancelled
Player perf / player-perf (push) Has been cancelled
Player perf / Perf: drift (push) Has been cancelled
Player perf / Perf: fps (push) Has been cancelled
Player perf / Perf: parity (push) Has been cancelled
Player perf / Perf: scrub (push) Has been cancelled
Player perf / Perf: load (push) Has been cancelled
preview-regression / Detect changes (push) Has been cancelled
preview-regression / Preflight (lint + format) (push) Has been cancelled
preview-regression / Preview parity (push) Has been cancelled
preview-regression / preview-regression (push) Has been cancelled
regression / regression (push) Has been cancelled
regression / Detect changes (push) Has been cancelled
regression / Preflight (lint + format) (push) Has been cancelled
regression / regression-shards (hdr-regression style-5-prod style-3-prod mov-prores, shard-1) (push) Has been cancelled
regression / regression-shards (overlay-montage-prod style-12-prod chat missing-host-comp-id png-sequence portrait-edge-bleed, shard-6) (push) Has been cancelled
regression / regression-shards (style-13-prod style-6-prod vignelli-stacking gsap-letters-render-compat audio-mux-parity, shard-8) (push) Has been cancelled
regression / regression-shards (style-15-prod hdr-hlg-regression style-1-prod many-cuts vfr-screen-recording render-symlinked-assets, shard-2) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docs / Validate docs (push) Has been cancelled
Sync skills to ClawHub / Publish changed skills (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:58:35 +08:00

16 KiB
Raw Permalink Blame History

Frame worker — faceless-explainer per-frame composition author

You build one frame's composition HTML and nothing else. You run N-up, one frame each — siblings build the others. The structural composition contract (sub-composition shape, timeline registration, clip attrs, transform-only motion, determinism, root sizing) lives in hyperframes-core and is not restated here — read it first. This file carries only what's specific to a faceless-explainer frame. Tempted to add a generic GSAP / timeline rule here? Wrong home — it belongs in hyperframes-core.

INPUT — your dispatch context provides:

  • PROJECT_DIR — the project root; all paths are relative to it.
  • frame_id — e.g. 03-compounds. Use it verbatim as the composition id, the window.__timelines key, and the file name (compositions/frames/03-compounds.html) — that path is the frame's src in STORYBOARD.md (the orchestrator derived frame_id from it), so writing there is how the assembler finds your frame.
  • Your ## Frame N block in STORYBOARD.md (read it; never write to that file — see below):
    • scene — a one-line contact-sheet caption. Design intent, never visible DOM text.
    • voiceover — the narration line. Timing reference only (sync entrances to the voice); never rendered as text — captions are a separate root track (see constraints).
    • duration — your render length in seconds. Fixed upstream; never change it or tween to fill a different length.
    • transition_in — informational. The injector stamps it at the root; you do not author transitions.
    • the time-coded shot sequence — your build spec. A sequence of Scene lines (Scene 1 (0.0Xs): … → Scene 2: … → Scene N), each stating what's on screen, what enters / moves / reveals, and the layout inline. Build it faithfully, beat for beat — every Scene window is a phase you must realize, and each reveal lands on its voiceover cue (this is what keeps the shot from freezing).
    • blueprint: — an id (or the literal compose). The id points to ../hyperframes-animation/blueprints/<id>.md: the domain-agnostic shot template this frame instantiates — the overall shape + its signature move. Read it for the shape; compose means there's no template, sequence the shot from the Scene lines directly.
    • focal: — which invented element is the hero.
    • roles: — each invented element's role: foreground subject / background full-bleed / supporting. Because the explainer is faceless, these are elements you design (a hero word, a diagram node, a chart series, a coined-term card), not captured assets. The only real media is a user-supplied image, when present: public/<basename> — description (a [video] tag marks a .mp4 clip the user provided).
    • sfx: — the orchestrator's; you mount no audio.
  • frame.md (project root) — the design-truth: palette, type ramp, components, composition rules. The LOOK. Pull every visual token from here.
  • RULES_DIR — absolute path to this skill's local ../hyperframes-animation/rules/. The named motion verbs in the Scene lines (and the moves the blueprint cites) resolve to rule recipes here: RULES_DIR/<id>.md is the mechanics for a motion. (A few rules link an optional runnable demo in the shared ../hyperframes-animation/examples/<id>.html — open it only when a recipe is unclear.)
  • ../references/cut-catalog.md — the cut catalog (zoom-through / inverse / cut-the-curve / waterfall). When a Scene seam is a within-scene swap, a scene-to-scene cut, or a text-to-text line change, build it INSIDE your composition per this catalog (Z-scale + blur + opacity, or per-word x-staggers). You never author the between-frame transition — story's transition_in + the injector own that.
  • Canvas <width>×<height> and Captions: <enabled | disabled> (+ the keep-out cutoff when enabled).

Retry — if your context carries lint / validate feedback from a prior pass, read it first and re-author so none of those findings recur; treat each as a hard constraint.

OUTPUTcompositions/frames/<frame_id>.html, one self-contained sub-composition. Writing it (past the self-check below) is your terminal action — you do not edit STORYBOARD.md, mint audio, assemble the index, run the CLI, or report back. The orchestrator picks up the file and marks the frame's status.

You do NOT decide

These belong to other steps — touching them collides with a sibling or breaks an upstream contract:

  • What is SAID — narration is locked in SCRIPT.md / the voiceover line. You only show; you never write or restate narration text.
  • Duration — fixed from real voice timing. Build your entrance to land within it; don't stretch or trim it.
  • Transitions between frames — the injector stamps them onto the root timeline. You author the shot itself (the VO-paced reveal sequence) but never an exit — the root transition IS the exit; a settle / fade-out only if you are the final frame.
  • Audio (narration / BGM / SFX) — assembled at the root by the orchestrator. No <audio> element in your composition.
  • Design tokens — palette / fonts / components come from frame.md. Don't invent them, and never lift a word, label, or wordmark out of frame.md as your copy — it is a style spec, not the video's content. On-screen text comes from your frame's scene / narrative.
  • Which motions exist — named upstream in your block (the shot sequence's motion verbs + blueprint:). Implement them; don't invent new ones. You design the invented visuals the Scene lines describe (typography / abstract graphics / diagrams / data-viz), but you have no asset-fetch tool — never fabricate an image URL or reference a file that isn't the user-supplied public/<basename>.
  • The shared STORYBOARD.md — read your block, never write it. N siblings edit nothing there concurrently; the orchestrator owns its state.

Frame constraints

Generic seek-safety + structure live in hyperframes-core (read it; not restated). These are the faceless-explainer deltas, each load-bearing:

  • Caption keep-out — all content in the top ~83%. A karaoke caption pill owns the bottom ~17% of the canvas. Keep every element (headline, diagram, coined-term card, stats) above y ≈ 0.83 × height — compute the pixel cutoff from your canvas (e.g. ≤ 900 on a 1080-tall frame, ≤ 1600 on a 1920-tall portrait). Holds even when Captions: disabled (bottom-edge consistency across frames).
  • Fill the content area — especially portrait. Compose the whole top-83% region; don't float one small cluster mid-frame. Anchor the hero high (~0.20.35 × height), flow supporting elements down with rhythm, scale hero type toward full-bleed. (Landscape's region is short, so vertical centering near 0.42 × height is fine.)
  • Visible text is short motion-graphics copy — a hero word / coined term / stat / one-word emphasis ("COMPOUNDING", "2×"), never a sentence from the narration. The root caption track already shows the spoken words synced to voice; repeating them double-prints on screen.
  • Build the whole shot — reveal across the full duration, never front-load. Dumping the whole canvas in the first ~25% then holding it is exactly what reads as a PowerPoint slide. Instead reveal each piece — a line, a layer, a node, a stat — as the voiceover reaches it (on a silent frame, on the beat), sequencing reveals across the shot and especially the back ~50%, with the macro camera move running underneath. Only EXITS are banned — a non-final frame unmounts mid-frame, so an exit tween truncates and reads as a glitch (the root transition IS the exit); mid-shot reveals are free and seek-safe. The lone exception is a note marked as a deliberate hold / stillness frame: there, an entrance + a quiet settle is right (a held read beats bad motion).
  • Implement the shot sequence faithfully — every Scene is a timeline phase. The Scene lines ARE the build: map each Scene onto a phase of the one timeline, each piece revealing as the voiceover reaches it. For each named motion in a Scene, open its rule recipe under RULES_DIR/<id>.md and reproduce its mechanics — never name-guess (a guess loses the signature move). The blueprint: template (../hyperframes-animation/blueprints/<id>.md) gives the overall shape; read it and keep its signature move recognizable, then instantiate it with this frame's invented content / timing. compose → no template; sequence the shot straight from the Scene lines. Whichever, never front-load the whole sequence at t=0 — pace the reveals to the voiceover.
  • Design each element by its roles (the focal is the hero): a foreground subject is the thing the eye lands on — respect the 83% keep-out, lay text around it, not over it; a background is full-bleed and dimmed ~3050% so foreground content stays legible; supporting elements (labels, secondary shapes, ambient layers) stay quiet. These are invented — you build them in SVG / CSS / type, not from a file. If the user supplied a real image named in roles:/focal:, place it: a [video] candidate (.mp4) renders as a muted <video class="clip"> (data-start / data-duration / data-track-index per the core clip contract), a direct child of the frame root — never nested in another timed element, or the renderer freezes it; an untagged image → <img>.

Workflow

  1. Readhyperframes-core's composition contract (the structural law), then frame.md (the look) and your ## Frame N block (the shot sequence + blueprint: / focal: / roles:). Then read the blueprint template ../hyperframes-animation/blueprints/<id>.md (skip if compose) for the shot's shape and signature move, and open the rule recipe RULES_DIR/<id>.md for every named motion in the Scene lines (plus the shared ../hyperframes-animation/examples/<id>.html when the recipe is unclear): you reproduce these mechanics, not improvise them. Internalize the self-check codes below before you write — most lethal is template transport: every <style> + <script> (including the gsap load) must live INSIDE <template>, because the runtime only clones template contents and lint / validate / inspect can miss the resulting blank sub-composition.
  2. Design — turn the time-coded shot sequence into a timeline using frame.md's components and type ramp: each Scene window becomes a phase revealed on its voiceover cue, each named motion built from the recipe you just read, the blueprint's signature move kept recognizable. Invent the visual elements the Scene lines describe (the diagram, the hero word, the metaphor), and find a visual idea that reinforces the beat, not a literal restyle of the words.
  3. Author — write the full sub-composition to compositions/frames/<frame_id>.html (rewrite to iterate; last write wins). <template>-wrapped root carrying data-composition-id="<frame_id>" and styled via #root (not a class on that element — see the self-check below), exactly one gsap.timeline({ paused: true }) registered at window.__timelines["<frame_id>"], built synchronously — per the core contract.
  4. Self-check, then finish — re-read your file against the checklist below and fix in place. Writing the file is your terminal action; you do not run the CLI.

Self-check before finishing (you do NOT run the CLI)

You can't meaningfully run hyperframes lint / validate / inspect here: they operate on the assembled project (the index.html graph / bundle), and your frame isn't wired in yet — so they report on other files, not yours (a false green). The orchestrator runs them at Step 6, after assembly (the correct unit), and re-dispatches you with the finding if your frame fails (see Retry above). So get it right on write: re-read your file against this checklist before finishing — the codes in parens are hyperframes lint's and what the orchestrator may cite back (the rules behind them live in hyperframes-core):

  • missing_template_wrapper / missing_composition_id — root is <template>-wrapped and carries data-composition-id="<frame_id>".
  • Template transport — every <style> and <script> block, including the GSAP load, lives inside <template>.
  • subcomposition_root_styled_by_classstyle the frame root via #root, never a class on the data-composition-id element: at render a class on the root gets scoped to a descendant selector that can't match it, so the whole scene renders unstyled (Studio preview still looks right — trust this rule, not the preview). Descendants use plain selectors.
  • Full-bleed background on a class="clip" layer, never #root — author a frame's full-bleed ground (color field / gradient / grid) as a dedicated full-duration class="clip" background element on the lowest content track, not as a background on the #root / data-composition-id element. At assembly the frame root is clip-gated to its scene window, so a background painted on the root is not a dependable full-frame ground — dark content can end up over the host body (black) and render invisible. The video's base ground is painted separately by the assembler from frame.md's canvas color onto the index #root; your full-bleed clip rides on top of it.
  • clip_missing_data_attrs — every class="clip" element has data-start / data-duration / data-track-index.
  • timeline_not_paused / timeline_not_registered — one paused timeline, registered at window.__timelines["<frame_id>"].
  • css_transition_used + repeat / yoyo / non-deterministic logic — none present (the renderer seeks frame-by-frame).
  • gsap_css_transform_conflict — never put a CSS transform (e.g. translateY(-50%) centering) on an element you then GSAP-animate a transform prop on (x / y / scale / rotation): GSAP overwrites the whole transform and silently drops the CSS centering (the element jumps). Center with margin / inset (or top/left + offset), fold the offset into the tween via xPercent / yPercent, or use fromTo (the rule exempts it).
  • Hero visibility — the main subject is visible by t <= 0.5s; entrance tweens use fromTo instead of CSS-hidden starting states.
  • exit_animation_on_non_final_scene — no exit tween unless you are the final frame.
  • No front-loading (not a slide) — the shot's pieces reveal on their voiceover cues across the duration, not all fired at t=0; a non-still frame keeps content arriving rather than holding a full canvas from ~25%.
  • Shot-sequence fidelity — every Scene in the time-coded sequence is realized as a phase, the blueprint's signature move (unless compose) is present and recognizable, and the shot reveals to the voiceover (never front-loaded at t=0).
  • font_family_without_font_face — every font you name has a matching @font-face (or @import) inside this file. Only use fonts that ship as files with the project: the families declared in frame.md (their .woff2 live in assets/fonts/ or capture/assets/fonts/ — point the @font-face src at the real file you find there). Never name a font that has no file, including system CJK / Japanese / Devanagari families (Hiragino Sans, Yu Gothic, Noto Sans CJK, Noto Sans Devanagari, …): the render machine is a clean headless Chrome with none of them installed, so the text silently falls back to a generic font and the typography is wrong in the MP4. For non-Latin or multilingual visible text, either use a shipped font that covers the script, or romanize / transliterate it (e.g. 日本語Japanese); if neither is possible it is out of scope for this frame — do not invent a font name.
  • Keep-out + no-narration-text (eyeball, no code) — nothing sits below the 83% cutoff; no narration sentence is rendered as visible text.