Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
e934b13
docs: design spec for hyperframes integration (4 tracks)
shreyaskarnik Jul 7, 2026
de0df62
docs: implementation plan for hyperframes shader port (track 1)
shreyaskarnik Jul 7, 2026
97d9808
feat(transitions): accent color derivation for shader uniforms
shreyaskarnik Jul 7, 2026
112470c
feat(transitions): accent + resolution uniforms threaded through shad…
shreyaskarnik Jul 7, 2026
275e2b0
feat(transitions): port domain-warp, ridged-burn, thermal-distortion,…
shreyaskarnik Jul 7, 2026
79bcb5e
feat(transitions): port whip-pan, gravitational-lens, cinematic-zoom,…
shreyaskarnik Jul 7, 2026
5564938
feat(transitions): port sdf-iris and ripple-waves shaders from hyperf…
shreyaskarnik Jul 7, 2026
83a10ee
docs: document 11 new shader transitions and accent config
shreyaskarnik Jul 7, 2026
106fefc
docs: sync shader attribution table with 16-shader registry
shreyaskarnik Jul 7, 2026
0b75daa
fix(transitions): normalize accent hex case in shader cache key
shreyaskarnik Jul 7, 2026
b90864b
docs: implementation plan for argo add + registry compat (track 2)
shreyaskarnik Jul 7, 2026
92944bc
feat(hf): hyperframes registry client with injectable fetch
shreyaskarnik Jul 7, 2026
17dfeb8
feat(hf): installItem/listItems with path-traversal guards
shreyaskarnik Jul 7, 2026
3a06655
feat(cli): argo add command + blocksDir/registry config
shreyaskarnik Jul 7, 2026
516f6d1
feat(hf): component snippet parser + CSS param safety checks
shreyaskarnik Jul 7, 2026
b8c4056
fix(hf): use escape sequences for control-char regex (was raw bytes b…
shreyaskarnik Jul 7, 2026
7f40043
feat(hf): applyComponent/removeComponent page injection
shreyaskarnik Jul 7, 2026
1a83212
feat(overlays): hf-component cue + ARGO_BLOCKS_DIR env bridge
shreyaskarnik Jul 7, 2026
2d83d8d
feat(validate): hf-component install checks + transition accent hex v…
shreyaskarnik Jul 7, 2026
054136c
docs: argo add command, hf-component cue, blocksDir config
shreyaskarnik Jul 7, 2026
b1f22ff
fix(hf): clearer errors for unsupported registry types and non-JSON r…
shreyaskarnik Jul 7, 2026
95a913c
docs: implementation plan for hf-block pre-render adapter (track 3)
shreyaskarnik Jul 7, 2026
89453cb
feat(hf): block frame renderer seeking window.__timelines
shreyaskarnik Jul 7, 2026
84f44d5
fix(hf): edge-inclusive frame sampling with N=1 midpoint fallback
shreyaskarnik Jul 7, 2026
d9ec921
feat(hf): ffmpeg composite filter builder for block PNG sequences
shreyaskarnik Jul 7, 2026
5ca1d70
feat(export): composite pre-rendered hf-block sequences as cutaway ov…
shreyaskarnik Jul 7, 2026
ca85f00
feat(overlays): hf-block cue (export-time cutaway) + validate checks
shreyaskarnik Jul 7, 2026
76f3819
feat(pipeline): resolve + pre-render hf-block cues across all export …
shreyaskarnik Jul 7, 2026
217f11f
docs: hf-block cutaway cue, pre-render cache, speedRamp caveat
shreyaskarnik Jul 8, 2026
21d7f14
fix(hf): include holdLastFrame in block cache key; bound fonts.ready …
shreyaskarnik Jul 8, 2026
83e707d
feat(demos): argo × hyperframes 'Better Together' showcase
shreyaskarnik Jul 8, 2026
4862f44
feat(demos): comprehensive argo × hyperframes full-tour showcase (3:21)
shreyaskarnik Jul 8, 2026
4573d0d
fix(transitions): clamp shader boundaries past video end (mark drift …
shreyaskarnik Jul 8, 2026
1d779d0
fix(hf): seek timelines with suppressEvents=false + renderer-version …
shreyaskarnik Jul 8, 2026
6d34348
Merge branch 'main' of https://github.com/shreyaskarnik/argo
shreyaskarnik Jul 27, 2026
893194e
Merge remote-tracking branch 'origin/main' into feat/hyperframes-inte…
shreyaskarnik Jul 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 27 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ Both the primary export path and blur-fill format variants (1:1, 9:16) are encod

Pre-rendered WebGL shader transitions. At export time, a pre-pass extracts two boundary frames per scene boundary (last frame of outgoing, first frame of incoming) via ffmpeg, then Playwright Chromium renders the GLSL shader at each of `N = D × fps` progress values, producing a PNG sequence. `buildShaderSpliceFilter` generates a filter_complex three-segment concat (`scene_a + PNG_seq + scene_b`) that replaces the fade window.

Cache key: `sha256(shader, durationMs, fps, width, height, sha256(aPng), sha256(bPng))`. Cached at `.argo/<demo>/shaders/<hash>/`. Second export with unchanged boundaries hits cache with no browser launch.
Cache key: `sha256(shader, durationMs, fps, width, height, sha256(aPng), sha256(bPng), accent)`. Cached at `.argo/<demo>/shaders/<hash>/`. Second export with unchanged boundaries hits cache with no browser launch.

Shaders live in `src/transitions/shaders/*.glsl`. Build step copies `.glsl` files to `dist/` (tsc does not). v1 ships: `crosswarp`, `swirl`, `ripple`, `luma-mask`, `light-leak` — adapted from gl-transitions.com (MIT).
Shaders live in `src/transitions/shaders/*.glsl`. Build step copies `.glsl` files to `dist/` (tsc does not). v1 ships: `crosswarp`, `swirl`, `ripple`, `luma-mask`, `light-leak` — adapted from gl-transitions.com (MIT), plus 11 hyperframes ports (Apache-2.0): `domain-warp`, `ridged-burn`, `thermal-distortion`, `swirl-vortex`, `whip-pan`, `gravitational-lens`, `cinematic-zoom`, `chromatic-split`, `flash-through-white`, `sdf-iris`, `ripple-waves`. `export.transition.accent` (default `'#0ea5e9'`) tints accent-aware shaders: `domain-warp`, `ridged-burn`, `thermal-distortion`, `sdf-iris`, `ripple-waves`.

Per-boundary shader selection is NOT supported in v1 — `export.transition.shader` applies to all boundaries. Future work: a sidecar for per-boundary overrides.

Expand Down Expand Up @@ -111,7 +111,27 @@ Static v1 blocks: `x-post`, `macos-notification`, `yt-lower-third`, `data-chart`

Animated blocks (ship with `defaultMotion` using GSAP): `instagram-follow` (pulsing Follow button), `tiktok-follow` (rotating avatar ring + side slide), `reddit-post` (upvote card, simple entrance), `logo-outro` (scale-in end-card), `flowchart` (stacked nodes + arrows revealed with stagger), `app-showcase` (hero card with floating icon loop), `ui-3d-reveal` (perspective tilt-to-flat reveal of a screenshot). These use `BlockDefinition.defaultMotion` — a cue-level `motion` still overrides. Inspired by hyperframes blocks of the same names (Apache-2.0); implementations are original. Selector hooks used by motion loops/staggers: `.argo-ig-follow-btn`, `.argo-tt-ring`, `.argo-app-hero`, `.argo-3d-image`, `.argo-flow-node, .argo-flow-arrow`.

Folder format is designed for a future `argo add <block>` command (not shipped yet).
These built-in blocks ship inside the package (`src/blocks/`) — distinct from the hyperframes registry items installed into `blocksDir` by `argo add` (see HyperFrames Catalog below).

### HyperFrames Catalog (`src/hf/`)

`argo add <name>` installs blocks/components from the hyperframes registry (default `https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry`, override via `--registry <url>` or config `registry.url`) into `blocksDir` (config, default `blocks/`, git-tracked). `argo add --list` (+ `--json`) browses the registry. Registry examples (`hyperframes:example`) are not installable. Item files are stored verbatim in native hyperframes format at `blocks/<name>/` plus a `registry-item.json` sidecar. Modules: `registry-client.ts` (fetch-injectable client — unit tests never touch the network), `add.ts` (`installItem`/`listItems`, path-traversal guards on item names and file paths), `component.ts` (snippet parser + CSS param validators), `apply-component.ts` (page injection).

Overlay cue: `{ "type": "hf-component", "name": "vignette", "params": { "--vignette-size": "40%" } }` — full-frame injection (fixed, pointer-events none, high z-index) that bypasses the zone/theme/template machinery; duration comes from `showOverlay`. Script API: `applyComponent(page, name, { params?, blocksDir? })` / `removeComponent(page, name)` exported from the package — persists until removed. `params` are validated CSS custom properties (`--kebab-case` names, conservative value allowlist — no `url()`, `;{}<>`, control chars). Injection uses the same no-op `page.evaluate()` render fence as overlays and the same disposal-error-swallowing pattern as `showConfetti`.

Trust model: components are trusted-at-install (user ran `argo add`, files are git-reviewable); only runtime `params` are validated. Component `<script>`s run via `new Function()` and may be blocked by strict CSP on the recorded page — logged as a warning, not a failure. Caveat: `caption-*` components install fine but word-level timing support is future work (STT alignment).

`argo validate` checks that referenced hf-components are installed (`blocks/<name>/<name>.html` exists) and validates `export.transition.accent` as a 6-digit hex color.

**`hf-block` cutaway cue** (`src/hf/block-render.ts`, `src/hf/block-filter.ts`): `{ type: 'hf-block', name, params?, durationMs?, fit?: 'cover' | {x,y,scale}, holdLastFrame? }`. Unlike `hf-component` (live DOM injection), `hf-block` is a no-op at recording time — it only paces the wait, nothing is injected into the page. The block's paused GSAP timeline is rendered later, at export time, by a dedicated headless Chromium pre-pass (`renderBlockFrames`) that loads the installed `blocks/<name>/<name>.html`, waits for the hyperframes convention `window.__timelines[<id>] = { duration(), pause(), seek(t) }` to register, then seeks it frame-by-frame and screenshots each frame with `omitBackground: true` for an alpha PNG sequence. `holdLastFrame` pins the timeline at its native duration instead of linearly retiming the whole animation into the window; sampling is edge-inclusive (last frame lands exactly at the window end) with an N=1 midpoint fallback.

Content-addressed cache at `.argo/<demo>/hf-blocks/<hash>/` — `computeBlockHash(blockHtml, params, durationMs, fps, width, height)` — a cache hit skips the Chromium launch entirely (`renderHfBlocks` in `block-render.ts` shares one browser instance lazily across cache misses in a batch, mirroring the shader-render pre-pass pattern).

`resolveHfBlockCues(rawManifest, placements)` anchors each cue to its scene's placement window: `endMs = startMs + (durationMs ?? windowMs)`, capped at the placement's own `endMs` unless the requested duration overshoots — in which case it's allowed to run into the gap after the scene, capped at the next placement's `startMs` (uncapped on the last scene; ffmpeg's `enable=between(t,...)` window is naturally clipped by the video's total duration regardless). Scenes with no matching placement are skipped with a warning, same as overlays/subtitles.

Compositing (`buildHfBlockFilters`) adds one `-framerate`/`image2` input per cue and an `enable`-gated `overlay` with `eof_action=pass` — applied immediately after camera moves and overlay PNGs, before the frame effect/watermark (same layer priority as overlay PNGs). It's a pure cutaway: `-shortest` and total output duration are unaffected.

Wired at all four export paths: pipeline primary, pipeline variants, CLI `argo export`, and `argo preview` export (`PreviewExportConfig` gained `blocksDir`). Blocks need network access at export time — real registry blocks load GSAP + Google Fonts from CDNs during the pre-render pass, so offline exports of demos with `hf-block` cues will fail at that step. **Known limitation:** `hf-block` + `export.speedRamp` is untested and can misalign windows — `resolveHfBlockCues` anchors windows to post-ramp scene placements (the same ones subtitles/chapters use), but the ramp's dead-time compression (`computeSegments` in `src/speed-ramp.ts`) is computed independently in source-timeline coordinates before cues are resolved. Avoid combining the two until this is verified end-to-end.

### Effects (`src/effects.ts`)

Expand Down Expand Up @@ -191,8 +211,9 @@ Custom `test` fixture extends Playwright's `test` with a `narration` fixture tha
- `argo record/export/pipeline/validate` take bare demo names (e.g., `argo pipeline example`)
- `argo pipeline --all` runs the full pipeline for every demo discovered in `demosDir` (finds all `.scenes.json` files)
- `argo pipeline [demo]` — demo argument is optional when `--all` is used
- `argo validate <demo>` checks scene name consistency between script and scenes manifest, validates overlay fields (no TTS/recording)
- `argo validate <demo>` checks scene name consistency between script and scenes manifest, validates overlay fields, checks hf-component installs, and validates `export.transition.accent` hex (no TTS/recording)
- `argo clip <demo> <scene>` extracts a scene clip from an exported MP4 using chapter markers. `--format gif` produces a palette-optimized GIF. Clips go to `videos/clips/`. Useful for release notes and docs.
- `argo add <name>` installs a hyperframes registry item into `blocksDir`; `argo add --list` (+ `--json`) browses; `--registry <url>` overrides the registry (also config `registry.url`). Item names share the demo-name validation regex (path-traversal guard).
- `--base-url <url>` flag on `record` and `pipeline` overrides `config.baseURL`
- `--headed` flag on `record` and `pipeline` runs the browser in visible mode
- `--all` flag on `pipeline` runs all demos in batch (sequential execution, continues on failure)
Expand Down Expand Up @@ -245,6 +266,7 @@ Custom `test` fixture extends Playwright's `test` with a `narration` fixture tha
- `ARGO_SCENE_DURATIONS_PATH` — path to `.scene-durations.json` (loaded by narration fixture)
- `ARGO_OVERLAYS_PATH` — path to `.scenes.json` manifest (loaded by overlay functions for manifest-based resolution)
- `ARGO_AUTO_BACKGROUND` — set to `'1'` when config `overlays.autoBackground` is true
- `ARGO_BLOCKS_DIR` — blocks directory for installed hyperframes items (loaded by `applyComponent`/hf-component cues). Set by `record()` from `config.blocksDir` at all three call sites: pipeline primary, pipeline variants, and CLI `argo record`.
- `ARGO_ALLOW_RAW_GSAP` — set to `'1'` when config `overlays.allowRawGsap` is true. Re-checked by the runtime before executing `motion.raw` (defense in depth against inline cues that skip the validator).
- `ARGO_OUTPUT_DIR` — output directory for timing JSON
- `DEBUG` — when set (e.g., `DEBUG=pw:api`), Playwright debug output is forwarded to stderr even on success
Expand Down Expand Up @@ -319,6 +341,7 @@ Custom `test` fixture extends Playwright's `test` with a `narration` fixture tha
- ffmpeg `gradients` source filter rejects negative `x0/y0/x1/y1` values — angle-to-coordinate conversion can produce negatives for certain angles. Always clamp.
- `narration.mark()` does sync `appendFileSync` which can trigger app re-renders on the same event loop tick — overlay injection fence mitigates but apps with very aggressive DOM updates may still need manual `waitForTimeout()` after `mark()`.
- `-shortest` must be skipped when frame PNG overlay is present — PNG has 0 duration and truncates the entire output.
- `hf-block` cutaway cues are not validated in combination with `export.speedRamp` — cue windows are resolved from post-ramp placements while the ramp's gap-compression segments are computed independently in source-timeline coordinates, which can misalign the block window against the retimed video. Don't combine them until this is revisited.

## Security Invariants

Expand Down
Loading
Loading