The background-removal route dedupes on the body's request_id and ignores the Idempotency-Key
header, so a retry after a lost answer started a second job: runJob now also sends the key in the
body for such a route. Uploads typed every non-MP4 file as audio, so WebM and MOV inputs were
refused: they now take their type from the extension. Fails without the fix: the new cutout test
in generate.test.ts sees audio/mpeg for WebM and MOV.
style-remix drops metadata.internal and is listed with the other published skills.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(producer): report browser-computed reference backgrounds
Preserve one initial root/page reference across local, streamed and distributed
render results without guessing from film pixels.
— Jerrai
* fix(producer): preserve plan compatibility and optional inspection
Keep reference metadata outside the v2 manifest hash and read it from the
verified execution-plan artifact. Static planning uses a non-gating page
inspection instead of requiring render-worker capabilities.
— Jerrai
* fix(producer): poll readiness independently of animation frames
Use timed polling while the render clock parks animation frames. Cover readiness
that changes after the first check with a short real-browser regression.
— Jerrai
* fix(producer): budget measured reference metadata runtime growth
Producer adds 2,228 compressed bytes over current main. Tests, fixtures and source
maps are already absent from its dist-only tarball. Allow 7,766 bytes of headroom
above the verified 837,234-byte payload while retaining the unpacked limit.
— Jerrai
* feat(skills): publish reverse-any-video, render it at high quality, and stop on a stuck shot
From Wanbi's Pro test of reverse-any-video (11-01, checked against 0.9.2):
- A standard install never found the skill: it was internal, and /hyperframes did not route to
it. It is now an on-demand skill on every catalog surface, and the routing line names it.
- The cloud render in SKILL.md had no --quality high, so the final rendered at standard.
- A re-shoot that stayed pending had no rule; the run waited four hours. people.md now runs the
command once more after generate's 30-minute stop, then treats the shot as failed.
* feat(cli): generate takes a language for --voice, so a Pro sign-in gets a HeyGen voice
Wanbi's Pro run fell back to Kokoro: --voice needed an id, and the only way to list ids was the
separate heygen CLI and its own login. /v3/voices answers on the HyperFrames sign-in, so:
- --voice <language> picks HeyGen's first public voice in it and names it in the output.
- A missing --voice lists six English voices to pick from instead of a bare error.
- A voice id still goes straight through, with no extra call.
* fix(cli): a voice picked by language stays with its request across re-runs
Rames: `--voice Chinese` re-picked HeyGen's first voice on every run, and the voice is part of the
body and the idempotency key. If the catalog reordered while a job was pending, the same command
could start a second paid job instead of resuming the first.
The pick is now bound to the request (every field, plus the account scope a job bills) in the
project's heygen-jobs.jsonl, or the CLI config dir outside a project, and a re-run reuses it.
Regression: a pending timeout, a reordered catalog, the same command, for voice and avatar; the
second POST keeps the voice, body and key.
* fix(cli): a voice pick waits for a known account instead of binding to none
Rames: when /v3/users/me failed, senderScope() was null and the pick was bound to an empty
scope; once the account came back, the same command missed that binding and could pick another
voice and paid key. pickVoice now stops before picking or posting while the account is unknown,
and never reads or writes a blank-account binding.
Regressions, both directions: unknown then known refuses with no POST, then picks once; known
then unknown refuses the re-run with no second POST, then resumes with the same body and key.
Also README: a non-interactive install gets all 8 skills, not 7.
* 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(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(core): a resized preview video keeps its picture while its new copy loads
Swapping a video to a copy made for its new size emptied it until the copy
decoded, so the preview showed black for a moment. The swap now shows a still
of the frame it had, as the video's poster, until the new copy can draw.
* fix(core): a held preview still is let go when no file loads back
* chore(core): raise the packed size budget for the held preview still
* ci: split the cli test job into two shards so each stays well under its limit
* ci: map the sharded cli test run in the reachability audit
* ci: the reachability audit treats --shard as selection-neutral instead of a pinned mapping
* ci: map the sharded cli test run in the reachability audit (pinned), not a blanket --shard rule
* fix(skills): paid-plan cloud final keeps the composition fps and high quality
Follow-up to #6884 from its review: the cloud command agents copy had
no --fps, so a 24 or 60 fps composition rendered at 30, and cloud's
default `standard` is below the local `delivery` final. Step 5 and
cloud.md now spell out `cloud render --fps <data-fps> --quality high
-o <path>`; the render table uses `-o` to stay within the entry's byte
budget and restores the "Hosted, no local Chrome or FFmpeg" row.
— wanbi
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(skills): pin --fps and --quality high on the paid-plan cloud final
Review on #6886: a later byte-budget trim of hyperframes-cli/SKILL.md or
cloud.md could drop the flags silently. The test asserts both files'
paid-plan command reads `cloud render --fps <data-fps> --quality high -o …`.
— wanbi
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(core): motion blur preview no longer slows down later in the timeline
With data-motion-blur-events="on", which Studio's Subtle and Strong write,
both blur tiers ran timeline callbacks at every per-frame pose read. Each
frame's reads step back from half a shutter ahead to the frame, so a
callback that keeps state and replays from zero on a backward seek (a
ripple simulation, for example) redid its whole history most frames, and
the cost grew with the playhead.
The mover scan still runs callbacks, so motion set from a render(t)
callback is still found. With events on, it also scans once without them,
and the per-frame reads run callbacks only when the two scans disagree.
The eventful scan runs first, so tweens take their start values after the
callbacks before them and a one-off call fires as often as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(core): keep the motion blur comments to the invariant
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(core): motion blur reads run callbacks when a callback moves any box
Comparing which elements each scan finds missed a callback that pans a
mover's ancestor while a tween also changes that ancestor: both scans
found the same movers and the same changing elements, so the reads left
the pan out. The scans now compare every changing element's box at each
step, and the reads run callbacks when any step differs.
Raises @hyperframes/core's unpacked size budget to the measured size plus
the usual 5%: main already sat 0.98% over the old budget, and this change
adds 1,232 bytes, which tipped it past the 1% tolerance.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(core): silent motion blur reads land back on the frame as its own seek left it
After silent pose reads, the restore seek back to the frame left GSAP in a
different state than the frame's own eventful seek did:
- A step exactly on the frame (a .call() or set) was re-armed, so the next
frame fired it again. Calls fired 2-5 times per crossing, and a toggle
could end inverted.
- In renders, which keep no GSAP structure, landing back on the frame from
later skipped same-time steps' authored order, so the frame showed the
first of two sets instead of the last.
Events-off blur always read silently and had both. This PR moves events-on
roots without callback-driven motion (the Studio default) onto that path,
so both are fixed here.
The host's new holdFrame snapshots every landing mark on the root and scene
timelines before the reads, and its restore seeks back silently, redraws in
authored order where there is no structure, and puts the marks back. Each
call now fires once per frame crossing, as without blur, and same-time sets
land as authored.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(core): a held render frame draws its scenes first, so the root lands last
holdFrame redrew the root and then the scenes, the reverse of the frame's
own seek. When a scene and the root tween one property, the frame landed
on the scene's value instead of the root's.
The hold and landing move into the GSAP adapter as holdFrameMarks, which
takes the scenes and the root separately, so the order lives in one place
a real-GSAP test can pin.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(cli,skills): say a step's credits are its total, and keep cost out of retries
An out-of-credits refusal named what the step needs, and an agent read
that as the shortfall. It now says the number is the step's total.
After its last try, an agent told the person what was spent and what
another try would cost. The HeyGen reference now says not to.
* fix(cli): plan avatar credits from the HyperFrames price rows
An avatar's credits came from its old API price, about five times what
the plan charges, so --max-credits refused calls that fit and a top-up
asked for too much. They now use the Avatar IV photo row plus the
general avatar video row, 950 credits a minute.
* fix(cli): pin the avatar price test and keep pricing under the comment share
* fix(cli): plan avatar seconds on the Avatar IV row alone
The general avatar row is only checked at admission and billed on
seconds left after Avatar IV, so adding it to every second made a 15 s
script plan at 238 instead of 200 and a 220 limit refuse a call that fits.
* fix(cli): the credits link opens the web plan page, which is live
* test(cli): pin the production credits link too
* chore(skills): regenerate the skills manifest after merging main
* docs(cli): keep the person measure's doc comments to four lines
* docs(cli): keep the measure doc comments to four lines
* docs(cli): keep the measure comment lines to 120 characters
---------
Co-authored-by: Miguel Angel Simon Sierra <miguel.sierra@heygen.com>
* fix(cli): exit-time telemetry no longer loses a render's events on Windows
* fix(cli): telemetry smoke stand-in reads the batch file; format the warning
* docs(catalog): put each item page's title, description and Docs text in the server HTML
Mintlify renders snippet components in the browser only, so everything inside
CatalogDetail (title, standfirst, the Docs tab) was missing from the HTML search
engines read: 448 catalog pages served Googlebot no body text. The generator now
writes the H1 and standfirst as page markup above the component and the Docs tab
as page markup after it, shown with that tab (docs/custom.css). The component no
longer draws its own title. Pages patched structurally only; registry drift on 14
pages is left for a regeneration of its own.
* docs(catalog): keep the title's top spacing the component gave it
* fix(docs): catalog titles and descriptions keep markdown markers literal
MDX reads text inside the static <h1> and <p> as inline markdown, so @_blue_sweater_
rendered as italic blue_sweater. mdxText now writes backslash, backtick, * _ [ ] ~ as
character references too, with a unit test; the four affected pages are re-escaped.
SKILL.md holds the catalog, the Pro openings, the pull and the credit
rules. references/remix.md is how to make the episode, from Desktop's
creator-remix with only what the app alone has swapped for the CLI.
The packs now hold only the template, so their hashes change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* revert: feat(skills): reverse any video (#6754)
Keep the reverse-any-video skill and its CLI billing commands out of main until
web subscriptions are live (/pricing and /account/plan return 404 today).
Re-land together with #6833 once those pages ship.
This reverts commit cb2389cf35.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* chore(scripts): allow the #6754 revert deletions
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A Pro account asks for a video in a creator's format and the agent pulls that
template on demand. The packs sit in the private bucket behind the plan-gated
bundle route, so a Free account gets a 402 and the upgrade step. A pull costs
no credits; making the film spends them as Reverse does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(skills): reverse any video on the HyperFrames plan
Reverse rebuilds a video as an editable HyperFrames project and re-shoots its
people with HeyGen. It runs for a person signed in on HyperFrames Pro; Free is
told it is a Pro feature and given the upgrade step.
- skills: reverse-any-video; media-use is the one gate for HeyGen calls
(sign-in, credits, refusals)
- cli: media-use generate (image, video shot, avatar, voice, music) with --plan
pricing and --max-credits; measure, diff and analyze for the rebuild; the
HyperFrames plan in auth status; billing subscribe and top-up wait on the
plans or credits page
- remotion-to-hyperframes compares with `hyperframes diff` instead of its own
shell scripts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore(skills): satisfy CI gates for reverse-any-video
- docs: list diff, measure, analyze and billing in "Find your command" in
docs/packages/cli.mdx, each with its own section.
- skills: bring media-use/SKILL.md under its 5120 B budget by pointing setup,
direct-recipe and treatment detail at the references that already hold it;
the sign-in, credits, --max-credits and billing rules stay in SKILL.md.
- comments: register transcript.json and REVERSE.md as project files for the
citation checker, and drop a history note from face.test.ts.
- comments: cut restating prose or move it into names so every changed file is
at or under its comment share. Allowlist the two deleted remotion scripts in
check-no-main-deletions.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(cli): terminate measure's ffprobe options with --
probeVideo passed the input path straight after its options, so a path
starting with "-" would be read as a flag; the producer's ffprobe argv
contract test failed on it. runFfmpeg's fixed flags move to a constant
so the contract no longer mistakes that ffmpeg wrapper for a probe.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(cli): split diff's video tests into three files under CI's per-file limit
The file took ~131 s on CI against the flake gate's 120 s, and its heaviest test (four
comparisons) hit the 20 s per-test limit. The video tests now live in three files sharing
diff.test-helpers.ts, and that test is two tests of two comparisons each.
analyze reads each part once and checks the bytes it read, instead of a stat then a read
(CodeQL js/file-system-race).
* fix(skills,cli): one credits rule; ask before dropping a step that won't fit
On dev with a 100-credit limit the agent priced a re-shoot with --plan and dropped it without
asking (J5), and in J6 it told the person a price. SKILL.md's Credits rule now covers both:
--plan is for the agent, and when a step won't fit (a --plan over what is left, or a refused
call) the agent says what doesn't fit and asks before dropping or shrinking it.
setup-providers.md's older confirm-before-paying rule points to it.
--plan now runs the same checks as the real call (checkRequest), so it no longer prices a
request that would be refused. The re-shoot recipe in people.md gains the --prompt it needs.
heygen.md: a sign-in also makes images, and --plan's usd is the API charge rounded to $1/60
while its credits come from the list price.
* fix(cli): auth status drops 'balance unavailable' above the plan's credits; sign-in page title
* test(cli): keep diff's video tests inside the flake gate's limits
Under the gate's CPU contention every diff test needing face detection or
optical flow paid opencv.js's 11 MB download and WebAssembly start-up, and
every diffVideos call wrote four worst-moment pictures (about 28 ffmpeg runs)
no assertion read. Tests ran 21-28 s against the 20 s limit.
- loadOpenCvFirst() stages opencv.js in a beforeAll (from this machine's
cache, a temp copy kept between runs, else the network).
- diffVerdicts() runs diffVideos with worst: 0 for tests that only read
verdicts; the worst-moment tests keep the pictures.
- The carried-text test is split into its dirty and clean halves.
Locally with 8 CPU burners on 8 cores: slowest test 7.7 s, files 24-30 s.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(cli): generate --plan refuses what the real call refuses
checkRequest now holds every pre-send check (the --out extension and
--mask without --motion joined the route body and guide checks), and
generate() runs it first, so --plan and the real call share one function.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(cli): plans link uses from=cli and follows HYPERFRAMES_ENV; no balance notice beside the plan
- pageUrl sets from=cli, the parameter the pricing page reads to send a
CLI buyer back to the terminal.
- The plans and credits pages are on dev.hyperframes.dev with
HYPERFRAMES_ENV=dev, hyperframes.studio otherwise.
- auth status --json omits the 'HyperFrames balance is unavailable' notice
when the plan was read.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(skills): reverse-any-video re-checks sign-in on resume; REVERSE.md holds no prices
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(cli): split diff-people's whole-frame reuse test into three
It ran five diff comparisons in one test and took 20.2-20.6 s under the flake
gate's contention, over the 20 s limit. Each piece now runs at most two
comparisons against a shared ledger: copy and burned-in title, footage cut
whole, and cuts that only fed a generation. Under 24 CPU burners locally the
slowest of the three takes 3.0 s, where the combined test took 4.9 s under 8.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(cli,skills): reverse resume asks after one failed retry; out-of-credits names the step's price
- media-use generate: when HeyGen refuses for credits without saying how many, the refusal
names the CLI's own price for the call (what --plan prints) as "needs about N" and
"billing top-up --needed N", instead of "needs more".
- reverse-any-video: never repeat auth status's plan/credits line; a step an earlier session
gave up on gets one more try, then ask; a command stored in REVERSE.md is a note, rebuilt
from its recipe and checked with --plan, never run as written.
- people.md / heygen.md: a refused or failed job gets one more try, test calls included,
then stop and ask.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(cli,skills): send a failed HeyGen job again as new; resume asks if the limit still applies
- runJob: a key whose job failed or was refused is recorded in the config dir
(heygen-failed-keys); the next run with the same inputs sends key-1, key-2, ... so HeyGen
starts a new job. A finished or still-running job keeps its key and is picked up, so
nothing is paid twice. "fix the request it names, then run again" now holds.
- reverse-any-video: on resume after a run under a credit limit, just ask whether it still
applies, naming no costs or numbers.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* refactor(cli,skills): drop what reverse-any-video no longer uses
The skill rebuilds from measure probe, cuts, frames, events and face, and checks with
diff. analyze, diff --explain and eight measure commands (trail, box, ink, rhythm,
objects, track, flow, continuity) have no caller, so they go, with their tests and docs.
Examples now use original.mp4 instead of the old .reverse/source.mp4 evidence folder,
and diff's --segments is described as a file rather than REVERSE.md.
* fix(cli): check no longer copies VP9 and AV1 videos before it starts
Chrome plays both, alpha included, and the proxy is made on demand if a browser ever
asks. Copying remove-background's VP9 cutouts took minutes on projects that have them.
* fix(cli,skills): keep paid retries, credit limits and lip sync honest
Retries keep their key after 401, 403, 408, 409, 425 and 429, which say
nothing about the job, and failed keys are scoped to the API and payer.
Credits come from the plan's video rows instead of list dollars, and
reference pictures past the first two count toward --max-credits.
Lip sync reads sound on its own timestamps, so a delayed track fails
the gate. Look and energy call two near-zero values the same.
--plan refuses a missing input as the real call does. The skills teach
the direct sign-in, its credential path and the billing commands.
* chore(cli): keep the new pricing and energy files under the comment share
* fix(cli): scope failed HeyGen keys to the signed-in account
Failed keys were scoped by the local user block, which is absent for a
host's token and display-only otherwise, so two accounts could share a
scope and one could start a second paid job.
The scope now comes from the account /v3/users/me reports for the token
in use. When that cannot be told, no failure is recorded and the key
stays the same.
* fix(cli): fix a job's sender when it starts
Failure scope was looked up again after polling, so a sign-in during a
job could record one account's failure under another and rotate its
finished key. The credential is now read once per job: it signs the POST
and owns any failure, even if someone else signs in meanwhile.
Prefer username to email, since /v3/users/me returns no user id.
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: James <james.russo@heygen.com>
* ci: one regression shard writes the test image cache, so it stops evicting the rest
* test(ci): each image cache has one writer, shard 0 of a main push
* fix(ci): skills sync steps past any taken ClawHub version, not one hard-coded skill
* fix(ci): skills sync steps past the five hidden ClawHub versions blocking it
* ci: keep the bun cache warm and skip binary downloads in preflight
* test(ci): pin the single cache writer and preflight's skipped downloads
* test(ci): keep the cache-writer scan out of the test body
* perf(aws-lambda): small-chunk cloud renders stitch their output in parallel
When every chunk's payload is under S3's 5 MiB part minimum (typical for short 4K chunks), the assemble step could not copy any part server-side and pulled the whole output through one Lambda's network link. Those range-only parts now go to parallel invocations of the warm function that just rendered the chunks, about 32 MiB each and never more at once than the stack's reserved concurrency allows. The output bytes are unchanged.
* fix(aws-lambda): a throttled stitch helper builds its parts in the assemble instead
Another render can hold the stack's reserved concurrency while this one assembles, so a helper invocation may be refused. The assemble now builds that batch itself, logs it, and counts it in assemble_stages; the helper's error type is kept as the error name. Comments trimmed back to each file's share at the base.
* chore(aws-lambda): raise the package size budget to the measured size plus headroom
The parallel stitch helpers add about 1.7 KB packed and 9.9 KB unpacked; the new budget is the measured size times the checker's own 1.05 headroom.
- .claude/settings.json: the typecheck step piped through grep inside
single quotes, which cmd.exe splits into a broken pipeline, so the hook
denied every commit on Windows. On every OS, `|| true` also made the step
pass regardless of type errors. Test-file errors are now filtered in JS
and any remaining `error TS` line fails the step.
- scripts/check-studio-copy-dashes.mjs: report paths with forward slashes
so EXEMPT entries match on Windows.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* test(studio): record each edit-accuracy shard's file writes and reloads when HF_WRITE_TRACE is set
* style(studio): format the write trace
* fix(core): hash the write trace with sha256
* style(studio): wrap the trace hash helper
* style(studio): format the trace hash helper
* fix(studio): turn the reload log on in the Studio page the trace watches
* ci: a release PR runs a fast release check, not the heavy suites
* ci: keep the release check's header within the comment limit
* ci: shorten the release check's usage line
* ci: trim the release check's comments
* ci: the release check's classifier hands the manifest test to a helper
* fix(core): preview seeks land on the composition's own frame rate
* fix(studio): frame step, frame readout and ruler use the composition's frame rate
* fix(studio): an end trim previews the clip's last frame at the composition's frame rate
* refactor(studio): ruler labels follow the ticks' frame rate and fps parsing splits by form
* style(studio): format the ruler label helper and its test
* chore(inspector): raise the package size budget to the measured size plus headroom
* fix(producer): convert HEIC images to JPEG at compile time
Tiled HEIC photos probe as several video streams, so the media-type
preflight rejected them as image:video, and Chrome cannot decode HEIC.
Sniff the ftyp brands of every <img>, join and rotate the tile grid with
ffmpeg, verify the declared size, and swap in a cached JPEG.
* fix(producer): shorten the HEIC prep comments
* fix(producer): name host tool failures and keep a failed HEIC preview image as authored
Also: HEIC tests run in the integration lane; the primary grid is picked by default disposition.
* chore(producer): raise the package size budget for the HEIC prep
* fix(core): a composition behind a wrapper takes its host's start
Fixes#4334.
* fix(core): a composition inside a timed wrapper starts with the wrapper
The nearest host or timed wrapper above a mounted root sets its start, so its clock starts when the wrapper shows it, matching how CSS animations already pick their clip.
* test(producer): re-pin the test classification digest
* docs(examples): the inspiration page, with films and remixes to start from
The Examples page becomes Inspiration: two rows of films made with
HyperFrames, then 41 remixes of popular videos, each crediting and linking
the original post, with how to remix it in the Studio app. The rows play
one film at a time, since a second playing film drops the page from 120 to
30 fps, and they get a Pause button. The block and template tables stay.
* docs(examples): remixes honour reduced motion; one film while the dialog is open
Remix previews now follow reduced motion (read on the first render,
stopped if it turns on), and each film is a button that plays it on
purpose with sound. The film rows stop their previews while a full film
plays in the dialog, so only one film plays at a time. Two remixes get
the credit handles the app lists for them.
* docs(examples): a motion setting change keeps remix cards as they are
The shelf keeps the reduced-motion setting in a ref, so a change no
longer re-renders it and remounts every card: a playing remix, its
focus and its open instructions stay.
* docs(examples): one film at a time across the whole Inspiration page
The dialog paused only the film-row previews, so a chosen remix kept
playing behind it. Any video that starts with sound now makes each remix
card stop its own, and the film rows rest their previews while a film
with sound plays anywhere on the page.
* docs(examples): remix cards and film-row controls in the docs palette
The remix cards used Tailwind zinc greys and white, the film rows the old
green focus ring and fixed tints; they now use the palette tokens, so
they sit on the beige ground and in the Night theme like the rest.
* test(studio): real-Chrome tests inline GSAP so their page waits on no request
* test(ci): smoke projects inline GSAP so required checks wait on no CDN
* docs: one Troubleshooting page, four tabs, and the start pages folded
* docs: keep how Studio app updates arrive on the help page
Folding the Studio app troubleshooting page into /help dropped its
"How updates arrive" section: the update button, Settings > Updates
and the Mac menu route, the Automatic updates toggle, and what to do
when an update stalls or cannot check. It is back word for word; every
label is still in the app.
* feat(skills): a Remotion port carries Remotion's box-sizing rule so bordered boxes keep their size
* chore(skills): regenerate the skills manifest after the format pass
* fix(hooks): the skills manifest is hashed after the formatter has run
* test(studio): a drag is measured against the box the preview shows
* test(studio): another element is read bare until it is selected
* fix(core): preview audio fades play as ramps on the audio clock, so cuts no longer click
data-fade-in / data-fade-out were folded into one volume per 50 ms sync tick and applied as
a step (el.volume, or gain.value in the Web Audio transport), so a 20 ms fade never played
and each clip started and stopped at near full level: a click at every cut. The export
applies the fades (ffmpeg afade) and does not click.
The transport now books each clip's fades on a gain of its own when it schedules the
clip, for both decoded buffers and captured media elements: a linear ramp from where the
clip starts sounding up to its fade-in, and down to silence at its end. The sync tick hands
the transport the level without the fade (onElementVolume's new fifth argument, sent only
where a fade applies), so a booked fade is not applied twice. The plain el.volume fallback
keeps its stepped fade.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(core): one doc block on setElementVolume
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(core): keep the comment share where it was
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(core): booked fades hold through pre-roll, follow rate changes, skip unbounded ends
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* chore: raise the producer unpacked budget to the measured size plus 1%
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* docs(studio): the browser editor in six pages, named the browser editor
* docs(studio): mute where the editor has it, and no solo control
The audio pages still described a solo control and a group-row mute that
the editor does not render. Muting is the speaker button on an audio
track (Mute track / Unmute track), the same toggle on a selected clip,
and the Muted switch; a group is silenced through its members' tracks.
The audio reference drops its solo section for the same reason.
* docs(prompting): the Prompt Guide as six topic pages
* docs(prompting): call the skill the way each agent does
The overview told every own-agent user to start with /hyperframes; Codex
uses $hyperframes, and other agents have their own way. The line now
names both and keeps the caveat the old page had.
* 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
* docs(guides): media pages titled by the task people search for
* docs(guides): background fixes as a visible section; list color grading as an agreed deletion
* docs(guides): one page per kind of video, with its prompts folded in
* docs(guides): two tiles per row on What to make; list the folded prompt pages as agreed deletions
* fix(producer): reject regression audio stream mismatches
* chore: prepare conflict resolution against current main
* chore: preserve reviewed fix on current main
* chore: prepare conflict resolution against current main
* fix(producer): retain audio regression checks with current main
* chore: prepare conflict resolution against current main
* fix(producer): preserve audio presence checks on current main
* ci: re-run changed test files to catch flaky tests before merge
* ci: give the flake gate the full test workspace setup
* ci: share the test workspace setup between the Test lanes and the flake gate
* ci: split the flake gate main for the fallow complexity gate and format
* ci: run node:test files with node, skip uncollected files, bound the gate's run time
* 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
* 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
* 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