* fix(engine): a scene that has ended gives up its WebGL context so the next scene is not slowed
* build(engine): raise the engine package size budget for the WebGL release module
* chore(engine): shorten the WebGL release comment
* fix(engine): a scene gives up its WebGL context only when the runtime says its window ended
* chore(engine): drop a comment the type already says
* chore(engine): shorten a comment line
* fix: cache paths follow HYPERFRAMES_CACHE_DIR
Every place HyperFrames keeps downloaded or built files now takes its
folder from one helper, hyperframesCacheDir() in @hyperframes/core. The
default stays ~/.cache/hyperframes; HYPERFRAMES_CACHE_DIR moves it.
* fix: register the cache-dir subpath for bun and node, and build inspector from core source
* test: the cache-dir override test uses a folder that is absolute on every platform
* fix: the opencv cache follows HYPERFRAMES_CACHE_DIR
* fix(cli): npm installs run in their staging dir, so no cache path reaches a command line
* fix(cli): whisper.cpp is cloned inside its build dir, so no cache path is a git argument
* test(cli): the whisper clone test finds no system whisper on Windows either
* fix(engine): GPU renders use the NVENC preset names the host's ffmpeg lists
* fix(engine): one ffmpeg stdout reader for encoder detection and preset listing
* ci(engine): raise the engine unpacked size budget for the NVENC preset reader
* fix(engine): passthrough output follows the page frames and holds a short video's last frame
* test(engine): a film whose audio outlasts its video still qualifies for passthrough
* test(engine): passthrough encodes one output frame per page frame when the video is shorter
* test(engine): end the ffprobe options before the file in the passthrough frame count test
* 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
* refactor(engine): one builder for the video frame filter chain
* refactor(engine): keep the filter chain builder module-private until it has a caller
* 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
* 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
* 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(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(cli): capture's public-address check uses the engine's list; 192.0/16 is not all reserved
Only 192.0.0.0/24 and 192.0.2.0/24 are special-use. The capture proxy blocked all of
192.0.0.0/16, so sites on WordPress VIP (192.0.64.0/18, e.g. nasa.gov at 192.0.66.108)
failed every capture with net::ERR_TUNNEL_CONNECTION_FAILED. isPublicIPv4 now reads the
engine's isBlockedNetworkHost, so capture and asset downloads share one list.
— Jerrai (cutroom-debug)
* refactor(engine): one special-use network list, in a light subpath capture imports
Importing all of @hyperframes/engine into capture's publicNetwork slowed the lookup child's
start-up past publicNetwork.process.test's 6 s mid-lookup exit. The list and
isBlockedNetworkHost move to engine/src/utils/networkPolicy.ts, exported as
@hyperframes/engine/network-policy; urlDownloader and the engine index read it there.
— Jerrai (cutroom-debug)
* test(engine): pin 192.0.0.0/24 and 192.0.2.0/24 blocked, the rest of 192.0/16 public
— Jerrai (cutroom-debug)
* fix(cli): alias the engine's network-policy subpath in the CLI bundle
esbuild's root alias rewrote @hyperframes/engine/network-policy to src/index.ts/network-policy;
the exact-subpath list now carries it. The publicNetwork docstring is cut to keep the file's
comment share under its base.
— Jerrai (cutroom-debug)
Frames ffmpeg has written now reach the render's stage progress, so a long source video no longer leaves the render silent between extract_video and capture.
* fix(engine): a video clip starting between frames shows its media on the right frame
* chore(engine): trim the helper comment to one line
* chore(engine): drop the helper comment
* test(engine): extract once per start in the off-grid clip test
* fix(engine): a looped clip keeps its loop origin when the start is off the frame grid
* test(engine): three off-grid starts cover the failure modes and keep the flake gate under its limit
* fix(cli): a missing registry item names the closest ones, and a blocked browser says how to allow it
* fix(cli): let closestNames carry its rule in its name
* fix(engine): a video on a non-square pixel grid renders at its display shape
* chore(engine): drop comments the names already carry
* fix(engine): a rotated clip with a non-square pixel grid keeps its display shape
* test(engine): the frame probes end their options with -- like every ffprobe call
* fix(engine): a render no longer waits for a sub-composition timeline no script can register
* fix(engine): the timeline wait matches a twice-mounted id and a template-literal key
* fix(engine): late-starting videos play to their last frame
A video whose first frame is after timestamp 0 could end early (frozen
tail) or late: ffprobe's stream duration does not run from the first
frame in every container (fragmented MP4, Matroska). Such a video's end
is now read from its last packet, once, where its facts are probed.
* test(engine): the WebM-with-audio case measures its first frame from the file
* test(engine): the first-frame probe ends its arguments with the input file
* fix(engine): long films start capturing at once instead of after a full effect scan
* fix(engine): give the effect scan two minutes, under the app's stall limit
* fix(engine): show effect scan progress so a long scan reads as preparing, not stalled
* perf(cli): give render the heap its auto workers need in small containers
Auto worker sizing caps capture workers at (V8 heap - 1024 MB) / 640 MB.
Node sizes its default heap from visible memory, so in a 10 GB container
the heap is 2,096 MB and every render captures with one worker, whatever
the core count.
When no --workers is given, no --docker, no heap was set by hand, and the
heap is the only bound holding workers back, render relaunches itself once
with --max-old-space-size set to what CPU and memory allow (capped at half
of memory). The engine exposes heapMbForAutoWorkers() for this.
* fix(cli): count a heap-relaunched render once, keep detached renders alive
The relaunched child skips the CLI's command telemetry, update and skills
notices, and app-history notice; the parent already records and prints
them. A detached render no longer gets SIGHUP forwarded, which its
default handler would treat as a kill. A heap set as --max_old_space_size
counts as set by hand.
* fix(engine): keep the desktop app's per-worker heap figure at 640 MB
Inside Electron the heap is a hard 4,192 MB cage, and that is where the
field OOM behind the old figure happened (#5149: 5 auto workers on a heavy
export). The new 320 MB figure is measured only in a system Node, so the
desktop app keeps 640 MB (4 workers there) until a heavy desktop export is
measured. Containers and plain Node keep 320.
* fix(engine): know the packaged desktop app by its client tag, which runs the CLI on plain Node
* fix(producer): a BeginFrame capture that draws no first frame in 20 s switches to screenshot
* test(cli): cover the Chrome launch failure messages a failed render prints