- 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>
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>
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>
- 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>
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>
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>
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>
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>
- 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>
- 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>
- 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>
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>
- 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>
- 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>
- 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>
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>
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>
- ConsoleMessage.type() returns "warn" not "warning"
- Type pageerror callback parameter as Error
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
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>
.en models (small.en, medium.en) translate non-English audio into English
instead of transcribing it. This silently destroys the original language.
- Add non-negotiable language rule to captions skill
- Update model tables to default to models without .en suffix
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
Documents the inline TRANSCRIPT requirement, JSON-quoted keys, and
CSS class conventions needed for the studio caption editor to detect
and edit captions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The player needs window.__timelines registered immediately to start
playback. GSAP timelines are mutable so tl.call() entries added
later in the fetch callback still fire when seeked.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Never overflow: hidden on caption containers — clips glow/scale effects
- Music requires audio-reactive captions (any genre, any energy level)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 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>
## 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)