* fix(cli): remove stale briefs when re-freezing recipes
* chore: prepare conflict resolution against current main
* chore: preserve reviewed fix on current main
* chore: prepare conflict resolution against current main
* fix(cli): retain recipe brief cleanup with safe directory copies
* chore: prepare conflict resolution against current main
* chore: merge main and refresh recipe skill manifest
* 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>
* fix(sdk): keep added elements when reopening saved overrides
* fix(sdk): replay saved overrides in latest edit order
* fix(sdk): preserve saved positions after sibling edits
* test(sdk): cover child edits after base parent restoration
* fix(producer): report unavailable frame checks in debug logs
* chore: prepare conflict resolution against current main
* fix(producer): retain scoped diagnostics with current artifact handling
* fix(render): align container Chrome with the CLI
* chore: prepare conflict resolution against current main
* fix(render): retain Chrome visuals and current audio baselines
* fix(cli): preserve ducking gain when music starts later
* test(cli): keep ducking regressions standalone
* chore: prepare conflict resolution against current main
* chore: preserve reviewed fix on current main
* chore: prepare conflict resolution against current main
* fix(cli): refresh delayed music ducking against main
* fix(cli): include external video posters in project archives
* chore: prepare conflict resolution against current main
* fix(cli): retain poster localization with shared archive resolvers
* fix(cli): reap only orphaned browsers HyperFrames launched
Orphan cleanup on preview and render killed every PPID-1 process of the
current user whose args named chrome-headless-shell or a Puppeteer temp
profile, which included detached browsers started by other tools.
Every HyperFrames Chrome launch now passes an inert
--hyperframes-owned-browser switch, and the reaper only kills PPID-1
processes that carry it as a whole argument.
Fixes#6203
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(core): serve the browser marker from its own subpath
The GCP image's BeginFrame contract step imports the engine's
browserManager straight from source before core's generated runtime
exists, so pulling the marker from the core root entry broke it.
A dependency-free @hyperframes/core/browser-marker subpath keeps that
import chain as light as it was.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* test(cli): expect the browser marker in the capture fallback launches
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* docs(cli): say orphan cleanup runs before preview and render
The cleanup only stops browsers HyperFrames launched, so the opt-out is
no longer needed to protect other tools' browsers.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Blur finds movers by seeking, and only runs timeline callbacks while it
scans when data-motion-blur-events is on, so motion set from a render(t)
function (k3-promo's rods and pills) showed no blur. Subtle and Strong
now write the attribute and Off removes it.
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* docs(studio-app): show the app working, three new pages, today's UI
* docs(studio-app): correct what the app does today (fact-checked against both repos' main)
Script: audio goes to HeyGen to transcribe; cuts wait in Pending video edits until Send; today's toolbar; suggested
cuts. Continue from Claude Code: Mac only, inside an agent session, catch-up after that hand-off, real output.
GitHub Copilot added. 4K price, 10-minute sign-in, Auto as the default, real sign-in errors, motion-guide limit,
the right-panel row. Looping clips pause and respect reduced motion.
* docs(studio-app): hold the Share page until Share launches (desktop-share is at 0%)
* docs(studio-app): Home screenshot from a regular account on b467 (Make, Reference, Script)
* Revert "docs(studio-app): hold the Share page until Share launches (desktop-share is at 0%)"
This reverts commit 201070ac7b7a551c5515c1a770be4a5e8ab36ab9.
* docs(studio-app): Export link label names its page
* docs(studio-app): where-is-it clips re-recorded on a regular account, b467
Staff-only items are gone (Pit Crew Academy, View as a user, the HyperFrames agent, Learn). # opens tools by typing;
the tools list and the @ menu match a regular account; the import clip ends before the folder list.
* docs(studio-app): new Script clip on a regular account: Cut, Pending video edits, Send 1 edit, 0:22 to 0:17
* docs(studio-app): Draw clip re-recorded on a regular account (Send 1 edit to Claude Code, 1 min 52 s edit)
* docs(studio-app): Export clip re-recorded on a regular account (1080p, 16 s); cloud rendering as the app shows it
* docs(studio-app): Video 1 change-a-clip demo re-recorded on a regular account (Up to $1.09 shown before Make)
* docs(studio-app): Share clips re-recorded on a regular account; the page mentions HeyGen's Checking step
* docs(studio-app): first-video demo re-recorded on a regular account (question cards, Send answer, 29 min storyboard, 10 min build)
* docs(studio-app): Reference page follows today's flow (the app reads the video, then Copy editing style or Video brief; What to swap); demo re-recorded on a regular account
* docs(studio-app): autoplaying demos start muted so browsers play them; Automatic updates in troubleshooting
* docs(studio-app): update home.jpg in place instead of replacing it
* docs(snippets): DocsVideo stops an autoplaying loop when Reduce Motion turns on mid-visit; play loads it again
* docs(studio-app): motion guides need the person's picture; drop a pending cut by deleting its row; remove two unused stills
* fix(cli): init and skills update only touch HyperFrames skills; --skip-skills works
* fix(cli): init never touches installed skills and no command removes one
* fix(cli): shorten the init skills comment
* fix(cli): shorten the init skills comment
* fix(cli): the stale-skills notice reaches agents that run without a terminal
* test(cli): a run without a terminal now checks skills in the background
* fix(cli): the stale-skills notice also prints on --json runs
* docs: align render, fps and transcribe docs with the CLI
- docs/guides/rendering.mdx: default render quality is `looks` (CRF 16),
not `standard`.
- docs/packages/cli.mdx, render --help (render.ts): default --output is the
timestamped `renders/<project>_<YYYY-MM-DD>_<HH-MM-SS>.<ext>`.
- skills/hyperframes-cli/references/preview-render.md: local --fps accepts
1-240 or a rational and defaults to the root data-fps, else 30; only
lambda and cloudrun are limited to 24, 30, 60.
- skills/hyperframes-cli/references/lint-validate-inspect.md: snapshot
--range is documented as START,END like every other reference.
- skills/media-use/references/audio.md, skills/media-use/audio/references/
transcribe.md, skills/hyperframes/references/captions.md: name the entry
point for each transcription default (transcribe.mjs prefers Parakeet;
`hyperframes transcribe` uses Parakeet when installed, else the small.en
whisper model).
- transcribe --model help text and docs/packages/cli.mdx: list `small` and
`medium`, which the CLI already accepts.
- skills-manifest.json: regenerated hashes for the three skills touched.
— Jerrai
* docs: lambda and cloud run also accept 25 fps
Distributed renders accept 24, 25, 30 and 60 fps (DISTRIBUTED_FPS in
packages/core/src/distributedFps.ts).
- skills/hyperframes-cli/references/preview-render.md: the --fps note
lists 24, 25, 30, 60 for lambda and cloudrun.
- skills/hyperframes-cli/references/lambda.md, cloudrun.md: --fps lists
include 25.
- packages/cli/src/docs/rendering.md (`hyperframes docs rendering`): --fps
said 24, 30 or 60 and --quality defaulted to standard; local render takes
1-240 or a rational, default root data-fps else 30, and the default
quality is looks.
- skills-manifest.json: regenerated hyperframes-cli hash.
— Jerrai
* chore(skills): regenerate skills manifest after formatting
The format hook rewrote skill markdown after the manifest hook had hashed
it, leaving the hyperframes-cli hash stale.
— Jerrai
Note that the desktop hand-off in `open` is macOS-only for now, that
`timeline apply --plan` prints only a count without --json, and what
`history undo --who` reverts.
Refs #5386
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(studio): motion blur panel with Off / Subtle / Strong
One three-way control in the right dock. Subtle is the default blur
(240 deg shutter, samples sized to the smear); Strong is 720 deg and 64
samples. Writes the root data-motion-blur attributes through the SDK so
renders match and undo works; reads live attributes from the preview.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* fix(studio): motion blur panel edits the file's own root
The edits were worked out against the preview DOM but written to the file,
so a stale preview (undo inside the hold, a slow reload) could save a level
other than the one picked. They are now computed inside the mutation from
the candidate's root. While locked to a sub-composition no level is shown.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* fix(studio): shorten the motion blur tab so the dock strip fits
Five tabs overflowed the default dock width by 10px and clipped a label
(Studio: load smoke). The tab is now titled Blur.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* feat: check names the filter fallback, the render log names the tier, Studio stops opting out
hyperframes check reports motion_blur_filter_fallback (info) with the reason capture could not
run and the way back to it. The engine's per-session log line gains tier=, and its pixel test
covers the fallback over a WebGL page. Studio no longer sends previewMotionBlur: every Chrome
can preview blur now, through the fallback where it lacks html-in-canvas; the server still
accepts false from an older Studio.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* feat(core): blur picks the cheaper filter tier when every mover only translates
The scan now notes whether any mover turns or scales (the linear part of its transform chain
changes) and whether it already forms a stacking context while it moves. When every mover only
translates, the filter tier draws as well as capture and costs about what no blur costs: on an
8 s bench shot a render took 7.0 s against 9.9 s with capture and 7.3 s with no blur. Anything
that turns or scales, or moves without a transform, stays on capture.
data-motion-blur-tier="capture" or "filter" forces a tier. The report says which one drew
and why (translation-only, tier-attribute); hyperframes check stays quiet for those two, which
are choices, not fallbacks. The pixel tests run the shared assertions through both tiers.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* docs(motion-blur): the two tiers, the tier attribute, and what changed in limits
The guide described blur turning off for WebGL, a root transform and similar. It now says the
filter tier draws instead, how the tier is chosen and forced, what each does with a turn or a
zoom, the measured cost of each, and which findings check emits. The skill note drops the
off-for-WebGL advice and the 1.7x figure.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* feat(core): blur defaults match a 240 degree shutter and at least 32 samples
The shutter opens 240 degrees by default (was 180) and each mover gets at least 32 taps (was
sized to the smear alone, down to 2), growing with the smear as before. These are the settings
the shutter blur Jake built uses; on the card whip the default render is now 51 dB from his
(34.6 dB at the old defaults). The guide's attribute table says so.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* chore(core): trim tier.ts comments to the ratchet
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* chore: allow deleting the Studio previewMotionBlur render test
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* feat(studio): add a preview seek bar
* fix(studio): preserve preview height with the seek bar
* fix(studio): keep the preview seek thumb fully visible
* 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
* feat(aws-lambda): price arm64 chunk invocations at the arm64 rate
getRenderProgress takes chunkArchitecture, the architecture of the function
that renders chunks. RenderChunk invocations are priced at its per GB-second
rate, both inline and from a Distributed Map's output; plan, audio and
assemble stay at the x86 rate.
* feat(cli): render chunks on an arm64 function with lambda deploy --video-arch
--video-arch arm64 pushes the arm64 video function image to the stack's own
ECR repository and deploys the template with VideoImageUri set to its digest.
Outside a checkout the image is the one the installed adapter pins; inside a
checkout it must come from HYPERFRAMES_LAMBDA_VIDEO_IMAGE, built from that
checkout. The default stays x86_64, which always sends an empty VideoImageUri
so a redeploy drops a video function rather than keeping it.
destroy deletes the repository, render and progress price chunks at the arm64
rate, and policies lists the ECR actions.
* fix(cli): push the video image only to an ECR repository this stack tagged
* fix(cli): price the plan at the video function's rate now that it runs there
* feat(cli): lambda deploy renders on arm64 by default; --video-arch x86_64 keeps x86
* refactor(cli): drop two doc comments the names already carry