Commit Graph
266 Commits
Author SHA1 Message Date
Vance IngallsandClaude Opus 5.5 c569760a7a fix(core): grade groups resolve per composition; freeze frames keep their lane (#6685)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 10:51:17 -07:00
Miguel Ángel 5a0179b88e fix(studio-server): the render route passes a MOV ProRes profile to the render (#6644)
* fix(studio-server): the render route passes a MOV ProRes profile to the render

* fix(studio-server): drop the added comment line so the comment share does not rise
2026-10-28 09:44:32 +00:00
Miguel Ángel 30df5a48af fix(studio-server): undo and redo on Windows restore a file something holds open (#6628)
* fix(studio-server): undo and redo on Windows restore a file something holds open

* fix(studio-server): a restore waits for the preview's reads to close before replacing the file

* fix(studio-server): a restore checks the project folder after the preview's reads close
2026-10-28 09:12:54 +00:00
Miguel Ángel bff39004b0 fix(preview): the proxy copy for the video at the playhead is made ahead of the rest (#6586)
* fix(preview): the proxy copy for the video at the playhead is made ahead of the rest

* test(preview): the resolveProxy assertions carry the priority option

* fix(preview): the proxy copy asked for last goes first, and a repeat ask moves it to the front

* refactor(preview): the proxy wait loop moves its header and progress handling into helpers
2026-10-27 20:08:57 +00:00
Miguel Ángel d6e16aa187 fix(studio-server): undo and redo on Windows wait out a briefly held file (#6559)
* fix(studio-server): undo and redo on Windows wait out a briefly held file

* style(core): format the atomic write test
2026-10-27 14:28:43 +00:00
Miguel Ángel 88068af950 fix(studio-server): a history lock whose start cannot be recorded is given back (#6557)
* fix(studio-server): a history lock whose start cannot be recorded is given back

claim() took owner.pid and then wrote the start record; if that write threw, the error left owner.pid held by this process, so later opens waited on and then named their own pid as the busy owner. Creation and recording now release the file together.

* refactor(studio-server): drop the comment the ratchet counts against ownerLock.ts
2026-10-27 13:30:06 +00:00
Miguel Ángel cfef528d3a fix: phone videos with an undecodable first audio track check, preview and render (#6551)
* fix: phone videos with an undecodable first audio track check, preview and render

* fix(engine): a rate-lane clip bakes its ramp from the decodable audio track

* test: phone-clip tests end ffprobe options before the input
2026-10-27 13:09:02 +00:00
Miguel Ángel 404e8254e6 fix(studio-server): an HDR clip previews and snapshots the way the SDR export renders it (#6537)
* fix(studio-server): an HDR clip previews and snapshots the way the SDR export renders it

Footage tagged HLG or PQ played raw in the preview, where the browser tone-maps it its
own way, while the SDR export tone-maps it with zscale and hable: the same clip came
out lighter and with different reds and blues. HDR video now takes the tone-mapped
preview copy (the filter the export uses), and a snapshot still of HDR footage runs
the same tone map. Without an ffmpeg that has zscale and tonemap the clip plays
natively, with a warning that names the missing library.

* fix(studio-server): an HDR clip is never playable for preview, whatever its codec

Only HDR H.264 and ProRes took the tone-mapped copy: an HDR HEVC, VP9 or AV1 clip kept
its codec's test type, so a browser that can decode it was judged able to play it and
the original played with the browser's own mapping. An HDR clip now has no test type, so
the swap always fires. The missing-library warning no longer names the file.

* test(cli): the HDR still test ends ffprobe's options before its input

The repo's ffprobe argv contract requires "--" immediately before the input path; the new
test passed the still's path without it.
2026-10-27 12:49:04 +00:00
Miguel Ángel 6092785348 fix(studio-server): preview loads root-path media, images and CSS from the project, as render does (#6552)
* fix(studio-server): preview loads root-path media, images and CSS from the project, as render does

* fix(studio-server): preview writes root paths project-relative, the form Studio reads back
2026-10-27 12:29:17 +00:00
Miguel Ángel 859281a60d fix(studio-server): preview resolves root-path module imports to the project (#6534)
* fix(studio-server): preview resolves root-path module imports to the project

* test(studio-server): preview's own scripts make no module import; lint flags a broken import map
2026-10-27 09:48:42 +00:00
Miguel Ángel 592ca3daf1 fix(studio-server): history lock retries a busy url write and releases on failure (#6525)
* fix(studio-server): history lock retries a busy url write and releases on failure

* style(studio-server): wrap the fs mock

* style(studio-server): format the owner lock test
2026-10-27 07:09:09 +00:00
Miguel Ángel 54d0af57eb feat(engine): render sets a keyframe interval and a peak bitrate cap (#6444)
* feat(engine): encoders cap peak bitrate and place keyframes on a set interval

* style(engine): shorten the rate-cap comment

* style(engine): drop the doc lines the comment ratchet flagged

* feat(producer): mp4 renders honour a keyframe interval and a peak bitrate cap on every encoder path

* style(producer): shorten the delivery encoder file headers

* style(producer): drop a comment line from the segment manifest

* style(producer): shorten the integration test header

* test: delivery encoder tests follow the ffprobe argv contract and run in the integration lane

* feat: the render request, server and Lambda plan carry the keyframe interval and peak bitrate cap

* fix(studio): the Studio render route passes the keyframe interval and bitrate cap to the encoder

* chore(studio-server): drop a comment the helper's name already says

* feat(cli): render and lambda render take --keyframe-interval, --max-bitrate and --buffer-size
2026-10-27 00:29:06 +00:00
Miguel Ángel 097cc094b7 fix(studio): a reload refused because the page is missing mid-edit warns instead of erroring (#6491) 2026-10-26 23:53:48 +00:00
Miguel Ángel 3e16603d37 fix(render): a PNG's colour tags no longer change its pixels in preview, check, snapshot or render (#6453)
* 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
2026-10-26 17:48:57 +00:00
SophiaHuangCc 2d695a8436 feat(studio): support GIF export with loop count (#5593)
* feat(studio): support GIF export with loop count

* fix(studio): cap GIF exports at 30 fps

* fix(studio): complete GIF export review follow-ups
2026-10-26 14:57:55 +00:00
Miguel Ángel 52b3d32114 fix(studio-server): a failed preview copy reports its causes and what its source is (#6350)
* fix(studio-server): a failed preview copy says why its output could not open

* fix(studio-server): a failed preview copy reports its causes and what its source is

* fix(studio-server): failure reports hide spaced paths and survive a vanished source

* refactor(studio-server): read a probe's result in its own function for the complexity gate

* fix(studio-server): close the source facts function the rebase left open

* fix(studio-server): a failure report reads the source size through one open descriptor
2026-10-26 10:54:09 +00:00
Miguel Ángel 84fd701daa feat(studio): preview copy progress, retry and a remembered quality (#6366)
* 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
2026-10-26 09:20:07 +00:00
Miguel Ángel 2e23b7e434 fix(studio-server): copies are found by content and made by one process at a time (#6358)
* perf(studio-server): a preview copy is keyed by the file's content, so copied projects reuse it

* fix(studio-server): copies are keyed by content and made by one process at a time

* docs(studio-server): shorten the copy lock comment

* fix(studio-server): find a copy by place and time first, by whole-file content when that misses

* fix(studio-server): hash small sources in one read; wait longer in tests for the hash

* test(studio-server): cache folder listings ignore the copy index files

* style(studio-server): format the copy lookup files

* docs(studio-server): shorten the copy lookup comment

* fix(studio-server): hash a source through one open handle, not a check then a read

* fix(studio-server): hash through one descriptor, keeping the small-file path synchronous

* style(studio-server): format the cache key file
2026-10-26 06:20:03 +00:00
Miguel Ángel 38d9aeb5ea feat(core): lighter preview copies of heavy videos below full quality (#6365)
* feat(core): tell which video files deserve a lighter preview copy, and at which size

* feat(studio-server): serve lighter copies of heavy playable files, a keyframe every second

* feat(core): the preview plays lighter copies of heavy videos below full quality

* style: shorten two comments

* style: shorten a comment

* style: shorten a comment

* fix(core): format the preview quality files and split the copy sizing for the complexity gate

* style: format the studio-server preview files

* fix(core): type the copy job's level as a preview quality

* chore(producer): raise the package size budget for the preview runtime growth
2026-10-26 03:53:21 +00:00
Miguel Ángel badb61aa99 fix(studio-server): take over a history lock whose pid now runs another process (#6386)
* fix(studio-server): take over a history lock whose pid now runs another process

* style(studio-server): drop two comments from the history lock
2026-10-26 03:21:37 +00:00
Miguel Ángel e4bdf8c51c perf(studio-server): copy alpha cut-outs several pieces at a time (#6342) 2026-10-26 02:14:49 +00:00
Miguel Ángel 6f4d46ad34 fix(studio-server): a refused preview frame says so at once instead of timing out (#6074)
* fix(studio-server): a refused preview frame says so at once instead of timing out

A frame the server refuses (project gone, bad variables) loaded a JSON body: no runtime and no error, so Studio waited three 15s budgets and reported 'waiting for runtime'. The refusal now reaches the frame as a page that reports it, like a failing preview script.

* chore(studio-server): remove redundant preview refusal comments
2026-10-25 23:05:51 +00:00
Akshar Patel 6a20ec9e9a fix(cli): preserve clip lengths when duplicating media (#6280)
* fix(cli): prevent overlaps when duplicating natural-duration media

* fix(cli): preserve absolute ends when duplicating clips

* fix(cli): keep rippled clip bounds together
2026-10-25 23:01:27 +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 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 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 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
James RussoandClaude Opus 5.5 7f19f2335a feat(studio): load GSAP from the CLI's bundled copy by default (#6256)
* feat(studio): load GSAP from the CLI's bundled copy by default

Studio preview now loads the GSAP, CustomEase and MotionPathPlugin scripts
it adds from files the CLI ships and serves same-origin at /api/vendor/npm,
instead of jsDelivr. HYPERFRAMES_GSAP_BASE_URL still wins when set, and a
composition on another GSAP version gets its plugin from jsDelivr.

Renders now honor HYPERFRAMES_GSAP_BASE_URL when they replace a missing
local GSAP script, and the setting accepts plain http only for loopback
hosts.

Closes #6205

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

* refactor(studio): keep comment share flat for the GSAP local root

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

* fix(cli): find the installed gsap with node resolution when run from source

The Windows CI lane installs with the hoisted linker, so gsap is not under
packages/core/node_modules there.

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

* fix(producer): load a nested composition's missing GSAP from the mirror

A sub-composition's missing local gsap/dist script was hoisted into the
render unchanged, so HYPERFRAMES_GSAP_BASE_URL (and the jsDelivr fallback)
only reached the entry document. Run the same rewrite on every
sub-composition before it is compiled, resolving its path the way the
inliner will hoist it.

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-25 03:08:42 +00:00
James RussoandClaude Opus 5.5 685cd75a92 fix(studio): load GSAP core when only a GSAP plugin is on the page (#6260)
* fix(studio): load GSAP core when only a GSAP plugin is on the page

The Studio preview checked for GSAP core by matching "gsap" anywhere in a
script src, so the MotionPathPlugin it had just injected (served from
gsap@x/dist/) counted as core. A composition with a motion path and
Studio-edited motion but no GSAP script of its own then got the plugin and
no core, and its animations did not run.

Detect core by file name (gsap.js, gsap.min.js, or a vendored
gsap-<version>.min.js) on any host or path, and inject Studio motion's core
before the MotionPathPlugin so the plugin can register onto it. Apply the
same core-only check to the sub-composition preview fallback and the
missing_gsap_script lint rule.

Refs #6205

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

* refactor(studio): keep GSAP core detection within the comment budget

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

* fix(studio): load GSAP core ahead of head plugins and only on a real core script

Studio motion and sub-composition previews now insert GSAP core before the
first GSAP plugin script in the head, so an authored MotionPathPlugin or
CustomEase registers on core instead of running first. Core detection keys on
a core script (src, inlined marker, license banner or library body), not on
usage like gsap.registerPlugin() or gsap.defaults(). The missing_gsap_script
lint rule drops the same usage-based and plugin-banner shortcuts.

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-25 02:37:08 +00:00
Miguel Ángel 85fb19f1e8 fix(studio-server): undo and restore keep the media they delete or replace (#6215)
* fix(studio-server): undo and restore keep the media they delete or replace

* fix(studio-server): set-aside media is freed only after a whole undo or restore lands

* fix(studio-server): set-aside media does not count toward the history budget

* fix(studio-server): set-aside media keeps its own ledger apart from copied blobs

* fix(studio-server): a set-aside copy waits for every path that counts on it

* fix(studio-server): an unreadable history folder fails the read instead of looking empty

* fix(studio-server): history opens past a temp file a crash left behind

* fix(studio-server): trim a comment so the comment share holds
2026-10-25 01:48:06 +00:00
Abdullah M. Nazif 1c14fbb542 fix(studio): allow a local GSAP dependency mirror (#6247) 2026-10-24 22:13:28 +00:00
Miguel Ángel 0eae4946d3 fix(studio-server): a history restore refuses to remove every tracked file (#6175)
* fix(studio-server): a history restore refuses to remove every tracked file

* test(studio-server): compare the held clip by bytes so the adopting check stays fast

* fix(studio-server): history's own hidden records do not count as files a restore keeps

* refactor(studio-server): name the restore guard's check instead of commenting it

* chore(studio-server): re-baseline the packed size budget for the restore guard
2026-10-24 12:51:30 +00:00
Miguel Ángel 5ebbc8ef6d fix(studio-server): project history no longer copies video, audio or huge files (#6171)
* fix(studio-server): project history no longer copies video, audio or huge files

* fix(studio-server): clean drops old video and audio copies in every history

* fix(studio-server): TypeScript source is not media, so its history is kept

* fix(studio-server): split the media reclaim and point the slow-copy route test at a non-media file

* fix(studio-server): no unused expression when collecting a history's hashes
2026-10-24 04:12:48 +00:00
Abdullah M. Nazif 0aa728b1aa fix(studio-server): decode authored audio attributes for loudness (#5289) 2026-10-24 04:11:25 +00:00
Miguel Ángel 28ac4d76b6 fix(studio): the thumbnail capture page loads media near the playhead like the preview (#6146) 2026-10-23 19:21:51 +00:00
Miguel Ángel 8411956534 fix(cli): long projects check to completion: boxed resumable copies, --prepare-media, idle deadline (#6104)
* fix(cli): check copies hostile videos at the composition size, not the source size

* fix(cli): the check server serves the copy the page names with hf-proxy-box

* feat(cli): check --prepare-media makes the video copies alone, and a stopped copy resumes

A long source is copied in one-minute pieces that stay in the cache, so a copy
that is killed or runs out of time continues from its finished pieces.
check --prepare-media runs only that step: no browser, no report, a per-file
result as JSON, and exit 1 when a copy failed.

* fix(studio-server): keep finished copy pieces while their copy runs

The short limit for dead partial copies would delete the first pieces of a
long copy that is still encoding. Finished pieces now follow their own
one-hour limit and are never swept while their copy is in flight.

* refactor(studio-server): shorten two comments

The comment gate limits a comment line to 120 characters and a run to four lines.

* refactor(studio-server): drop comments the names already carry

The comment ratchet lets a file's comment share only go down.

* fix(cli): the check server pre-warms the copy the page asks for, not the source-size one

The server started a source-size copy of every hostile video next to Chrome on
each check, because its pre-warm passed no box while the page and the up-front
copy use the composition's box. The pre-warm now takes the same box, found by
the one helper the up-front copy uses.

* fix(cli): end a check on no progress, not on a 120 s wall clock

The check ended 120 s after Chrome was ready no matter what it was doing, so a
long project whose sampling and overlap pass each kept working still failed
(the overlap pass was one page call that reported nothing for its whole run).

The deadline is now an idle clock: every phase entered, every sample finished,
every overlap batch of eight times and every operation that settles restarts it,
and the check ends only after 120 s with none. The overlap grid is asked for in
batches so it can report between them.

* refactor(cli): shorten the idle deadline comment

The comment gate allows 120 characters on a comment line.

* test(cli): check's overlap grid runs in page tasks of at most 8 samples

* fix(cli): check test types for the cli lib, studio-server size budget for resumable copies
2026-10-23 16:32:28 +00:00
Miguel Ángel 2ed882ccf2 fix(studio-server): the history lock works on drives without hard links (#6125)
* fix(studio-server): the history lock works on drives without hard links

Taking the owner lock links a prepared file into place. exFAT and some network drives refuse hard links (ENOTSUP, EPERM), so the claim threw and the project history could not open. A refused link now falls back to creating the lock exclusively. A reader treats a just-created empty lock as being taken, and clears one a crashed claimant left behind after two seconds.

* test(studio-server): run the owner lock tests where node:fs can be stubbed

* fix(studio-server): the evict lock also waits out a claimant's empty file

The evict lock takes the same exclusive-create fallback as the owner lock, so it can be empty for a moment too. One deadHolder check now decides a holder is gone at all three sites: owner, evictor and the lock itself.

* chore(studio-server): trim comments on the owner lock

* test(studio-server): an empty lock is taken over once it is old, not while being written

* chore(studio-server): shorten the claim comment
2026-10-23 12:32:47 +00:00
Miguel Ángel 6225271173 fix: preserve motion blur off through cloud render requests (#6087)
* fix: preserve motion blur off through cloud render requests

* fix: keep generated blur docs concise and format examples

* fix: freeze motion blur intent for distributed render workers
2026-10-22 23:54:54 +00:00
Vance IngallsandClaude Sonnet 5.5 1f8ead7bde feat(producer,studio): wire capture motion blur into renders and Studio (#5969)
Carry the runtime's blur through the render pipeline and the Studio render
panel.

- engine: installPageMotionBlur sets the page-side off switch and the render's
  device scale before any author script runs; CaptureOptions.pageMotionBlur
  lets a caller stand the page blur down.
- producer: a composition that asks for data-motion-blur on the root the
  runtime would pick forces screenshot capture, because html-in-canvas needs
  a real paint; the probe session carries the request's pageMotionBlur like
  every other session.
- studio-server and Studio: the render panel sends previewMotionBlur, the
  render route turns "off" into pageMotionBlur once, and the Studio dev server
  and the CLI server both receive the final value.
- mover scan: a large element that is see-through while it moves (a sweep, a
  light leak) is no mover, including a large image or canvas with alpha; the
  scan skips elements moved by a 3D individual transform property.
- cli snapshot injects video frames before the seek settles, matching the
  engine's order.

Folded review fixes: R1, R2, R3 (scan half), R7, R16, R18, T3 (render
adapters).

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 13:32:50 -07:00
Miguel Ángel d67e81c7f1 fix(studio-server): keep transparency in proxies of VP8/VP9 alpha webm (#6025)
* fix(studio-server): keep transparency in proxies of VP8/VP9 alpha webm

A webm whose alpha lives in the VP8/VP9 side channel (alpha_mode=1,
pix_fmt yuv420p) was proxied to opaque H.264, and the VP8 proxy path
decoded it with ffmpeg's native decoder, which drops alpha. The alpha
rule and the alpha-reading decoder now live once in
@hyperframes/parsers/media-alpha, used by engine and studio-server.

* build(parsers): raise the packed size budget for the media-alpha subpath

parsers already sat at its packed budget on main; the shared alpha rule
adds 676 B of JS and 803 B of declarations (190958 B packed in CI).
2026-10-21 13:49:30 +00:00
Miguel Ángel 468467003c fix(studio-server): a history claim on Windows waits out a lock file being deleted (#5923)
* fix(studio-server): a history claim on Windows waits out a lock file being deleted

* style(studio-server): drop the two history-lock comments, names carry them

* style(studio-server): keep the history-lock test comment under 120 columns
2026-10-20 16:13:36 +00:00
Miguel Ángel 6d357ff0d8 fix(cli): check, snapshot and Studio read project text as UTF-8 (#5843)
* fix(cli): check, snapshot and Studio read project text as UTF-8

The preview servers sent HTML, CSS, JS and JSON with no charset, so Chrome
read a page without <meta charset> as windows-1252 and stills showed garbled
non-Latin text while the render, whose file server already sends utf-8,
was correct. The shared MIME table now carries charset=utf-8 for text types,
and the two hard-coded headers use it.

* fix(cli): snapshot shows accented text when the page's meta charset sits outside its head

A <meta charset> past Chrome's first 1024 bytes and outside <head> is ignored, so with no
charset in the header the page decoded as windows-1252. The browser test serves such a page
through the static server snapshot uses. play compares the media type without its charset,
so JSON assets keep their content-hash revalidation.
2026-10-20 13:52:43 +00:00
Miguel Ángel b7fa007bf3 fix(cli): check says it is making video copies instead of going silent for minutes (#5953)
A check on a project whose videos Chrome cannot decode (HEVC, HDR) first transcodes each
of them, which on phone footage takes minutes and printed nothing until it was done.
2026-10-20 09:52:28 +00:00
Miguel Ángel 5583ae4293 perf(cli): commands run without esbuild, puppeteer-core or sharp until they need them (#5930)
* perf(cli): commands run without esbuild, puppeteer-core or sharp until they need them

A host that bundles the CLI can now leave these packages out: each command
loads them only where it uses them, and sharp installs once on first use when
it is missing. The build check fails on any static import of them again.

* fix(cli): use a host's sharp from NODE_PATH and let hosts turn first-use installs off

* fix(cli): capture fails when sharp is missing, and one install serves every caller

A missing sharp now stops the asset download up front instead of counting every
image as unavailable. Concurrent first uses share one install, a failed install
is not retried in the same run, and preview thumbnails report why. Hosts can turn
off installs for a list of packages, e.g. HYPERFRAMES_NO_OPTIONAL_INSTALL=sharp.

* fix(cli): snapshot --describe still works without sharp, resizing only when it is installed

* style(cli): format the sharp loader and its tests

* build(cli): sharp is an optional dependency, so a host can ship the CLI without it

* build(studio-server): keep sharp out of the bundle now that it is optional
2026-10-20 09:12:47 +00:00
Miguel Ángel 93c321285c fix(studio): render scratch stays out of the project and error telemetry counts real failures only (#5902)
* fix(studio): a lost preview server or a superseded edit no longer files as a crash

* fix(studio-server): a render's scratch files no longer stream changes or list as compositions
2026-10-19 19:52:04 +00:00
Miguel Ángel 35c03a509e fix(cli): snapshot --proxy exits once its frames are written, without waiting for copies (#5895)
Closing the page server now stops the media proxy copies it started (pre-warms and the page's own asks). A copy whose last caller leaves is now stopped even after its ffmpeg started; callers without a signal still always get their copy.
2026-10-19 17:24:05 +00:00
Miguel Ángel cadd7af2b5 fix(studio-server): send Studio's history edits to the process that owns the project's history (#5826)
* fix(studio-server): send Studio's history edits to the process that owns the project's history

* fix(studio-server): a forwarded history call gives up after 5 s, so a stopped owner cannot hang
2026-10-18 06:07:33 +00:00
Miguel Ángel 7b1b0bb005 fix(studio-server): history routes say why a project's history cannot open instead of a bare 500 (#5821) 2026-10-18 05:21:15 +00:00
Miguel Ángel 20611541b1 fix(core): font lookup on a Mac no longer waits 5 s on system_profiler (#5810) 2026-10-17 18:04:15 +00:00
Miguel Ángel c38e9f350b fix(core): a composition mounted twice keeps each mount's own timeline (#5729) 2026-10-17 00:47:28 +00:00