mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
0d551e36140c320efd9cc33212df503aa0b536fd
58
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0d551e3614 | chore: release v0.4.11-alpha.1 | ||
|
|
00af29c169 |
fix(cli): forward --hdr through Docker render + HDR docs (#346)
## Summary This PR ended up covering the full HDR Docker/docs follow-through plus the producer/engine work needed to make HDR still images render and regress correctly in CI. The branch now does four things: - forwards `--hdr` through the Docker render path in the CLI - adds and expands HDR documentation across the docs site - adds first-class HDR still-image support to the engine/producer pipeline - adds targeted HDR regression coverage, including a CI-safe fallback for PNG HDR metadata detection when `ffprobe` does not expose PNG color tags ## What changed ### CLI and docs - `hyperframes render --docker --hdr` now preserves `--hdr` when invoking the in-container CLI - added a dedicated HDR guide and linked it from CLI, producer, engine, rendering, and common-mistakes docs - documented HDR constraints and verification flow: HDR source requirements, MP4/H.265 Main10 output, PQ/HLG handling, Docker usage, and common SDR fallback causes ### Engine and producer HDR image support - added `ImageElement` support to the engine composition model and parsing path - threaded image elements through producer compilation and orchestration - probed image sources for HDR color spaces so image-only compositions can trigger HDR output without requiring an HDR video source - included HDR image start times in stacking queries so the layered compositor can place images correctly in z-order - integrated HDR image compositing into the layered HDR render loop alongside native HDR video layers and SDR DOM overlays - forced screenshot mode for HDR layered compositing where required to keep DOM/HDR layer composition deterministic - skipped readiness waiting for natively extracted HDR videos in the engine path where it was unnecessary and could block layered HDR flows ### HDR metadata robustness - added a fallback in `extractVideoMetadata()` to read PNG `cICP` metadata directly when `ffprobe` omits color-space fields for PNGs - this specifically fixes CI/Docker detection for the `hdr-image-only` fixture, where the render was falling back to SDR because the PNG was not being recognized as BT.2020 PQ ### Regression coverage and fixture cleanup - added `hdr-image-only`, a regression fixture that validates HDR still-image rendering end to end - added `hdr-pq`, a focused HDR PQ regression fixture for the video path - updated regression CI to run an `hdr` shard with `--sequential hdr-pq hdr-image-only` - removed the older larger `hdr-regression/*` fixture set in favor of the smaller targeted regressions used by CI - added the necessary fixture generation/readme material and checked-in golden outputs for the new HDR tests ## Why The original PR description only covered the CLI flag forwarding and docs work. Since then, the branch also picked up the missing runtime support needed for HDR still images and the regression coverage to keep that path from breaking. The practical issue this closes is: - local host runs could pass while CI failed `hdr-image-only` - the failure was a full-frame visual mismatch caused by SDR fallback, not unstable rendering - root cause was PNG HDR metadata not being surfaced by `ffprobe` in the CI Docker environment - parsing the PNG `cICP` chunk directly makes HDR detection deterministic across environments ## Test plan ### Local targeted checks ```bash bunx oxlint packages/engine/src/utils/ffprobe.ts packages/engine/src/utils/ffprobe.test.ts bunx oxfmt packages/engine/src/utils/ffprobe.ts packages/engine/src/utils/ffprobe.test.ts bun --cwd packages/engine test src/utils/ffprobe.test.ts src/utils/hdr.test.ts ``` ### Producer regression runs on host ```bash bun run --cwd packages/core build:hyperframes-runtime:modular bun --cwd packages/producer test -- --sequential --exclude-tags slow,render-compat,hdr bun --cwd packages/producer test -- --sequential hdr-pq hdr-image-only ``` Observed result: - `fast` shard: 7 passed, 0 failed - `hdr` shard: 2 passed, 0 failed ### CI-equivalent Docker verification ```bash docker build -f Dockerfile.test -t hyperframes-producer:test . docker run --rm \ --security-opt seccomp=unconfined \ --shm-size=4g \ -v "$PWD/packages/producer/tests:/app/packages/producer/tests" \ hyperframes-producer:test \ --sequential hdr-pq hdr-image-only ``` Observed result: - `hdr-image-only`: passed - `hdr-pq`: passed - shard summary: 2 passed, 0 failed ### Specific regression fixed Before the PNG `cICP` fallback, the Docker/CI run failed `hdr-image-only` with: - missing `"[Render] HDR source detected — output: PQ ..."` log line - full-frame visual mismatch across all 100 checkpoints - PSNR ~17 on every frame, indicating a consistent SDR-vs-HDR pipeline mismatch After the fallback, the same Docker path recognizes the PNG as HDR and the shard passes. |
||
|
|
99a903be2f |
feat(hdr): layered HDR compositing, shader transitions, and HDR image support (#268)
* feat(hdr): shader transitions, --hdr flag, and SDR rendering fixes - 15 GLSL→TypeScript shader transitions on rgb48le buffers - Dual-scene compositing with scene detection via window.__hf.transitions - --hdr flag gates ffprobe probing (zero overhead on SDR compositions) - Cross-transfer conversion (PQ↔HLG) via OOTF-corrected composite LUT - Buffer.from() copy in writeFrame() fixes streaming encoder race condition - SDR rendering fixes (three stacked bugs) - Object.assign fix for window.__hf preservation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: tighten shader smoke thresholds + assert .scene contract - Tighten the all-transitions smoke test thresholds: at progress=0 we now require the center pixel R-channel > 35000 (was > 25000) and at progress=1 < 15000 (was < 25000). The old midpoint of 25000 sat exactly halfway between the test from-pixel (40000) and to-pixel (10000), so a half-blended transition would silently pass. - Add a runtime assertion in HyperShader.init() that every scene id resolves to a DOM element with the .scene class. Without this, missing ids silently no-op when textures + querySelectorAll(.scene) run later. Addresses deferred review feedback from PR #268. * fix(hdr): restore VIRTUAL_TIME_SHIM and applyRenderModeHints in renderOrchestrator Commit c6b4619c ("feat(hdr): shader transitions, --hdr flag, and SDR rendering fixes") accidentally removed two pieces of the deterministic rendering pipeline: 1. The `VIRTUAL_TIME_SHIM` injected via `createFileServer.preHeadScripts`, which freezes `Date.now()` and `requestAnimationFrame` so RAF-driven animations advance only when `window.__hf.seek(t)` is called. 2. The `applyRenderModeHints` function and its post-`compileForRender` call site, which auto-forces screenshot capture mode for compositions the compiler flagged as needing it (RAF, iframes, etc.). Without (1), RAF animations advanced by wall-clock between the main-loop seek and the per-DOM-layer seek inside `compositeToBuffer`, producing the sawtooth PSNR pattern on `raf-ball-render-compat` (high PSNR at integer seconds, ~24 dB everywhere else). Without (2), `iframe-render-compat` lost its automatic fallback to screenshot mode and the child-document motion stopped being captured. Both helpers are still produced by `htmlCompiler` and exercised by `renderOrchestrator.test.ts` — the orchestrator just stopped calling them. Restored: - Re-import `VIRTUAL_TIME_SHIM` from `./fileServer.js` - Pass `preHeadScripts: [VIRTUAL_TIME_SHIM]` to both `createFileServer` call sites (probe + main render) - Re-add `applyRenderModeHints` (matching the test expectations) and call it immediately after `compileForRender` - Persist `renderModeHints` in `summary.json` and the "Compiled composition metadata" log line Fixes the `iframe-render-compat` and `raf-ball-render-compat` regression failures on `feat/hdr-layered-compositing`. Made-with: Cursor * test(engine): expand sampleRgb48le coverage + audit Uint16Array alignment Adds: - 8 new sampleRgb48le bilinear-interpolation tests covering boundary pixels, sub-pixel weights, edge clamping, and odd-byte-offset Buffers. - uint16-alignment-audit.test.ts documenting the alignment requirement for Uint16Array views over Buffer slices vs. readUInt16LE/writeUInt16LE. Background: ~105 hot-loop sites in shader transitions still use readUInt16LE/writeUInt16LE. Switching to Uint16Array views would cut overhead but requires guaranteed even byteOffsets — these tests document the contract before any future refactor lands. * fix(engine,producer): mask DOM layers during HDR layered compositing The HDR layered compositor blits z-ordered layers over a shared canvas. DOM layers used a full-page screenshot from `captureAlphaPng`, which captures *every* painted pixel on the page — root background, sibling-scene content, overlay UI elements that aren't part of the current layer. Those opaque pixels were then blitted over the canvas, overwriting any HDR content composited beneath in earlier layers. The previous workaround toggled `display:none` on hide ids via `hideVideoElements`/`showVideoElements`. That correctly hid native videos but did nothing about the root composition's background or about overlay elements that the layer grouping considered part of a different layer. This commit replaces the workaround with a precise CSS mask installed before each DOM screenshot: 1. `applyDomLayerMask` injects a stylesheet that hides every `body *` and re-shows the layer's elements (and their descendants and their injected `__render_frame_*` siblings) with `visibility: visible !important`. CSS visibility is *not* multiplicative through descendants — a child with `visibility: visible` overrides an ancestor's `visibility: hidden`, so deeply nested layer content still paints even though every intermediate ancestor is hidden by the mass-hide rule. 2. Non-layer data-start ids are inline-hidden with `visibility: hidden !important`. Inline `!important` beats stylesheet `!important`, so this overrides the show rule for elements that fall under a show selector but should NOT paint — most importantly HDR videos and other-layer SDR videos that live as descendants of `#root`. 3. `removeDomLayerMask` tears the stylesheet down and clears the inline `visibility`/`opacity` properties so subsequent video frame injection gets a clean slate. Crucially the mask only sets `visibility`, never `opacity`. CSS opacity *is* multiplicative — `opacity: 0` on `#root` would zero out every descendant including layer videos, even with `visibility: visible`. We also extend `initTransparentBackground` to force the composition root (`[data-composition-id]`) transparent in addition to `html`/`body`, because compositions almost always set `#root { background: ... }` and that background paints across the whole viewport otherwise. Both compositing paths use the new helpers: - The per-layer DOM branch (`compositeToBuffer`) for normal frames. - The transition path (single DOM screenshot per scene) so transition frames also get a clean per-scene capture. Adds extensive `KEEP_TEMP=1`-gated diagnostics to `compositeToBuffer`: per-layer pixel-add accounting, dumps of every captured DOM PNG, and a periodic raw `rgb48le` snapshot of the composite buffer. These were essential to diagnosing the root-overwrite bug and stay zero-cost in normal renders. Also stops the workDir / per-video frame-dir cleanup when `KEEP_TEMP=1` so the dumps survive past frame N. Made-with: Cursor * fix(engine): preserve GSAP-applied opacity across DOM-layer captures SDR clips inside an HDR composition were rendering at full opacity even when the user had animated their wrapper opacity (e.g. fade-in or yoyo). Two bugs in the per-layer screenshot path conspired to drop the GSAP-applied opacity on the floor: 1. removeDomLayerMask was unconditionally calling `el.style.removeProperty("opacity")` on every wrapper after each layer capture. applyDomLayerMask only ever sets `visibility`, so the only inline opacity present is the value GSAP wrote. Stripping it between layer captures means that on the next capture (at the same timestamp), GSAP's `totalTime(t, false)` no-ops because the timeline is already at that time — the opacity is never restored, and the wrapper renders fully opaque. 2. injectVideoFramesBatch was reading the source <video>'s computed opacity via `parseFloat(computedStyle.opacity) || 1` and copying it onto the injected <img>. Because syncVideoFrameVisibility forces the <video> to `opacity: 0 !important` to hide it during capture, the computed value is always 0, which `|| 1` then silently flips to full opacity. The <img> is a sibling of the <video> inside the same wrapper, so it should inherit opacity from the wrapper directly instead of having a value hard-set on it. Fix both: drop the opacity removal in removeDomLayerMask, skip opacity when copying visual properties from <video> to <img>, and explicitly clear any stale inline opacity on the <img> so it inherits from the wrapper that GSAP is animating. Made-with: Cursor * fix(producer): correct hdrLayerStartTimes typo to hdrVideoStartTimes The diagnostic logging block in executeRenderJob's HDR layer composite path referenced an undeclared `hdrLayerStartTimes` map. The correct variable, declared and populated earlier in the same function, is `hdrVideoStartTimes`. The typo was introduced alongside the DOM-layer masking work and broke the producer build/typecheck on CI. Made-with: Cursor * fix(engine): restore video opacity copy to injected frame img Commit 188ebcca removed the opacity copy from `injectVideoFramesBatch` on the assumption that the <img> sibling would inherit GSAP's opacity from a shared wrapper. That breaks any composition where GSAP animates opacity directly on the <video> element itself: the <img> has no animated ancestor and renders at full opacity throughout any fade, even when the user's intent is partial or zero opacity. The CI `style-7-prod` and `style-8-prod` regressions caught this: the <video id="aroll"> fade-in from 3.0-3.5s rendered as a hard cut because the <img> inherited opacity 1 regardless of GSAP's tween. Restore the old explicit copy from `computedStyle.opacity` to the <img>'s inline opacity, with the `|| 1` fallback intentionally preserved. The fallback is load-bearing: GSAP's seek does not re-apply tweens that have already completed, so post-fade frames read opacity 0 from the stale `opacity: 0 !important` we apply to hide the native <video>. The `|| 1` recovers the tween's end-state opacity 1 for those frames, matching the final on-screen intent and the existing baseline renders. Handles both DOM shapes: - GSAP on wrapper: video's own computed opacity is 1, img set to 1, wrapper's opacity applies via stacking as before. - GSAP on <video>: video's computed opacity is the tween value, copied to img directly since they are siblings. Fixes: - style-7-prod: 0 failed frames (was 2 @ t=3.17, 3.33) - style-8-prod: 0 failed frames (was 2 @ t=3.05, 3.24) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
8548a17771 |
feat(hdr): GSAP transforms and border-radius masks on HDR video (#290)
## Summary HDR video elements with GSAP animations (position, scale, rotation, opacity) and CSS border-radius rendered without any transforms applied — the video just sat at (0,0) full-size. This PR adds affine transform support and rounded-corner masking for natively-composited HDR video. ## What it does **Affine blit with bilinear interpolation:** - `blitRgb48leAffine()` — Takes a 4x4 DOMMatrix and maps each destination pixel back to source coordinates via the inverse transform. Bilinear interpolation between the 4 nearest source pixels produces smooth edges under rotation and non-integer scaling. Optional opacity and border-radius parameters. - `parseTransformMatrix()` — Parses CSS `matrix(a,b,c,d,e,f)` strings into `[a,b,c,d,e,f]` tuples. **Accumulated viewport matrix:** - `getViewportMatrix()` — Walks the `offsetParent` chain from element to viewport, accumulating position offsets and CSS transforms at each level. Correctly handles `transform-origin` using the CSS sandwich: `translate(origin) × M × translate(-origin)`. This is critical because GSAP animates transforms on wrapper divs, not directly on the video element. **Effective opacity:** - `getEffectiveOpacity()` — Multiplies opacity values walking up the ancestor chain. Uses `Number.isNaN()` (not `|| 1`) so opacity:0 isn't incorrectly treated as 1. **Border-radius masks:** - `roundedRectAlpha()` — Per-pixel anti-aliased rounded-rectangle mask with support for independent corner radii. - `getEffectiveBorderRadius()` — Walks ancestors for `overflow:hidden` + border-radius. Resolves percentage values (e.g., `50%` for circles) via `offsetWidth`/`offsetHeight`. **Layout dimensions for extraction:** - Uses `offsetWidth`/`offsetHeight` (unaffected by CSS transforms) instead of `getBoundingClientRect()` (which returns the transformed bounding box and wobbles under rotation). ## Files changed | File | What changed | |------|-------------| | `packages/engine/src/utils/alphaBlit.ts` | `blitRgb48leAffine()`, `parseTransformMatrix()`, `roundedRectAlpha()`, `cornerAlpha()` | | `packages/engine/src/services/videoFrameInjector.ts` | `getViewportMatrix()`, `getEffectiveOpacity()`, `getEffectiveBorderRadius()`, `layoutWidth`/`layoutHeight` on `ElementStackingInfo` | | `packages/producer/src/services/renderOrchestrator.ts` | Affine blit path, extraction at layout dimensions, border-radius parameter passing | ## How to test Render a composition with an HDR video that has GSAP scale + rotation animation and a `border-radius: 50%` wrapper (circle mask). The video should rotate smoothly with round edges — no wobble, no sharp corners. ## Stack position **5 of 6** — Stacked on #289 (z-ordered layers). Adds transform and masking support to the HDR blit that the layer compositor uses. 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
0cc79a35b0 |
feat(hdr): z-ordered multi-layer compositing with PQ support (#289)
* feat(hdr): add z-ordered multi-layer compositing with PQ support Per-frame z-order analysis groups elements into DOM and HDR layers, composited bottom-to-top. Adjacent DOM elements merge into single screenshots. PQ (HDR10/smpte2084) support via sRGB-to-PQ LUT with 203-nit SDR reference white. queryElementStacking walks DOM for effective z-index, groupIntoLayers splits on HDR/DOM boundaries. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(hdr): address review feedback across stack - Document groupIntoLayers tie-break (V8 stable sort → DOM order). - Expand layerCompositor docstring: merge rationale, visibility inclusion. - Add tests: empty input, negative z-index, stable tie-break at equal z. - Document getEffectiveZIndex CSS stacking-context limitations. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
a21a62b574 |
feat(engine): add HDR two-pass compositing — DOM layer + native HLG video (#288)
## Summary Compositions with HDR video AND DOM overlays (text, graphics, SDR video) couldn't render both correctly — either HDR data was lost (Chrome captures sRGB only) or DOM overlays were missing (FFmpeg pass-through skips Chrome). This PR adds in-memory alpha compositing that combines both. ## What it does **Per-frame two-pass capture:** 1. **DOM pass** — Chrome screenshots the page with a transparent background (CDP alpha). HDR videos are hidden, leaving transparent holes where they go. 2. **HDR pass** — Pre-extracted native HLG/PQ frames (16-bit PNG from FFmpeg) are read from disk. 3. **Composite** — DOM pixels (sRGB RGBA8) are alpha-composited over HDR pixels (rgb48le) in Node.js memory, with sRGB→HLG/PQ conversion via a 256-entry lookup table. **Key components:** - `decodePng()` / `decodePngToRgb48le()` — Pure Node.js PNG decoders (no native dependencies). Support all 5 PNG filter types. - `blitRgba8OverRgb48le()` — Alpha composite with per-pixel sRGB→HDR LUT conversion. Fast paths for alpha=0 (skip) and alpha=255 (overwrite). - `initTransparentBackground()` + `captureAlphaPng()` — Split CDP transparent background setup (once) from per-frame screenshot capture (eliminates 2 CDP round-trips per frame). - Single-pass FFmpeg extraction — All HDR frames extracted in one sequential FFmpeg run (avoids duplicate frames from per-frame `-ss` fast seek). ## Key design decisions | Decision | Why | |----------|-----| | In-memory compositing (not FFmpeg overlay) | Eliminates ~2400 process spawns + temp files per render. Pure pixel math is 10x faster. | | 16-bit PNG intermediate | Raw `-f rawvideo` loses color metadata, causing moiré artifacts. PNG is self-describing. | | sRGB→HLG LUT (256 entries) | DOM content is sRGB. Without conversion, it appears orange-shifted in HLG stream. | | Native HDR detection before extraction | `extractAllVideoFrames` converts SDR→HDR. Pre-extraction probe identifies original HDR sources so only truly-HDR videos get native extraction. | ## Files changed | File | What changed | |------|-------------| | `packages/engine/src/utils/alphaBlit.ts` | **NEW** — PNG decode, sRGB→HDR LUT, alpha compositing (14 tests) | | `packages/engine/src/services/screenshotService.ts` | Transparent background CDP, `captureAlphaPng()` | | `packages/engine/src/services/videoFrameInjector.ts` | `hideVideoElements()` / `showVideoElements()` | | `packages/engine/src/services/streamingEncoder.ts` | Input color space tags for rgb48le | | `packages/producer/src/services/renderOrchestrator.ts` | Two-pass HDR capture loop, native HDR detection | ## How to test Render a composition with an HDR video background and text overlays. Both should be visible — HDR video at full quality, text crisp with correct colors (not orange-shifted). ## Stack position **3 of 6** — Stacked on #265 (HDR output pipeline). This is the foundation for all layered compositing that follows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
5a3fde19d4 |
feat(engine): add HDR video output pipeline (#265)
## Summary Adds the ability to render HDR video output (H.265 10-bit, BT.2020) from HyperFrames compositions. When the renderer detects HDR source video, it automatically switches to the HDR output pipeline — no flags needed. ## What it does - **Auto-detection** — Probes each video source with `ffprobe`. If any has bt2020/PQ/HLG color metadata, the output switches to H.265 10-bit with correct color tags. SDR-only compositions are unaffected (H.264, bt709). - **HLG pass-through** — Native HLG pixels from FFmpeg extraction are piped directly to the encoder without conversion. This avoids brightness loss from HLG→linear→PQ conversion (which requires an OOTF system gamma we can't reliably apply). - **Encoder HDR support** — Both chunk and streaming encoders accept HDR presets: `libx265`, `yuv420p10le`, BT.2020 color primaries, `hvc1` codec tag (required for Apple playback). - **WebGPU HDR capture (gated)** — A complete WebGPU float16 readback pipeline is implemented and tested but gated behind headed Chrome (headless doesn't expose WebGPU). Ready for future use with WebGPU canvas content. - **HDR utilities** — `detectTransfer()` (PQ vs HLG), `getHdrEncoderColorParams()`, `analyzeCompositionHdr()`. 15 unit tests. ## Key design decisions | Decision | Why | |----------|-----| | No `--hdr` flag | SDR content encoded as HDR causes orange shift in browsers. Auto-detect eliminates this. | | HLG pass-through (not HLG→PQ) | Conversion loses brightness without OOTF. Pass-through matches source exactly. | | `hvc1` codec tag | Apple QuickTime requires `hvc1` (not `hev1`) for HEVC playback. | | 1-hour streaming timeout | HDR capture at ~6fps needs more time than the default 10-minute FFmpeg timeout. | ## Files changed | File | What changed | |------|-------------| | `packages/engine/src/utils/hdr.ts` | **NEW** — HDR detection, transfer types, encoder params (15 tests) | | `packages/engine/src/services/hdrCapture.ts` | **NEW** — WebGPU readback, HLG conversion, PQ encode | | `packages/engine/src/services/streamingEncoder.ts` | HDR presets, raw rgb48le input, color tags | | `packages/engine/src/services/chunkEncoder.ts` | HDR presets, conditional color tags | | `packages/producer/src/services/renderOrchestrator.ts` | Auto-detection loop, HDR pass-through capture path | ## How to test Render a composition with an HDR video source. The output should be H.265 10-bit with HDR metadata visible in `ffprobe` (bt2020, arib-std-b67 or smpte2084). Plays correctly in QuickTime and on HDR displays. ## Stack position **2 of 6** — Stacked on #258 (SDR/HDR normalization). Provides the encoder infrastructure that phases 1-5 build on. 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
a262ad59f3 |
chore(skills): remove 1,685 lines of redundant skill content (#283)
* chore(skills): remove 1,685 lines of redundant and irrelevant skill content - Remove 5 GSAP references irrelevant to HyperFrames (scrolltrigger, plugins, react, frameworks, utils) — no scroll, no frameworks, no interactive plugins in video compositions - Remove shader-setup.md and shader-transitions.md — duplicated by @hyperframes/shader-transitions package (packages/shader-transitions/) - Remove marker-highlight.md and examples.md — JS library docs superseded by css-patterns.md (deterministic, GSAP-driven, fully seekable) - Trim CLAUDE.md to dev-only instructions — move product docs (transcription, TTS, player) to skills where they belong - Deduplicate house-style.md typography/motion sections — point to dedicated references instead of repeating rules - Clean up stale references to deleted files across SKILL.md and catalog.md - Update gsap skill description to reflect HyperFrames-only scope Skills: 5,230 → 3,714 lines (29% reduction) CLAUDE.md: 204 → 50 lines (75% reduction) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(skills): update broken marker-highlight.md references in captions.md Point to css-patterns.md instead of deleted marker-highlight.md. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(skills): update stale shader CSS rule to reference package API BG_COLOR was from the old manual setup. Now it's bgColor in the @hyperframes/shader-transitions init() config. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(skills): address 6 doc gaps surfaced by eval agents P0: Document HyperShader as IIFE global name in shader-transitions README P1: Replace async fetch() with sync XHR in effects.md audio data loading (fetch violates synchronous timeline construction rule in SKILL.md) P1: Change <div> to <span> in css-patterns.md marker highlight patterns (<div> inside <p> is invalid HTML, breaks layout in inline contexts) P2: Clarify bgColor as fallback color in shader-transitions README P2: Add data-start to Composition Clips table in SKILL.md (root composition element needs data-start="0", linter enforces it) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(templates): update init templates to match trimmed skill scope - Remove ScrollTrigger/plugins/React/Vue/Svelte from gsap skill description - Replace class="clip" with accurate pattern examples in skill intro text (class="clip" is still in Key Rules where it belongs) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(skills): remove contradictory 5:1 contrast threshold from house-style house-style.md said 5:1 minimum, but hyperframes validate enforces WCAG AA (4.5:1 normal text, 3:1 large text). Now defers to validate instead of stating a conflicting number. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
cb3d94c2a5 |
feat: add @hyperframes/shader-transitions package (#251)
## Summary
New `@hyperframes/shader-transitions` package that encapsulates WebGL shader transitions into a single `HyperShader.init()` call. Replaces ~200 lines of per-composition boilerplate that LLMs failed to wire correctly 60% of the time.
### API
```js
var tl = HyperShader.init({
bgColor: "#0a0a1a",
accentColor: "#6366f1",
scenes: ["scene1", "scene2", "scene3", "scene4", "scene5"],
transitions: [
{ time: 7.2, shader: "cross-warp-morph", duration: 0.7 },
{ time: 15.2, shader: "domain-warp", duration: 0.7 },
]
});
tl.from("#s1-title", { y: 50, opacity: 0, duration: 0.7 }, 0.3);
```
### What the library handles
- **13 shader programs**: domain-warp, ridged-burn, whip-pan, sdf-iris, ripple-waves, gravitational-lens, cinematic-zoom, chromatic-split, glitch, swirl-vortex, thermal-distortion, cross-warp-morph, light-leak
- **html2canvas** bundled as dependency (not CDN) — single script tag for CLI users
- **DOM-during-holds**: canvas hidden between transitions, GSAP animations play on live DOM
- **Async capture with pause/resume**: timeline pauses during capture, resumes after textures uploaded — prevents progress tween from running ahead
- **Accent color theming**: `accentColor` derives dark/mid/bright uniforms. Burns, glows, leaks match the composition palette
- **Graceful degradation**: falls back silently when WebGL unavailable
### Code quality (from 3 review agents)
- No `!` non-null assertions — all WebGL creation calls throw on failure
- Vertex shader compiled once, cached across all programs
- Uniform/attribute locations cached per program via WeakMap (not looked up every frame)
- Captured canvases freed after texture upload (8MB each)
- Single timeline creation (was creating two, discarding one)
- Shared `tickShader()` render callback (was copy-pasted)
- `.finally()` for DOM restore in capture (was duplicated in `.then`/`.catch`)
- `parseHex` validates input (was silently producing NaN on invalid hex)
- Dead `ND`/`CP` shader library exports removed
### Shader-compatible CSS rules (transitions.md)
6 rules for compositions using shader transitions:
1. No `transparent` in gradients (canvas interpolates through black)
2. No gradient backgrounds on elements < 4px
3. No CSS variables on captured elements
4. `data-no-capture` for uncapturable decoratives
5. No gradient opacity < 0.15
6. Every `.scene` must have explicit `background-color` matching `bgColor`
### Build output
- IIFE (~214KB with html2canvas bundled, ~65KB gzipped) — `window.HyperShader`
- ESM + CJS + TypeScript declarations
- tsup build following `@hyperframes/player` conventions
## Test plan
- [ ] `bun run build` succeeds (includes shader-transitions)
- [ ] `bunx oxlint packages/shader-transitions/src/` — 0 errors
- [ ] Create a composition using `HyperShader.init()` — verify transitions fire, DOM animations play, accent colors match
- [ ] Test graceful degradation: composition works without WebGL (no transitions, no crash)
- [ ] Verify pause/resume: scrub to transition boundary — no jump in progress
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||
|
|
5de2af5bde |
feat(skills): improve hyperframes composition quality rules (#250)
## Summary
Overhaul the hyperframes composition skill based on 26 eval rounds (~100 generated compositions). The goal: prevent known AI design tells and composition bugs while giving the LLM maximum creative freedom.
### Typography (`fonts.md` → `typography.md`)
- Two-tier banned font list (32 fonts): tier 1 bans training-data defaults, tier 2 bans the reflex replacements
- Font discovery script: queries Google Fonts API, 5 dynamic categories, top 5 randomized per run
- Selection philosophy: register-first thinking, cross-check assumptions
### Google Fonts on-demand (`deterministicFonts.ts`)
- Any Google Font works without pre-bundling — compiler fetches woff2 at compile time
- Cached to `~/.cache/hyperframes/fonts/<slug>/<weight>-<style>.woff2`
- Parallel woff2 fetches via `Promise.allSettled` (was sequential)
- Single `mkdirSync({ recursive: true })` per family (was `existsSync` x11)
- Skip redundant `readFileSync` when buffer is already in memory from fetch
### Layout rules (`SKILL.md`)
- Flexbox with gap for content text — prevents overlap from absolute positioning
- `position: absolute` reserved for decoratives only
- Cards/containers explicitly banned
### Background layer (`house-style.md`)
- 3-5 persistent decorative elements per scene (glows, ghost text, accent lines)
- All decoratives MUST have ambient GSAP animation — static decoratives banned
- WRONG/RIGHT code examples
### Transition rules (`SKILL.md`)
- Always use transitions, always entrance animations, exit animations banned except final scene
- WRONG/RIGHT code examples showing banned exit patterns
### Other
- Flash cut transition removed
- CLAUDE.md: `bun install` / `bun run build` / `bun run test` (was pnpm)
- house-style.md trimmed from 184 to ~80 lines
- SKILL.md trimmed from 364 to ~230 lines
## Test plan
- [ ] `bun install` succeeds, workspace links resolve
- [ ] `bun run build` succeeds
- [ ] `npx hyperframes lint` passes on existing compositions
- [ ] Generate a composition with `/hyperframes` skill — verify flexbox, background decoratives with animation, entrance-only animations, no banned fonts
- [ ] Verify Google Fonts on-demand: use a non-bundled font, run `npx hyperframes preview`
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||
|
|
d86e4cb3c3 |
feat(skills): add layout-before-animation approach to hyperframes skill (#233)
Adds structured approach (what, structure, timing, layout, animate) and layout-before-animation guidance — build end-state CSS first, then add gsap.from() entrances and gsap.to() exits. Prevents unintentional overlap by making layout problems visible before adding motion. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
4c5b8e38a1 |
feat(skills): add typography and motion principles, fix validate $& bug (#228)
Add two new skill reference files that address measured LLM composition failures: - fonts.md: Typography principles — banned fonts, guardrails for violations (pairing two sans-serifs, defaulting to 400/700 weight), and guidance the LLM genuinely doesn't apply without being told (register switching, tension as meaning, easing direction as emotion). Includes Google Fonts API discovery script with 7-category multi-strategy query. - motion-principles.md: Motion design principles — guardrails for same-ease and same-speed defaults, y-axis entrance monotony, and guidance for build/breathe/ resolve scene structure, hard cuts as intentional transitions, visual composition rules for video-not-web density. Both files validated against baseline evals: 3 compositions created without guidance confirmed the LLM reaches for banned fonts (Inter, Cormorant Garamond, Playfair Display, Roboto Condensed), uses power2.out on 45-72% of tweens, enters 80%+ of elements from y-axis, and pairs multiple sans-serifs. Also: - Fix validate.ts $& replacement bug (runtime source containing $& caused String.prototype.replace to re-insert the matched <script src=""> tag) - Clean up font loading guidance across skills (compiler embeds automatically) - Update house-style.md to reference fonts.md Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
fc973ee2e8 |
feat(lint): add rules for missing data-start, template wrapper, and DOCTYPE
Three new lint rules that catch structural issues causing compositions to fail silently in preview: - root_composition_missing_data_start: Root composition needs data-start="0" for the runtime to begin playback - standalone_composition_wrapped_in_template: index.html should not be wrapped in <template> (only sub-compositions use that) - root_composition_missing_html_wrapper: index.html needs <!DOCTYPE html> and <html> wrapper for the bundler Also adds rawSource to LintContext so rules can inspect pre-template-stripped HTML, and isSubComposition to linter options so rules can distinguish root from sub-composition files. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
85a76c0043 |
feat(cli): implement Docker rendering for deterministic output (#215)
## Summary - **The `--docker` flag was a no-op stub** — `renderDocker` called the same local `executeRenderJob` as `renderLocal`, no container was ever launched - Now `renderDocker` generates a Dockerfile, builds a versioned `hyperframes-renderer:<version>` image with Chrome/FFmpeg/fonts/chrome-headless-shell, and runs the render inside a container - Image is cached per CLI version — first render builds (~2 min), subsequent renders reuse it - Forces `linux/amd64` platform since chrome-headless-shell has no ARM Linux binary - Uses `execFileSync` (array form) throughout to prevent shell injection - Forwards `--quiet`, `--gpu`, and render config flags into the container - `Dockerfile.render` added as a reference for manual builds ## Test plan - [x] `hyperframes render --docker` builds image and produces valid MP4 - [x] Second run reuses cached image (no rebuild) - [x] `--quiet` suppresses container output while keeping stderr for errors - [x] Typecheck, lint, format all pass - [ ] Verify `--gpu` with `--docker` on a machine with GPU access 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
569513145b |
feat(skills): add WebGL shader transitions and restructure catalog (#213)
* feat(skills): add WebGL shader transitions and restructure catalog Add 14 WebGL fragment shader transitions to the transitions skill: domain warp dissolve, ridged burn, whip pan, SDF iris, ripple waves, gravitational lens, cinematic zoom, chromatic radial split, glitch, swirl vortex, thermal distortion, flash through white, cross-warp morph, and light leak (shader). Restructure catalog.md from a 1045-line monolith into a 105-line routing layer with 15 reference files. SKILL.md loads at 101 lines, catalog.md loads at 105 lines — reference files loaded on demand only for the transition type being implemented. Key additions: - Full WebGL setup boilerplate with media capture (images, video, object-fit: cover, live video re-upload during transitions) - Hard rules for shader transitions capturing all bugs found during development (Y-flip, preserveDrawingBuffer, fwidth, boomerang, tween proxy reuse, tl.call vs onComplete) - CSS vs Shader decision guide in SKILL.md - Visual pattern warning against repeating geometric patterns - Shader transitions slotted into mood/energy mapping tables - Noise libraries: quintic C2, ridged, erosion FBM, cosine palette Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(skills): fold transitions into hyperframes skill Move transitions from a standalone skill (4th top-level) into hyperframes/references/transitions/, aligning with the consolidation in #211 that reduced 15 skills to 3. Fewer standalone skills means higher trigger reliability for multi-skill tasks. Also removes stale text-burn-dom.html reference from css-destruction.md. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
c47e710ffc |
feat(skills): add scene transitions skill with 35-type catalog (#212)
## What A scene transition selection framework and implementation catalog covering 35 transition types across 8 categories. ### transitions skill **SKILL.md** — Selection framework: - Energy → transition mapping (calm/medium/high) - Mood → transition mapping (warm, cold, editorial, tech, edgy, playful, dramatic, premium, retro) - Narrative position guidance (opening, between sections, climax, outro) - Blur intensity scaling by energy level - Configuration presets (snappy, smooth, gentle, dramatic, instant, luxe) **catalog.md** — Implementation reference: - GSAP code for all 35 transitions - Hard rules from real bugs (scene visibility, iframe compatibility, VHS clone pattern, z-index, overlay sizing) - Scene template ### Categories | Category | Transitions | |----------|------------| | Content-transforming | Push slide, vertical push, elastic push, squeeze, zoom through, zoom out, gravity drop, 3D flip | | Reveal/mask | Circle iris, diamond iris, diagonal split, clock wipe, shutter | | Dissolve | Crossfade, blur crossfade, focus pull, color dip | | Cover | Staggered blocks, horizontal blinds, vertical blinds | | Light | Light leak, overexposure burn, film burn | | Distortion | Glitch, chromatic aberration, ripple, VHS tape | | Pattern | Grid dissolve | | Instant | Flash cut, morph circle | ## Why Agents building multi-scene compositions were using the same opacity crossfade for every scene change regardless of video mood/energy. The transitions skill provides context-aware selection so a wellness video gets blur crossfades while a sports promo gets flash cuts and a cyberpunk event gets VHS distortion. ## How - SKILL.md follows writing-skills guide: description uses "Use when..." triggers, no workflow summary, under 500 words - catalog.md is heavy reference with table of contents - Hard rules consolidated from real rendering bugs discovered during 16 A/B eval comparisons - Mood mappings designed from a motion design perspective ## Test plan - [x] 16 A/B eval compositions comparing with/without skill across moods - [x] 39-scene transition catalog composition demoing every type - [x] Skill audit against writing-skills guide - [x] All transition types mapped to at least one mood - [x] Lint passes Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
d8bffd41f9 |
feat(lint,skills): add caption/audio-reactive lint rules and skill guidance (#207)
## What Bumped all package versions to `0.2.2-alpha.4` and added five new lint rules for caption and GSAP animation quality checks. ## Why The new lint rules address common issues in HyperFrames compositions: - Caption overflow clipping when emphasis words are scaled above 1.0x - Text shadow artifacts on caption group containers with semi-transparent children - Mismatch between fitText maxWidth and scaled word dimensions - Imperceptible audio reactivity from single tweens instead of time-sampled animations - Scene layer visibility conflicts when relying only on opacity tweens ## How Added three new caption-specific lint rules in `captions.ts`: - `caption_overflow_clips_scaled_words` - detects `overflow: hidden` on caption containers when scripts scale words above 1.0x - `caption_textshadow_on_group_container` - flags textShadow tweens applied to group containers instead of individual words - `caption_fittext_scale_mismatch` - calculates effective width from fitText maxWidth × max scale factor and warns when it exceeds safe bounds Added two new GSAP lint rules in `gsap.ts`: - `audio_reactive_single_tween_per_group` - identifies audio-reactive captions using peak values instead of time-sampled loops - `scene_layer_missing_visibility_kill` - detects multi-scene compositions missing hard visibility kills after opacity exit tweens Enhanced documentation with new mask reveals guide and updated existing skills with overflow handling, scene management, and audio reactivity best practices. ## Test plan - [x] Lint rules tested against existing composition patterns - [x] Documentation updated with new techniques and constraints - [x] Version bumps applied consistently across all packages |
||
|
|
e9c2e6f772 |
fix(cli): resolve SyntaxError in bundled ESM output (#203)
Two issues prevented `npx hyperframes` from running: 1. The tsup banner declared `const __filename` which collided with esbuild's CJS-to-ESM `var __filename` shim. ESM strict mode rejects const+var redeclaration. Changed to `var` so both declarations coexist. 2. postcss (producer dependency) was not resolvable during bundling due to bun's isolated module layout. Added postcss as an external dependency of the CLI package. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
cb0b17062a |
feat(skills): add marker-highlight skill for animated text highlighting (#190)
## Summary - **New skill:** **`marker-highlight`** — integrates [MarkerHighlight.js](https://github.com/Robincodes-Sandbox/marker-highlight) into HyperFrames compositions. Canvas-based animated text highlighting with 5 drawing modes: marker pen, circle, burst, scribble, and sketchout. - **Studio fix:** added missing `captionSync` to useEffect dependency array (oxlint exhaustive-deps) - **Studio fix:** `loadOverrides` now checks `res.ok` before parsing, preventing 404 console noise on projects without captions ## Skill details The skill documents the non-obvious GSAP integration pattern discovered during development: 1. **One highlighter per container** — the library clears ALL `.highlight` divs from the shared parent on init, so multiple instances on sibling marks conflict 2. **`data-color`** **\+** **`data-original-bgcolor`** — prevents the CSS background-color flash that occurs when the library reads and clears the mark's background 3. **Canvas pre-draw + clear + reanimate** — `animate: false` pre-draws statically, canvases are hidden, then cleared and shown with `reanimateMark()` at trigger time for clean animated reveals 4. **`onReverseComplete`** **for rewind** — hides highlight divs when the timeline seeks backward past the trigger point ## Test plan - [ ] `npx hyperframes lint` passes on test-composition - [ ] Studio preview shows marker highlight on "something" at 1s, circle on "love" at 2.2s - [ ] Rewind past trigger points hides highlights - [ ] No 404 console errors for caption-overrides.json on non-caption projects [Screen Recording 2026-04-02 at 1.56.30 AM.mov <span class="graphite__hidden">(uploaded via Graphite)</span> <img class="graphite__hidden" src="https://app.graphite.com/user-attachments/thumbnails/53b03f4e-538e-477a-b738-7a033b99a84e.mov" />](https://app.graphite.com/user-attachments/video/53b03f4e-538e-477a-b738-7a033b99a84e.mov) 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
5e2781b459 |
fix(studio): address caption designer PR feedback (#200)
* fix(studio): address caption designer PR feedback Fixes from review comments on feature/caption-designer (#180): - fix(generator): guard named colors in hexToRgba — "red", "transparent" no longer produce NaN rgba values - fix(sync): log auto-save failures instead of silently swallowing them - fix(sync): check res.ok before parsing caption-overrides response - refactor(components): extract Section, Row, inputCls into shared.tsx to eliminate duplication between CaptionPropertyPanel and CaptionAnimationPanel - fix(store): replace non-deterministic Date.now()+Math.random() ID with counter-based group IDs - fix(store): read selectedGroupId from state param instead of get() to avoid stale reads in batched set() calls - fix(overlay): remove cssScale multiplier from getBoundingClientRect coords — the browser already accounts for CSS transforms - docs(parser): add comment explaining the lazy ]; regex assumption Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(studio): address remaining caption designer feedback Overlay: handle both per-word spans (generator output) and grouped text nodes (existing templates). Wraps text nodes into individual spans on demand so the overlay can target words in any caption format. Property panel: add Typography (font, size, weight, spacing) and Color (color, active, dim, opacity) sections alongside existing Position and Transform controls. Timeline: move caption timeline into a dedicated flex-shrink-0 section below the main timeline tracks instead of inside the scrollable area. Gives it fixed 60px height that's always visible. Caption overrides: classify color tweens by comparing target color to the dim baseline instead of relying on timeline position order. This handles compositions with custom color tweens correctly. App.tsx: remove polling interval, rely on runtime postMessage events for caption detection. Add clarifying comment on why useEffect is appropriate (external event subscription). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(studio): restore cssScale in overlay coordinate conversion getBoundingClientRect() on iframe-internal elements returns coordinates in the iframe's native resolution (1920x1080), not the CSS-scaled display size. The cssScale multiplier is needed to convert to parent window coordinates. The earlier removal was incorrect — it only worked at 1:1 scale. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(studio): fix reversed scaling on left-side corner handles Scale interaction used horizontal dx from start position, which goes negative when dragging left handles outward. Now uses distance from box center — dragging away from center increases scale regardless of which corner handle is used. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(studio): make rotation respond to horizontal drag only Rotation handle sits directly above the word, so atan2-based rotation barely responds to left/right movement. Replace with linear horizontal mapping: drag right = clockwise, drag left = counter-clockwise, 200px = 90 degrees. Vertical movement is ignored. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(studio): remove animation tab and typography/color from property panel Keep only Position (X, Y) and Transform (Scale, Rotation) controls. Remove tab switcher UI since there's only one view now. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: fix oxfmt formatting in CLAUDE.md and captions skill docs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
d36c1785b9 |
feat(captions): energy-based technique selection and mandatory quality checks (#176)
## Summary - Rewrite script-to-style mapping as an energy detection table (high → low) with mandatory animation requirements: karaoke baseline, 2+ highlight techniques, kinetic exits - Replace `tl.call()` per-frame audio-reactive pattern with group-level GSAP tweens — read peak bass/treble for each group's time range and modulate entrance intensity at build time, no per-frame callbacks needed - Add transcript quality check with automatic retry rules (>20% music tokens = retry with larger model) - Add caption word structure lint rule (`.caption-group` + `<span>`) for studio editor compatibility - Add multilingual model guidance and decision tree for model selection ## Test plan - [ ] Skill files render correctly as markdown - [ ] Cross-references between SKILL.md, dynamic-techniques.md, and transcript-guide.md resolve correctly - [ ] `dynamic-techniques.md` audio-reactive section uses `tl.to()`/`tl.set()` only, no `tl.call()` loops 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
ad2d63db32 |
feat(cli): skill install targets + remove custom install in favor of vercel-labs/skills (#177)
## Summary **Skill install targets (original):** - Add project-level skill install targets: Windsurf, Cline, Roo Code, Trae (opt-in via flag) - Split install logic into global vs project-level - Fix lint false positive: timed tags with `data-composition-id` no longer flagged by media rule **Skill system cleanup (folded from #189):** - Delete `install-skills.ts` (~485 lines) — remove custom installation wrapper entirely - Strip skill logic from `init` — no more project-level `.claude/skills/` copies, no `--skip-skills` flag; replaced with post-scaffold message: `npx skills add heygen-com/hyperframes` - Front-load SKILL.md trigger words — all 5 skill descriptions rewritten so activation language comes first (~150 chars) - Update CLAUDE.md — install instructions now point to [vercel-labs/skills](https://github.com/vercel-labs/skills) - Fix `.claude/settings.json` — pre-commit hook changed from `pnpm` to `bun` ## Test plan - [ ] `npx hyperframes skills` → "Unknown command skills" - [ ] `npx hyperframes init test --template blank --non-interactive --skip-transcribe` → prints `npx skills add heygen-com/hyperframes` - [ ] `grep -r "install-skills" packages/cli/src/` → no results - [ ] All 5 `skills/*/SKILL.md` have front-loaded descriptions 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
37404f23da |
feat(whisper+captions): language detection, audio-reactive captions, multilingual defaults (#175)
## Summary **Whisper improvements:** - Auto-detect language and switch from `.en` to multilingual model when needed - Detect speech onset in WAV to strip hallucinated words before speech begins - Merge whisper-cpp token fragments: contractions (`didn` + `'t` → `didn't`), split capitals (`C` + `aught` → `Caught`), dropped-g (`shin` + `in'` → `shinin'`) - Interpolate zero-duration word clusters for reliable karaoke timing **Captions skill updates (folded from #176):** - Rewrite script-to-style mapping as an energy detection table (high → low) with mandatory animation requirements: karaoke baseline, 2+ highlight techniques, kinetic exits - Replace `tl.call()` per-frame audio-reactive pattern with group-level GSAP tweens — read peak bass/treble for each group's time range and modulate entrance intensity at build time - Add transcript quality check with automatic retry rules (>20% music tokens = retry with larger model) - Add caption word structure lint rule (`.caption-group` + `<span>`) for studio editor compatibility **Multilingual defaults (folded from #186):** - Default whisper model changed from `small.en` to `small` to prevent silent translation of non-English audio - Added non-negotiable language rule to captions skill ## Test plan - [ ] `pnpm test` passes (contraction merging, fragment merging, zero-duration interpolation, speech onset) - [ ] Transcribe non-English audio — verify it transcribes in original language, not translates - [ ] Skill files render correctly, cross-references resolve - [ ] `dynamic-techniques.md` audio-reactive section uses `tl.to()`/`tl.set()` only, no `tl.call()` loops 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
159a2e7113 |
feat(skills): add dynamic caption techniques and split captions skill into references (#173)
## Summary - Split the captions skill from a single 611-line file into focused references: `SKILL.md` (core rules), `transcript-guide.md` (whisper/transcription), `dynamic-techniques.md` (animation patterns) - Add `audio-reactive` skill with "Content, Not Medium" constraint — steers away from generic visualizations (equalizer bars, spectrum analyzers, waveforms) toward content-grounded animation where audio drives *when* and *how much*, not *what to show* - Add initial dynamic caption technique selection by energy level ## Test plan - [ ] All skill files render correctly as markdown - [ ] Cross-references between files use correct relative paths - [ ] `audio-reactive/SKILL.md` contains the anti-pattern list and content-grounded examples 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
1e4c101fb4 | feat: lint for audio tag existingon found project audio (#169) | ||
|
|
0dcf73d62a |
feat: async skills install (#172)
## What
Added progress reporting to the skills installation process by converting synchronous operations to asynchronous ones and implementing progress callbacks.
## Why
The skills installation process can take a significant amount of time, especially when cloning repositories or running npm operations. Users need feedback about what's happening during the installation to understand progress and know the system hasn't frozen.
## How
- Converted `execFileSync` calls to a new `execFileAsync` function using promises
- Made all installation functions (`runSkillsAdd`, `gitClone`, `fetchRepo`, `fallbackInstall`) asynchronous
- Added an optional `onProgress` callback parameter to `installAllSkills` that accepts progress messages
- Integrated progress reporting in the `init` command by passing spinner message updates to the progress callback
- Added progress messages for key installation steps like "Installing {source} skills..." and "Cloning skill repositories..."
- Added "giget" to the external dependencies list in the build configuration
## Test plan
- [ ] Unit tests added/updated
- [ ] Manual testing performed
- [ ] Documentation updated (if applicable)
|
||
|
|
9d54192e83 | chore: remove pnpm lock (#171) | ||
|
|
116e6aa8e0 |
feat(skills): add audio visualizer effect with extraction script (#168)
* feat(skills): add audio visualizer effect with extraction script Adds reactive audio visualization patterns for HyperFrames: Script: extract-audio-data.py pre-extracts per-frame RMS amplitude and frequency band data via ffmpeg. Uses a 4096-sample FFT window for clean frequency resolution and per-band normalization across the full track so treble activity is visible alongside louder bass. Patterns: spectrum bars, mirrored waveform, pulsing circle, circular visualizer, background glow — all Canvas 2D driven from the GSAP timeline via tl.call() at each frame. Includes smoothing helper, band count guide, band ordering rules (horizontal: low-left high-right), and combining patterns section. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(skills): replace prescriptive examples with data model + motion principles Removes five hardcoded draw functions that would get copy-pasted verbatim. Replaces with: - Clear data model docs (what rms and bands mean, how to index) - Rendering approach setup for Canvas 2D, WebGL/Three.js, and DOM - Motion principles (smoothing, value mapping, what makes it feel good) - Spatial mapping conventions (low-left/high-right, etc) The LLM invents the visualization; the skill teaches the data contract and motion constraints. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(skills): off-by-one in band slicing, add data loading, fix trigger - Fix exclusive slice end: high_bin clamped to n_bins (not n_bins-1) so the last FFT bin in each band is included - Add data loading section to skill doc (inline and fetch patterns) - Fix example JSON to show frame 0 at time 0.0 - Update description to trigger when audio is analyzed and music detected Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(skills): require numpy, fix bugs, clean up skill doc Script rewrite: - numpy is now required (pure-Python DFT was unusable for real files) - Use np.frombuffer instead of struct.unpack (~10x less memory) - Precompute Hann window and band edges (were recalculated every frame) - Extract SAMPLE_RATE as module-level constant - Clamp band bins to prevent max() on empty slice - Validate --fps and --bands inputs Skill doc fixes: - Fix fetch loading example (was null ref on sync for-loop) - Remove redundant Canvas 2D section (was duplicate of Step 3) - Fix opening line (said "Canvas 2D" but doc covers 3 approaches) - Fix undefined W/H in example Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
e7283e5ce3 |
feat(skills): add gsap-effects skill with typewriter pattern (#158)
* feat(skills): add gsap-effects skill with typewriter pattern Distills typewriter text animation into a reusable reference: basic typewriter, blinking cursor, word rotation, appending words, and a characters-per-second timing guide. Uses GSAP TextPlugin. Also references the new skill from compose-video. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(skills): emphasize cursor must always blink when idle and sit flush Two key rules added to the typewriter skill: 1. Cursor must blink in every idle state (after typing, after clearing, during hold pauses) — a solid idle cursor looks broken. 2. No whitespace between text and cursor elements in HTML — any gap between the last character and the caret looks wrong. Also adds cursor-hide state for multi-line handoffs and updates word rotation example to include cursor state management. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(skills): backspace must delete from end, not front TextPlugin's text:{value:""} removes characters from the front, which looks wrong. Added a backspace helper that steps through substrings from right to left using tl.call(). Updated word rotation example to use it. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(skills): handoffs must blink before typing, use margin for spacing Two lessons from testing: 1. Cursor handoffs need a blink pause — going hide→solid directly skips the idle state. Pattern: hide→blink→pause→solid→type→blink. 2. Use margin-left on a wrapper span for spacing between static and dynamic text. Flex gap spaces the cursor away, trailing spaces collapse. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(skills): enforce single visible cursor as a hard rule Only one cursor may be visible at a time. Multiple cursors on screen looks broken. Every other cursor must be cursor-hide. Promoted to rule #1 in the cursor section. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
256c7a74fe |
feat(cli): add gradient ASCII banner to init command (#156)
Displays a white → #74E1B9 → #6ADCFF gradient HYPERFRAMES banner using ANSI Shadow figlet font when running hyperframes init. Gracefully skips in non-TTY or no-color environments. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
9cbfec1eca |
feat(skills): add hyperframes-cli skill (#154)
* feat(skills): add hyperframes-cli skill for CLI workflow guidance Adds a new skill that teaches AI agents how to use the HyperFrames CLI (init, lint, dev, render, doctor). Previously, agents had no way to discover the CLI — the compose-video skill only covered HTML authoring. This led to agents searching for binaries, finding the monorepo, and running bun run studio manually instead of using npx hyperframes dev. Also registers the skill in init.ts so new projects get it bundled alongside hyperframes-compose and hyperframes-captions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(cli): rename dev command to preview The command starts a preview server — "preview" describes what users are doing more accurately than "dev". Updates the command name, file name, all CLI references, docs, skills, and template CLAUDE.md. 22 files updated across CLI source, docs, skills, and templates. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(skills): replace stale dev reference with preview in CLI skill Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(docs): catch remaining dev references missed in rename - testing-local-changes.mdx: two inline command examples - troubleshooting.mdx: anchor link #dev → #preview, "dev server" → "preview server" - cli.mdx: "dev server" → "preview server" Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
ef6225da1d |
feat(core): add fitTextFontSize utility for pixel-accurate text measurement (#152)
Add @chenglou/pretext dependency and fitTextFontSize() utility that uses canvas measureText to compute the largest font size that fits text within a given width. Replaces character-count heuristics with actual font-aware measurement. - New fitTextFontSize() in @hyperframes/core/text, exposed on window.__hyperframes - Generalized for all text elements (captions, titles, etc.), not just captions - Unit tests (mocked pretext) + browser integration test (real Chromium canvas) - Updated captions skill docs with usage, exit guarantee, and self-lint patterns Co-authored-by: James <james.russo@heygen.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
a97dc75702 |
fix(lint): detect GSAP animations targeting clip elements (tab crash) (#114)
* fix(lint): detect GSAP animations targeting clip elements (tab crash) The runtime manages clip visibility via inline styles. When GSAP also writes inline styles on the same element, both systems trigger style recalculations every frame, creating a runaway loop that crashes the browser tab. New rule gsap_animates_clip_element (error severity): - Builds map of all elements with class="clip" (by id and class) - Checks if any GSAP selector resolves to a clip element - Nested selectors like "#overlay .title" are correctly ignored - Merged into existing GSAP script loop (no redundant parsing) * fix: remove non-null assertions and add missing test coverage - Replace `!` assertions with optional chaining in lint.ts and tests - Add shouldBlockRender tests for --strict-all without --strict - Add clip element test for class-only detection (no id) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use optional chaining for array access in lintProject tests TypeScript's strict mode flags array indexing as possibly undefined. Use optional chaining and fallbacks instead of non-null assertions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
229538c622 |
fix: add media rendering guardrails to prevent silent failures (#112)
## Summary - **Lint rules** catch media elements missing `id` (renderer silently skips them), missing `src`, `preload="none"` (blocks renderer), and video nested in timed divs (freezes playback). Upgraded `video_nested_in_timed_element` from warning to error. - **Compiler** strips `preload="none"` from media during compilation. Runs parallel, cached keyframe interval analysis via ffprobe — warns on sparse keyframes (>2s) that cause seek failures and audio/video desync. Suggested ffmpeg command preserves audio (`-c:a copy`). - **Pre-render lint** lints `index.html` + all `compositions/*.html` sub-compositions before render via shared `lintProject()` helper. Warns by default; `--strict` blocks on errors, `--strict-all` blocks on errors + warnings. - **Render orchestrator** logs a hint to retry with `--workers 1` when parallel capture times out on video-heavy compositions. - **Refactor**: extracted `runFfprobe()` + `parseProbeJson()` helpers to deduplicate ~80 lines of spawn boilerplate across 3 ffprobe functions. Extracted `shouldBlockRender()` so strict flag tests exercise production code. Shared `lintProject()` used by both `lint` and `render` commands. ## Context Discovered during a real composition build session where: 1. `<audio>` without `id` rendered silently (preview worked fine because runtime queries `[data-start]`, but renderer queries `[id][src]`) 2. `<video>` inside timed `<div>` froze on first frame 3. `preload="none"` caused 45s renderer timeout 4. YouTube clips with sparse keyframes from `yt-dlp --download-sections` caused audio/video desync 5. Parallel workers timed out on video-heavy compositions ## Test plan - [x] Core: 365/365 tests passing (5 new lint tests) - [x] Engine: 24/24 tests passing - [x] CLI: 14/14 tests passing (7 lintProject + 7 shouldBlockRender) - [x] Lint + format hooks pass - [ ] Manual: create a composition with `<audio data-start="0" src="test.wav">` (no id) — verify `npx hyperframes lint` catches it - [ ] Manual: run `npx hyperframes render --strict` with lint errors — verify it blocks - [ ] Manual: run `npx hyperframes render --strict-all` with lint warnings — verify it blocks |
||
|
|
f0a8644208 |
feat(lint): add template_literal_selector rule (#107)
Detects querySelector/querySelectorAll calls that use template literal
variables (e.g. `${compId}`) inside script tags. The HTML bundler's
cheerio/css-what parser crashes on these during compilation, causing
silent fallback to raw HTML without runtime injection.
Severity: error (breaks bundling)
Fix: replace template literal with hardcoded composition ID string
|
||
|
|
476c20747d |
feat: add templates (#102)
## What
Adds 4 new composition templates and fixes structural issues across all templates.
### New templates
- **decision-tree** — animated flowchart with branching paths
- **kinetic-type** — bold kinetic typography promo
- **product-promo** — multi-scene product showcase with SVG assets (3 scenes)
- **nyt-graph** — animated data chart in NYT print editorial style
### Fixes across all templates
- GSAP updated from 3.12.2 → 3.14.2 (all templates, including warm-grain, swiss-grid, vignelli, play-mode)
- New templates restructured with proper root wrapper div, `data-duration`, sub-composition refs with `data-composition-id` / `data-width` / `data-height`
- nyt-chart: replaced `${compId}` template literal variables with hardcoded `"nyt-chart"` string — cheerio's css-what parser crashes on template literals during bundling, causing silent fallback to raw HTML without runtime injection
- nyt-chart: added DOM readiness retry for dynamically created SVG elements
- kinetic-type: removed external S3 audio URL
- All templates: GSAP script loaded in `<head>` before any scripts reference it
## Why
The new templates expand the range of content types available via `hyperframes init`. The fixes ensure all templates work correctly in the studio preview (bundler inlines sub-compositions and injects the runtime).
## Test plan
- [x] All 4 new templates render in studio preview
- [x] nyt-graph chart animates bars, line, and labels on playback
- [x] Existing templates unaffected (GSAP version bump is backwards compatible)
- [x] `hyperframes lint` passes on all templates
- [x] `generators.ts` updated with new template IDs
|
||
|
|
e3fad3029c |
feat(skill): add data-in-motion guide and house style refinements (#91)
data-in-motion.md — minimal guide for data/stats in video: - Visual continuity: related data stays in same visual space - Numbers need visual weight: pair metrics with fills/shapes - Avoid web patterns: no pie charts, dashboards, axes, legends house-style.md refinements from eval iterations: - Layout approach variety (step 4 in Before Writing HTML) - Explicit weight contrast requirement - SVG illustration anti-default - Overlap anti-default - Ambient motion variety (not always zoom) SKILL.md — added reference to data-in-motion.md Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
238962adff |
fix(skill): house style anti-defaults for SVG, overlap, and zoom (#68)
## Summary - Anti-default: don't draw real-world objects with SVG paths — they look crude. Geometric shapes and abstract forms only. - Anti-default: every element needs clear space — overlapping text is always ugly. - Replaced zoom-in monoculture in choreography — now offers 6 ambient motion options instead of always zooming. - Added scene pacing: build/breathe/resolve phases. Follow-up to the house style PR (#59, merged). These fixes came from visual review of 20+ eval compositions. ## Test plan - [ ] Generate a composition with a sparse prompt and verify no SVG illustrations of real objects - [ ] Verify elements don't overlap in generated compositions - [ ] Verify ambient motion varies (not always zoom-in) 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
f8fad54261 |
feat(skill): house style guide for compose-video (#59)
* feat(cli): non-interactive by default, --human-friendly for UI Following ElevenLabs CLI pattern: default mode is agent-friendly (flag-driven, plain text output, fail fast on missing args). Interactive clack UI is opt-in via --human-friendly. Init command: - --template required in default mode (errors with example if missing) - --video / --audio flags for media input - --skip-skills / --skip-transcribe to control optional steps - --human-friendly enables the existing interactive prompts - --help shows examples for every flag combination - Transcription runs automatically in default mode (unless --skip-transcribe) - Plain console.log output, process.exit(1) on errors Skills command: - Added --human-friendly flag - Added examples to --help output Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): improve --help documentation and add --yes/--check to upgrade - upgrade: add --yes and --check flags to skip interactive prompt - benchmark: clarify description — preset fps/quality/worker configs - browser: describe each subcommand (ensure/path/clear) in help - docs: list available topics inline in --help output Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(skill): add house style guide with motion defaults, palettes, and anti-defaults When no visual-style.md is provided, compose-video now follows house-style.md for professional output quality. Includes: - Motion: easing variety, timing, entrance patterns, choreography - Sizing: text scale contrast, element fill, travel distance - Visual depth: gradient/shadow/texture guidance - Typography: weight contrast, tracking, case - Anti-defaults: table of generic AI patterns to avoid - 72 curated color palettes across 9 categories - Content interpretation: generate real content, not prompt text Eval-validated across 5 iterations with 50+ test compositions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(skill): add container anti-default and typography guidance - Anti-default: discourage card/container patterns in favor of content placed directly on canvas (professional video style vs web UI style) - Typography section: weight contrast, deliberate case, tracking, one typeface at two weights - Visual depth: softened to avoid templating (content-appropriate, not every-composition-the-same) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(skill): add scene pacing guidance to house style Three-phase composition structure: build (staggered entrances), breathe (subtle motion to keep holds alive), resolve (fast exits with intention). Prevents front-loading all animation into the first second and dead static holds. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(skill): replace zoom-in monoculture with ambient motion variety The viewport scale and end emphasis code examples were being copied verbatim to every composition. Now offers 6 ambient motion options (pan, rotation, scale in/out, parallax, color shift, stillness) and 4 ending options instead of always zooming. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
25f4af428e |
feat(cli): agent-friendly CLI — non-interactive by default (#57)
* feat(cli): non-interactive by default, --human-friendly for UI Following ElevenLabs CLI pattern: default mode is agent-friendly (flag-driven, plain text output, fail fast on missing args). Interactive clack UI is opt-in via --human-friendly. Init command: - --template required in default mode (errors with example if missing) - --video / --audio flags for media input - --skip-skills / --skip-transcribe to control optional steps - --human-friendly enables the existing interactive prompts - --help shows examples for every flag combination - Transcription runs automatically in default mode (unless --skip-transcribe) - Plain console.log output, process.exit(1) on errors Skills command: - Added --human-friendly flag - Added examples to --help output Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): improve --help documentation and add --yes/--check to upgrade - upgrade: add --yes and --check flags to skip interactive prompt - benchmark: clarify description — preset fps/quality/worker configs - browser: describe each subcommand (ensure/path/clear) in help - docs: list available topics inline in --help output Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
f4367d5726 |
feat(cli): add whisper transcription and template improvements (#53)
* feat(cli): add whisper transcription to init flow New modules: - whisper/manager.ts: download/cache whisper.cpp binary + model (~/.cache/hyperframes/whisper/) - whisper/transcribe.ts: extract audio, run whisper, save transcript.json Init flow changes: - "Got a video or audio file?" now accepts audio-only files (mp3, wav, m4a) - "Generate captions from audio?" prompt after file selection - Transcription produces transcript.json in project root - Graceful fallback if whisper/ffmpeg unavailable Supports: macOS ARM64/x86, Linux x86_64. Downloads whisper.cpp v1.7.3 from GitHub releases and ggml-base.en model from Hugging Face. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): use brew/system whisper instead of downloading binaries whisper.cpp doesn't ship pre-built macOS/Linux CLI binaries. Use brew install whisper-cpp on macOS (auto-installs if brew available), system PATH lookup otherwise. Model still downloaded from Hugging Face. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): simplify whisper install — detect or instruct, don't build Remove build-from-source complexity. If whisper-cpp is found on PATH, use it. If not, show install instructions instead of blocking: "To generate captions, install whisper-cpp: brew install whisper-cpp" The transcription prompt only appears when whisper is available. When it's not, the user sees the install command and can re-run init. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(cli): auto-install whisper via brew or build from source ensureWhisper() now tries 4 strategies in order: 1. System PATH (whisper-cli or whisper already installed) 2. Homebrew (macOS: brew install whisper-cpp) 3. Build from source (git clone + cmake, ~30-60s) 4. Show install instructions as last resort Init flow always asks "Generate captions?" — whisper is installed automatically in the background if needed. No user intervention required on macOS with Xcode CLI tools or any system with git+cmake. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): add window.__timelines guard to all templates The studio bundler doesn't always initialize window.__timelines before template scripts run, causing "Cannot set properties of undefined" errors. Add defensive guard to every template. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(cli): patch template captions with actual transcript data After scaffolding, if transcript.json exists, replace the hardcoded word array in the template's captions composition with the real transcript data. The template's caption animation and styling are preserved — only the word data changes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): show install notice when whisper needs to be installed When whisper-cpp isn't found, show an info message before the spinner: "whisper-cpp not found — installing automatically..." Then the spinner shows "Installing whisper-cpp (this may take a moment)..." Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): add muted and playsinline to all template video elements The framework requires video elements to have muted and playsinline attributes. All four templates were missing these, causing video to not play in the studio preview. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): flat asset structure + separate audio tracks in templates Assets: video, images, fonts all go at project root (not assets/ or fonts/ subdirectories). The studio preview can't resolve relative paths from subdirectories due to the /preview URL suffix. Audio: added <audio> elements alongside muted <video> in all 4 templates so the video's audio plays back. The framework requires muted video + separate audio element. Removed assets/ and fonts/ directory creation from scaffoldProject. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(studio): inject base tag for asset resolution in preview The preview iframe serves bundled HTML from /api/projects/:id/preview but relative asset paths (video.mp4, font.woff2) resolve to the wrong URL without a <base> tag. Now injects <base href="/api/projects/:id/preview/"> so relative paths route through the static asset handler. Also adds proper MIME types for video, audio, image, and font files served from the preview asset route. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(studio): serve HyperFrames runtime in dev mode The preview runtime script had an empty src — the framework never loaded, so video playback and clip lifecycle didn't work. Now auto-detects packages/cli/dist/hyperframe-runtime.js and serves it at /api/runtime.js. No env var needed in dev mode. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): filter whisper special tokens from transcript Use --output-json instead of --output-json-full to avoid special tokens like [_TT_485] and [BLANK_AUDIO]. Also filter remaining bracket tokens when building the word array for captions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): use --output-json-full for word-level timestamps --output-json only produces segment-level timing (no tokens). --output-json-full is required for word-level timestamps that the captions template needs. Special tokens are filtered out by the patchTranscript function. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(cli): patch template durations to match uploaded video Templates now use __VIDEO_DURATION__ placeholder that gets replaced with the actual probed video duration. All data-duration values on the root composition, video, audio, and caption clips are updated. Without a video, defaults to 10 seconds. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): merge punctuation tokens with preceding word Whisper outputs punctuation (. , ! ?) as separate tokens. These appeared as standalone words in captions, sometimes in the wrong group. Now merged with the preceding word during transcript normalization. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): match both TRANSCRIPT and script variable names in templates Three templates use `const TRANSCRIPT = [...]` while warm-grain uses `const script = [...]`. The patchTranscript function now matches both. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): security and template fixes - Replace shell injection risk (execSync rm) with unlinkSync in transcribe.ts - Add GIT_TERMINAL_PROMPT=0 to whisper buildFromSource git clone - Fix hardcoded data-duration="18" in warm-grain captions template - Add data-start="0" to root compositions in swiss-grid, vignelli, warm-grain - Add data-start="0" to warm-grain grain-overlay composition - Deduplicate hasFFmpeg: remove from init.ts, import from whisper/manager.ts - Add my-video/ and packages/studio/data/ to .gitignore Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): format warm-grain captions and fix TS nullability errors - Format warm-grain/compositions/captions.html - Add optional chaining on token.offsets (may be undefined) - Use intermediate variable for lastWord to satisfy TS strict checks Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(cli): add blank template option, smart defaults for video vs audio - Blank template: minimal scaffolding (root composition, video, audio, GSAP timeline) with __VIDEO_SRC__ and __VIDEO_DURATION__ placeholders - Template defaults: video uploads default to "blank" (user brings their own content), audio-only defaults to "warm-grain" (motion graphics template since there's no video to show) - Audio-only projects now tracked with isAudioOnly flag Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): address whisper review feedback - Clean stale builds: if BUILD_DIR exists but no binary, nuke and retry - Build failures clean up BUILD_DIR so next attempt starts fresh - patchTranscript regex scoped within <script> blocks to prevent matching across block boundaries - Removed hardcoded model size hint (~148MB) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): add missing rmSync import to whisper manager Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: remove test project and lock file Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): address review items 7-12 — execFileSync, build diagnostics, WAV verification - manager.ts: replace all execSync with execFileSync to prevent command injection - manager.ts: capture cmake stderr and include in build failure error message - transcribe.ts: verify WAV is 16kHz mono via ffprobe before passing to whisper - init.ts: replace fragile JSON formatting with JSON.stringify(words, null, 2) - init.ts: fix default duration from "10" to "5" matching DEFAULT_META - init.ts: add probeAudioDuration() and --audio/--skip-transcribe flags - init.ts: extract finalizeProject() to reduce code path duplication - init.ts: wire transcription into non-interactive path Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
5fceab9279 |
feat(cli): add skills install command and init integration (#48)
* fix(ci): publish CLI from temp copy to avoid workspace mutation Copy packages/cli to a temp directory before renaming to "hyperframes" for publish. Avoids corrupting the workspace if the job fails mid-way. Addresses review feedback on #47. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): resolve leftover conflict markers in publish.yml Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(cli): add skills install command Adds `hyperframes skills install` to download and install HyperFrames and GSAP skills globally to ~/.claude/skills/. Also adds `hyperframes skills list` to show installed skills. - HyperFrames skills: bundled in CLI dist, copied from dist/skills/ - GSAP skills: cloned from github.com/greensock/gsap-skills - Cache: ~/.cache/hyperframes/gsap-skills/ (shallow clone, updated on install) - Handles overwriting existing skills (removes before copy) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(cli): simplify skills to flat command `hyperframes skills` directly installs + shows summary. No subcommands needed — list was redundant since install already prints all installed skills. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): scope skills summary to only HyperFrames and GSAP skills The summary now only lists skills installed by this command, grouped by source (HyperFrames vs GSAP), instead of listing everything in ~/.claude/skills/. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): correct dev path for bundled skills directory Path needed 4 levels up from cli/src/commands/ to reach repo root, not 3. Was resolving to packages/.claude/skills/ instead of .claude/skills/. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(cli): support multiple AI coding tools for skills install Install to Claude Code, Gemini CLI, and Codex CLI by default. Use flags to target specific tools: hyperframes skills # claude + gemini + codex hyperframes skills --cursor # cursor only (project-level) hyperframes skills --claude # claude only Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(cli): move skills to repo root, support multi-CLI install - Move skills from .claude/skills/ to skills/ (tool-agnostic location) - Install to Claude Code, Gemini CLI, Codex CLI by default - Add --claude, --gemini, --codex, --cursor flags for targeting specific tools - Update build script to copy from skills/ instead of .claude/skills/ Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add infographics skill for data visualization compositions Professional infographic design and animation patterns: - Typography hierarchy (hero stat, label, context) - Layout rules (grid-aligned, generous whitespace, 2-color max) - 5 infographic types: single stat, comparison, bar chart, progress, steps - Animation patterns: count-up, bar growth, entrance choreography, exits - Narration sync (stat appears when narrator says the number) - Design constraints (no gradients, no shadows, no clip art) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add evals for infographics skill Three eval scenarios testing design quality and animation correctness: 1. Single stat — count-up animation synced to narration 2. Comparison — before/after reveal with visual hierarchy 3. Process steps — sequential reveal with dimming Each eval has PASS/FAIL criteria covering: composition structure, design rules (typography, layout, color), animation patterns (timing, easing, choreography), and anti-patterns. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: remove infographics and transitions skills, add asset-management Removed skills that don't add value beyond model knowledge: - infographics: model already produces equivalent output without it - transitions: patterns are derivable from compose-video constraints Added: - asset-management: organize user-uploaded files into assets/ and fonts/ - Typography section in compose-video: min font sizes, font loading (Google Fonts + local @font-face), weight pairing, font-display:block - Assets section in compose-video: project structure with assets/ and fonts/ directories, path rules, CORS, "check before creating" rule Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor: consolidate skills to 3, split compose-video under 500 lines Removed skills that don't add value beyond model knowledge: - media: duplicates compose-video - social-media: platform safe areas are the only unique content - infographics: model produces equivalent output without it - transitions: patterns derivable from compose-video Remaining skills (3): - compose-video: core framework contract (452 lines, under 500 limit) - patterns.md: PiP, title card, slideshow examples (loaded on demand) - typography-and-assets.md: font loading, sizes, asset paths (on demand) - captions: tone-adaptive caption styling from script analysis - asset-management: organize uploaded files into project directories Rewrote captions skill to focus on style detection from transcript content (per-word styling, tone mapping) rather than mechanical rules. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(cli): create assets/ and fonts/ directories on init - scaffoldProject now creates assets/ and fonts/ directories - Video files are placed in assets/ instead of project root - Template __VIDEO_SRC__ placeholders resolve to assets/filename Aligns with the compose-video skill's project structure convention where user-provided media goes in assets/ and fonts in fonts/. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(cli): fetch skills from GitHub repos instead of bundling Skills are now fetched directly from their source repos at runtime: - HyperFrames skills: github.com/heygen-com/hyperframes (skills/ dir) - GSAP skills: github.com/greensock/gsap-skills (skills/ dir) Both cached in ~/.cache/hyperframes/ and updated on each run. Removed skills bundling from build:copy step. Requires the hyperframes repo to be public for HyperFrames skills to install. GSAP skills work immediately (public repo). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(cli): install skills automatically during init After scaffolding a new project, `hyperframes init` now runs `hyperframes skills` to install HyperFrames and GSAP skills. Best-effort — if skill installation fails (no git, no network), project creation still succeeds. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(cli): show skills install feedback during init Added installAllSkills() export for programmatic use by init. Init now shows a spinner and result message: "11 AI skills installed (Claude Code, Gemini CLI, Codex CLI)" Falls back gracefully if git or network unavailable. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(cli): let users select which AI tools to install skills for Interactive init now shows a multi-select prompt: "Install AI coding skills for: Claude Code, Gemini CLI, Codex CLI, Cursor" Users can deselect tools they don't use or add Cursor (off by default). Non-interactive mode still installs to all default targets. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): prevent git credential prompt from hanging skills install Set GIT_TERMINAL_PROMPT=0 so git clone/pull fails immediately on private repos instead of hanging for username/password input. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): show skipped skill sources and accurate counts Skills install now reports which sources failed (e.g., private repo) and only counts skills that actually installed. Prompt text simplified to "Install skills for:". Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): clean up skills command output - Skipped sources shown as dim text, not error with full command - Summary shows "Skipped: HyperFrames (repo not accessible)" - Outro says "ready" not "installed" - Shows "No skills installed" if everything failed Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): clarify partial skill install in outro message When some sources fail, the outro now says which skills are ready and which are unavailable, instead of a misleading total count. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: remove planning docs and test scaffolding Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: remove eval projects and test examples Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: remove remaining test project data Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): use generic source names in skills install outro message Replace hardcoded "GSAP skills ready. HyperFrames skills unavailable." with a dynamic message listing which sources succeeded and which were skipped, so the message stays correct as sources change. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): address review feedback on skills install - Lazy process.cwd() for Cursor target (getter, not module-load) - Track overwritten skills (logged in install output) - Add --skip-skills flag to init for agent-friendly non-interactive use - Guard both interactive and non-interactive paths with skipSkills Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): remove assets/ and fonts/ directories from init Video files go to project root, not assets/. Removes assetsDir, fonts/ directory creation, and assets/ prefix from video path. Flat project root convention consistent with compose-video skill. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): address review items 5, 6, 13 — execFileSync, resilient cache, clear counting - Replace all execSync with execFileSync for git commands (prevent injection) - On git pull failure, reuse stale cache if skills dir exists instead of nuking - Extract gitClone() helper for consistent clone calls - Use explicit counted flag instead of confusing target === targets[0] Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
71e00b4533 |
fix(cli): resolve npx hyperframes from inside monorepo (#47)
* fix(cli): rename workspace package so npx resolves from registry Rename packages/cli from "hyperframes" to "@hyperframes/cli" so that npm/npx stops resolving it as a local workspace package when run from inside the monorepo. The publish workflow sets the name back to "hyperframes" before publishing so the npm package name is unchanged. Root cause: npm sees workspaces in root package.json, finds packages/cli named "hyperframes", assumes it's local, but bun manages node_modules so there's no bin symlink — npx fails with "command not found" instead of falling back to the registry. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): update lockfile for workspace package rename Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): publish CLI from temp copy to avoid workspace mutation Copy packages/cli to a temp directory before renaming to "hyperframes" for publish. Avoids corrupting the workspace if the job fails mid-way. Addresses review feedback on #47. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): resolve leftover conflict markers in publish.yml Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
0ce9dd8496 |
feat: add HyperFrames skills for AI coding tools (#46)
## Summary
- 2 skills in `skills/` directory (tool-agnostic, installed via `hyperframes skills`)
- **compose-video** (112 lines): Core HyperFrames composition authoring contract — data attributes, timeline, video/audio rules, GSAP constraints, editing consistency
- `patterns.md`: PiP, title card, slideshow examples (loaded on demand)
- **captions** (138 lines): Tone-adaptive caption styling from transcript analysis — per-word styling, script-to-style mapping, whisper.cpp format reference
## Test plan
- [ ] Skills load in Claude Code
- [ ] compose-video under 500 lines, no `assets/` or `fonts/` references
- [ ] Captions skill triggers on tone detection keywords
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
||
|
|
61c5257402 |
fix(ci): update publish workflow to use bun install (#36)
* fix(ci): update publish workflow to use bun install pnpm-lock.yaml was removed in the bun migration but publish.yml still referenced it. Use bun for install/build, keep pnpm for publish (publishConfig overrides + --provenance). * docs: update stale pnpm references to bun across docs and scripts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
94e25443ae |
build: migrate from pnpm to bun as package manager (#28)
## Summary - Replace pnpm with bun for dependency installation, script running, and ad-hoc execution - Keep pnpm for publish workflow only (`publishConfig` overrides + `--provenance`) - `bun install` replaces `pnpm install` (~4-5x faster cold installs) - `bun run` replaces `pnpm run` (~28x less startup overhead) - `bunx` replaces `npx` in lefthook hooks - CI workflows updated (`oven-sh/setup-bun@v2` + `actions/setup-node@v4`) - `pnpm-lock.yaml` removed, `bun.lock` generated - `pnpm-workspace.yaml` kept for publish compatibility - CLI source code (`packages/cli/src/`) unchanged — shipped to end users who may not have bun Part 5/5 of [VA-851](https://linear.app/heygen/issue/VA-851/pre-migration-configure-eslint-prettier-and-conventional-commits) ## Test plan - [x] `bun run lint` — 0 errors - [x] `bun run format:check` — all files pass - [x] `bun run build` — all 5 packages build - [x] 330 core tests pass - [x] 18 engine tests pass - [x] `publish.yml` unchanged (pnpm stays for npm publishing) - [x] No `bunx`/`bun run` references in shipped source code (`packages/*/src/`) |
||
|
|
a6c5e08abb |
ci: add lint and format check job, update CONTRIBUTING.md (#26)
## Summary - Add `lint-and-format` job to CI workflow (`pnpm lint` + `pnpm format:check`) - Fix lefthook commands to use `npx` prefix (bare binaries not on PATH) - Update CONTRIBUTING.md: document new tooling, commit conventions, and lefthook hooks Part 4/4 of [VA-851](https://linear.app/heygen/issue/VA-851/pre-migration-configure-eslint-prettier-and-conventional-commits) ## Test plan - [x] CI job matches existing pattern (pnpm 10, node 22, frozen lockfile) - [x] Git hooks work end-to-end (bad messages rejected, valid commits pass) - [x] CONTRIBUTING.md accurately reflects new tooling |
||
|
|
20be2ea1c2 |
style: apply oxfmt baseline formatting across all source files (#25)
## Summary - Run `oxfmt .` across the entire codebase to establish formatted baseline - 299 files changed — mechanical formatting only, no logic changes - Double quotes, semicolons, 2-space indent, trailing commas, 100 print width Part 3/4 of [VA-851](https://linear.app/heygen/issue/VA-851/pre-migration-configure-eslint-prettier-and-conventional-commits) ## Test plan - [x] `pnpm format:check` — all 426 files pass - [x] `pnpm -r typecheck` — all packages pass - [x] `pnpm build` — all packages build - [x] All 348 tests pass |
||
|
|
323ff8f860 |
fix: resolve oxlint errors across codebase (#24)
## Summary - Remove 5 unused `beforeEach` imports from test files - Remove unused imports (`existsSync`, `TimelineCompositionElement`) - Remove unused destructured variables (`options`, `width`, `height`, `goldenEl`) - Remove dead `formatDuration` function - Fix unused catch parameters (`catch (err)` → `catch`) - Prefix unused `renderError` state with `_` - Add `eslint-disable-next-line` for 2 React exhaustive-deps false positives (stable ref + zustand setter) Part 2/4 of [VA-851](https://linear.app/heygen/issue/VA-851/pre-migration-configure-eslint-prettier-and-conventional-commits) ## Test plan - [x] `pnpm lint` — 0 errors on 193 files - [x] All 348 tests pass (core + engine) |
||
|
|
17e90f0671 |
build: add oxlint, oxfmt, commitlint, lefthook, knip, and editorconfig configs (#23)
## Summary - Install oxlint, oxfmt, commitlint, lefthook, knip as dev dependencies - Add `.oxlintrc.json` (correctness rules + React plugin) - Add `.oxfmtrc.json` (double quotes, semicolons, 2-space indent, trailing commas) - Add `commitlint.config.js` (conventional commits) - Add `lefthook.yml` (pre-commit lint+format, commit-msg commitlint) - Add `.editorconfig` and `knip.config.ts` - Add scripts: `pnpm lint`, `pnpm format`, `pnpm format:check`, `pnpm knip` Part 1/4 of [VA-851](https://linear.app/heygen/issue/VA-851/pre-migration-configure-eslint-prettier-and-conventional-commits) ## Test plan - [x] `pnpm lint` runs (reports pre-existing errors, expected) - [x] `pnpm format:check` runs (reports pre-existing diffs, expected) - [x] `commitlint` validates and rejects messages correctly - [x] lefthook hooks install via `pnpm run prepare` - [x] `pnpm knip` runs |
||
|
|
0276255b76 |
fix: rename root package to avoid shadowing published CLI (#17)
`npx hyperframes init` failed with "could not determine executable to run" because the monorepo root package.json shared the same name as the published npm package. npx resolved the local root (which has no bin field) instead of the published package. Renaming to "hyperframes-monorepo" follows the same convention as remotion-monorepo. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
1a713b37b9 |
fix: change workspace:* to workspace:^ for npm publish compatibility (#12)
pnpm converts workspace:^ to ^X.Y.Z during publish, but workspace:* was left unconverted in the registry. This caused install failures for external consumers. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
00909c8258 |
fix: revert parity harness to puppeteer-core with channel: chrome
The puppeteer import caused protocol timeout issues. Revert to puppeteer-core but add channel: "chrome" to auto-discover the Chrome binary installed by 'puppeteer browsers install'. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
b4023ae249 |
fix: update parity fixture to use local runtime path
The fixture HTML had an empty src after the static.heygen.ai URL was scrubbed. Point to the local hyperframe.runtime.iife.js which will be copied into the fixtures dir by CI before the test runs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
8866fc5791 |
fix: use puppeteer instead of puppeteer-core in parity harness
puppeteer-core requires an explicit executablePath or channel. puppeteer auto-discovers Chrome installed by 'puppeteer browsers install'. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
6ad12df1b6 |
fix: commit regression test golden baselines
- Update .gitignore to allow packages/producer/tests/*/output/ - Commit compiled.html snapshots and output.mp4 golden baselines (MP4s tracked via Git LFS) These were excluded by the blanket output/ gitignore rule but are needed for regression tests to pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
fbfb6c6156 |
fix: resolve TypeScript strict null errors in producer
- server.ts: default token param to empty string to satisfy string type - renderOrchestrator.ts: capture fileServer in local const before closure to preserve TypeScript null narrowing Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
9f8e5ba5a1 |
initial code (#2)
* feat: initial code port from hyperframes-internal Port all OSS-ready packages from the internal monorepo: - @hyperframes/core — shared types, HTML generation, GSAP utilities, runtime - @hyperframes/cli — CLI for creating, previewing, and rendering compositions - @hyperframes/engine — framework-agnostic rendering engine (BeginFrame + FFmpeg) - @hyperframes/producer — video rendering pipeline (Puppeteer + FFmpeg) - @hyperframes/ui-player — browser-based video player component - @hyperframes/studio — composition editor (React frontend + Hono backend) Includes regression test suite with Docker-based test harness. All HeyGen-internal references, deployment infrastructure, and proprietary assets have been removed. Package names migrated from @app/* to @hyperframes/*. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: scrub internal codenames and stale references from OSS port - Replace static.heygen.ai runtime URLs in test fixtures - Remove internal CDN publish script (publish-hyperframe-runtime.ts) - Replace sandbox-studio, sandbox-interceptor, __magicEditRuntime with neutral names (studio, hyperframe-runtime, __hyperframeRuntime) - Fix stale Vault API / localhost references in docs - Remove broken deprecated_studio link Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: remove remaining internal codenames and stale references - Delete stale producer README.md and PIPELINE.md (referenced nonexistent files) - Replace "Cerberus" codename with "HyperFrames" in test design reviews - Replace magic-edit postMessage identifiers with hf-preview/hf-parent - Rename debug-magic-edit-timeline.ts to debug-timeline.ts - Replace "Motion Cut" with "HyperFrames" in Timeline comments - Fix studio/CLI references to nonexistent archive package (use local data/projects/ dir, stub render proxy) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |