* perf(studio): the layer walk keeps its selector index, so scrubbing into new scenes is cheaper
* perf(studio): the layer walk keeps its selector index, so scrubbing into new scenes is cheaper
* fix(snapshot): video frames keep the colours render gives the same footage
* fix(snapshot): say so once when video colours cannot be matched to render
* test(snapshot): cut the HEVC fixture's encode time so the flake gate stays inside its timeout
* style(snapshot): format the HEVC fixture line
* 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
* docs(changelog): weekly digest 2026-10-19-2026-10-26
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* docs(changelog): embed weekly video for 2026-10-19 to 2026-10-26
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* feat(cli): check catches audio the render would lose or play as silence
check gains an Audio section, read from the media list the render mixes:
- error: a clip's source has no audio the render can read (its mix fails,
the film ships without sound)
- warning: a clip starts past the end of its file, or the part it plays
is silent
- info: the film has no sound at all
The bundle and media list are now built once and shared with the HDR
inspection; the audio pass runs while the browser pass does.
Fixes#2775
* fix(cli): check reports an unreadable audio source the way render handles it
A source with no audio track is refused by render's own media-type rule, now exported from the producer and called by check; a source that does not decode as audio fails the mix and stops the render.
* perf(cli): check samples a long clip at a few spots before reading all of it for silence
Sound at any spot ends the decode; a clip is only called silent after its whole played part is read.
* chore(cli): drop a comment the shared finding helper's name already says
* 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
* fix(engine): name where a root composition script threw and the element it looked up
* style(engine): format throw-site helper
* fix(engine): shorten the throw-site comment
* fix(engine): name a lookup only when the throwing line holds exactly one
* feat(cli): hyperframes <dir> opens the project in the app
* refactor(cli): one command-word lookup and one rewritten command line for the folder shortcut
* refactor(cli): shorten the folder shortcut comments to fit the comment ratchet
* fix(producer): deduplicate scene font faces before embedding
* fix(producer): address font deduplication review
* docs(help): document render font-face deduplication
The troubleshooting guide this section originally targeted was folded
into docs/help.mdx on main, so the section now lives under the render
troubleshooting entries there.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: James <james.russo@heygen.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(media-use): serialize concurrent preference records
* chore: prepare conflict resolution against current main
* fix(media-use): serialize concurrent preference records
* docs(media-use): clarify project preference file guidance
* chore: prepare conflict resolution against current main
* fix(media-use): refresh preference records against main
* chore: prepare conflict resolution against current main
* chore: merge main and refresh preferences skill manifest
* chore: prepare conflict resolution against current main
* chore: merge main and refresh preferences skill manifest
* chore: prepare conflict resolution against current main
* chore: merge main and refresh preferences skill manifest
* test(player): playbench measures first play, audio joins, automation, cuts and the autoplay fallback
* fix(player): playbench takes-heard windows follow the composition playhead
The host page has no player, so its samples carry no playhead.
The windows fell back to nominal cut times.
* fix(cli): preserve UTF-8 across subprocess output chunks
* chore: prepare conflict resolution against current main
* fix(cli): retain UTF-8 output handling with current main
* chore: prepare conflict resolution against current main
* fix(cli): retain UTF-8 decoding with process teardown updates
* chore: prepare conflict resolution against current main
* fix(cli): preserve decoded subprocess progress on current main
* docs(cli): scope Unicode output guarantees to cancellable workers
* docs(cli): name commands with Unicode-safe diagnostics
* fix(core): the fonts gate starts loading declared faces no laid-out text uses yet
Before composition scripts run, waitForFonts forces one layout and awaits
document.fonts.ready. A face that only script-built text uses has not started
loading at that point, so ready does not wait for it and the script measures
the fallback face. Start every face still unloaded after the forced layout,
under the existing timeout; a load that rejects falls through to the same
timeout warning instead of stopping the scripts.
Refs #5112
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(core): keep the fonts gate's comment share level; cover a face that never settles
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
The examples topic listed title-card and video-edit, which do not exist,
and none of the eight examples in registry/examples. List blank plus the
registry examples with their registry-item descriptions, and add a test
that fails when the topic and registry/examples disagree.
Fixes#5393
* fix(producer): a media src that resolves to the page itself is no source
A src of '#', '#t=1', '?' or '?x=1' reads back from the browser as the page URL
plus a suffix. decodedUrlPath strips the suffix, so the probe opened index.html as
a media file and failed the render with 'media sources are text documents'.
Discovery now drops any src whose URL without query and hash equals the page URL,
which also covers the blank and whitespace forms from the earlier fix.
* fix(producer): name the page-itself check instead of commenting it
The beginFrame probe now tells a timeout from a real rejection.
A timeout relaunches Chrome and probes once more with a longer budget.
An unsupported answer still falls back at once.
The off-canvas indicators (always mounted) and the Layers panel rebuild their layer list every
100 ms while a scrub mutates the preview, and each rebuild read the computed style of every element
of every scene, on screen or not. The runtime already hides everything inside a clip its visibility
pass hid, so those subtrees hold no layer: the walk now skips a clip marked data-hf-clip-hidden
with an inline visibility of hidden. The marker constant moves to studio-preview-mark so the runtime
and Studio share one owner.
* fix(producer): a render whose capture stops advancing fails within minutes
* fix(producer): keep the bounded probe read the stall change dropped
* chore(producer): shorten a test comment
* fix(producer): every retry batch gets its own first-frame window; the stall test waits for the render to settle
A retry that runs as several batches opened one stall window for the whole attempt, so a later batch whose browsers take longer than the no-progress window to land a frame was killed where main would have finished. The window now opens per batch. The stall tests also waited a fixed number of turns for real file IO after the last frame, which failed about 3 runs in 10 on a loaded machine; they now wait for the render to settle.
* fix(producer): a test reads the stall error fields instead of suppressions; size budget follows
* test(producer): split the scripted capture attempt into small helpers
* test(producer): the stall test advances fake time only while the scripted capture waits
* docs: light mode in the studio app's palette
Light mode takes the Studio app light palette (packages/desktop index.css)
instead of the hyperframes.dev beige ground: paper, cards, ink levels and
the light accent ink for links. Plain code text uses the page ink.
Dark mode (Night) is unchanged.
* docs(examples): film rows move by hand and remix shelves stack
The Inspiration film rows drift by a script, as on the homepage
(HyperFramesShowcase): drag, a sideways swipe or shift+wheel moves a row,
and keyboard focus centres a card. Remix shelves are columns that each
stack their cards, so cards of different shapes leave no holes.
* 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
* fix(producer): chunk capture round-robins only for callers that bill wall time
* fix(engine): worker, extraction and encoder sizing read the CPU quota, not the host core count
* Revert "fix(engine): worker, extraction and encoder sizing read the CPU quota, not the host core count"
This reverts commit cdd4d858c8.
* chore(producer): drop the doc comments the comment ratchet rejects
* feat(cli): skipContrastInMotion moves to HF_TOOL_CONTEXT check-review
The --skip-contrast-in-motion flag and HYPERFRAMES_CHECK_SKIP_CONTRAST_IN_MOTION
env give way to a check-review capability in HF_TOOL_CONTEXT, so only a harness
turns it on. The skill text and manifest go back to main.
* feat(cli): check-review findingFrames saves one plain frame per finding time
With HF_TOOL_CONTEXT check-review.findingFrames, every error/warning finding in
layout or contrast with a real bbox gets frameTime (the middle sample of its
longest held run, by the heldMs gap rule) and frame (the absolute path of an
unannotated full frame shared by every finding at that time). One seek and
screenshot per distinct time, capped at 60 times with errors first, into the
run's snapshot folder; not listed in snapshots, history or events. Without the
capability, output is unchanged.
* docs(cli): finding-frame comments name the check-review capability, not a flag
* fix(cli): validate finding frame times and preserve cancellation
* fix(cli): count finding frame progress toward the check deadline
* feat(cli): check-review inspect attaches the inspector's report to each framed finding
With findingFrames on, the finding-frames page is served with the
inspector's GSAP hooks. After each frame is captured, every finding framed
at that time is inspected by its data-hf-id, else its selector, and the
inspector's own report is attached as the finding's inspect field. A failed
inspection becomes that finding's { error } and never fails the check.
Without the capability nothing changes.
Drops the unused @types/mime-types devDependency that the dependency audit
flags once packages/cli/package.json is touched.
* fix(cli): stop finding inspections after cancellation
* fix(cli): reject ambiguous finding inspection targets
* fix(ci): build inspector before Windows CLI smoke
* fix(cli): parse viewport HTML with explicit server parser
* 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
* 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.
* feat(cli): skipContrastInMotion moves to HF_TOOL_CONTEXT check-review
The --skip-contrast-in-motion flag and HYPERFRAMES_CHECK_SKIP_CONTRAST_IN_MOTION
env give way to a check-review capability in HF_TOOL_CONTEXT, so only a harness
turns it on. The skill text and manifest go back to main.
* feat(cli): check-review findingFrames saves one plain frame per finding time
With HF_TOOL_CONTEXT check-review.findingFrames, every error/warning finding in
layout or contrast with a real bbox gets frameTime (the middle sample of its
longest held run, by the heldMs gap rule) and frame (the absolute path of an
unannotated full frame shared by every finding at that time). One seek and
screenshot per distinct time, capped at 60 times with errors first, into the
run's snapshot folder; not listed in snapshots, history or events. Without the
capability, output is unchanged.
* docs(cli): finding-frame comments name the check-review capability, not a flag
* fix(cli): validate finding frame times and preserve cancellation
* fix(cli): count finding frame progress toward the check deadline
* 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: an FAQ, a free-agents page, and 30 Days as a numbered list
The FAQ answers the questions people ask the docs assistant most, each
linking to its page. Make videos with a free agent shows OpenCode's free
models in the Studio app, local models through Ollama, and how open models
scored in Code2Video. 30 Days of HyperFrames becomes one numbered row per
lesson with its film on hover, and drops a link to a section that no
longer exists.
* docs: 30 Days previews honour reduced motion and have a play button
Hovering a lesson started its film even with reduced motion on, and
keyboard and touch visitors had no way to play it. Hover previews now
follow the setting (read on the first render, and stopped if it turns
on), and each film is a button that plays and pauses on purpose.
* docs: 30 Days plays one chosen film at a time
Choosing a lesson film pauses the one chosen before, so every button
matches its film, and other rows do not preview while a film plays.
* docs: one film at a time across the whole 30 Days page
The page mounts three lesson lists, and each only paused its own films.
A chosen film now plays with sound, and any unmuted start on the page
makes every list stop its own films and drop its choice; previews stay
muted and wait while a film with sound plays.
* docs: 30 Days list in the docs palette
The lesson list used fixed black and white tints; its rules, numbers,
lines and film backgrounds now use the palette tokens, so they match the
beige ground and the Night theme.