* feat(engine): composite a source video inside the streaming encoder
* feat(engine): opt-in flag and rule for compositing a lone full-timeline video
* feat(producer): opt-in video layer passthrough for a lone full-canvas video
* feat(producer): launch the probe, drop its page and capture PNG when passthrough is on
* refactor(engine): trim the passthrough doc comments to one line
* fix(producer): refuse passthrough when a wrapper or sub-composition paints a background
* chore(producer): mark the in-page layout check as one deliberate unit
* fix(engine): a video left out of capture no longer blocks static-frame dedup
* refactor(engine): shorten two long comments in the static-frame eligibility scan
* feat(studio): a host that draws its own loader can turn off both of Studio's preview loaders
Player and NLEPreview take loadingUi: 'none', which hides the composition cover
and the 'Preparing preview assets' wait while still reporting that the preview is
loading. suppressLoadingOverlay keeps its narrower meaning: skip only the
composition cover on a later load.
* refactor(studio): name who draws the loader in the value, loadingUi 'studio' | 'host'
The value says the host draws its own loader, so no doc line has to tell it apart
from suppressLoadingOverlay, and the files' comment share does not rise.
* refactor(studio): the loader choice sits outside Player, the stage size beside NLEPreview
Player's draw decision moves to a small helper so the component's complexity does
not rise, and resolvePreviewStageSize moves to its own file so NLEPreview stays
under the 600-line cap with the new prop.
* refactor(studio): Player reads the host's loader choice without another destructured prop
Destructuring one more prop raised Player's cognitive complexity above main's; the
rest of the props carries loadingUi instead.
* refactor(studio): NLEPreview's stage constants live in one small module
* style(studio): format NLEPreview's stage import
* feat(core): WebKit plays bare audio from a decoded buffer on the audio clock
* fix(core): a handed-over WebKit track goes back to its element when the speed changes
* test(player): play the WebKit audio edits in real Safari on a macOS runner
* fix(core): Safari keeps playing edits' audio without seeks or rate changes
* fix(core): keep the 0.1 s audio sync bound outside WebKit
* fix(core): restore main's audio steering outside WebKit
* fix(core): tell WebKit from the test DOM's user agent
* fix(core): name WebKit's audio start lag instead of commenting it
* fix(core): count embedded WKWebViews as WebKit for audio sync
* fix(cli): snapshot gives a seek that will not settle its own --timeout, not three minutes
* fix(cli): a seek budget never outlives the protocol timeout; say so in --timeout help
* refactor(engine): one builder for the video frame filter chain
* refactor(engine): keep the filter chain builder module-private until it has a caller
* ci: split the cli test job into two shards so each stays well under its limit
* ci: map the sharded cli test run in the reachability audit
* ci: the reachability audit treats --shard as selection-neutral instead of a pinned mapping
* ci: map the sharded cli test run in the reachability audit (pinned), not a blanket --shard rule
* fix(producer): a failed video extraction names which clip files failed
* fix(producer): the extraction source label hides scheme payloads and drive paths
* style(producer): format the extraction source label
* feat(core): the playback health reading says which clock the film followed
* style(core): format the clock's new reading
* refactor(core): let the clock's names say what the comments said
* fix(cli): billing and Desktop handoff read their hosts from settings
* fix(cli): OAuth client and API hosts read from settings
* test(cli): host and client settings tests run the same on any machine
* chore(cli): format and refresh the skills manifest
* chore(cli): trim a comment
* chore(cli): keep the stylesheet as it was
* fix(engine): scale static-frame verification budget with the job
* fix(engine): keep comment share flat
* fix(engine): drop added comment
* fix(engine): time seek in verify budget, state the policy
* feat(cli): measure ink reads text size, --out and --output both work, logo --variant wordmark
measure ink reports the bounds, size and lines of text in a region of one frame, so a rebuild can be sized without a script. measure and snapshot take --out and --output for the same folder. media-use resolve --type logo --variant wordmark returns the brand's wordmark when theSVG has one.
* fix(cli): measure ink stays under the complexity and comment limits, one projectOf
* fix(cli): snapshot and measure also accept --output-dir
* fix(cli): projectOf moves without adding comment lines
* fix(cli): measure output alias list type-checks as an argument
* fix(cli): a logo variant never answers a default logo request; test output flags
* style(cli): format the new tests
* fix(producer): render telemetry reports beyond-viewport capture as the workers used it
* fix(producer): apply the sessions' beyond-viewport value at the summary, on every capture path
* chore(engine): drop a comment so the types file's comment share does not rise
* fix(cli): one cut detector that tells real cuts from flashes and fast moves
* fix(cli): a flash must come back within a few frames; trim comments, format
* test(cli): the cut fixture gets a 60 s timeout; format
* test(cli): drop a history comment
* fix(cli): cloud render uses the composition's data-fps and skips unused heavy files
* fix(cli): satisfy typecheck and comment ratchet for cloud render fps
* fix(skills): paid-plan cloud final keeps the composition fps and high quality
Follow-up to #6884 from its review: the cloud command agents copy had
no --fps, so a 24 or 60 fps composition rendered at 30, and cloud's
default `standard` is below the local `delivery` final. Step 5 and
cloud.md now spell out `cloud render --fps <data-fps> --quality high
-o <path>`; the render table uses `-o` to stay within the entry's byte
budget and restores the "Hosted, no local Chrome or FFmpeg" row.
— wanbi
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(skills): pin --fps and --quality high on the paid-plan cloud final
Review on #6886: a later byte-budget trim of hyperframes-cli/SKILL.md or
cloud.md could drop the flags silently. The test asserts both files'
paid-plan command reads `cloud render --fps <data-fps> --quality high -o …`.
— wanbi
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
When `auth status` shows a paid HyperFrames plan, the render the person
receives goes through `hyperframes cloud render -o <path>`; authoring
checks stay local, Free keeps local renders unless the person asks, and
a refused or failed cloud render falls back to a local one with the
reason. reverse-any-video, which already requires a paid plan, now
renders and diffs the cloud output; the detach guidance stays for the
local fallback.
— wanbi
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(cli): stopping remove-background on a Mac no longer aborts the process
* fix(cli): a quit after background removal released its session exits at once
* Revert "fix(cli): a quit after background removal released its session exits at once"
This reverts commit 3b9d6ffc24.
* fix(core): a seek that never settles ends after 30 s and names what it waits on
* fix(core): keep the seek-wait comments within the comment limits
* fix(core): only a caller's limit ends a seek wait; check and snapshot end it before their timeout
* fix(core): style a sub-composition root written as #root[data-composition-id]
* fix(core): keep an anonymous host's root styled when #root[data-composition-id] is scoped
* fix(core): scope a compounded authored root as a selector list so pseudo-elements stay valid
* fix(producer): auto renders with color-graded media stay SDR instead of promoting to HDR
* fix(producer): keep auto renders SDR when a graded canvas is present
* fix(core): Safari stops chopping audio by seeking it over and over during playback
* test(player): the WebKit playbench also counts audio seeks and frozen audio
* fix(core): audio still catching up is seeked once its lag passes hard sync
* fix(core): motion blur preview no longer slows down later in the timeline
With data-motion-blur-events="on", which Studio's Subtle and Strong write,
both blur tiers ran timeline callbacks at every per-frame pose read. Each
frame's reads step back from half a shutter ahead to the frame, so a
callback that keeps state and replays from zero on a backward seek (a
ripple simulation, for example) redid its whole history most frames, and
the cost grew with the playhead.
The mover scan still runs callbacks, so motion set from a render(t)
callback is still found. With events on, it also scans once without them,
and the per-frame reads run callbacks only when the two scans disagree.
The eventful scan runs first, so tweens take their start values after the
callbacks before them and a one-off call fires as often as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(core): keep the motion blur comments to the invariant
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(core): motion blur reads run callbacks when a callback moves any box
Comparing which elements each scan finds missed a callback that pans a
mover's ancestor while a tween also changes that ancestor: both scans
found the same movers and the same changing elements, so the reads left
the pan out. The scans now compare every changing element's box at each
step, and the reads run callbacks when any step differs.
Raises @hyperframes/core's unpacked size budget to the measured size plus
the usual 5%: main already sat 0.98% over the old budget, and this change
adds 1,232 bytes, which tipped it past the 1% tolerance.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(core): silent motion blur reads land back on the frame as its own seek left it
After silent pose reads, the restore seek back to the frame left GSAP in a
different state than the frame's own eventful seek did:
- A step exactly on the frame (a .call() or set) was re-armed, so the next
frame fired it again. Calls fired 2-5 times per crossing, and a toggle
could end inverted.
- In renders, which keep no GSAP structure, landing back on the frame from
later skipped same-time steps' authored order, so the frame showed the
first of two sets instead of the last.
Events-off blur always read silently and had both. This PR moves events-on
roots without callback-driven motion (the Studio default) onto that path,
so both are fixed here.
The host's new holdFrame snapshots every landing mark on the root and scene
timelines before the reads, and its restore seeks back silently, redraws in
authored order where there is no structure, and puts the marks back. Each
call now fires once per frame crossing, as without blur, and same-time sets
land as authored.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(core): a held render frame draws its scenes first, so the root lands last
holdFrame redrew the root and then the scenes, the reverse of the frame's
own seek. When a scene and the root tween one property, the frame landed
on the scene's value instead of the root's.
The hold and landing move into the GSAP adapter as holdFrameMarks, which
takes the scenes and the root separately, so the order lives in one place
a real-GSAP test can pin.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(cli,skills): say a step's credits are its total, and keep cost out of retries
An out-of-credits refusal named what the step needs, and an agent read
that as the shortfall. It now says the number is the step's total.
After its last try, an agent told the person what was spent and what
another try would cost. The HeyGen reference now says not to.
* fix(cli): plan avatar credits from the HyperFrames price rows
An avatar's credits came from its old API price, about five times what
the plan charges, so --max-credits refused calls that fit and a top-up
asked for too much. They now use the Avatar IV photo row plus the
general avatar video row, 950 credits a minute.
* fix(cli): pin the avatar price test and keep pricing under the comment share
* fix(cli): plan avatar seconds on the Avatar IV row alone
The general avatar row is only checked at admission and billed on
seconds left after Avatar IV, so adding it to every second made a 15 s
script plan at 238 instead of 200 and a 220 limit refuse a call that fits.
* fix(cli): the credits link opens the web plan page, which is live
* test(cli): pin the production credits link too
* chore(skills): regenerate the skills manifest after merging main
* docs(cli): keep the person measure's doc comments to four lines
* docs(cli): keep the measure doc comments to four lines
* docs(cli): keep the measure comment lines to 120 characters
---------
Co-authored-by: Miguel Angel Simon Sierra <miguel.sierra@heygen.com>
* fix(lint): the podcast-lanes.html page Desktop writes is not a second root composition
HyperFrames Desktop's podcast edit writes its camera lanes page, podcast-lanes.html,
at the project root beside index.html: Studio reads no composition inside a dot
folder, and Desktop opens the page directly as its own view. index.html never mounts
it and no render plays it, but multiple_root_compositions fails lint and check on
every podcast project, and its fix hint tells an agent to remove the file. Exempt it
the way caption-skin.html already is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* refactor(lint): name the pages opened beside index.html instead of commenting them
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(core): audio that decodes short plays from its element in preview
* fix(core): keep the decoded copy when the element's duration is unknown
* fix(player): preview pauses with Buffering until every track due can play
* fix(core): keep the transport awake while it buffers
* fix(core): wait only for tracks the clock would also treat as playable
* test(core): buffering fixture pauses like a browser, with a pause event
* fix(core): hold for a clip only after media sync has asked it to play
* test(core): hold lands on the frame a slow tick skips into a clip with no data
* fix(core): a held clip counts as ready once it has data buffered ahead
Make a video, Studio app and Developers move into a Docs menu (each keeps its
own sidebar), with Free lessons, Community and the app changelog under them;
Catalog stays a tab. The top-bar copy of Ask Assistant goes (Ask stays in
search and the page's bottom bar); the logo opens the homepage; the button
reads Download.
* feat(core): playback health reports how long a video on screen showed no moving picture
* fix(core): a frozen picture is judged by the clip's own timing, not the film's
* style(core): shorten a test comment under the line limit
* fix(core): the frozen-picture check reads a video from any realm
* refactor(core): cut the frozen-picture comments the comment ratchet counts
* perf(studio): a long timeline no longer lays itself out on every render while it settles
* perf(studio): compare row data with sameData and drop the added comments
* fix(lint): a timeline registered in a local script file counts as registered
* fix(lint): a module script file is read as a module
* style(lint): format the module script check