## 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)
* 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>
## 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)
## 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)
* 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>
- Replace hardcoded template list in build:copy with `cp -r src/templates/*`
so new templates are included automatically (kinetic-type, decision-tree,
product-promo, nyt-graph were missing from published package)
- Fix captions SKILL.md: reword `!` and `>` in inline backticks that
triggered Claude Code's bash permission checker
- Add scripts/lint-skills.ts to catch shell-unsafe patterns in SKILL.md
files (runs as part of `bun run lint` in CI)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
* feat(cli,core): add standalone transcribe command, transcript normalization, and caption lint rules
- Add `hyperframes transcribe` command for transcribing audio/video and importing
existing transcripts (SRT, VTT, OpenAI Whisper API JSON, whisper.cpp JSON)
- Add transcript format normalizer (normalize.ts) with auto-detection and
conversion to standard [{text, start, end}] word arrays
- Upgrade default whisper model from base.en to small.en for better accuracy
- Add --model and --language flags to both `transcribe` and `init` commands
- Extract shared patchCaptionHtml() to eliminate duplication between init.ts
and transcribe.ts (init.ts reduced by ~55 lines)
- Add 3 caption lint rules: caption_exit_missing_hard_kill,
caption_text_overflow_risk, caption_container_relative_position
- Update captions skill with model guide, format docs, music guidance,
text overflow prevention, caption exit guarantee pattern
- Expand captions skill trigger to cover lyrics, karaoke, lyric videos
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs(cli): add transcribe command and --model/--language flags to CLI docs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(cli): fix blank template lint issues
- blank/index.html: remove data-start from video (was nested in timed parent),
add class="clip" for initial hidden state
- blank/captions.html: add max-width + overflow:hidden to prevent text clipping,
add tl.set hard kill after exit tween to prevent stuck captions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add lint-after-edit rule to repo and project CLAUDE.md
Agents must run `npx hyperframes lint` after editing compositions.
Also expand captions skill description in project template.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* style: format _shared/CLAUDE.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>
Namespace skill names with `hyperframes-` prefix for clearer identity in
OSS contexts where users may have other skills installed.
Updates skill directories, SKILL.md frontmatter, CLAUDE.md, README.md,
CLI build script, init command, and project template.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>