Commit Graph
391 Commits
Author SHA1 Message Date
Miguel Ángel 58ddb11bc5 chore: release v0.3.0 (#249)
## Summary

Coordinated minor bump across all published packages. No source changes in this PR itself; it is the version stamp for everything that landed on main since v0.2.5.

## Version bumps

| Package | from | to |
|---|---|---|
| `@hyperframes/cli` | 0.2.5 | **0.3.0** |
| `@hyperframes/core` | 0.2.5 | **0.3.0** |
| `@hyperframes/engine` | 0.2.5 | **0.3.0** |
| `@hyperframes/player` | 0.2.7 | **0.3.0** |
| `@hyperframes/producer` | 0.2.5 | **0.3.0** |
| `@hyperframes/studio` | 0.2.9 | **0.3.0** |

Between 0.2.5 and this release, `player` and `studio` received several patch versions on npm as we iterated on the bundler, entry point, and SSR issues. 0.3.0 collapses that into a single coordinated minor so the ecosystem is aligned again.

## What is in v0.3.0

### `@hyperframes/player`

**Restored package entry points to the compiled `dist/` output.** 0.2.5 shipped with `"main": "./src/hyperframes-player.ts"` but the published tarball only included `dist/` via the `"files"` field. Every consumer trying to import the package failed with `Module not found: Can't resolve '@hyperframes/player'`. Entry points now point at the built JS/`.d.ts` files inside `dist/`.

**DOM-based root timeline resolution in the ready probe.** In a bundled preview, `window.__timelines` contains the master composition alongside its sub-compositions, for example:

```js
{
  main: GSAPTimeline(14s),
  intro: GSAPTimeline(1.5s),
  'scene2-4-canvas': GSAPTimeline(12.6s),
  'scene5-logo-outro': GSAPTimeline(3.2s),
}
```

The probe used to select the adapter with `keys[keys.length - 1]`. Object key ordering meant the last-registered sub-composition would win, so the `ready` event reported a sub-composition's duration (e.g. 3.2s) instead of the master's 14s. The probe now looks up the root composition id from the outermost `[data-composition-id]` element in the iframe DOM and uses its key. Falls back to the last key when no element is present, so standalone sub-composition previews keep working.

### `@hyperframes/studio`

**`useTimelinePlayer.getAdapter()` uses the same DOM-based root id lookup** as the player. Previously play, pause, seek, and duration readout were all driven by whichever sub-composition happened to register its timeline last.

**`Player.tsx` loads `@hyperframes/player` lazily.** The component used to call `import "@hyperframes/player"` at module scope, which runs the package's `customElements.define(...)` side effect during module evaluation. `HTMLElement` does not exist in the Node runtime, so any consumer page that transitively imported the studio during server rendering threw:

```
ReferenceError: HTMLElement is not defined
  at module evaluation (@hyperframes/studio/src/player/components/Player.tsx)
```

The import now runs inside the mount effect via `import(...)` so it only evaluates in the browser. Added a cancellation flag and deferred cleanup so a fast unmount before the dynamic import resolves does not leak listeners or DOM nodes.

**Captions module imports stripped of `.js` extensions.** Files under `src/captions/` imported siblings as `./types.js` and `./parser.js`. That is legal ESM TypeScript but Turbopack and several other bundlers refuse to resolve those specifiers against `.ts` files inside `node_modules`, breaking any consumer build that transitively pulled in the captions module. Captions now uses extensionless imports, matching the rest of the studio codebase.

### `@hyperframes/core`, `@hyperframes/cli`, `@hyperframes/engine`, `@hyperframes/producer`

Version bump only, no source changes since 0.2.5. Kept on the same version so the ecosystem is easier to reason about.

## Impact for consumers

If you use `@hyperframes/studio` in a Next.js app:
- The play button in a bundled preview reports the correct composition duration and drives the master timeline.
- The session page no longer 500s in dev mode when the studio barrel is imported (the SSR fix).
- Turbopack builds that transitively load the captions module no longer fail on `Cannot resolve './types.js'`.

If you use `@hyperframes/player` directly:
- Consumer bundlers can resolve the package again (dist entry points restored).
- The `ready` event duration reports the master, not a sub-composition.

## After merge

Publish each package to npm with `pnpm publish` (workspace deps auto-resolve).
2026-04-14 01:16:06 +02:00
Miguel Ángel 1dd898786c chore: release v0.2.5 (#246) 2026-04-13 17:55:40 +02:00
Miguel ÁngelandClaude Opus 4.6 dce2c6ee14 chore: release v0.2.4
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 23:44:08 +00:00
James b33bbfa0f9 chore: release v0.2.3 2026-04-08 18:19:07 +00:00
Miguel Ángel 1c61a0b25a chore: release v0.2.3-alpha.2 2026-04-08 03:19:08 +02:00
Vance Ingalls 85a76c0043 feat(cli): implement Docker rendering for deterministic output (#215)
## Summary

- **The `--docker` flag was a no-op stub** — `renderDocker` called the same local `executeRenderJob` as `renderLocal`, no container was ever launched
- Now `renderDocker` generates a Dockerfile, builds a versioned `hyperframes-renderer:<version>` image with Chrome/FFmpeg/fonts/chrome-headless-shell, and runs the render inside a container
- Image is cached per CLI version — first render builds (~2 min), subsequent renders reuse it
- Forces `linux/amd64` platform since chrome-headless-shell has no ARM Linux binary
- Uses `execFileSync` (array form) throughout to prevent shell injection
- Forwards `--quiet`, `--gpu`, and render config flags into the container
- `Dockerfile.render` added as a reference for manual builds

## Test plan

- [x] `hyperframes render --docker` builds image and produces valid MP4
- [x] Second run reuses cached image (no rebuild)
- [x] `--quiet` suppresses container output while keeping stderr for errors
- [x] Typecheck, lint, format all pass
- [ ] Verify `--gpu` with `--docker` on a machine with GPU access

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-04-07 16:38:53 -07:00
James RussoandClaude Opus 4.6 0a0d5d3654 refactor(skills): consolidate 15 skills into 3 (#211)
* refactor(skills): consolidate 15 skills into 3 for better trigger reliability

Merge 9 GSAP skills (core, timeline, scrolltrigger, plugins, utils, react,
frameworks, performance, effects) and 6 HyperFrames skills (compose, captions,
tts, audio-reactive, marker-highlight, cli) into 3 consolidated skills:

- `gsap` — core API + timelines + performance in SKILL.md; scrolltrigger,
  plugins, utils, react, frameworks, effects in references/
- `hyperframes` — composition authoring rules in SKILL.md; captions, tts,
  audio-reactive, marker-highlight in references/
- `hyperframes-cli` — CLI commands (init, lint, preview, render, etc.)

Why: With 15 separate skills, agents must correctly trigger the right subset
for any task. "Create an animated video with captions" needed 6+ skills to
fire — each with ~90% trigger accuracy means ~53% chance of getting all of
them. With 3 skills, that same task needs just `hyperframes` + `gsap` (~90%
both fire). Progressive disclosure still works via references/ files loaded
on demand.

Also fixes: CLAUDE.md referenced `window.__GSAP_TIMELINE` (incorrect) —
corrected to `window.__timelines`.

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

* feat(cli): add --skip-skills flag to init command

Allow skipping the AI coding skills installation prompt during
`hyperframes init` with `--skip-skills`. Useful when skills are
already installed or when the user wants to scaffold without them.

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

* fix(skills): address code review feedback on consolidation

Restore content lost during over-compression:

- captions: fix overflow to `visible` (not hidden — clips glow effects),
  add container pattern warning, scale headroom formula, and self-lint
  placement guidance
- audio-reactive: restore sampling frequency pattern (per-frame tl.call
  loop vs single tween) and textShadow-on-container gotcha
- effects/typewriter: restore word rotation, appending words, spacing
  with static text, and multi-line cursor handoff patterns
- effects/audio-visualizer: restore spatial mapping conventions, fetch vs
  inline loading, WebGL/DOM rendering approaches, and canvas layering
- hyperframes-cli: restore --strict-all flag in render flags table

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

* fix(cli): update build:copy and template for consolidated skill names

- build:copy: reference skills/hyperframes, skills/hyperframes-cli,
  skills/gsap instead of the old 15 skill directory names
- _shared/CLAUDE.md template: update skill table to consolidated names

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-06 11:21:49 -07:00
Miguel Ángel e2c8ed5d83 refactor(core): replace cheerio with linkedom to drop deprecated whatwg-encoding (#187)
## Summary

- `cheerio` pulls `encoding-sniffer` → `whatwg-encoding@3.1.1` (deprecated), causing a warning on every `npm install -g hyperframes`
- `linkedom` was already bundled into the CLI via tsup `noExternal` and has zero deprecated transitive deps
- Rewrote `htmlBundler.ts` and `subComposition.ts` to use standard DOM APIs via `linkedom`
- Added a `parseHTMLContent` helper that wraps HTML fragments in a full document structure (required for `linkedom` to populate `document.body`)
- Removed `cheerio` from `cli` dependencies and tsup `external` list
- Replaced `cheerio` with `linkedom` in `core` `optionalDependencies`

## Test plan

- [x] All 411 tests pass (`bun run test` in `packages/core`)
- [x] Full monorepo build succeeds (`bun run build`)
- [x] TypeScript typecheck passes
2026-04-03 16:06:30 +02:00
James 06e3da9ad3 chore: release v0.2.2 2026-04-03 04:36:19 +00:00
Vance IngallsandClaude Opus 4.6 e9c2e6f772 fix(cli): resolve SyntaxError in bundled ESM output (#203)
Two issues prevented `npx hyperframes` from running:

1. The tsup banner declared `const __filename` which collided with
   esbuild's CJS-to-ESM `var __filename` shim. ESM strict mode
   rejects const+var redeclaration. Changed to `var` so both
   declarations coexist.

2. postcss (producer dependency) was not resolvable during bundling
   due to bun's isolated module layout. Added postcss as an external
   dependency of the CLI package.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 14:52:37 -07:00
Miguel Ángel 7f4e4333e5 fix(cli): read actual composition dimensions in info and fix semver update check (#194)
## Summary

- **`info`** **command** now reads actual `data-width`/`data-height` from the root composition element instead of hardcoding 1920x1080 or 1080x1920 based on a parser heuristic that defaults to "portrait"
- **Update check** now uses proper semver comparison instead of string inequality (`!== VERSION`). Previously reported "update available" when installed `0.2.1` and npm had `0.2.0`

**Part 2 of 5** in a stacked PR series fixing E2E test findings.

## Test plan

- [x] `npx hyperframes info` on a 1920x1080 project shows "1920x1080" (not "1080x1920")
- [x] `npx hyperframes info --json` returns correct width/height fields
- [x] Version check no longer reports downgrade as available update
2026-04-02 14:12:48 -07:00
James RussoandClaude Opus 4.6 7389c0c89b feat(cli): add tts command for local text-to-speech via Kokoro-82M (#201)
* feat(cli): add `tts` command for local text-to-speech via Kokoro-82M

Adds `hyperframes tts` — generate speech audio locally using Kokoro-82M
(ONNX), no API key needed. Mirrors the transcribe command architecture.

- New command: `hyperframes tts "text" --voice af_heart --output speech.wav`
- 54 voices across 8 languages, ~5x realtime on CPU
- Auto-downloads model (~311 MB) + voices (~27 MB) to ~/.cache/hyperframes/tts/
- Requires Python 3.8+ with kokoro-onnx installed
- Extracted shared `downloadFile` utility from whisper/manager.ts with
  atomic .tmp→rename to prevent partial download corruption
- Added hyperframes-tts skill with voice selection guide
- Updated CLAUDE.md with TTS docs, voice table, and skill reference

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

* docs(tts): improve skill per skill-creator guidelines

- Move trigger info from body to frontmatter description
- Remove `trigger` field (not a valid frontmatter field)
- Remove CLI flag docs Claude can derive from --help
- Remove redundant voice tables (keep content-to-voice mapping)
- Fix composition audio example to use actual <audio> element pattern
- Keep non-obvious workflows: TTS+transcribe for captions, long scripts

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

* docs(tts): add guidance for using external TTS sources

Help users understand when to use cloud TTS (voice cloning, broader
languages, higher quality) vs the built-in Kokoro model, and how
external audio integrates into the same composition workflow.

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

* docs(tts): prioritize HeyGen API as recommended cloud TTS

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

* docs(tts): remove external TTS section for now

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

* fix(tts): set required: false on input arg so --list works standalone

Citty treats positional args as required by default unless explicitly
set to required: false. Without this, `hyperframes tts --list` fails
with "Missing required positional argument".

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

* feat(tts): add --help examples and fix required:false for --list

Add examples section to `tts --help` matching the pattern from other
commands (transcribe, render, etc.). Fix citty positional arg requiring
explicit `required: false` for --list to work standalone.

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

* docs: add CLI command checklist to CLAUDE.md

Ensure new commands always get --help examples in help.ts.

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-02 14:09:25 -07:00
James 870ca76c8b chore: release v0.2.1 2026-04-01 21:36:24 +00:00
James 1a5badc803 chore: release v0.2.0 2026-04-01 03:28:05 +00:00
James Russo b866a28545 feat(cli): add remote template fetching via giget (#162)
* feat(cli): add remote template fetching via giget

* fix: update remote.ts to use templates/ instead of examples/

* fix(cli): validate template ID against manifest before downloading

Fails fast with available template list instead of downloading
an empty directory for nonexistent templates.

* refactor(cli): simplify to single --template flag with dynamic validation

- Remove --example flag (--template handles bundled + remote)
- Remove static ALL_TEMPLATE_IDS list (validates against GitHub manifest)
- No CLI release needed to add new templates — just add to templates/ and templates.json
- scaffoldProject auto-detects bundled vs remote

* chore: update lockfiles for giget dependency

* fix(cli): remove undefined isAudioOnly reference
2026-03-31 13:41:01 -07:00
James bc956bb6da chore: release v0.1.15 2026-03-31 03:34:27 +00:00
James RussoandClaude Opus 4.6 a9d49cd528 fix(cli): auto-copy all templates to dist and add skill lint (#153)
- 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>
2026-03-30 20:33:54 -07:00
James 25767fd8dd chore: release v0.1.14 2026-03-31 03:15:36 +00:00
James cf0ec5d27d chore: release v0.1.13 2026-03-30 22:21:18 +00:00
Vance Ingalls 229538c622 fix: add media rendering guardrails to prevent silent failures (#112)
## Summary

- **Lint rules** catch media elements missing `id` (renderer silently skips them), missing `src`, `preload="none"` (blocks renderer), and video nested in timed divs (freezes playback). Upgraded `video_nested_in_timed_element` from warning to error.
- **Compiler** strips `preload="none"` from media during compilation. Runs parallel, cached keyframe interval analysis via ffprobe — warns on sparse keyframes (>2s) that cause seek failures and audio/video desync. Suggested ffmpeg command preserves audio (`-c:a copy`).
- **Pre-render lint** lints `index.html` + all `compositions/*.html` sub-compositions before render via shared `lintProject()` helper. Warns by default; `--strict` blocks on errors, `--strict-all` blocks on errors + warnings.
- **Render orchestrator** logs a hint to retry with `--workers 1` when parallel capture times out on video-heavy compositions.
- **Refactor**: extracted `runFfprobe()` + `parseProbeJson()` helpers to deduplicate ~80 lines of spawn boilerplate across 3 ffprobe functions. Extracted `shouldBlockRender()` so strict flag tests exercise production code. Shared `lintProject()` used by both `lint` and `render` commands.

## Context

Discovered during a real composition build session where:
1. `<audio>` without `id` rendered silently (preview worked fine because runtime queries `[data-start]`, but renderer queries `[id][src]`)
2. `<video>` inside timed `<div>` froze on first frame
3. `preload="none"` caused 45s renderer timeout
4. YouTube clips with sparse keyframes from `yt-dlp --download-sections` caused audio/video desync
5. Parallel workers timed out on video-heavy compositions

## Test plan

- [x] Core: 365/365 tests passing (5 new lint tests)
- [x] Engine: 24/24 tests passing
- [x] CLI: 14/14 tests passing (7 lintProject + 7 shouldBlockRender)
- [x] Lint + format hooks pass
- [ ] Manual: create a composition with `<audio data-start="0" src="test.wav">` (no id) — verify `npx hyperframes lint` catches it
- [ ] Manual: run `npx hyperframes render --strict` with lint errors — verify it blocks
- [ ] Manual: run `npx hyperframes render --strict-all` with lint warnings — verify it blocks
2026-03-30 11:07:19 -07:00
Miguel ÁngelandClaude Opus 4.6 808d196fe0 chore: bump to v0.1.12
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 10:10:03 +00:00
Miguel ÁngelandClaude Sonnet 4.6 6d105a3e64 chore: bump to v0.1.11
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 19:39:38 -04:00
James 7d7af7902b chore: release v0.1.10 2026-03-28 03:59:00 +00:00
JamesandClaude Opus 4.6 b8149abef5 chore(skills): rename compose-video → hyperframes-compose, captions → hyperframes-captions
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>
2026-03-28 01:26:13 +00:00
James b6e2d74cb7 chore: release v0.1.9 2026-03-27 22:16:04 +00:00
James 8b784cca3d chore: release v0.1.8 2026-03-27 22:09:23 +00:00
James 8d7c33a4aa fix(publish): add repository.url to all packages for npm provenance
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".
2026-03-27 22:07:55 +00:00
James 4a49d461df chore: release v0.1.7 2026-03-27 22:03:09 +00:00
JamesandClaude Opus 4.6 42c79f3bd3 feat(cli): scaffold CLAUDE.md and project-level skills into init
New HyperFrames projects created via `hyperframes init` now include:

- CLAUDE.md + AGENTS.md — teaches AI agents about skills, commands,
  project structure, and framework rules (class="clip", timeline
  registration, determinism). Agents know to invoke /compose-video
  before writing compositions.
- .claude/skills/{compose-video,captions} — project-level skills for
  immediate availability in the current agent session (global skills
  require a session restart to discover).
- Updated next-steps output with `hyperframes docs <topic>` and a
  link to hyperframes.heygen.com.
- Updated README with "AI Agent Skills" section documenting
  `npx hyperframes skills` and `npx skills add` install paths.
- Repo-level CLAUDE.md for framework contributors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 19:15:54 +00:00
github-actions[bot] 55ff721552 chore: release v0.1.6 2026-03-27 06:04:18 +00:00
github-actions[bot]andgithub-actions[bot] ffd0fb678f chore: release v0.1.5 (#77)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-27 01:50:05 +01:00
github-actions[bot] 87fc3e3d8f chore: release v0.1.4 2026-03-26 22:09:12 +00:00
JamesandClaude Opus 4.6 7e059e15f9 fix(producer): embed font data at build time instead of runtime require.resolve
The CLI bundle uses tsup to inline @hyperframes/producer, but the
deterministicFonts module used require.resolve('@fontsource/*/package.json')
at runtime to find woff2 files on disk. When installed via npx, these
@fontsource packages don't exist, causing "Cannot find module" errors.

Replace runtime filesystem lookups with a build-time generator that reads
all @fontsource woff2 files and produces a TypeScript module with base64
data URIs. The generator runs before both producer and CLI builds, making
the bundle fully self-contained with zero @fontsource runtime dependencies.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 20:59:11 +00:00
Miguel Ángel 36c91cecbb chore: release v0.1.3 (#60)
## 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.
2026-03-26 19:17:09 +01:00
Vance IngallsandClaude Opus 4.6 f4367d5726 feat(cli): add whisper transcription and template improvements (#53)
* 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>
2026-03-26 11:05:15 -07:00
Vance IngallsandClaude Opus 4.6 71e00b4533 fix(cli): resolve npx hyperframes from inside monorepo (#47)
* fix(cli): rename workspace package so npx resolves from registry

Rename packages/cli from "hyperframes" to "@hyperframes/cli" so that
npm/npx stops resolving it as a local workspace package when run from
inside the monorepo. The publish workflow sets the name back to
"hyperframes" before publishing so the npm package name is unchanged.

Root cause: npm sees workspaces in root package.json, finds packages/cli
named "hyperframes", assumes it's local, but bun manages node_modules
so there's no bin symlink — npx fails with "command not found" instead
of falling back to the registry.

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

* fix(cli): update lockfile for workspace package rename

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

* fix(ci): publish CLI from temp copy to avoid workspace mutation

Copy packages/cli to a temp directory before renaming to "hyperframes"
for publish. Avoids corrupting the workspace if the job fails mid-way.

Addresses review feedback on #47.

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

* fix(ci): resolve leftover conflict markers in publish.yml

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-03-26 10:25:19 -07:00
github-actions[bot] 7d75ba30db chore: release v0.1.2 2026-03-24 04:32:13 +00:00
Vance Ingalls 94e25443ae build: migrate from pnpm to bun as package manager (#28)
## Summary
- Replace pnpm with bun for dependency installation, script running, and ad-hoc execution
- Keep pnpm for publish workflow only (`publishConfig` overrides + `--provenance`)
- `bun install` replaces `pnpm install` (~4-5x faster cold installs)
- `bun run` replaces `pnpm run` (~28x less startup overhead)
- `bunx` replaces `npx` in lefthook hooks
- CI workflows updated (`oven-sh/setup-bun@v2` + `actions/setup-node@v4`)
- `pnpm-lock.yaml` removed, `bun.lock` generated
- `pnpm-workspace.yaml` kept for publish compatibility
- CLI source code (`packages/cli/src/`) unchanged — shipped to end users who may not have bun

Part 5/5 of [VA-851](https://linear.app/heygen/issue/VA-851/pre-migration-configure-eslint-prettier-and-conventional-commits)

## Test plan
- [x] `bun run lint` — 0 errors
- [x] `bun run format:check` — all files pass
- [x] `bun run build` — all 5 packages build
- [x] 330 core tests pass
- [x] 18 engine tests pass
- [x] `publish.yml` unchanged (pnpm stays for npm publishing)
- [x] No `bunx`/`bun run` references in shipped source code (`packages/*/src/`)
2026-03-23 19:50:57 -07:00
Vance Ingalls 20be2ea1c2 style: apply oxfmt baseline formatting across all source files (#25)
## Summary
- Run `oxfmt .` across the entire codebase to establish formatted baseline
- 299 files changed — mechanical formatting only, no logic changes
- Double quotes, semicolons, 2-space indent, trailing commas, 100 print width

Part 3/4 of [VA-851](https://linear.app/heygen/issue/VA-851/pre-migration-configure-eslint-prettier-and-conventional-commits)

## Test plan
- [x] `pnpm format:check` — all 426 files pass
- [x] `pnpm -r typecheck` — all packages pass
- [x] `pnpm build` — all packages build
- [x] All 348 tests pass
2026-03-23 17:15:14 -07:00
James 4fa627d36a chore: release v0.1.1 2026-03-23 03:24:59 +00:00
Vance IngallsandClaude Opus 4.6 9f8e5ba5a1 initial code (#2)
* 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>
2026-03-21 22:43:56 -07:00