## Summary
- Surfaces error message when render fails due to producer server not running
- Two cases covered: initial POST failure and SSE connection drop
- Failed jobs now show the error reason in red text in the Renders panel
## Test plan
- [x] Start studio without producer server
- [x] Click render → should show "Could not reach render server" in red
- [x] Start render then kill producer → should show "Connection lost"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
- Replaces ~850 lines of inline route handlers in `vite.config.ts` with the shared `createStudioApi(adapter)` module
- Implements `StudioApiAdapter` for the Vite dev server context (SSR-loaded bundler/linter, producer HTTP proxy, Puppeteer thumbnails)
- Bridges Hono `fetch()` to Vite's Connect middleware with streaming support for SSE
Now **both** consumers (CLI + studio) use the same shared API module, ensuring feature parity.
## Test plan
- [x] `pnpm --filter @hyperframes/studio dev` starts correctly
- [x] Home page shows project grid with thumbnails
- [x] Preview plays with correct fonts/animations
- [x] Sub-composition drill-down works
- [x] Lint modal shows findings
- [x] File read/write works in code editor
- [x] Render queue works (requires producer server)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
- CLI render: use timestamped filenames (`project_date_time.ext`) matching the studio's naming convention, preventing overwrites of previous renders
- studioServer: read `fps`/`quality`/`format` from POST body instead of hardcoding `fps:30`/`quality:standard`/`mp4`
- studioServer: use timestamped job IDs matching the studio pattern
- studioServer: fix download endpoint to serve correct content-type for WebM
## Test plan
- [x] `hyperframes render --format webm` outputs timestamped WebM file
- [x] `hyperframes render` outputs timestamped MP4 (no overwrite)
- [x] Studio embedded server (`hyperframes dev`) renders with correct format when selected in UI
- [x] Download endpoint serves correct MIME type for WebM renders
## Summary
- Add render queue panel with progress tracking, download, and delete actions
- Restructure App layout: home page with project picker, session-based routing
- Add ExpandOnHover component for preview-on-hover interactions (uses motion/react)
- CompositionsTab now supports hover preview with expanded iframe view
- Vite config: guard setInterval cleanup to dev-only (fixes CI build timeout)
- Add favicon and update studio package deps
## Test plan
- [x] Render queue shows progress, completes, and allows download
- [x] Home page lists projects and navigates to session view
- [x] ExpandOnHover shows expanded preview on mouse hover with spring animation
- [x] `vite build` exits cleanly (no hanging process from setInterval)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
- Add collapsible left sidebar with Compositions and Assets tabs
- Compositions tab lists all sub-compositions with thumbnail previews and navigation
- Clicking a composition opens it in the code editor AND navigates the preview to show that composition
- Assets tab categorizes project files by type (images, fonts, media)
- Race condition guard on composition fetch with functional state update
- Thumbnail error fallback shows name initial instead of empty box
## Test plan
- [x] Sidebar opens/closes with smooth transition
- [x] Compositions tab lists files from project API
- [x] Clicking a composition changes the preview iframe to that composition
- [x] Assets tab categorizes files by type
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
- Replace Split/Delete buttons with Edit Range toolbar
- Edit button opens a time-range selection on the timeline
- "Copy to Agent" exports the selected range as a prompt-ready description
- Range selection shows start/end times with drag handles
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
- **NLELayout**: Add toolbar slot, composition breadcrumb navigation, improved responsive layout
- **Vite config**: Add full project API (preview, thumbnail, render, file CRUD) for standalone dev mode
- Remove AgentActivityTrack component (replaced by timeline clips)
- Add HTML editor utilities for composition source editing
- Guard setInterval cleanup to dev-only to prevent `vite build` from hanging in CI
🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary
- Add zoom state (zoomMode, pixelsPerSecond) to player store
- Add timeline element updates (setElements, clearElements)
- Remove unused/duplicate exports from player barrel file
🤖 Generated with [Claude Code](https://claude.com/claude-code)
npm's sigstore provenance verification requires package.json
repository.url to match the GitHub repo. Without it, publish
fails with E422 "expected to match https://github.com/heygen-com/hyperframes".
The blank template was a bare `<div>` fragment without `<!DOCTYPE>`,
`<head>`, or `<body>`. This caused:
- Blank preview (bundler/runtime can't initialize from a fragment)
- "Failed to run lint" (parsing errors on the malformed document)
All other templates (swiss-grid, vignelli, warm-grain, play-mode) are
proper HTML documents — blank was the only outlier.
Also improves lint error reporting to show the actual error message
instead of the generic "Failed to run lint."
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When installed via npx, `hyperframes dev` now starts a standalone
Hono HTTP server that serves the pre-built studio SPA and implements
the project API (file listing, read/write, preview bundling,
sub-composition rendering, runtime serving, SSE file watching).
Three modes are auto-detected:
1. Monorepo dev (running from .ts source) → spawn Vite (existing)
2. Local @hyperframes/studio installed → spawn Vite via package (new)
3. Default → embedded Hono server (new, zero extra deps needed)
Also patches the studio SPA to use EventSource SSE fallback when
Vite HMR is unavailable (production/embedded builds).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## Release v0.1.3
Bumps all packages to v0.1.3.
### Changes since v0.1.2
- fix(producer): resolve manifest from sibling dist/ directory
- fix(producer): add margin reset when wrapping HTML fragments
- fix(producer): exclude test files from tsc build
- feat(cli): add opt-out anonymous telemetry via PostHog
- feat: add HyperFrames skills for AI coding tools
- fix(cli): resolve npx hyperframes from inside monorepo
- fix(ci): publish workflow fixes (remove provenance, idempotent steps, remove prepublishOnly)
After merging, the release tag is created automatically, which triggers npm publish.
* 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>
- Add READMEs for all 5 packages (core, engine, producer, cli, studio)
with install, overview, basic usage, and links to full docs
- Rewrite core README from internal doc to OSS-facing format
- Polish root README: add badges, packages table, docs link, requirements
- Add AI usage policy and BDFL governance statement to CONTRIBUTING.md
- Genericize license references (pending final license decision)
- Docs URL set to hyperframes.heygen.com
Addresses VA-850.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* 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>