The audio-locked attribute was correctly setting `muted = true` and posting
`set-muted` to the iframe runtime, but on warm-cache reloads of claude.ai
and inside the Claude desktop Electron client, the iframe finishes loading
*after* the parent has already sent control messages — the iframe runtime's
postMessage listener isn't installed yet, so the messages are silently
dropped. Audio plays unmuted with no UI to recover.
Confirmed via:
- "First open" on claude.ai: cold cache, iframe slow → listener up before
`set-muted` lands → audio muted ✅
- "Hard refresh" on claude.ai: warm cache, iframe fast → listener up after
message arrives → message lost → audio plays ❌
- Claude desktop: Electron renderer consistently fast → race always loses
→ audio plays ❌
Fix: add a `{source: "hf-preview", type: "ready"}` event the runtime emits
once `installRuntimeControlBridge` has registered the listener. The player
listens for it and replays current bridge state (`set-muted`, `set-volume`,
`set-playback-rate`). Pre-ready messages are now safe to send — they'll be
replayed once the runtime can receive them.
The replay is idempotent — re-asserting defaults is a no-op — so it's also
safe across iframe reloads (new runtime instance emits ready again).
Tests: 6 new (1 bridge: ready posted on install; 5 player: replays muted /
volume / playback-rate / audio-locked-forced-mute / handles second ready /
ignores ready from wrong source). Suites green: core 1387, player 137.
Refs:
- Investigation: heygen-com/hyperframes#1300 (UA-fallback attempt — unrelated
to actual root cause)
- claude.ai-web.log analysis revealed cross-origin iframe + race condition,
not attribute stripping as originally hypothesized
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor: split useTimelinePlayer.ts and hyperframes-player.ts into focused modules (<500 LOC each)
* fix(ci): scope 500 LOC check to packages/studio, add allowlist for grandfathered files
* feat(cli): Linux ARM64 support — auto-install Chromium on DGX Spark / GB10 / Jetson
Chrome Headless Shell has no Linux ARM64 binary. On arm64 Linux:
- Detects the platform automatically
- Tries to auto-install system Chromium via apt-get (works on Ubuntu/Debian ARM)
- Falls back to clear manual instructions with exact commands
- 'hyperframes browser ensure' guides through the setup interactively
- After setup, all render commands work without any flags
* fix(ci): disable Windows Defender real-time monitoring to prevent EPERM builds
Path exclusions are insufficient — Defender re-scans new files created
during bun install before the exclusion takes effect. Disable real-time
monitoring for the entire job duration instead (standard CI practice).
* refactor(studio): split all files >500 LOC + extract useToast, delete allowlist
All 11 large files split into focused modules under 500 LOC.
App.tsx extracted toast logic into useToast hook (493 LOC now).
.filesize-allowlist deleted — no longer needed.
* fix: remove unused imports from split files, extract useToast from App.tsx
App.tsx: 504 → 493 lines (toast logic extracted to useToast hook)
timelineDOM.ts: remove unused imports from re-export pattern
MotionPanel.tsx: remove unused clampStudioCustomEasePoints import
studioMotionOps.ts: remove unused StudioGsapMotionDirection import
* fix(ci): use Set-MpPreference to fully disable Windows Defender (both jobs)
* fix(producer): use node --experimental-strip-types instead of tsx for build:fonts
Eliminates the tsx binary dependency that Windows Defender locks during
bun install, causing EPERM errors. Node 22.6+ strips TypeScript types
natively with no external binary.
* chore: remove .filesize-allowlist — App.tsx is now 493 lines (<500)
* fix(ci): disable Windows Defender before checkout to prevent all EPERM races
* fix(producer): skip build:fonts if fontData.generated.ts already exists
The generated file is tracked in git, so CI doesn't need to regenerate
it. This avoids @fontsource/inter node_modules access on Windows which
triggers EPERM from Defender scanning during bun install.