Commit Graph
196 Commits
Author SHA1 Message Date
Vance IngallsandClaude Opus 4.6 cbb7616fbd refactor: remove word IDs, use positional index only
Word IDs were unnecessary complexity — indices are stable for the lifetime
of a transcript. On re-transcription, caption-overrides.json is automatically
deleted since word indices are no longer valid.

- Remove id field from Word interface and normalization
- Remove wordId from CaptionOverride, CaptionSegment, sync hook
- Runtime matches by wordIndex only
- transcribe command deletes caption-overrides.json on re-transcription
- Update skill docs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:14 -07:00
Vance IngallsandClaude Opus 4.6 4479ef3006 fix: code review fixes — remove non-null assertions, fix hooks ordering
- Replace doc!/win!/captionSrcPath! with guards in App.tsx async callback
- Move useCallback hooks above early return in CaptionAnimationPanel
  (React hooks must be called unconditionally in the same order)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:14 -07:00
Vance IngallsandClaude Opus 4.6 731295aa4a refactor: cleanup from /simplify review
- Delete dead useCaptionDrag.ts (superseded by overlay inline drag)
- Self-cancel 200ms poll after caption activation
- Remove stale effectiveStyle deps from applyToIframeDom useCallback

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:14 -07:00
Vance IngallsandClaude Opus 4.6 e10921ddba fix: property panel transforms apply to wrapper via gsap.set
Panel rotation/scale/position changes now go through the wrapper span
using gsap.set, reading current values first to preserve other properties.
Previously set el.style.transform directly on the word span, overwriting
all existing transforms.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:13 -07:00
Vance IngallsandClaude Opus 4.6 767d9a9b71 fix: close edit panel when caption word is deselected
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:13 -07:00
Vance IngallsandClaude Opus 4.6 b29f656a98 fix: resolve caption source via compIdToSrc map on initial load
The runtime strips data-composition-src after loading, so the DOM query
couldn't find the captions file path. Now uses the compIdToSrc map (built
from raw index.html) as the primary lookup. Also adds compIdToSrc to
the effect dependency array so it re-runs when the map is populated.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:13 -07:00
Vance IngallsandClaude Opus 4.6 b2586b3959 fix: caption detection on initial index.html load
- Reduce poll interval from 500ms to 200ms for faster detection
- Fall back to DOM querySelector for iframe when ref isn't set yet
- Runtime wrapper sets data-caption-wrapper for consistent detection

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:13 -07:00
Vance IngallsandClaude Opus 4.6 70897fc635 fix: double-wrapper bug causing transform value drift
Runtime hook's wrapper was missing data-caption-wrapper attribute, so
the overlay created a second wrapper on top. Transforms were applied to
different wrappers causing values to compound on reload.

Also:
- Suppress auto-save during loadOverrides to prevent immediate re-write
- Only sync non-default transform values to avoid spurious overrides

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:13 -07:00
Vance IngallsandClaude Opus 4.6 614a67dfd3 fix: caption init race condition, auto-save, and panel auto-open
- Remove store reset from useEffect cleanup — was wiping state on dep changes
  and breaking auto-save subscription
- Move panel auto-open into useEffect instead of during render
- Add fallback 500ms poll alongside postMessage listener for initial load

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:13 -07:00
Vance IngallsandClaude Opus 4.6 f2718783c2 fix: use runtime postMessage events to detect caption composition loading
Replace polling with a message listener for hf-preview state/timeline
events. These fire after loadExternalCompositions completes, guaranteeing
.caption-group elements exist in the DOM. Eliminates the race condition
on initial page load.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:13 -07:00
Vance IngallsandClaude Opus 4.6 96866cddf6 feat: auto-detect captions from any composition, edit from master view
Caption edit mode now activates automatically when .caption-group elements
are found in the iframe DOM. Works from index.html (master) without
requiring drill-down into the captions sub-composition. Finds the captions
source file via data-composition-src attribute.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:12 -07:00
Vance IngallsandClaude Opus 4.6 76a667c281 feat: auto-activate caption editor when viewing captions composition
- Caption overlay and hover states always active on captions compositions
- Clicking a word auto-opens the property panel
- Remove Edit Captions button — edit mode activates automatically
- Reset edit mode when navigating away from captions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:12 -07:00
Vance IngallsandClaude Opus 4.6 0e1148556f feat: arrow key nudge for selected caption words
1px per press, 10px with Shift held. Syncs to store and auto-saves.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:12 -07:00
Vance IngallsandClaude Opus 4.6 f321269a65 refactor: strip property panel to position, scale, rotation only
Remove typography, color, opacity, stroke, and background controls.
Keep only Position (X/Y) and Transform (Scale/Rotation) — the controls
that work reliably with the wrapper-based override system.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:12 -07:00
Vance IngallsandClaude Opus 4.6 9dd394eb70 feat: active/dim color states for caption word overrides
Replace single "color" with activeColor (spoken) and dimColor (inactive).
Runtime hook modifies existing GSAP color tweens by detecting dim vs active
states via opacity heuristic. Property panel shows both color pickers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:12 -07:00
Vance IngallsandClaude Opus 4.6 613ed774ae refactor: remove preview refresh suppression during caption edit mode
No longer needed — caption overrides are a JSON data file, not HTML patches.
The runtime hook re-applies overrides on every reload, so preview refresh
during editing is safe and ensures the preview stays in sync.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:12 -07:00
Vance IngallsandClaude Opus 4.6 933b72b633 feat: auto-save caption overrides on model change
Replace manual Save button with 800ms debounced auto-save. Writing to
caption-overrides.json is safe because preview refresh is suppressed
during caption edit mode.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:12 -07:00
Vance IngallsandClaude Opus 4.6 b7d7a1c2b0 fix: wrapper-aware element resolution for caption overlay
- getWordEl skips wrapper spans and finds actual word elements inside them
- readWordBoxes handles wrapped words in position calculations
- getOrCreateWrapper detects if element IS a wrapper to prevent double-wrapping
- Remove debug logging from save

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:11 -07:00
Vance IngallsandClaude Opus 4.6 bf27578755 fix: caption overrides runtime hook — run in both load paths, write to project root
- applyCaptionOverrides() now runs both when compositions load async AND when
  already ready (standalone composition view)
- Overrides file written to project root (not compositions/) to match <base href>
- Remove debug logging

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:11 -07:00
Vance IngallsandClaude Opus 4.6 a60c9e523b feat(lint): enforce .caption-group > span convention for caption overrides
- New lint rule `caption_word_structure`: warns when caption compositions don't
  use <span> word elements inside .caption-group containers
- Document required DOM structure in the captions skill
- Document word ID field in transcript format

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:11 -07:00
Vance IngallsandClaude Opus 4.6 b6bdce065e feat: wrapper-based caption overrides — preserve all GSAP animations
Instead of killing transform tweens, wrap each overridden word span in an
inline-block wrapper span. Editor transforms (position, scale, rotation)
are applied to the wrapper while the inner span keeps all GSAP entrance,
exit, and karaoke animations intact.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:11 -07:00
Vance IngallsandClaude Opus 4.6 7c0f825d72 feat: caption overrides with stable word IDs and killTweensOf
- Add optional `id` field to transcript Word interface, assigned as `w{index}` during normalization
- caption-overrides.json uses `wordId` (preferred) with `wordIndex` fallback
- Runtime hook calls gsap.killTweensOf for overridden transform properties before applying
- Studio parser carries wordId through from transcript to segments
- Add typecheck to lefthook pre-commit

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:11 -07:00
Vance IngallsandClaude Opus 4.6 5d2efb24c1 feat: replace HTML patching with caption-overrides.json data file
- New caption-overrides.json format: [{wordIndex, x, y, scale, rotation, color, ...}]
- Runtime hook in packages/core applies overrides via gsap.set() after compositions load
- Studio writes/reads caption-overrides.json instead of injecting code into HTML
- Original composition source is never modified
- Remove old parseExistingOverrides and source patching code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:11 -07:00
Vance IngallsandClaude Opus 4.6 b00f062bdf chore: revert unrelated LintModal changes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:10 -07:00
Vance IngallsandClaude Opus 4.6 7c07d09cf3 chore: add typecheck to lefthook pre-commit and fix container type
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:10 -07:00
Vance IngallsandClaude Opus 4.6 cb335ba2a7 feat(studio): caption designer canvas controls, real-time editing, and save-as-patch
- Canvas: move (translate), scale, and rotate handles on selected words via gsap.set()
- Property panel: real-time style updates applied directly to iframe DOM
- Save: patches original source with gsap.killTweensOf + gsap.set per edited word
- Parser: reads existing editor overrides on re-entry to preserve prior edits
- Suppress preview refresh during caption edit mode to prevent reload cycle

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:10 -07:00
Vance Ingalls 9f73dae176 feat(studio): add caption module barrel export 2026-04-01 23:37:10 -07:00
Vance IngallsandClaude Sonnet 4.6 2e850cb360 feat(studio): add caption timeline track with word-level blocks and drag-to-retime
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 23:37:10 -07:00
Vance Ingalls 876a053b90 feat(studio): add caption animation panel with entrance/highlight/exit controls 2026-04-01 23:37:10 -07:00
Vance Ingalls 0233ae2361 feat(studio): wire caption property panel and parser integration 2026-04-01 23:37:10 -07:00
Vance IngallsandClaude Sonnet 4.6 97b88d86fc feat(studio): add caption property panel with typography, color, and transform controls
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 23:37:09 -07:00
Vance IngallsandClaude Opus 4.6 9370e4fb7b feat(studio): wire caption overlay and edit mode into Studio
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:09 -07:00
Vance Ingalls fce9268304 feat(studio): add drag-to-reposition for caption segments 2026-04-01 23:37:09 -07:00
Vance Ingalls 9128524cf4 feat(studio): add caption overlay with segment selection boxes 2026-04-01 23:37:09 -07:00
Vance Ingalls 486a018dc9 feat(studio): add caption model → HTML source sync hook 2026-04-01 23:37:09 -07:00
Vance IngallsandClaude Sonnet 4.6 980ca1f750 feat(studio): add caption HTML generator
Implements generateCaptionHtml(model) which serializes a CaptionModel
into a complete captions.html HyperFrames composition with template
wrapper, CSS per-group classes, TRANSCRIPT array, and GSAP timeline
registration via window.__timelines["captions"].

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 23:37:09 -07:00
Vance Ingalls 39712ba6e7 feat(studio): add caption editing Zustand store 2026-04-01 23:37:09 -07:00
Vance Ingalls be97bfadd6 feat(studio): parse caption styles from live iframe DOM 2026-04-01 23:37:08 -07:00
Vance IngallsandClaude Sonnet 4.6 7b5cfea5e8 feat(studio): build caption model from transcript words
Adds `buildCaptionModel` to the caption parser — groups TranscriptWord
arrays into CaptionGroups (default 5 words each), creates CaptionSegments
with correct timing and groupIndex, and wires in DEFAULT_STYLE,
DEFAULT_CONTAINER, and DEFAULT_ANIMATION_SET. Covers 23 tests (13 new).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 23:37:08 -07:00
Vance IngallsandClaude Sonnet 4.6 012d1afbf3 feat(studio): add transcript extraction from caption source
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 23:37:08 -07:00
Vance Ingalls 0d310fc05d feat(studio): add caption model types 2026-04-01 23:37:08 -07:00
Vance IngallsandClaude Opus 4.6 68821df356 fix(cli): handle unknown type in pageerror handler
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:08 -07:00
Vance IngallsandClaude Opus 4.6 aa9175adc1 fix(cli): fix typecheck errors in validate command
- ConsoleMessage.type() returns "warn" not "warning"
- Type pageerror callback parameter as Error

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:07 -07:00
Vance IngallsandClaude Opus 4.6 2ba35674a0 feat(cli): add hyperframes validate command
Loads a composition in headless Chrome and reports console errors,
uncaught exceptions, and failed network requests for project assets.
Agents run this before opening the studio to catch runtime issues.

- Bundle HTML with inlined runtime (no CDN dependency)
- Minimal file server for project assets
- Captures JS errors, pageerror events, 404s on project files
- JSON output mode for programmatic consumption
- ~3s execution time, suitable as a blocking pre-preview step

Updates CLAUDE.md and compose skill to require lint + validate
before opening the studio.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:07 -07:00
Vance IngallsandClaude Opus 4.6 949ff52177 fix(studio): serve runtime locally instead of from CDN
The Vite dev server was loading the HyperFrames runtime IIFE from
cdn.jsdelivr.net. This caused compositions using window.__hyperframes
(e.g., fitTextFontSize) to fail when the CDN script didn't load in
the iframe context.

Add a /api/runtime.js endpoint to the Vite plugin that serves the
local built runtime, matching what studioServer.ts already does for
the CLI embedded server.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:37:07 -07:00
Vance IngallsandClaude Opus 4.6 41d1286c33 feat(lint): warn when captions use fetch() instead of inline TRANSCRIPT
Adds two lint rules for caption compositions:
- caption_transcript_not_inline: warns when transcript is loaded via
  fetch() instead of an inline var TRANSCRIPT array
- caption_transcript_parse_error: warns when the inline TRANSCRIPT
  array is not valid JSON (common with unquoted keys + apostrophes)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 06:25:12 +00:00
Vance IngallsandClaude Opus 4.6 4e516327ed feat(whisper): language detection, speech onset, contraction merging
- 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
- Replace non-null assertions with optional chaining in tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 06:25:08 +00:00
James 870ca76c8b chore: release v0.2.1 2026-04-01 21:36:24 +00:00
Miguel Ángel 1efb23dfae fix(producer): inline CDN scripts for offline render and detect black video (#182)
## Context

`npx hyperframes render` fails to load assets and produces all-black video. Root causes:

1. CDN scripts (GSAP, Lottie) are left as `<script src="https://...">` in compiled HTML — the headless browser must fetch them over the network, which fails in Docker, CI, and firewalled environments
2. Assets referenced from outside the project directory (e.g. `../shared-assets/hero.png`) 404 because the file server only serves from `projectDir`
3. When GSAP fails to load, the timeline never registers, all `.clip` elements stay `visibility: hidden`, and every frame is black — with no diagnostic output
4. The linter reports `missing_gsap_script` when GSAP is bundled inline (no `<script src>` tag), which blocks users from working around the CDN issue

## What changed

### 1\. CDN script inlining (`htmlCompiler.ts`)

`compileForRender` now downloads all external `<script src="https://...">` tags at compile time and inlines their content into the HTML. Rendering no longer needs network access.

| Before | After |
| --- | --- |
| `<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>` stays in HTML, browser must fetch at runtime | Script downloaded during compilation, embedded as `<script>/* inlined: https://... */\n...code...</script>` |
| Docker/CI render → `net::ERR_NAME_NOT_RESOLVED` → black video | Render works fully offline |
| No feedback when CDN fails | `[Compiler] WARNING: Failed to download CDN script: ... Consider bundling it locally` |

### 2\. External asset copying (`htmlCompiler.ts`, `renderOrchestrator.ts`)

After compilation, the HTML is scanned for `src`, `href`, and CSS `url()` references that resolve outside `projectDir`. These files are copied into the compiled output directory so the file server can serve them.

| Before | After |
| --- | --- |
| `background-image: url(../shared-assets/hero.png)` → 404 (file server can't serve outside `projectDir`) | Asset detected, copied to compiled dir, path rewritten → serves correctly |
| `<img src="../shared-assets/logo.png">` → 404 | Same fix — works for all `src`/`href` attributes and CSS `url()` |

### 3\. Black video diagnostics (`renderOrchestrator.ts`)

When composition duration is 0 (which would produce a black video), the error now probes the browser for diagnostics instead of a generic message.

| Before | After |
| --- | --- |
| `Invalid composition duration: 0. Check that GSAP timelines are registered.` | `Composition duration is 0 — this would produce a black video.\n\nDiagnostics:\n  - GSAP is not loaded — CDN script may have failed to download. Bundle GSAP locally...\n  - Browser: [Browser:PAGEERROR] gsap is not defined` |
| Asset 404s during page load silently logged | `[Render] Asset load failure: ...` + `[WARN] Browser encountered network failures during page load` |

### 4\. Linter: recognize inline GSAP (`core/lint/rules/gsap.ts`)

The `missing_gsap_script` rule now recognizes GSAP bundled inline — matching the producer's inlining comment (`/* inlined: ...gsap... */`), GSAP library internals (`_gsScope`, `GreenSock`), and large inline scripts (>5KB) referencing gsap.

| Before | After |
| --- | --- |
| User inlines GSAP → linter errors with `missing_gsap_script` | Inline GSAP detected, no false error |
| Producer inlines CDN → linter errors on the compiled HTML | Producer's `/* inlined: ... */` comment recognized |

## Test plan

- [x] `pnpm build` passes
- [x] Core tests pass (410/410, +2 new)
- [x] **Reproduced baseline failures on** **`main`**: CDN scripts not inlined, external assets 404, no diagnostics
- [x] **Verified fixes**: CDN script inlined, external assets copied and served, diagnostics printed
- [x] Render with working CDN → `[Compiler] Inlined CDN script: ...` → render succeeds
- [x] Render with broken CDN → `[Compiler] WARNING: Failed to download CDN script` + browser errors surfaced
- [x] Render with assets outside project dir → `[Compiler] Found 1 asset(s) outside project directory` → assets served correctly
- [x] Linter with inline GSAP → no `missing_gsap_script` false positive
2026-04-01 23:15:00 +02:00
James RussoandClaude Opus 4.6 2d30654632 feat(cli): improve --help with grouped commands and per-command examples (#184)
* feat(cli): improve --help with grouped commands and per-command examples

Replaces citty's flat COMMANDS list with kubectl-style grouped categories
and adds examples to every subcommand. Root help now groups 14 commands
into 5 categories (Getting Started, Project, Tooling, AI & Integrations,
Settings). Per-command --help now appends a formatted Examples section
with practical usage patterns.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor(cli): use shared colors, lazy-load help, fix description drift

- Replace hand-rolled ANSI helpers with existing ui/colors.ts (fixes
  non-conformant NO_COLOR handling)
- Add cyan and gray to shared color module
- Lazy-load help.ts via dynamic import to avoid allocating help data
  on non-help invocations
- Fix description drift: benchmark and transcribe descriptions now
  match their command meta.description
- Unify tuple order: ROOT_EXAMPLES now uses [comment, command] to
  match COMMAND_EXAMPLES
- Remove redundant comment restating type annotation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cli): resolve typecheck error in showUsage generic signature

The lazy-load wrapper needs to cast CommandDef<T> to CommandDef when
forwarding to the help module, since TypeScript's generic variance
makes the direct assignment incompatible.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore(cli): remove kubectl references from help comments

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 12:55:26 -07:00