Chrome's profile lockfile is held for a moment after the browser exits, so removing the
capture profile threw EBUSY and a finished capture exited 1. The profile and staging
removals now go through a core helper that retries transient locks and, for cleanup of
finished work, warns instead of failing.
* fix(core): audio clips join on time and their volume lane follows the audio
An incoming <audio> clip is now pre-seeked to its in-point like a video, and
a playing audio element that drifts is steered by rate instead of seeked
while it sounds. A routed clip's volume lane and fades are re-aimed at the
element's own position whenever it starts, is seeked or changes rate.
* fix(core): sounding audio is nudged at most 10%, and a larger lag seeks under a short dip
Speech audibly changes tempo past a 10% rate nudge, and a bare seek clicks.
Audio that holds a lag rate cannot close in about a second is seeked under a
15 ms dip on its routed graph; an unrouted element is seeked plainly. Video
keeps its steps.
* fix(core): a routed clip's automation has one owner, its element, looping included
A rate change re-aimed a routed clip's lane on the context clock and again
when the element took the new rate. Only the element re-aims it now. A
looping element's wrapped position is placed on the pass the transport is in,
so a looping bed's lane follows it too.
* style(core): wrap a steering comment to the line limit
* refactor(core): one drift policy for playing media, exported for the parent proxy
The steering steps, the audio cap and limit, and the steering state move to
runtime/mediaSteer.ts as steerMedia(el, offset, baseRate, settled) and
forgetSteering(el), exported as @hyperframes/core/runtime/media-steer so the
player's parent-frame proxy uses the same policy instead of a second one.
* style(core): trim the drift policy's comments to the package's share
* fix(core): the dipped seek lands on the frame-start media time
Routes the one currentTime write this branch adds through mediaElementTime,
so Chrome's truncated currentTime cannot land it a frame early.
* test(core): expect the plain audio seek at the frame-start media time
* fix(render): a PNG's colour tags no longer change its pixels in preview, check, snapshot or render
* fix(render): the play and present server also serves a PNG without its colour tags
* fix(render): bundled pages inline a PNG without its colour tags too
* feat(core): one function gives the media time that shows the frame starting at an instant
* fix(core): paused preview and render show the same, correct video frame
* fix(core): register the media-clock subpath in its descriptor and keep the reason beside the test
* fix(core): media seeks lead by 10 us so Chrome's timestamp rounding never shows the prior frame
* fix(core): generated pages lead media seeks by a literal, on every generator branch
* feat(studio-server): a copy being made says how far it has got, and can be retried
* feat(core): the preview reports its lighter copies' progress and retries failed ones
* feat(studio): the preview quality and its lighter copies' progress live in the player store
* feat(studio): every host forwards the preview quality, and the choice is kept per computer
* feat(studio): the preview quality knows if a person or the app chose it
* fix(core): the first report of the lighter copies is not held back by the throttle
* test(studio): the preview quality preference test writes under the real key
* fix(core): a quality change reports its copies at once, not after the throttle
* style: format the preview quality files
* feat(core): report how playback is going, and what the preview holds, to the host
* fix(studio): the preview quality levels come from a browser-safe core entry
* style: format the new entry's files
* style: drop two comments the file's ratchet and the citation check refuse
* feat(core): tell which lower levels would give a video a lighter copy
* fix(studio): drop the old level's playback health when the level changes
* feat(studio-server): preview facts carry the probed codec of every video
* refactor(studio): route the preview's reports through a table for the complexity gate
* fix(core): a copy's retry flag is sent on the first ask only, not on every poll
* fix(core): type the health report and restore the player hook's complexity directive
* style: cut four comments the comment ratchet refuses
* fix(studio): keep playerStore.ts within its 600-line cap
* fix(core): a scene mounting does not restart the playback health window
* fix(core): the playback health window restarts only when the display scale changes
* fix(core): a repeated display scale no longer restarts the playback health window
* fix(studio): route the preview's reports by an explicit list of message types
* perf(core): Safari stops re-serializing inline styles on every GSAP write in a playing film
* perf(core): declare the clip-reveal subpath in its descriptor and cut added comment lines
* fix(shader-transitions): shader transitions across a scene boundary keep the scene's contents
Every revealer now goes through one core helper that shows the clip and
lifts the runtime's hidden mark together; the page-side compositor's scene
copies were still carrying the mark. The contents rule keeps its old weight.
* fix(core): films on an older shader-transitions build keep their transition scenes
Published shader builds reveal scenes by inline style without lifting the
hidden mark. When one is on the page, the runtime keeps the inline-style
rules; builds that lift the mark declare it on load and keep the fast rules.
* test(core): pin the hidden-clip contents rule's weight on its selector
* style(core): format the reveal test and move hidden-clip rule notes into names
* test(producer): pin hidden-clip behaviours in real Chrome under the mark-keyed rules
* test(producer): read the forced restyle's result so lint accepts the restyle test
* fix(cli): reap only orphaned browsers HyperFrames launched
Orphan cleanup on preview and render killed every PPID-1 process of the
current user whose args named chrome-headless-shell or a Puppeteer temp
profile, which included detached browsers started by other tools.
Every HyperFrames Chrome launch now passes an inert
--hyperframes-owned-browser switch, and the reaper only kills PPID-1
processes that carry it as a whole argument.
Fixes#6203
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(core): serve the browser marker from its own subpath
The GCP image's BeginFrame contract step imports the engine's
browserManager straight from source before core's generated runtime
exists, so pulling the marker from the core root entry broke it.
A dependency-free @hyperframes/core/browser-marker subpath keeps that
import chain as light as it was.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(cli): expect the browser marker in the capture fallback launches
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* docs(cli): say orphan cleanup runs before preview and render
The cleanup only stops browsers HyperFrames launched, so the opt-out is
no longer needed to protect other tools' browsers.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat(studio): motion blur panel with Off / Subtle / Strong
One three-way control in the right dock. Subtle is the default blur
(240 deg shutter, samples sized to the smear); Strong is 720 deg and 64
samples. Writes the root data-motion-blur attributes through the SDK so
renders match and undo works; reads live attributes from the preview.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* fix(studio): motion blur panel edits the file's own root
The edits were worked out against the preview DOM but written to the file,
so a stale preview (undo inside the hold, a slow reload) could save a level
other than the one picked. They are now computed inside the mutation from
the candidate's root. While locked to a sub-composition no level is shown.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* fix(studio): shorten the motion blur tab so the dock strip fits
Five tabs overflowed the default dock width by 10px and clipped a label
(Studio: load smoke). The tab is now titled Blur.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
html-in-canvas captures a few things wrongly, and a wrong blurred frame is
worse than a sharp one. Add a defect list the runtime checks before it wraps
a composition: a WebGL or WebGPU canvas (captured frozen), backface-hidden
faces in a 3D context (drawn mirrored), an iframe (reloaded by the wrap and
not recoverable), a transform or opacity below 1 on the root (ignored by the
capture) and a canvas that is already a capture canvas (nesting hangs the
renderer). Any hit turns blur off for the whole composition and the runtime
says which one.
The backface scan reads rules nested inside a style rule (CSS nesting),
sheets pulled in with @import and adopted style sheets. Shadow DOM stays a
documented limit.
A face whose backface-visibility: hidden arrives through an animation counts
too: the scan also takes the elements of running CSS or WAAPI animations that
key it, and elements naming a @keyframes rule that does, as candidates.
Support Chrome 157's html-in-canvas API alongside the older one: a canvas
marked content="drawable" with a drawable attribute on captured children
replaces layoutsubtree. Blur, the VFX runtime and Studio's browser check
detect both through the new @hyperframes/core/html-in-canvas subpath.
Folded review fixes: R14, R9 (the iframe defect entry).
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* chore: release v0.8.182
* chore: list #5953 in the v0.8.182 notes
* chore: add #5843, #5932 and #5945 to the v0.8.182 notes
* chore: add #5923, #5943, #5956, #5960, #5961, #5963, #5964 and #5966 to the v0.8.182 notes
* chore: add #5972, #5977, #5981 and #5982 to the v0.8.182 notes
* chore: add the 13 PRs merged since the last refresh to the v0.8.182 notes
* chore: add #6003, #6005 and #5579 to the v0.8.182 notes
* chore: add #6006 to the v0.8.182 notes