5551 Commits
Author SHA1 Message Date
James RussoandClaude Opus 5.5 74e17fa9cf chore: release v0.8.156 (#5577)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
v0.8.156
2026-10-14 04:11:14 +00:00
James RussoandClaude Opus 5.5 a59a422195 perf(studio): speed up the edit accuracy bench and trim its grid (#5569)
* perf(studio): cut fixed waits from the edit accuracy bench

- Settle window 1000 ms -> 500 ms (EDIT_ACCURACY_STILL_MS overrides it).
- Drop the fixed 1 s sleep in openStudio; the settle that follows is the
  real readiness wait.
- Mark composition hosts data-no-timeline in the producer's page when the
  saved project has no GSAP timeline, so the render skips the 10 s
  playerReadyTimeout wait.
- Run the blank-page smoothness control once per gesture per browser.

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

* ci(studio): trim the edit accuracy grid to 741 cases

Drops zoom 200, the pct and center placements, and nested keyframed cases
from the grid (2061 -> 741 cases; the pr slice 435 -> 265). Drag paths and
sequences are unchanged.

baseline.json loses the 1320 removed cases. The ratchet no longer counts a
base case that the head's baseline.json does not hold, and lists such cases
as retired, so removing cases from the grid and baseline.json together does
not fail the gate.

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

* fix(studio): measure the blank-page control again after it fails

The control is cached per browser and gesture, so a rejected measurement stayed
cached and failed every later case with that gesture before Studio was tested.
Drop a rejected entry so the next case measures again.

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

* ci(studio): keep the nested keyframed resize cases in the edit accuracy PR slice

Two historical gate runs failed only on resize-scale-px-r0-nested-z100-on,
which the trim removed, so the PR slice would have passed that build. Keep
the nested keyframed resize on a keyframe at zoom 100 without rotation, one
case per GSAP mode, with their baseline entries. PR slice 265 -> 271 cases,
full grid 741 -> 747.

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

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 03:46:17 +00:00
Miguel Ángel 95837a3df7 fix(core): keep the colour grade on a graded video's first rendered frame (#5575)
* fix(core): keep the colour grade on a graded video's first rendered frame

A graded clip rendered ungraded for one frame wherever a capture session
first drew it: the clip's own first frame, a parallel worker's first frame
inside it, or a capture segment's first frame inside it. The grade dropped
the clip while its injected frame was still decoding, so the redraw after
decode had nothing to draw.

Part of #5548

* chore: pin the producer test-classification hash and drop an added comment
2026-10-14 03:32:35 +00:00
Miguel Ángel 9b996059af fix(engine): renders under WSL use the GPU through full Chrome when the headless shell cannot (#5574)
* fix(engine): renders under WSL use the GPU through full Chrome when the headless shell cannot

Part of #5548.

* fix(engine): a missing or unreadable full Chrome cache never stops a WSL render
2026-10-14 02:49:08 +00:00
Xuanru Li abe97f6e4c refactor(inspector)!: drop the predefined agent tools; harnesses define their own (#5570)
* refactor(inspector)!: drop the predefined agent tools; harnesses define their own

* chore(ci): agree to the inspector's agent-tool file deletions

* docs(inspector): describe the HTTP contract only, export the request types

* fix(inspector): ship the HTTP server and give every refusal a stable code

* fix(inspector): keep the server export in the subpath descriptor

* fix(inspector): keep the HTTP server reusable after a large body

* fix(inspector): start the npm bin when it is a symlink

* fix(inspector): import the server test without a .ts extension

* fix(inspector): ship the HTTP server without a second copy of the service
2026-10-14 02:44:10 +00:00
Miguel Ángel d77b03c24c fix(producer): start segmented render workers together so slow media waits overlap (#5573)
* fix(producer): start segmented render workers together so slow media waits overlap

Each worker's page load used to wait for the previous worker's media,
fonts and Tailwind to be ready, so a slow readiness wait was paid once
per worker. Open every worker session at once, as non-segmented
parallel capture already does.

* fix(producer): trim the worker-opening comment

* fix(producer): name the worker-opening helper instead of commenting it
2026-10-14 02:26:11 +00:00
James RussoandClaude Opus 5.5 6afcb48395 fix(engine): sample VFR video by timestamp so it does not freeze on FFmpeg 8 (#5572)
* fix(engine): sample VFR video by timestamp so it does not freeze on FFmpeg 8

VFR sources were resampled with `-fps_mode cfr -r`, which on FFmpeg 8 honours
each frame's duration. In mp4 files with B-frames those durations are
decode-order gaps, so a frame can claim 0.4s while the next one starts 17ms
later, and the extracted video froze on it. VFR now uses the same fps filter
sampling as CFR (the frame on screen at each output tick), bounded by
-frames:v so a window that ends on a still keeps its rounded-up frame count.

The two-process raw pipe that worked around FFmpeg 5.1 dropping durations
under -vf is no longer needed and is removed. The vfr-screen-recording golden
is regenerated: FFmpeg 5.1's cfr mapping it was rendered with ran one to two
source frames late.

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

* fix(engine): move the extraction cache to v7 so v6 VFR frames are not reused

v6 entries for VFR sources hold frames sampled by packet duration, which
FFmpeg 8 freezes and FFmpeg 5.1 lags. The key is unchanged by the fix, so
a warm cache served them after the upgrade.

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

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 01:45:43 +00:00
James RussoandClaude Opus 5.5 9a2c1c6e80 ci(studio): run the edit accuracy slice on PRs and the full grid in the merge queue (#5566)
* ci(studio): run the edit accuracy slice on PRs and the full grid in the merge queue

The edit accuracy gate used about 83% of CI job-minutes, ran on changes that
cannot reach the bench (encoder, render and CLI command code), and never ran
in the merge queue, so main could drift and fail every later PR.

- Path filter: its own paths-filter step with some-with-excludes, limited to
  what the bench runs: Studio, studio-server, core, parsers, player, sdk, lint,
  the CLI preview server path, and the engine and producer modules in the
  import closure of browserManager, frameCapture and both fileServers. Test
  files, fixtures and docs no longer trigger it. The bench's webmcp-host.mjs,
  which it imports but the old filter missed, now does.
- merge_group: the filter's base is the event's base_sha there. In the queue
  the full grid always runs (20 shards) whenever the filter matches. The queue
  is the last point before main, and nothing else measures the full grid
  now that PRs run a slice. Skipping it would let zoom 50/200 and rotated
  regressions land on main, where they fail every later PR against the
  banked baseline. That drift caused about a fifth of past gate failures.
- PRs: the existing `pr` grid becomes zoom 100 with no rotation in both
  nestings (435 of 2061 cases), in 5 shards. Of the 11 PRs whose real
  regressions the full gate caught, this slice held a regressed case for 10.
- Gate: newly passing cases missing from baseline.json are listed in the
  comment and as a notice, not failed (45% of past gate failures). A partial
  grid compares only the cases it ran. `--shards` fails the gate when a shard
  reported no results, and only a full grid writes a bankable baseline.json.
- The required check "Studio: edit accuracy gate" keeps its name. It is
  skipped when the filter does not match, as before.
- check-test-reachability models negated globs as exclusions under
  some-with-excludes and still fails closed elsewhere.

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

* ci(studio): run the edit accuracy gate when its install inputs change

The path filter missed the root package.json, the dependency install script
and the ffmpeg setup action, which Build and every shard run. A change to any
of them could alter the bench while the gate skipped. Add them, and a test that
every local input of the jobs that build and run the bench is in the filter.

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

* ci(edit-accuracy): cover transitive bench inputs in the path filter

The filter test now walks the files the bench actually runs: the bench
entry files, their relative and @hyperframes/* imports, the CLI commands
the bench invokes, and the build scripts, configs and files those builds
read for every package the bench reaches. Globs are matched with
picomatch, the matcher dorny/paths-filter uses.

The walk found 183 inputs the filter missed, including the producer's
frameCapture.ts, build-hf-early-stub.ts and stubs/hf-early-stub.ts, the
engine services behind engine/src/index.ts, and the CLI browser command.
The filter now covers them.

picomatch is not in the no-install Test reachability job, so the filter
test moves to test:scripts.

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

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 01:45:40 +00:00
Ayodele Ayoola 3de4763d53 fix(engine): avoid padding every audio mix input (#5563)
* test(engine): cover unpadded audio mixing and legacy fallback

* fix(engine): avoid padding every audio mix input
2026-10-13 23:37:28 +00:00
James Russo 5a6c0575f8 chore: release v0.8.155 (#5567) v0.8.155 2026-10-13 23:07:51 +00:00
Miguel Ángel 102c14a6c4 fix(engine): stop multi-worker renders hanging on media-heavy pages on Windows and macOS (#5565)
* fix(engine): stop multi-worker renders hanging on media-heavy pages on Windows and macOS

Off Linux, parallel workers shared one pooled browser. Pages in one browser
share Chrome's six connections per host to the file server, so a page with
many videos and audio clips held all six and the next worker's page.goto
waited until it timed out. Every parallel worker now gets its own browser.

Part of #5548

* refactor(engine): shorten the worker browser comment
2026-10-13 22:48:57 +00:00
Miguel Ángel 96e2154d7c fix(engine): say why WSL renders fall back to software and how to get the GPU (#5560)
* fix(engine): say why WSL renders fall back to software and how to get the GPU

Under WSL the GPU check often falls back to SwiftShader and the only output
was the probe line, with Docker passthrough advice for --browser-gpu. Print
one note that says what was checked (can this user open the render node),
the exact fix, and the native Windows alternative. The CLI's WSL detector
moves to the engine so both use one.

Part of #5548

* fix(engine): keep WSL GPU notes true to the probe result and render mode

The WSL note now prints only when auto mode really selects software, and
says whether the GPU check found no GPU or could not finish. Explicit
--browser-gpu keeps its unverified and Chrome-install advice and adds the
WSL checks. The render-node check verifies group membership before
suggesting usermod.
2026-10-13 22:18:59 +00:00
Jerrai 5de2712ad5 feat(producer): apply the studio encoder options to the render server (#5550)
Use parseEncoderOptions from @hyperframes/studio-server in /render and
/render/stream so the producer server takes the same crf, videoBitrate,
videoFrameFormat and hdrMode as the studio render route, with the same
rules and messages. crf and videoBitrate are forwarded to the render job
and are ignored for mov; a bad value is a 400 (an SSE error event on the
stream route). The studio-server package gains an encoder-options subpath
export so the producer imports the helper instead of copying it.

An hdrMode that disagrees with outputDynamicRange is still rejected, as
before, so there is no precedence to resolve between the two.

— Jerrai
2026-10-13 22:10:26 +00:00
James RussoandClaude Opus 5.5 401354cd8f fix(engine): keep the rightmost 8 columns of SDR MP4 at widths like 1080 (#5559)
#4564 routes SDR colour through `format=gbrp`. Without accurate_rnd,
swscale's yuv-to-gbrp conversion leaves the last 8 columns black when the
width is 16n + 8 (1000, 1080, 1112...), so every 1080x1920 portrait MP4
since 0.8.81 has had an 8 px black strip on the right. The same step in
SDR jpg frame extraction blanked the right edge of footage at those widths.

Set accurate_rnd on the yuv-to-gbrp scale in both chains. It is also the
more accurate rounding: on a testsrc2 frame, worst luma error against a
float reference drops from 1.85 to 0.90 codes.

Fixes #5551

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 21:50:09 +00:00
James RussoandClaude Opus 5.5 bf4cf860fb fix(producer): keep the requested CRF in the distributed cfr re-encode (#5557)
The opt-in `cfr` step in assemble() re-encoded every mp4 at libx264
medium / CRF 18 regardless of how the chunks were encoded, so a render
asked for at CRF 12 (or quality "high", or a target bitrate) came out at
CRF 18 whenever cfr was on. It now reads quality / crf / bitrate from
meta/encoder.json and applies the same rate control the chunks used:
crf, else bitrate, else the quality preset's CRF. Default (standard, no
override) is unchanged: medium / 18.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 20:30:28 +00:00
James RussoandClaude Opus 5.5 ab8a4da53f feat(studio-server): accept crf, videoBitrate, videoFrameFormat and hdrMode on the render route (#5549)
* feat(studio-server): accept a crf override on the render route

The Studio render route only forwarded draft/standard/high, so a Studio or
desktop export could not ask for a specific CRF the way `hyperframes render
--crf` can. Film-grain finishing passes need CRF 12 to keep the grain.

The route now takes an optional integer `crf` (0-51 for mp4, 0-63 for webm),
rejects an out-of-range value with 400 instead of dropping it, ignores it for
mov, and both Studio adapters pass it to the render job.

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

* chore(studio-server): keep the crf change free of new comment lines

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

* feat(studio-server): accept videoBitrate, videoFrameFormat and hdrMode on the render route

Same options `hyperframes render` already takes (--video-bitrate,
--video-frame-format, --sdr/--hdr), so Studio callers are not limited to
the quality presets. crf and videoBitrate are an either/or, matching the CLI;
an invalid value is a 400. Parsing moves to helpers/encoderOptions.ts.

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

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 20:09:58 +00:00
Xuanru Li 8147f7b940 chore: release v0.8.154 (#5545)
* chore: release v0.8.154

* ci: give the studio test job fifteen minutes

The queue run installs and builds inside the same ten minute cap.
It was still inside the studio tests when that cap cancelled the job.
v0.8.154
2026-10-13 19:23:45 +00:00
Xuanru Li 6f76b30675 fix(inspector): stop audio waveform caches from making every live question stale (#5536)
* fix(inspector): stop audio waveform caches from making every live question stale

* fix(inspector): drop the comment and format the test

* refactor(core): share the folders Studio's preview ignores

* fix(core): register the project-folders subpath and bundle it into the inspector
2026-10-13 16:01:05 +00:00
Miguel Ángel 05786f5558 chore: release v0.8.153 (#5541) v0.8.153 2026-10-13 15:19:09 +00:00
Miguel Ángel c96099d39a fix(cli): bound checks of non-finishing compositions (#5537)
* fix(cli): bound checks of non-finishing compositions

* fix(cli): account for cold startup in deadline witness

* fix(cli): wait for profile owners before test cleanup

* test(cli): separate portable deadline and Linux process witnesses

* test(cli): preserve per-launch browser profile ownership

* fix(cli): start check deadline after browser readiness
2026-10-13 13:49:05 +00:00
Xuanru Li e8eb358fe2 chore: release v0.8.152 (#5534)
* chore: release v0.8.152

* docs(changelog): correct the v0.8.152 release notes

The summary overstated snapshot retention, history turns, and video state.
It also left out the new artifact paths and nullable keyframe times.
v0.8.152
2026-10-13 07:37:26 +00:00
Xuanru Li 23d1fe5b42 feat(cli): save check, snapshot and render artifacts per revision (#5522)
* feat(cli): save check, snapshot and render artifacts per revision

* fix(cli): keep clean --snapshots inside the project

A symlinked .hyperframes or artifacts folder was followed, so the command
deleted snapshot and check files outside the project.

* test(cli): keep check, snapshot, and render successful when recording fails

A held history or a corrupt output registry warns and leaves the command result alone.

* fix(cli): drop snapshot records when a replaced run is not recorded

A reused --output rewrites the files even when history is busy.
Those records are removed so the old revision does not keep the new pixels.
The preview checkpoint stops waiting after five seconds.
A bad PNG header no longer fails the snapshot.

* fix(studio-server): keep the video-state comment share from rising

The forget helper's doc comment pushed the file above its base share.

* fix(cli): post the preview checkpoint and keep cleanup inside artifacts

A preview checkpoint with no body was sent as GET, so Studio never ran it.
Cleanup now requires the real path to stay under the artifacts folder.
A failed follow-up drop does not warn twice.
The studio-server unpacked budget covers the record-dropping helper.

* docs(cli): shorten the snapshot skill notes

The command examples already show range and composition.
The prose only keeps the output path and the flags that are easy to misuse.

* chore: leave package headroom for artifact cleanup

* test(cli): compare artifact paths portably

* style(cli): format portable artifact assertion
2026-10-13 06:20:31 +00:00
Miguel Ángel fb4189ca72 fix(catalog): match preview length to authored compositions (#5533)
* fix(catalog): match preview length to authored compositions

* fix(catalog): derive undeclared preview duration from motion
2026-10-13 05:51:02 +00:00
Xuanru Li 893b4f6757 feat(studio): record video state, edit targets and artifact cleanup in project history (#5524)
* feat(studio): record video state, edit targets and artifact cleanup

* fix(studio): clear video-state CI gates

* fix(studio-server): pin cleanup reads to file descriptors

* chore: account for cleanup descriptor code in package budget
2026-10-13 05:12:34 +00:00
Xuanru Li f31b3c982b build(inspector): publish @hyperframes/inspector to npm (#5530)
* build(inspector): publish @hyperframes/inspector to npm

* fix(inspector): drop prepack so pnpm pack --json stays parseable

* fix(inspector): add .js to published declaration imports for NodeNext

* fix(inspector): declare @hyperframes/parsers, which the published types import

* chore(fallow): ignore @hyperframes/parsers, which inspector's published types import
2026-10-13 04:55:48 +00:00
Xuanru Li 8e4de38be1 fix(keyframes): report unresolved GSAP timing instead of guessing rest poses (#5526)
* fix(keyframes): report unresolved GSAP timing instead of guessing rest poses

* fix(keyframes): drop later times when a delay cannot be resolved

Boolean(null) treated an unreadable delay as no delay,
so the next cursor-relative tween kept a guessed start.

* fix(keyframes): keep a staggered rest pose in Studio

The parser stops are what Studio holds, so a staggered from keeps its rest
pose there. The CLI reports that pose as unresolved, and an unread position
no longer seeds the next tween at zero.
2026-10-13 04:52:22 +00:00
thegreatLUCYandClaude Opus 5.5 f25cfbd6bd fix(cli): turn off onnxruntime telemetry for Kokoro synthesis (#5425)
onnxruntime's 1DS telemetry, on by default in its macOS wheels, can abort the
Python process at exit when an upload response races teardown. execFileSync
then throws for a line whose WAV was already written, and the audio skill drops
it as non-fatal.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 04:32:45 +00:00
Xuanru Li 7c73b3caee feat(cli): guard history turns with --exclusive, --who and --if-open (#5518)
* feat(cli): guard history turns with --exclusive, --who and --if-open

* fix(cli): make exclusive history turns atomic

Re-read the open turn only after acquiring history ownership so
concurrent exclusive begins cannot both claim the same project.
Cover the race with a deterministic two-process regression test.

* fix(cli): refuse a second exclusive turn while a preview is open

The preview's begin window now rejects another agent's live turn, and an
idle turn no longer blocks exclusive begin. The two-process test waits on
pids collected from claim drafts instead of seeing both files at once.
2026-10-13 04:22:41 +00:00
Miguel Ángel 945c61ebb6 chore: release v0.8.151 (#5529) v0.8.151 2026-10-13 04:13:03 +00:00
Märten Tropp 63f32fefa9 docs(skills): fix frame-worker contract, lint codes and stale references (#5515)
* docs(skills): fix frame-worker contract, lint codes and stale references (#5467)

* docs(skills): use placeholder paths so comment citations resolve (#5467)
2026-10-13 04:12:09 +00:00
Austen StoneandCopilot App 1b2a629121 ci: skip re-running checks for PR title and body edits on a passing commit (#5488)
A title or body edit fires ci, regression and windows-render again on the
same head commit. When the previous run of that workflow on the commit
passed, Detect changes now reports no changes, so the check jobs skip and
their required checks still pass. Base changes, first runs and runs whose
predecessor failed, was cancelled or is unfinished still run everything.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-10-13 04:10:40 +00:00
James RussoandClaude Opus 5.5 51e0323af1 fix(core): a render keeps a fromTo's from-values when a frame lands just after its start (#5506)
* fix(core): a render keeps a fromTo's from-values when a frame lands just after its start

The frame redraw steps to 1 ms below the frame and back. When a tween starts
inside that 1 ms, the step down reverts its from-values and the step up does
not reapply them, so a wipe bar whose opacity comes only from its from-vars was
never drawn. Reapply them for every tween whose start the step crossed, as
#5125 does for a tween starting exactly on the frame.

Adds the gsap-redraw-start-and-overlap producer fixture, which also pins that a
slow drift resumes after a short move on the same property, as in playback.

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

* fix(core): keep the redraw's comment share and schedule the new fixture

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

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 04:00:45 +00:00
Xuanru Li 1b60a40eff feat(cli): HF_TOOL_CONTEXT artifact events and project lock for agent harnesses (#5520)
* feat(cli): HF_TOOL_CONTEXT artifact events and project lock for agent harnesses

* fix(cli): serialize stale project lock takeover

Use a short-lived break lock so only one waiter can remove a dead
holder's lock. Cover the contention path with six real processes.

* fix(cli): hold snapshot lock through finalization

Keep the project lock while contact sheets, artifact events, and frame
descriptions read captured files so another snapshot cannot replace them.

* fix(cli): recover a project lock after the breaker crashes

A killed takeover left project.lock.break in place, so later commands
waited out the timeout even though both holders were dead. Reclaim that
file only when its recorded process is dead, and compare artifact paths
after the platform resolves them.

* fix(cli): accept a killed breaker process with no signal name

Windows ends the process without reporting SIGKILL, so the regression
was failing before it checked that the leftover breaker can be reclaimed.

* fix(cli): leave a killed project-lock breaker in place

Reclaiming a dead breaker unlinks a name another process may just have
created, so two commands can hold the lock. The timeout names that file
and its pid and says to delete it to continue.
2026-10-13 03:55:36 +00:00
Yash Rastogi b75fa6d996 test: add tests for audio_reactive_single_tween_per_group and gsap_group_selector_keyframes (#5404)
Resolves #5390
2026-10-13 03:39:50 +00:00
Yash Rastogi 33cffbf06b test: add coverage for slideshow_unresolved_ref and subcomposition_root_styled_by_class (#5399)
Resolves #5391
2026-10-13 03:39:34 +00:00
Xuanru Li f09770385e fix(core): resolve composition timing without browser CSS (#5525)
Match composition timing references by their attribute value so Node-side
snapshot sampling does not require the browser CSS namespace.
2026-10-13 03:16:22 +00:00
Xuanru Li 36526bf1a6 feat(cli): scope check to a time range or a set of components (#5516)
* feat(cli): scope check to a time range or a set of components

* fix(cli): keep ranged check gates truthful

Refuse ranged caption and frame gates when none of their seek times
falls inside the requested range. Make ranged motion reporting,
overlap timestamps, filtering tests, and scope documentation match.
2026-10-13 03:13:26 +00:00
James RussoandClaude Opus 5.5 69f3306415 feat(cli): check warns when a tween ends just after a later tween on the same property (#5521)
A GSAP tween that outlives a later tween on the same target and property by a
moment takes the property back for that tail and leaves its own end value on
screen. A banner word whose drift ended 20 ms after its exit stayed in the
middle of the frame for the rest of the scene, in playback and in renders.
hyperframes check now reads every tween off the live timelines and warns on
that shape (tail up to 0.25 s), with the fix: end the earlier tween first, or
add overwrite: "auto" to the later one. Longer tails, like a slow drift that
resumes after a short move, are left alone.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 03:00:17 +00:00
Xuanru Li 072f53cda9 feat(inspector): element map, live inspection engine and Node runtime (#5509)
* feat(inspector): element map, live inspection engine and Node runtime

* fix(inspector): reject preview reload races

Return the live document generation from the same page invocation as each
query and reject mismatches. Rearm browser cleanup for failures before a
page operation starts, and cover reloads across all three live operations.
2026-10-13 02:45:46 +00:00
Xuanru Li f45788bb6d feat(inspector): query timelines and inspect the live preview (#5508)
* feat(inspector): query timelines and inspect the live preview

* fix(inspector): align live timeline contracts

Keep realm-safe target detection through the restack and align
floating-point interval boundaries. Update the live group fixture and
make the private runtime entry build itself before use.

* fix(inspector): declare generated build inputs
2026-10-13 02:45:45 +00:00
Xuanru Li 73bbd378df feat(inspector): add the component inspector package (#5507)
* feat(inspector): add the component inspector package

* fix(inspector): satisfy workspace and runtime gates

Declare the source-only package build boundary and use realm-safe element checks so the inspector passes workspace, type, and runtime contracts.

* fix(inspector): harden source file reads

* fix(inspector): satisfy audit gates

* fix(inspector): parse HTML raw-text endings
2026-10-13 02:45:44 +00:00
Miguel Ángel f073281bd9 fix(producer): stop JPEG frame capture adding colour noise to graded footage (#5514)
* fix(producer): stop JPEG frame capture adding colour noise to graded footage

Standard and high renders captured page frames as JPEG at quality 95 (the
probe session and distributed chunks at 80). Chrome's encoder turns graded
colour and grain into 8x8 chroma blotches at those settings, and they survive
the final encode. Capture now uses quality 100 for standard and high, from one
owner that the in-process render, the probe session and distributed chunks
all read. Draft keeps 80. The disk estimate for JPEG frames doubles to match
the larger files.

* fix(producer): key resume and disk estimate on the capture quality

Segment resume now fingerprints the capture jpeg quality, so a resume
across a quality change re-renders instead of splicing old-quality
segments. The disk estimate allows for larger frames at any jpeg quality
above draft's 80 instead of only at exactly 100.

* fix(producer): keep segment manifest comment share at base

* fix(producer): fit capture quality comment in four lines
2026-10-13 02:36:31 +00:00
Xuanru Li 1cd086f7e3 feat(cli): snapshot a time range or one composition (#5519) 2026-10-13 02:12:54 +00:00
Xuanru Li e9216456a5 fix(cli): resolve the render worker's tsx loader from the CLI install (#5517) 2026-10-13 01:42:22 +00:00
Leonel Rivas 9713e93a52 fix(lint): read a sub-composition's linked stylesheet from the project root (#5084)
A sub-composition that links a shared stylesheet with a plain path like
assets/fonts.css was only looked up next to its own HTML file, so lint
reported font_family_without_font_face for fonts that stylesheet declares.
The ../ spelling is rejected by invalid_parent_traversal_in_asset_path, so
no spelling passed.

Lint still looks next to the sub-composition first and now falls back to
the project root, which is where the preview and the bundler load the link
from. Links that start with / or contain a .. segment keep their old lookup.
A folder with the stylesheet's name is skipped instead of throwing EISDIR.

Fixes #5074
2026-10-13 01:28:07 +00:00
Diego López c8e105dc92 fix(engine): encode and tag ProRes mov output as BT.709 (#5382)
* fix(engine): encode and tag ProRes mov output as BT.709

* test(engine): read the ProRes colour tags from the first frame

* test(engine): end the ProRes tag probe's options with --
2026-10-13 00:07:44 +00:00
Meghna VasudevaandClaude Opus 5.5 b100ff16f3 refactor(core): share keyframe property defaults between SDK and Studio (#5438)
Move the keyframe rest-value table into @hyperframes/core/keyframe-defaults
and import it from the SDK backfill and Studio's gsapShared, replacing the two
hand-synced copies.

Closes #5387

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 00:07:40 +00:00
1d2aa5240d fix(media-use): transcript-cut video renders without repeated frames or a keyframe warning (#5463)
* fix(media-use): transcript-cut video renders without repeated frames or a keyframe warning

transcript-cut wrote each kept range to a temporary .mkv, and ffmpeg
writes Matroska timestamps in whole milliseconds. A 30 fps frame that
belongs at 2/30 s ended up at 0.067, the final copy kept it there, and
the renderer showed the previous frame wherever a timestamp was late.

The temporary files are now .mov, which holds the frame times exactly
and still carries PCM audio, so the audio is encoded once, as before.

The cut also gets a keyframe every second. x264 on its own left them
several seconds apart, and the renderer warned about the footage this
script had just written.

Fixes #5417

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(media-use): transcript-cut ranges start on their first frame with any ffmpeg

ffmpeg 7 and later start a kept range that begins between two source
frames one frame late, and .mov rounds that lead-in to the millisecond,
which put the frames off the grid again. ffmpeg 6 did not do this, so
the tests passed locally and failed in CI.

Each temporary file is now written at a constant frame rate, so the
first frame of a range is at zero whichever ffmpeg runs it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: James <james.russo@heygen.com>
2026-10-13 01:23:05 +01:00
Diego López 3bf9035d2a fix(cli): say when the Docker image build times out instead of blaming Docker (#5394) 2026-10-13 00:07:37 +00:00
b71c9f4e1b fix(media-use): keep frame precision on transcript-cut seek boundaries (#5426)
* fix(media-use): keep frame precision on transcript-cut seek boundaries

Fixes #5416.

A kept range whose boundary sits on a frame got rounded to the millisecond
twice on its way to ffmpeg, and at 29.97/30/60 fps that is enough to land just
past the frame: frame 386 at 30 fps is 12.866666 s, and -ss 12.867 starts that
segment on frame 387 instead. ffmpeg's accurate seek drops the frame, so the cut
came out a frame short from that join on and the picture drifted behind the
sound.

cutlist.mjs rounded the emitted range bounds to 3 decimals, so fmt() in
transcript-cut.mjs never saw more precision than that in the first place; fixing
only fmt() leaves the bug in place. Both now keep frame precision -- 6 decimals
in the cut list, full precision for the -ss/-to actually handed to ffmpeg --
while the 3-decimal fmt() is kept for the times that are only printed.

Measured at 30 fps: a cut of 0-1,1.0666666-3 produced 87 frames before and 88
after (frames 0-29 plus frames 32-89), with no timestamp gap at the join. 25 fps
is unaffected, as the issue notes.

* test(media-use): end ffprobe options with -- in transcript-cut test

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

---------

Co-authored-by: James <james.russo@heygen.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 01:08:20 +01:00