Commit Graph
266 Commits
Author SHA1 Message Date
Miguel Ángel 86fa10ced4 perf(studio): show a film's first frame sooner by loading preview assets by URL (#4357)
* fix(studio): serve preview assets by URL instead of inlining them as base64

The Studio preview route serves project files under a base href, but both preview adapters
called the bundler with its self-contained defaults, so every image and font was inlined
once per reference. One exported option set now owns how a preview bundle is built.

* test(studio): cover poster, srcset and sub-composition preview asset URLs

Also types the Vite adapter bundler with BundleOptions, so the shared preview options are
checked against the real option set.

* fix(studio): tag a preview asset for revalidation only once it has settled

Preview assets are now served by URL, so the asset route serves every image and video. It tagged
them by mtime and size; two same-size writes inside one filesystem tick share an mtime, and copy
tools (cp -p, rsync) set mtime back, so a browser could keep stale bytes after an edit. The route now
tags a file by inode, change time and size, and only once its last change is three seconds old; a
younger file is served without a tag and refetched.
2026-09-23 21:08:56 -04:00
Miguel Ángel 2c4bf075b5 chore: release v0.8.69 (#4376) 2026-09-23 20:13:08 -04:00
Miguel Ángel 86c5c53d25 chore: release v0.8.68 (#4361) 2026-09-23 17:16:00 -04:00
Miguel Ángel 9cee2bd345 chore: release v0.8.67 (#4354) 2026-09-23 16:14:31 -04:00
Miguel Ángel 4efe5f206d chore: release v0.8.66 (#4344) 2026-09-23 13:29:38 -04:00
Miguel Ángel aabfba5445 chore: release v0.8.65 (#4336) 2026-09-23 11:03:11 -04:00
Miguel Ángel 8cb7d06db6 perf(studio): re-render only the edited scene's thumbnails after an edit (#4315)
* perf(studio): refresh only the edited scene's thumbnails and let the preview load first

One scene edit regenerated every sidebar and timeline thumbnail, and those renders starved the new preview's requests on the Studio server. The thumbnail cache now keys on what a composition renders from, the server names the compositions a write affects, only those thumbnails refresh, and composition thumbnails wait while a shadow preview reload is in flight.

* test(studio): pin scene-scoped thumbnail reuse and the preview-reload hold

Memoizes the scoped thumbnail signature on the project signature so requests keep reusing the adapter's cached one, keeps the fresh-signature check that catches a write the watcher has not reported yet, and adds tests for sibling-scene reuse, root-head invalidation, the affected-composition set, per-path revisions and the scheduler hold.

* refactor(studio): share the file-change payload guard

* fix(studio): match thumbnail revisions for scenes mounted with a ./ path

* perf(studio): keep signature cache keys constant-size

* style(studio): format thumbnail-scope changes

* docs(studio): trim thumbnail input comments
2026-09-23 10:29:01 -04:00
Vance Ingalls ed75203cb6 chore: release v0.8.64 (#4326) 2026-09-23 04:03:02 -07:00
Vance IngallsandClaude Opus 5 6e5f460a0c fix(studio): split the empty-read reason and stop blaming the composition (#4325)
Two residuals from the cutover-dashboard work, both found in the data the
first pass shipped (0.8.61/0.8.62).

`absent_or_empty` was one label over two different failures: the route
answers `content: ""` both from the `optional=1` shim (nothing resolved at
that path) and for a real 0-byte file. The route now marks the shim with
`missing: true`, and the read path with `missing: false` so the field's
PRESENCE is what tells the client this server draws the distinction — a new
server's empty file would otherwise look exactly like either case from an
old one. Client splits them into `absent` and `empty_file`; a server without
the field still reports `absent_or_empty`, so the existing series stays
honest instead of silently folding into one of the new ones.

Why it is worth splitting: on 0.8.62, 75 tabs hit this class and not one of
them ever landed an SDK edit afterwards — it is terminal for the tab, while
the tab keeps playing back and navigating, so nothing surfaces to the user.
Every measured case with a loaded tree carries `path_in_tree: true`: a file
the tree lists and this read cannot get. Which of the two causes that is
decides the fix, and until now the event could not say.

`res.json()` at the end of the read was unguarded, so a 200 carrying HTML —
an SPA fallback or a proxy answering in the route's place — rejected out of
the function entirely and the effect's outer catch filed it as
`stage: "open"`, which asserts the COMPOSITION failed to parse. Observed in
production on 0.8.62: `Unexpected token '<', "<!-- /*!"... is not valid
JSON`. That is the third label to make the same wrong claim, after the
fetch-rejection split in #4240 and #4269. Now `reason: "invalid_json"` with
the response's content-type.

`path_in_tree` now rides all three empty-read reasons, not just the combined
one, so the split keeps the signal that justified it.

Tests: 4 new (absent / empty_file / combined-label fallback / non-JSON 200)
plus a server test pinning `missing` on both answers. 1172 tests green
across studio hooks + studio-server routes; oxlint, oxfmt, tsc clean.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 03:44:04 -07:00
miga-heygenandMiguel Angel Simon Sierra 99221c50a5 chore: release v0.8.63 (#4312)
Co-authored-by: Miguel Angel Simon Sierra <miguel.sierra@heygen.com>
2026-09-23 05:49:08 -04:00
Miguel Ángel d4485b51f1 chore: release v0.8.62 (#4293) 2026-09-22 16:12:35 -04:00
Vance IngallsandMiguel Angel Simon Sierra 8d2b6e7bdc chore: release v0.8.61 (#4283)
Co-authored-by: Miguel Angel Simon Sierra <miguel.sierra@heygen.com>
2026-09-22 11:42:28 -04:00
Miguel Ángel 9f19d9d325 fix(studio-server): write served files atomically (#4285)
* fix(studio-server): write project files atomically

* fix(studio-server): isolate atomic write temporary files

* test(studio-server): document Windows atomic replacement

* ci: allow change detection checkout to finish

* test(studio-server): assert atomic stamp cleanup without ESM spy
2026-09-22 08:55:45 -04:00
Vance IngallsandClaude Sonnet 5 24c3e197ef fix(studio): cutover gate classification, SSE cross-project bleed, benign races (#4269)
Implements the fixes from the 2026-09-21 cutover dashboard read
(plans/sdk/2026-09-21-cutover-dashboard-fixes-plan.md):

- sdk_cutover_failed now carries `family` and `error_kind`
  (network/conflict/http/sdk). The one production gate event was a raw
  `TypeError: Failed to fetch` from an unwrapped fetch (readProjectFile /
  writeProjectFile's preflight), which an instanceof-only classifier would
  still file as "sdk" and trip the rollback gate — added a message-based
  fallback for the browser's fetch-rejection strings.

- studio:sdk_session_unavailable's `network` reason now carries `elapsed_ms`
  and `hidden` (document.visibilityState at rejection), separating a
  near-instant policy block (CSP/PNA/extension) from a dropped connection
  the tab or server going away mid-flight.

- The ownership-race branch in useSdkSession's open effect now emits
  sdk_session_superseded instead of sdk_session_unavailable{stage:ownership}
  — it's a benign race (identity moved on mid-open), not a broken
  precondition, and was being counted as one on the dashboard.

- Root-caused and fixed the read-404 "storm": /api/events (CLI host) and
  the vite-dev hot-reload channel are one connection per SERVER, not per
  project. A stale tab left open after `hyperframes preview` restarts on a
  different folder (same port) received every save the CURRENT project
  made, reloading its preview (which then 404s) and re-reading its own
  composition on each one. Both transports now carry `projectId`; the
  client-side coordinator drops a delivery whose projectId doesn't match
  the tab's (absent field still passes, for one release of skew).

- studio-server's file routes report `why` on 403/404: `project_dir_missing`
  (new — the CLI host's static resolveProject kept answering for a project
  whose folder was renamed/deleted under a running server, which used to
  fail closed as an indistinguishable path-traversal 403) vs `nul` vs
  `outside_project`.

- absent_or_empty reads now carry `path_in_tree` (against the caller's
  loaded file tree). Instrumentation only, per the graveyard page
  (clearing-activecomppath-on-tree-absence) that refuted the "clear
  activeCompPath" fix this was the proposed next step for.

Tests: unit coverage added for every change (sdkCutover.test.ts,
useSdkSession.lifecycle.test.tsx, useExternalFileChangeCoordinator.test.tsx,
studioServer.test.ts, files.pathSafety.test.ts). oxlint/oxfmt clean,
tsc --noEmit clean on studio/studio-server/cli.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-22 01:40:47 -07:00
Miguel Ángel b12e767e5a chore: release v0.8.60 2026-09-22 03:11:40 -04:00
Miguel Ángel bf6cc7e32b chore: release v0.8.59 (#4252) 2026-09-21 19:07:41 -04:00
839ae426dd feat(studio): typed audio values, clip fades, audible video drops (#4250)
* feat(studio): typed audio values, clip fades, audible video drops

Audio rows in the media inspector (Volume, Speed, Media start) take a
typed value: click the readout, type "-6 dB", "1.5x" or "2.5s", Enter.
Speed presets are hidden on <audio> clips.

Clip-edge fades: data-fade-in / data-fade-out in seconds, honoured by the
preview runtime (per-tick gain), the render mixer (afade after volume) and
Studio (Fade in / Fade out rows, and drag dots at the top corners of audio
clips and audible videos). Linear on both sides so preview and render
agree; fades that outrun the clip scale to meet inside it.

Dropped videos with an audio stream now land with data-has-audio="true"
instead of muted, so the mixer keeps their sound. The media metadata
endpoint reports hasAudio from the ffprobe call it already makes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(audio): one owner for fades, dB, and afade-after-volume

Illegal fade values become no-fade at fadeGain; typed inspector input
still returns null so the readout stays red. Master and group mixes
share one filter chain with volume then afade. Fade-out stays anchored
to the trimmed clip end.

* fix(studio): share audio helpers and bound typed media values

* fix(studio): reserve sibling fade duration when editing

---------

Co-authored-by: Jake Moran <jakemoran2011@gmail.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Miguel Angel Simon Sierra <miguel.sierra@heygen.com>
2026-09-21 18:45:08 -04:00
Miguel Ángel 4e9059ad59 feat(cli): add agent timeline workflows (#4241)
* feat(cli): add timeline agent mutation commands

* fix(cli): type timeline a2 mutations

* style(cli): format timeline a2 commands

* test(cli): cover timeline a2 verbs

* test(cli): assert ids keep named refs

* refactor: centralize timeline duplicate mutation

* fix(cli): narrow set assignment field

* style(cli): format timeline mutation exports

* refactor(cli): split apply plan decisions

* fix(cli): tag apply plan refusals

* refactor(cli): move A2 runners beside timeline helpers

* fix(cli): remove stale A2 imports

* style(cli): format A2 command module

* fix(cli): avoid A2 command import cycle

* refactor(cli): isolate shared timeline mutation logic

* fix(cli): restore timeline mutation command factory

* fix(cli): remove stale command-surface remnants

* fix(cli): complete shared timeline support extraction

* fix(cli): close shared conflict helper

* fix(cli): import shared timeline formatter

* style(cli): format shared mutation runner

* refactor(cli): split mutation command orchestration

* fix(cli): keep shared mutation primitives in support module

* fix(cli): use shared mutation support helpers

* refactor(cli): centralize A2 refusals

* style(cli): format A2 refusal helpers

* fix(cli): clean shared A2 imports

* fix(cli): make A2 runner imports explicit

* refactor(cli): isolate apply plan target validation

* fix(cli): tag prepared plan edits

* style(cli): format plan target helper

* fix(cli): validate sequential plans and duplicate ids

* test(cli): account for stamped composition id

* fix(studio): stamp ids on patched html

* fix(studio): preserve no-op patch bytes

* test(studio): expect stamped patch markup

* fix(studio-server): duplicate elements in nested templates

* refactor(cli): share timeline mutation receipts

* fix(studio-server): harden duplicate and no-op mutations

* fix(studio-server): reduce duplicate helper complexity
2026-09-21 17:14:47 -04:00
Vance Ingalls c797f54542 chore: release v0.8.58 (#4242) 2026-09-20 20:14:03 -07:00
Miguel Ángel f92d017b99 feat(cli): add timeline editing verbs (#4221)
* feat(cli): add timeline editing verbs

* chore: satisfy fallow complexity gate

* test(cli): cover snap rounding at known fps

* test(cli): cover snap rounding at known fps

* test(cli): cover snap rounding at known fps

* test(cli): cover snap rounding at known fps

* fix(cli): derive split ids from stable refs

* fix(cli): remove timeline gate blockers

* fix(cli): close freshness and output gate gaps

* fix(cli): close remaining timeline review findings

* refactor(cli): simplify mutation decision flow

* refactor(cli): split trim and apply paths

* refactor(cli): isolate trim validation

* fix(cli): satisfy timeline lint gate

* style: format timeline mutation files

* fix(cli): preserve mutation result types

* fix(cli): close timeline receipt and nested bounds gaps

* fix(studio): rollback writes that fail during write

* fix(cli): bound moved clips by end time
2026-09-20 18:04:13 -04:00
Vance Ingalls f09ea7af3b chore: release v0.8.57 (#4231) 2026-09-20 14:13:17 -07:00
Miguel Ángel e45483aa0a chore: release v0.8.56 (#4218) 2026-09-20 10:57:46 -04:00
Vance Ingalls 96369c8acd chore: release v0.8.55 (#4216) 2026-09-20 00:45:18 -07:00
Miguel Ángel c204b3c757 chore: release v0.8.54 (#4212) 2026-09-20 00:22:52 -04:00
Vance Ingalls d11907c325 chore: release v0.8.53 (#4210) 2026-09-19 20:30:29 -07:00
Vance Ingalls 4d62e80921 chore: release v0.8.52 2026-09-19 14:38:56 -07:00
Vance Ingalls 1dce615c56 chore: release v0.8.51 (#4178) 2026-09-19 08:51:27 -07:00
Vance IngallsandClaude Opus 5 ff078accda feat(studio): say why the SDK session's file read came back empty (#4161)
* feat(studio): say why the SDK session's file read came back empty

`stage: read` was the largest remaining class of SDK-session failures and a
single opaque string covering four different outcomes. It is a hard block, not
a blip: 56 users hit it over a 7-day window and, between them, landed zero
successful SDK edits — while staying active in Studio (3107 playback events,
138 design inputs, 20 commit transactions). Every edit in those sessions fell
back to the server path with nothing recorded, because no session also means no
resolver-shadow event.

readProjectFileOptional now returns a discriminated result instead of
`string | undefined`, and the telemetry carries the branch that fired:

- unsafe_path      — the traversal/NUL guard rejected the path client-side
- http_error       — the request failed, with its status
- missing_content  — 2xx whose body was not the shape we expect
- empty_file       — `optional=1` answers an absent file with 200 + "", which
                     parses into a session with no elements; previously this
                     looked like a healthy session that merely went unused

Two candidate causes were investigated and ruled out first, with tests kept as
regression cover in studio-server (projectIdEncoding.test.ts): project ids that
percent-encode (the CLI derives the id from the folder name, so a space or
non-ASCII character produces one) and sub-composition paths whose separator
encodes as %2F. Both resolve correctly today because Hono hands over an
already-decoded `c.req.path`. The decoded/encoded seam in resolveProjectPath is
a real latent hazard, hence the tests, but it is not this bug.

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

* refactor(studio): fold the absent-file read reason into the result union

Review feedback on #4161. `empty_file` was synthesized by the caller after
the helper returned `{ok: true, content: ""}`, so reason attribution was split
across two places and `ProjectFileReadResult` was not the full reason surface.
It now joins the union, and the caller has one branch.

Renamed to `absent_or_empty`: the optional=1 absent-file shim and a genuinely
0-byte file are the same 200 on the wire and cannot be told apart here, so the
literal name says what the tag actually means. For a composition it is always
the former.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-19 08:34:01 -07:00
Miguel Ángel a25da94b77 refactor(studio): remove the storyboard view and its Storyboard/Preview switch (#4140)
* refactor(studio): remove the storyboard view and its Storyboard/Preview switch

* refactor(studio): fold the thumbnail output branches left after the storyboard mode went

* docs(studio): drop leftover storyboard references after the view was removed

* ci: allow the storyboard view deletions in the no-deletions guard

* docs(studio): wrap an over-long comment line in the caption overlay helper

* style(studio): reindent App.tsx after the view-mode wrapper was removed
2026-09-18 23:47:12 -04:00
Vance Ingalls 34644a1993 Merge pull request #4148 from heygen-com/release/v0.8.50
chore: release v0.8.50
2026-09-18 19:56:18 -07:00
Miguel Ángel b44f1f85dc feat(lint): flag nested structure, missing timing and caption tracks on the root composition (#4146)
* feat(lint): flag nested structure, missing timing and caption tracks on the root composition

One rule set for the root composition: nested layout inside a top-level
element, a timed element or media without data-duration, captions not marked
data-track-kind=captions, and captions spread over more than one track.
Findings are errors when the host is Studio and warnings elsewhere.
Sub-composition files are left free to hold layout.

* test(lint): keep the structure rules silent on the shipped catalog and skills

Runs the rules over every registry and skills html file and asserts no
findings, and treats a bare media element with no timing as a static layer.

* fix(lint): run the structure rules as Studio in both adapters and skip non-root files

The Vite Studio adapter now lints as the studio host, and HTML the project
lint did not treat as a root (components, snippets) is linted as a
sub-composition. Style, script and template content no longer count as nested
layout, sub-composition hosts need no duration, and a caption row without a
lane is not a second track.

* fix(lint): count legacy data-end as a length and pin the catalog test to real roots
2026-09-18 22:49:15 -04:00
Vance Ingalls 42f4afa367 chore: release v0.8.50 2026-09-18 19:41:06 -07:00
Miguel Ángel cb9ca4f0af chore: release v0.8.49 (#4122) 2026-09-18 17:44:18 -04:00
Miguel Ángel 7b4f3a22b0 chore: release v0.8.48 (#4085) 2026-09-18 11:11:40 -04:00
Miguel Ángel a8d44b2596 fix(studio): keep clips on their row through group, ungroup, and composition recovery (#4068)
* fix(studio): keep a split clip on its original track row

Splitting an unauthored clip cloned the DOM node without stamping
data-track-index, so the runtime's positional-index fallback renumbered
it onto a different track on insertion. Both halves now pin to the
element's current resolved track.

Also adds A, matching CapCut, to return from the razor tool to select
while it is armed.

* fix(studio): stop the in-point seek from also firing when A exits the razor tool

Both A bindings register on window in capture phase; the razor's listener
mounts after the playback one, so relying on preventDefault ordering let
the seek fire before the tool switch. Playback now checks activeTool
directly instead.

* fix(studio): stamp the resolved track through group, ungroup, and composition recovery

Same defect class as the split fix: an unauthored clip's track falls back to
its position in document order, and any DOM insertion elsewhere can shift
that position. Group and ungroup move elements into and out of a wrapper;
composition recovery clones a missing element back in. All three now stamp
each element's already-resolved track explicitly, the same way split does,
so none of them can drop a clip onto a different row.

Also closes a gap in the earlier "A" hotkey fix: the razor-armed guard was
unconditionally swallowing Shift+A and Alt+A, which should still seek to the
in-point since only plain A exits the razor tool.

* refactor(studio): reuse the existing patch-target and selection-match types

Simplify pass on the track-stamping fix: resolveGroupChildTracks built its
patch target by hand instead of calling buildDomEditPatchTarget, already used
one function up for the same shape. resolveAuthoredTrack's selection param
was a hand-typed copy of ElementMatchSelection instead of importing it.
Collapses the three duplicated optional-integer track checks added to the
wrap/unwrap/split routes into one isOptionalInteger helper. No behavior
change.

* fix(studio): match an ungroup child with no DOM id by its stable id

findMatchingTimelineElementId had no branch for hfId, the stable content-hash
id every element gets regardless of whether it has an authored id. An
ungroup child identified only by that id fell through every branch and kept
its old track instead of getting the resolved one stamped, leaving it
exposed to the same row-drift this PR closes for every other element.

* refactor(studio): resolve review lint suppressions with real fixes

Three lint suppressions from this branch's earlier commits, resolved instead
of left as noise:

- unwrapElementsFromHtml's complexity: extracted the child-track map build
  into its own named function.
- handlePlaybackKeyDown's complexity: extracted the razor/A-key check into
  its own named function.
- The duplicated param block between UseDomEditSessionParams and
  UseDomSelectionParams: pulled the shared fields into one
  DomEditTimelineParams type both interfaces now extend.

No behavior change.

* fix(studio): fall back to the resolved track when nothing was authored

resolveAuthoredTrack only ever returned authoredTrack, so an implicit or
ungrouped child with no explicit authoredTrack resolved to undefined
instead of its actual resolved track, silently skipping the row-pin for
exactly the unauthored population this change exists to fix. Falls back
to the resolved track, matching the function's own existing comment.

* fix(studio): pin the razor split's resolved track for unauthored clips

buildCutTarget had the identical gap resolveAuthoredTrack did: it only
stamped an explicitly authored track, omitting the field entirely for
an implicit/unauthored clip, so a split of it still drifted onto a new
row. Falls back to the element's resolved track, same as the group and
ungroup paths.

* refactor(studio): share one track-fallback rule between split and group

resolveAuthoredTrack and buildCutTarget each re-implemented "authored
track, else the runtime's resolved track" with a different rounding
rule (one rounded only the authored branch, the other neither). Extract
resolveElementTrack so both flows share the same rule and can't drift
out of sync again.
2026-09-18 09:01:15 -04:00
Vance Ingalls 5b7ff519a5 chore: release v0.8.47 2026-09-17 23:55:00 -07:00
Vance IngallsandClaude Fable 5.1 407cacf0ae feat(studio-server): default the GSAP writer to acorn
HYPERFRAMES_GSAP_WRITER=recast stays as the fallback for one watch window
before recast is deleted. acornDefaultBlockers -> behavioralParityOps: the 18
ops without a byte-diff oracle are now pinned in a test, not a gate.

files.test.ts "stale semantic no-op" pinned to the recast writer: its
interleave seam is recast's lazy import, which acorn (statically imported,
fully synchronous) never performs. The 409 revalidation it covers is
writer-independent.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 21:38:38 -07:00
Miguel Ángel a8a9fdb0fe chore: prepare v0.8.46 (#4033)
* chore: prepare v0.8.46

Draft only, not tagged: bun run release:prepare 0.8.46 --no-tag.
Changelog rewritten in plain words for the one change since v0.8.45
(#4031, adaptive per-frame motion-blur sample count). Cutting the
release is a separate step on Miguel's word.

* docs(release): fix false render-time savings claim in v0.8.46 notes

Adaptive sampling never drops below the old fixed-16 default; it only
adds cost on frames that measure real spatial motion. Per Miga's review
on #4033.
2026-09-17 01:48:54 -04:00
miga-heygenandMiguel Ángel 484a06fb65 chore: release v0.8.45 (#4027)
Co-authored-by: Miguel Ángel <miguel.sierra@heygen.com>
2026-09-16 22:56:40 -04:00
miga-heygenandMiguel Ángel 3c9462feea chore: release v0.8.44 (#4016)
- Lint no longer reports root_dimensions_mismatch for sub-compositions, the
  false positive v0.8.43 shipped with; a top-level mismatch is still reported
  and a viewport-meta-only mismatch gets its own wording (#4012)
- The streaming encoder accepts lockGopForChunkConcat and gopSize, emitting the
  same arguments as the disk encoder. Off by default, so an existing render
  produces an unchanged FFmpeg arg list (#3973)

Co-authored-by: Miguel Ángel <miguel.sierra@heygen.com>
2026-09-16 19:14:51 -04:00
miga-heygenandMiguel Ángel d6fe66707f chore: release v0.8.43 (#4007)
- Portrait compositions render the full frame when the project was
  scaffolded at a different size: html/body are sized to the composition
  root at runtime; new lint warning root_dimensions_mismatch (#4005,
  fixes #4001)
- render_complete telemetry adds composition_element_tags,
  aroll_video_count, heygen_video_count; render_error adds error_name and
  failed_stage_code (#4004)
- Catalog docs "Source" accordion titles are plain strings, so docs
  search no longer shows [object Object] (#4006)

Co-authored-by: Miguel Ángel <miguel.sierra@heygen.com>
2026-09-16 14:05:29 -04:00
miga-heygenandMiguel Ángel 71b274bbfd chore: release v0.8.42 (#3988)
* chore: release v0.8.42

- Studio preview works for projects whose path has non-Latin-1 characters (#3982)
- Studio render downloads use RFC 6266 headers for non-Latin-1 filenames (#3984)
- check --json reports HDR auto-promotion; render prints one warning (#3985)
- Forced --sdr renders tone-map HDR sources before extraction (#3981)
- Render/preview workers no longer survive as orphans after Ctrl-C or parent exit;
  cancellation reaches final artifact promotion; HYPERFRAMES_RENDER_DETACHED=1 for
  intentionally detached renders (#3974)
- HDR preflight test coverage for held tails (#3963)

Co-Authored-By: Miguel Ángel <miguel.sierra@heygen.com>

* chore: release v0.8.42 notes — name the #3982 symptom and scope

- #3982 entry: a Studio preview server crash for compositions whose file path
  has non-Latin-1 characters (the project directory is not involved)

Co-Authored-By: Miguel Ángel <miguel.sierra@heygen.com>

* chore: release v0.8.42 notes — name the #3982 symptom

- #3982 entry: the uncaught ETag TypeError took the preview server down, so
  call it a Studio preview server crash rather than a preview failure

Co-Authored-By: Miguel Ángel <miguel.sierra@heygen.com>

---------

Co-authored-by: Miguel Ángel <miguel.sierra@heygen.com>
2026-09-16 09:40:06 -04:00
heygengenesis[bot]andmiguel.sierra b599f031a9 fix(studio-server): make render download headers ByteString-safe (#3984)
Build RFC 6266 Content-Disposition values with an ASCII filename fallback and UTF-8 filename* parameter for each render file response. This preserves inline and attachment disposition semantics while preventing non-Latin-1 filenames from crossing the HTTP ByteString boundary.

Co-authored-by: heygengenesis[bot] <262951085+heygengenesis[bot]@users.noreply.github.com>
Co-authored-by: miguel.sierra <229591595+miguel-heygen@users.noreply.github.com>
2026-09-16 05:03:28 +00:00
heygengenesis[bot]andmiguel.sierra edb6e87ba7 fix(studio-server): make sub-composition ETags ByteString-safe (#3982)
Hash the decoded sub-composition path before including it in the preview ETag. This keeps ETags deterministic and path-specific while ensuring Hono receives an ASCII-safe header value for Unicode composition filenames.

Add regression coverage for percent-encoded CJK sub-composition paths, including stability and distinctness assertions.

Co-authored-by: heygengenesis[bot] <262951085+heygengenesis[bot]@users.noreply.github.com>
Co-authored-by: miguel.sierra <229591595+miguel-heygen@users.noreply.github.com>
2026-09-16 04:15:48 +00:00
terencecho edf6e4b373 chore: release v0.8.41 (#3966) 2026-09-15 17:24:09 -04:00
heygengenesis[bot]andmiguel.sierra cccc78c65e fix(studio): fail closed on backup failures (#3961)
Fail closed Studio-server mutations when a backup snapshot reports an error. Return the existing backup-failed HTTP 500 response before any raw or structured mutation can change the original.

Co-authored-by: heygengenesis[bot] <262951085+heygengenesis[bot]@users.noreply.github.com>
Co-authored-by: miguel.sierra <229591595+miguel-heygen@users.noreply.github.com>
2026-09-15 18:20:59 +00:00
Miguel Ángel cfe5dcfad3 chore: release v0.8.40 (#3938) 2026-09-14 12:41:47 -04:00
Miguel Ángel d13a89b670 chore: release v0.8.39 (#3936) 2026-09-14 11:13:53 -04:00
Miguel Ángel 10e8447ac0 chore: release v0.8.38 (#3929) 2026-09-13 22:02:19 -04:00
Miguel Angel Simon Sierra 7a5d2bf769 chore: release v0.8.37 2026-09-13 12:51:02 -04:00