Commit Graph
6090 Commits
Author SHA1 Message Date
heygen-rui-bot beec102ea9 codegen: resync cloud client from EF 85e0bb58 2026-10-25 22:52:01 +02:00
Miguel Ángel 0c169e2c56 perf(producer): fast capture stays on for frames a blend mode does not touch (#6330) 2026-10-25 20:24:49 +00:00
Miguel Ángel c163330a16 fix(engine): the drawElement self-check reads video comps' truth on a separate page too (#6321)
* fix(engine): the drawElement self-check reads video comps' truth on a separate page too

The video-frame injector now hands out a fresh hook with state of its own, so the check can paint the frames on its disposable page without the render page's injector losing track of what it has painted.

* fix(engine): the truth page gets the media render-id bridge the render page has

Videos that share a plain id (a bare video in each of two scenes) are told apart by a render id the bridge resolves; without it the truth page painted the wrong element.
2026-10-25 19:57:09 +00:00
Miguel Ángel d7efd809f6 feat(producer): record video frame extraction time in the chunk perf sidecar (#6329)
* feat(producer): record video frame extraction time in the chunk perf sidecar

* docs(producer): say videoExtractMs overlaps capture, not the boot
2026-10-25 19:52:49 +00:00
Miguel Ángel 70187a13b5 perf(core): the preview mounts and draws only the scenes near the playhead (#6189)
* perf(core): skip rendering preview scenes far from the playhead

* refactor(core): split the far-scene pass into small helpers

* perf(core): a first render reveals only the clips it reaches

* fix(core): reveal the clips holding first-rendered tweens, wherever their window is

* feat(core): a scene may load a project helper and wait for fonts and still swap

* perf(core): the preview mounts only the scenes near the playhead

* perf(core): a seek waits only for the scene it lands in; neighbours mount one per turn

* feat(studio): selecting a clip in an unmounted scene mounts that scene first

* fix(core): a source a play and a seek both need is fetched and decoded once

* fix(core): a scene mounting during play joins the mix without restarting what plays

* fix(studio): selecting a clip waits on a scene mount only in a preview that unmounts scenes

* fix(core): far scenes still mount after an edit, and an edit to a parked scene keeps its styles

* perf(core): web audio schedules a clip only as the playhead comes near it

* chore(studio): trim the scene hold helper's comments to its package's share

* fix(core): a scene the preview unmounts is freed, not kept by audio, caches or old roots

* fix(studio): an unmounted scene's rows and their state come from the markup it mounts with

* perf(core): the preview mounts scenes ahead of play and keeps recent ones mounted

* refactor(core): keep the far-scene neighbour rule private to its module

* style(core): format the far-scene neighbour rule

* perf(core): the preview unmounts scenes the playhead leaves, as kept ones slowed each mount

* fix(core): scenes that measure text, call their own animate or wait on font loads can unmount

* fix(core): each preview scene carries the helpers it loads, so it mounts on its own

* fix(core): a mounted scene shows though its stand-in was hidden by display

* fix(core): a helper scenes share runs once for the film, and a remounted scene keeps showing

* fix(core): a scene that also publishes its timeline under another id stays mounted

* feat(core): scenes that draw in 2D or chain the timeline they register can unmount

* perf(core): a play frame that draws nothing new skips the hidden-clip lookup

* fix(core): a GSAP file copied into a project counts as the library for scene unmounting

* style(core): drop an unneeded escape in the registered-chain pattern

* chore: core and producer size budgets make room for the preview scene window

* fix(core): a scene that draws on a 2D canvas stays mounted

* fix(core): a scene the preview unmounts lets go of timelines it built after a font wait

* perf(core): a scene leaving the playhead keeps its last rendered size, no forced layout at the cut

* refactor(core): the far-scene skip keeps last-render boxes in their own helper

* fix(core): a mounting scene never records the runtime's root timeline as its own

* chore(producer): raise the size budget to the measured size with the preview runtime inlined

* docs(core): keep the far-scene and scene-memory comments within the comment limits

* perf(core): style each hidden clip visible once ahead of its cut, so the cut frame does not

* fix(core): a seek into a scene still mounting as a neighbour waits for that mount

* chore(core): keep comment share and size budgets to main plus the measured core growth
2026-10-25 19:39:14 +00:00
Miguel Ángel c50918277a perf(engine): fast capture stays on for frames a shadow, blur or clip effect does not touch (#6318)
* perf(engine): fast capture stays on for frames a shadow, blur or clip effect does not touch

A single drop-shadow, blur, backdrop-filter or blend element, or one clip-path or mask
tween, sent the whole render to screenshot capture, 2-3x slower than drawElement. The two
init gates now name the frames the effect can show on. Only those frames capture by
screenshot, in a second browser opened on the first of them; every other frame keeps
drawElement.

* fix(engine): scan effects in a throwaway session; hand risk frames over only where captured

The effect scan seeked the render's own page to the end, leaving its timeline state ahead of
the render. It now runs in a throwaway session with the render's own seek. A hidden effect
element with a visible descendant counts as shown. Per-frame capture is opt-in per caller
(deFrameFallback), so disk, segmented, chunk and parallel-worker sessions keep the
whole-render fallback, and a route that cannot hand frames over drops drawElement up front.

* fix(engine): a timeline callback keeps the whole-render fallback; trim comment share

A tl.call or onComplete can add an effect's class to any element mid-render, so the elements
found at the start are not the only ones it can reach. Such a timeline is unbounded and keeps
the whole-render screenshot fallback.

* style(engine): shorten the live-frame scan comment

* perf(engine): after a timeline callback, not before, every frame may show the effect

A callback can add an effect's class to any element, so only the frames from the first
callback on lose their element bounds. Frames before it keep the scanned bounds, so a late
callback no longer sends the whole render to screenshot capture.

* perf(engine): open the second screenshot browser only when memory is available for it

Per-frame capture needs a second browser (479 MB measured at 1080p). When the render would
leave less than 1.5 GB available after it, the render keeps the whole-render screenshot
fallback instead. getAvailableMemoryMb moves from the CLI to the engine's system-memory
module, which already owns the host's memory readings; os.freemem() reads 367 MB on this
49 GB Mac, so the guard uses free, inactive, speculative and purgeable pages.

* fix(engine): budget the whole 1 GB per-frame capture adds, not just the second browser

The render's process tree goes from 1002 to 2001 MB at 1080p: the screenshot browser (479 MB)
plus a drawElement browser that is itself about 500 MB larger than a screenshot one. The guard
budgeted 500 MB. It now budgets the 1 GB measured, scaled by output pixels.
2026-10-25 19:15:18 +00:00
Miguel Ángel 69405a7736 perf(producer): size a chunk's capture browsers by its frame cost, not its frame count (#6319) 2026-10-25 19:14:30 +00:00
Miguel Ángel 8214cdcd29 ci: run cli tests in their own job so runtime tests stop timing out (#6326)
* ci: run cli tests in their own job so runtime tests stop timing out

* ci: format the test aggregator needs list

* ci: keep the test aggregator needs on one line for the reachability check

* ci(reachability): read wrapped and block needs lists instead of rejecting them
2026-10-25 18:54:01 +00:00
Miguel Ángel 1a37db4aee fix(producer): the audio peak correction sizes its cut from the PCM it re-encodes (#6312) 2026-10-25 18:53:14 +00:00
Miguel Ángel 42b84632a8 chore: release v0.8.199 (#6328) v0.8.199 2026-10-25 18:13:20 +00:00
Miguel Ángel b2124b2a56 perf(studio-server): the browser-playable copy of an alpha video encodes on four threads (#6325)
* perf(studio-server): the browser-playable copy of an alpha video encodes on four threads

libvpx runs on one thread unless told, so the alpha copy of a 4K video took several times its length on a small machine.

* docs(studio-server): drop a comment line the name already carries
2026-10-25 17:35:33 +00:00
Miguel Ángel 21f315e9cc fix(engine): the drawElement self-check reads its truth on a page the render does not use (#6316)
* fix(engine): the drawElement self-check no longer rejects count-up text it should accept

The check's ground-truth screenshot was taken after a seek that suppressed timeline callbacks, so text a callback writes (a count-up) was stale in the truth while the real render, which runs callbacks, showed it correctly. The check failed and the whole render restarted on the slower screenshot path.

* docs(engine): shorten a comment to the line limit

* fix(engine): the drawElement self-check reads its truth on a page the render does not use

Callbacks now run in the truth seek (a count-up or class swap no longer looks stale), and they run on a disposable page so a callback that changes the page cumulatively cannot change what the render draws. Compositions with videos keep the in-page path, because the video injector is tied to the render page.

* docs(engine): cut a comment to three lines

* fix(engine): a truth page that cannot open falls back to the render page
2026-10-25 17:34:41 +00:00
Miguel Ángel 7f5d6ed203 feat(producer): fail a render that cannot finish within its maxRenderMs budget (#6224)
* feat(producer): fail a render that cannot finish within its maxRenderMs budget

* chore(producer): raise the producer size budget to the measured size of the render time guard
2026-10-25 17:31:18 +00:00
Miguel Ángel 15b00007ab test(engine): tone-map concurrency test passes on macOS (#6323)
On macOS the extractor checks once per process whether ffmpeg has zscale. The test ran that
check through its counting shim, so it saw 11 ffmpeg runs instead of 8 cuts plus 2 probes and
failed on every Mac with zscale. Take the check with the real ffmpeg before the shim counts.
2026-10-25 17:14:41 +00:00
Miguel Ángel d4812a0393 test(cli): check the browser that ran the hanging script, and that every browser it started is gone (#6320)
* test(cli): check the browser that ran the hanging script, and that every browser it started is gone

* test(cli): type the launch recorder's pid file and drop the added comment
2026-10-25 16:52:39 +00:00
Miguel Ángel 26e3045e3b fix(producer): --workers N is a ceiling, drawElement renders keep their single worker (#6314)
* fix(producer): --workers N is a ceiling, drawElement renders keep their single worker

* docs(producer): keep the inversion predicate's doc as short as before
2026-10-25 15:33:05 +00:00
Miguel Ángel ad6771c435 feat(lint): warn when a scene's styles sit outside its template (#6317)
A sub-composition file with a <template> is mounted from the template alone, so a <style>
in its <head> (or a stylesheet link in its body) never applies in preview, snapshot or render.
Warn on it, with the fix in the message: move it inside <template>.
2026-10-25 15:26:20 +00:00
Miguel Ángel 7b76377534 test(engine): count what the mixer read from a cloud file, not what the server buffered (#6315) 2026-10-25 15:25:34 +00:00
Miguel Ángel d3a7599c60 fix(cli): a failed frame check no longer switches parallel capture off for good (#6313)
* fix(cli): a failed frame check no longer switches parallel capture off for good

The per-install breaker tripped on the first self-verify fallback and nothing ever cleared it. Now a second fallback within 20 router renders turns the router off, a new CLI version or 20 clean renders turn it back on, and the stored record keeps the rejected frame (reason, frame, dB, threshold, version, time).

* docs(cli): shorten three comments to the line limits

* refactor(cli): keep the breaker record parsing small and the comments within the ratchet
2026-10-25 15:24:47 +00:00
Miguel Ángel 7569ec1be5 fix(producer): extract HDR video frames a window at a time to bound scratch disk (#6304)
* fix(producer): extract HDR video frames a window at a time so scratch disk stays bounded

* fix(producer): drop the added compositor comment for the Comments gate

* fix(producer): keep the HDR scratch read plain and fail the render when a window cannot be extracted

* fix(producer): settle a looped HDR source's frame count before capture starts

* chore(producer): raise the packed size budget by the windowed HDR extraction's measured delta
2026-10-25 15:13:31 +00:00
Miguel Ángel 9763b68a33 fix(studio): scrubbing never auditions a track the music row did not name (#6310)
The last fallback took whatever audio came first in the preview, which is usually the voiceover, whenever the music row's own element was momentarily absent.
2026-10-25 14:52:41 +00:00
Miguel Ángel 80ba3f59ec fix(core): the decoded-audio cache keys a clip the same way everywhere (#6306)
* fix(core): the preview keeps a voiceover's decoded audio across an in-place reload

The decode cache and the release pass keyed the same asset two ways, so a clip the release pass was handed as live could lose its buffer and be re-fetched mid-playback.

* refactor(core): resolve the decode key through the runtime's own assetUrl

Smaller than the inline URL resolution it replaces, which tipped the producer package-size budget, and one owner for "resolve against the document".

* refactor(core): drop the decode-key comment the comment ratchet rejects

* fix(core): an unreadable clip src no longer stops the preview from playing

Resolving the decode key threw on a src that is not a URL, out of the release pass and through play(), so one bad clip silenced the whole preview instead of just failing its own fetch.
2026-10-25 14:52:35 +00:00
Miguel Ángel 9b80c2d01e docs(skills): say a volume lane multiplies data-volume (#6308) 2026-10-25 13:53:10 +00:00
Miguel Ángel ada9932d4e fix(cli): a long video copy reports progress, resumes in 15 s pieces and stops only when stalled (#6307)
* fix(cli): a long video copy reports progress, resumes in 15 s pieces and stops only when stalled

* perf(core): a video copy's size box follows the stage within a fifth, not a half

* docs(cli): shorten two comments to the line limit

* fix(cli): the copy heartbeat drops a finished copy; size budgets follow the stall and progress code

* chore: size budgets rise by this change's measured bytes
2026-10-25 13:53:03 +00:00
Miguel Ángel 89af290808 perf(core): mounting a scene no longer restyles it to look for raster hints (#6300)
* perf(core): mounting a scene no longer restyles it to look for raster hints

* perf(core): raster hints read inline hints directly instead of matching every style

* perf(core): raster hints read the stylesheets once per page check, not per batch

* perf(core): raster hints watch no attributes while nothing is marked or hinted

* fix(core): a copied or re-attached marked layer keeps being followed

* chore(producer): raise the packed size budget by the 336 bytes the raster-hint fix adds

The producer bundles the runtime. On main (aae32e43ed) it packs at 773560 bytes
(2821768 unpacked) and this branch at 773896 (2822665): packed rises by 336, unpacked by 897.
2026-10-25 12:52:45 +00:00
Miguel Ángel d7f323d319 chore: release v0.8.198 (#6305) v0.8.198 2026-10-25 12:42:50 +00:00
Miguel Ángel aae32e43ed perf(producer): cut distributed chunks by estimated capture cost (#6295)
* perf(producer): cut distributed chunks by estimated capture cost, same chunk count

Auto-sized plans place chunk boundaries so each chunk carries about the same estimated capture
cost (page base plus active video megapixels); chunk count, explicit chunkSize, uniform-cost
compositions and the targetChunkFrames ceiling are unchanged. Frame-cost calibration samples by
cost interval plus the heaviest frame, so a heavy window is always measured.

* fix(producer): hold cost-weighted chunks to the worker time limit's frame cap

The weighted cut could grow a chunk up to MAX_CHUNK_SIZE frames, past the per-chunk frame count resolveMaxParallelChunks sized against the worker time limit. The cut now takes the same cap, from one shared helper, and keeps the equal-frame size as its floor.

* fix(producer): keep the chunk-cut doc comment under 120 characters

* perf(producer): drop the calibration sampler change from the cost-weighted cut

The cut no longer touches how capture cost is measured; that keeps the producer bundle within its size budget and the change to the chunk boundaries alone.

* perf(producer): compute the chunk frame cap inline to keep the bundle small
2026-10-25 11:25:00 +00:00
Miguel Ángel 6b424e02c4 fix(cli): snapshot waits for a slow scene to finish building instead of saving a stale frame (#6290)
* fix(cli): snapshot waits for a slow scene to finish building instead of saving a stale frame

* fix(cli): drop the added comments, the names say it

* test(cli): give the contact-sheet engine mock the render-ready timeout snapshot reads
2026-10-25 10:59:47 +00:00
Miguel Ángel 36b2c3c2b9 fix(core): hide static data-hidden elements in render as preview does (#6296)
* fix(core): hide a static data-hidden element in render as preview does

* fix(core): name the static-hidden skip instead of commenting it
2026-10-25 10:33:10 +00:00
Miguel Ángel 683e9b1765 fix(producer): refuse private and non-HTTPS previewUrl fetches (#6297) 2026-10-25 09:50:18 +00:00
Miguel Ángel e938e53ea6 ci(release): keep the packed release tarballs as an artifact on release pull requests (#6292) 2026-10-25 09:50:12 +00:00
Ular Kimsanov f5ab7711fb docs(start): the Studio app first, the browser editor named, a showcase wall that renders (#6303) 2026-10-25 09:48:11 +00:00
Miguel Ángel f51650d8a7 chore: release v0.8.197 (#6298) v0.8.197 2026-10-25 09:17:12 +00:00
Miguel Ángel 382a14d73e perf(engine): vp9 and vp8 sources without an alpha plane extract as jpeg (#6284)
A VP8/VP9 source with no alpha tag has one frame decoded with the alpha-aware decoder when its
metadata is read; if that frame has no alpha plane, its frames extract as jpeg instead of png.
Tagged alpha, a decoded alpha plane, a failed probe, or a plan written without the field keep png.
2026-10-25 08:32:49 +00:00
Miguel Ángel 495dc95acd fix(sdk): release the parsed film when a session is disposed (#6289)
* fix(sdk): release the parsed film when a session is disposed

A disposed session kept its whole parsed document and element caches. Anything
that still referenced the session, such as a stale closure or React fiber,
therefore kept the replaced film in memory, so memory grew with every film swap.
dispose() now drops the document and caches.

* fix(sdk): warn when a disposed session is read

A disposed session answers reads from an empty document. Outside production it
now logs once, with a stack, so a caller still holding a stale session shows up
instead of silently reading nothing.
2026-10-25 07:34:26 +00:00
Miguel Ángel 2bff65b0cd feat(lint): warn when a round-capped line hidden by its dash settings can still show a dot (#6291)
* feat(lint): warn when a round-capped line hidden by its dash settings can still show a dot

* docs(lint): shorten the round-cap dash rule entry
2026-10-25 07:25:17 +00:00
Miguel Ángel 4d154c6a23 test: two flaky queue tests no longer race real time or the first copy's start (#6283) 2026-10-25 07:13:22 +00:00
Miguel Ángel a7273f7147 fix(cli): snapshot fails with the clip and time instead of a silent black frame (#6274)
* fix(cli): snapshot fails with the clip and time instead of a silent black frame

* fix(cli): one clip timing out in a snapshot no longer loses the other frames

* fix(cli): snapshot fallback seeks to a real frame time so the right frame is picked

* fix(cli): only a frame-extract timeout is recorded as a finding; other errors still stop snapshot
2026-10-25 07:13:16 +00:00
Miguel Ángel 1f740ca21a fix(cli): a check that is stopped by a signal prints the report it has so far (#6282)
* fix(cli): drop added comments, the names carry the meaning

* fix(cli): a check that is stopped by a signal prints the report it has so far

Killed by SIGTERM or SIGINT, check closed Chrome and exited 143 or 130 with
nothing on stdout, so a caller that stops a long check got no result at all.

The signal now ends the check the way an expired deadline does: findings
already made are kept, one finding says the check stopped before finishing,
and the report is printed before the process exits with the signal's code.
2026-10-25 07:13:09 +00:00
Miguel Ángel 751ca755f2 perf(core): text edits stop rebuilding the preview's caches every frame (#6276)
* perf(core): text edits no longer rebuild the preview's timing caches

* perf(core): color grading stops rescanning the page on text edits
2026-10-25 07:13:01 +00:00
Miguel Ángel 45167743a6 chore: release v0.8.196 (#6288) v0.8.196 2026-10-25 06:52:05 +00:00
Miguel Ángel 7bc363c87c fix(studio-server): undoing a video rename brings the file back under its old name (#6285)
* fix(studio-server): undoing a video rename brings the file back under its old name

* fix(studio-server): name the bytes a write's deletes set aside
2026-10-25 05:43:55 +00:00
Miguel Ángel 38afc0b013 fix(deps): update esbuild for the security advisory (#6277) 2026-10-25 04:52:58 +00:00
Miguel Ángel 221f3671f2 fix(cli): stop 'hyperframes add' hanging when a desktop clipboard is available (#6271)
* fix(cli): stop 'hyperframes add' hanging when a desktop clipboard is available

* test(cli): assert the clipboard copy options instead of waiting on real time
2026-10-25 04:44:34 +00:00
Akshar Patel 32d2d3cba0 fix(core): keep cache-busted images visible in bundles (#6264) 2026-10-25 04:15:02 +00:00
Akshar Patel c6c8d81ba3 fix(cli): include encoded external assets in archives (#6266) 2026-10-25 03:45:43 +00:00
Abdullah M. Nazif f5e15b2f77 fix(cli): remove stale briefs when re-freezing recipes (#5324)
* fix(cli): remove stale briefs when re-freezing recipes

* chore: prepare conflict resolution against current main

* chore: preserve reviewed fix on current main

* chore: prepare conflict resolution against current main

* fix(cli): retain recipe brief cleanup with safe directory copies

* chore: prepare conflict resolution against current main

* chore: merge main and refresh recipe skill manifest
2026-10-25 03:45:23 +00:00
Miguel Ángel 37f5f27290 fix(cli): check finishes on long projects with many video clips (#6270)
* fix(cli): check finishes on long projects with many video clips

check loaded every video in the project at once. On a 500 s project with 137
clips of one 1440p source a single renderer reached 6.4 GB, the second sampling
Chrome crashed, and a crashed puppeteer page never settles its calls, so the
run sat until the 120 s deadline.

- Load the check page in Studio's capture mode so only media near the
  playhead loads (peak renderer memory 15.75 GB -> 1.77 GB).
- Read audio durations for the clip-fit audit from a detached element when the
  page has not loaded the one in the composition.
- Settle every driver call with the page's crash or close, so a dead helper
  hands its samples back and a dead main page fails at once.

* fix(cli): shorten a comment to the line limit
2026-10-25 03:32:47 +00:00
Miguel Ángel 1776b42736 chore: release v0.8.195 (#6273) v0.8.195 2026-10-25 03:18:09 +00:00
Miguel Ángel 3eb1aeb0de perf(core): Safari stops re-serializing inline styles on every GSAP write (#5974)
* 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
2026-10-25 03:17:39 +00:00