Commit Graph
1513 Commits
Author SHA1 Message Date
Miguel Ángel 8dae5ef89b fix(core): make grading writes previewable and honor computed properties (#5157)
* fix(core): write only authored hue curves, so a single hue curve passes lint

* fix(cli): apply over a grading saved with empty hue curves instead of refusing it

* fix(core): read grading properties from computed style, and stop advising an inline start

* fix(cli): preserve validation during legacy hue repair

* fix(cli): preview grading writes without apply
2026-10-08 15:21:47 +00:00
Miguel Ángel 188475aaf2 fix(cli): group sampled check findings by element and rule (#5174)
* fix(cli): group sampled check findings by element and rule

* fix(cli): preserve unique elements when grouping audit samples

* fix(cli): retain geometry sample times until report grouping

* fix(cli): keep grouped finding evidence time paired with geometry
2026-10-08 10:38:45 +00:00
Miguel Ángel cf1b04bb71 feat(cli): accept host-managed HeyGen access tokens (#5177)
* fix(auth): accept host-managed HeyGen access tokens

* fix(auth): share environment header validation

* docs(auth): condense credential resolver guidance

* fix(auth): report the account a browser login just signed in

Identify the account from the tokens the login issued, not a fresh credential
lookup that an environment token or API key outranks.

* docs(auth): list the host access token in the CLI credential order

* fix(cli): name a rejected host access token on publish

Publish told a host-token user to log in again, which cannot help while the token outranks the login.

* docs(auth): show the media credential order main resolves

* fix(cli): warn at logout that a host access token still signs commands

One map of environment credential variables, owned by the resolver, now feeds the logout warning and both publish messages.

* docs(auth): scope the media credential order to the audio scripts

* refactor(cli): read every env credential name from the resolver's map
2026-10-08 10:33:57 +00:00
Xuanru LiandClaude Opus 5.5 4a335aa6a1 chore: release v0.8.141 (#5193)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 05:56:58 +00:00
Miguel Ángel 5c7f6316d3 chore: release v0.8.140 (#5175) 2026-10-07 10:04:56 +00:00
Miguel Ángel e21ab020da fix(cli): preserve transcribed words before speech onset (#5164) 2026-10-07 07:46:58 +00:00
Miguel Ángel dbb8222c8d fix(studio): catch up the preview after missed file changes (#5126)
* fix(studio): recover previews after missed file changes

* fix(studio): keep queued reloads with the current owner

* fix(studio): keep a queued outside change when switching compositions

* test(cli): the project watcher closes its parent folder's watch too

* fix(studio): keep the project-folder change out of the shared signature check

* fix(studio): keep the edited file's draft and reload every session after a reconnect

A reconnect or replaced folder is a project-wide change: every open SDK session reloads, a failed save is recorded against the file the draft belongs to, and two queued changes to different files merge into one project-wide reload.

* fix(studio): refresh the preview cache when the .hyperframes folder is replaced

* fix(cli): report the files of a folder moved into the project

Replacing a populated folder such as .hyperframes/ now reaches the signature cache through its files, so the shared signature rule stays as on main and Studio's own writes inside .hyperframes/ still invalidate nothing.

* refactor(studio): name the merged file-change payload and share the test sender

* fix(studio): keep a failed draft's recovery copy until that file itself saves

* style(studio): rewrap the merged payload line after the rename

* fix(studio): deliver a held change's full reload when its block clears

A clean save, Use external and Keep Studio now all deliver the merged scope a blocked change owes, and a moved-in .hyperframes folder reports its manifests so a dropped one still clears the cached signature.

* fix(studio): keep restored drafts out of the reload scope and clear the lint gates

* fix(studio): retain held reload scope before React renders
2026-10-07 07:32:16 +00:00
Miguel Ángel 4fcad1ed74 fix(media-use): use a host app's music tools; fail loudly without the heygen CLI (#5154)
* fix(media-use): use a host app's music tools; fail loudly without the heygen CLI

* fix(media-use): name the heygen CLI only for music and sound effects; keep the sfx test offline

* fix(skills): workflow skills use a host app's own music tools first

* fix(skills): say how a host app's music track reaches the narrated workflows

* fix(skills): set a host app's music track after the last audio pass

* fix(skills): a host-music film with no narration still fetches its sound effects

* fix(media-use): keep the stub-failure test off the machine's heygen CLI; sync the skills manifest

* fix(skills): audio passes keep a host app's music and sounds; silent needs no sound cues

* fix(skills): host audio keeps string frames, stops on a broken audio_meta.json, survives a re-run

* fix(skills): report a host audio entry with no path and a broken sidecar instead of crashing

* fix(skills): keep host audio written while an audio pass runs

* test(cli): serve the telemetry capture while the resolve child waits on it

* test(cli): fail the telemetry capture when the resolve child exits non-zero

* style(skills): wrap the host audio comment to the 120-column limit
2026-10-07 07:16:51 +00:00
Miguel Ángel 80c2547eac feat(cli): send missing features and host-app friction as ratingless feedback (#5140)
* docs(skills): ask agents inside a host app to report its friction as feedback

* feat(cli): send missing features and host-app friction as ratingless feedback

* fix(cli): keep the rating error, refuse blank reports and document ratingless feedback

* docs(skills): leave names and figures out of missing-feature reports

* docs(skills): keep anything private out of missing-feature reports
2026-10-07 06:40:37 +00:00
Miguel Ángel 9e0d05d450 fix(engine): auto worker count stays within the heap budget (#5149)
* fix(engine): automatic worker count never exceeds what the V8 heap can feed

A 24 GB Mac exporting a 43 s portrait project with 22 videos picked 5 capture workers against a 4192 MB heap that feeds about 4, and the render died at 62% with JavaScript heap out of memory. The heap budget was only advisory; it now caps the automatic count (bound 'heap'). An explicit --workers N is untouched.

* test(engine): heap-cap test restores its mocks even when an assertion fails

Review findings on #5149: the os and v8 mocks were restored only if every assertion passed, and two comments still called the heap cap advisory.

* docs(engine): heap budget comments say it caps auto sizing
2026-10-07 06:11:30 +00:00
Miguel Ángel 9fbec22596 fix(cli): load telemetry without the core and engine barrels, and stop the history test racing (#5153)
* fix(cli): load telemetry without the core and engine barrels, and stop the history test racing

* fix(cli): load history from its leaf subpath, and fail the history test fast on a crashed CLI
2026-10-07 06:05:41 +00:00
Miguel Ángel 2d98d5c15f fix(check): stop false sweep_static and text-overlap alarms on compositions that are fine (#5147)
* fix(check): see color, filter and media time under seek, and name data-no-timeline for stills

* fix(check): warn instead of fail when only audio advances, and judge text overlap on glyphs

* fix(check): judge glyph overlap by the glyph box, and see capitalized words and one-side borders

* fix(check): capitalize past leading punctuation, test outline color, trim a comment

* fix(check): capitalize words the way Chromium does, so digit-led words stay lowercase
2026-10-07 05:38:45 +00:00
Miguel Ángel cffc3aa4e1 feat(render): report encoding and assembling progress (#5137)
* feat(render): report encoding and assembling progress

* fix(render): close every progress stage and fail a render whose progress callback throws

* fix(render): send a repeated progress update once

* fix(render): keep Docker terminal output as before and drop the unneeded callback ending

* fix(render): keep one failure stage code per stage whatever its frame counts
2026-10-07 04:29:35 +00:00
Miguel Ángel 11e684d305 fix: the preview preloads the coming scene's images, and check stops failing on image swaps (#5128)
* fix(core): the preview loads the next scene's images before it starts

* fix(cli): check stops reporting image loads that its own scrubbing cancelled

* fix: preload only a coming clip's own images, and judge aborted images by whether they still decode

* fix(cli): count an aborted image as broken only when its decode fails

* style(cli): keep the aborted-image comment within the line limit

* test(cli): pin that a reload still pending at the cap is not reported

* test(cli): run the cap test on whatever timers are pending, and always restore real timers
2026-10-07 00:10:29 +00:00
Miguel Ángel e6daed3df0 chore: release v0.8.139 (#5136)
* chore: release v0.8.139

* chore: sharpen the v0.8.139 release summary
2026-10-07 00:06:22 +00:00
Miguel Ángel f7bba2288d fix(cli): read the WAV in JS so Built-in transcription runs inside Electron (#5129)
* fix(cli): read the WAV in JS so Built-in transcription runs inside Electron

* fix(cli): read an empty WAV as silence
2026-10-06 23:15:50 +00:00
Miguel Ángel b50dead1da feat(registry): gooey split block with metaball goo and directional motion blur (#5123)
* feat(registry): gooey split block with metaball goo and directional motion blur

A dot splits into a three-dot menu through a gooey neck, spins into a
swirl mark under shutter motion blur, breaks back into three dots and
merges into one. Timing, spacing and eases are measured off a reference
recording frame by frame.

The motion-blur snippet gains an opt-in `sharp: 0` option that hides the
crisp frame-time copy while an element moves, leaving only the shutter
average. The default is unchanged; the inlined copies carry the same text.

* feat(registry): grow gooey-split blades out of swelling dots

* refactor(registry): tidy gooey-split pose code and document sharp 0 limits

* fix(registry): hide a sharp 0 motion-blur target by marker, not its visibility

Hiding through the element's own visibility fought timelines that
write it (autoAlpha): a target faded in after attach stayed hidden at
rest. A marker attribute and one !important rule now do the hiding, the
default path writes nothing, and a declarative sharp other than 0 or 1
is refused like any other bad option.

* fix(registry): smear a motion-blur target that was hidden when blur attached

Copies froze the target's computed visibility at attach, so a target
entering with autoAlpha never smeared (and vanished entirely under
sharp 0). The group now carries the target's current visibility each
frame, read with the sharp 0 marker lifted, and a copy keeps a
descendant's visibility only where it differs from its parent's.

* test(registry): pin a copy keeping a descendant's own visibility

* fix(registry): keep a child's own hiding in the smear of a target hidden at attach

* refactor(registry): restore a lifted visibility in one call and lift any hidden form

* fix(registry): restore a lifted visibility even when copying styles throws

* fix(registry): let motion-blur copies hide with a clip that has ended

The smear group set an explicit visibility on every moving frame, and
copies used to freeze an explicit visible, so an ancestor clip hiding
after the last update could not hide them: copies from a 0-4 s
sub-composition stayed on screen at 16.4 s. The group now sets a
visibility only where the element's differs from its parent's.
Also regenerates skills-manifest.json for the motion-blur skill doc.
2026-10-06 21:24:38 +00:00
Miguel Ángel 156e87766c feat(cli): stream heard words on the transcribe --json progress stream (#5127)
* feat(cli): stream heard words on the transcribe --json progress stream

* test(cli): share the streaming transcribe helper

* fix(cli): keep install and download stoppable and whisper errors short while streaming
2026-10-06 20:44:45 +00:00
Miguel Ángel 34552a21d2 fix(cli): let parakeet-mlx detect the language and name the Parakeet install under auto (#5124)
* fix(cli): let parakeet-mlx detect the language and name the Parakeet install under auto

* docs(cli): say what --language does under each engine

* docs(cli): whisper transcribes as the --language, it does not filter

* chore(cli): drop a comment the install-hint condition already says
2026-10-06 19:04:28 +00:00
Miguel Ángel 0ca28db4f8 chore: release v0.8.138 (#5121) 2026-10-06 17:53:18 +00:00
WaterrrForever 6fe3c2ce94 feat(cli): catch up on what the desktop app did since a project was handed over (#5114)
* feat(cli): catch up on what the desktop app did since a project was handed over

The desktop app records each chat turn in .hyperframes/app-history.jsonl.
`hyperframes catch-up [dir]` prints the turns since the hand-off or the last
catch-up (what the person asked, what Framey did, which files changed) plus
the project files changed since, by the app or by hand, then marks them seen.

`hyperframes open` and Studio's Edit with Framey mark the hand-off moment.
Any command run on a project with unseen turns ends with a line naming
catch-up, so an agent back in the terminal hears about it even when it
forgets. /hyperframes and /hyperframes-cli tell the agent to catch up as
soon as the person writes again.

* feat(cli): hand a Grok terminal session over to the desktop app

Grok gives every tool command its session as GROK_SESSION_ID (grok 1.0.4),
so `hyperframes open` and Edit with Framey now name a Grok session in
.hyperframes/agent-handoff.json, after Claude Code and Codex. The desktop
app reads its turns from Grok's own session files.

* feat(cli): tell a new project's agent to catch up after the desktop app

Scaffolded projects' CLAUDE.md and AGENTS.md now say to run
`npx hyperframes catch-up` before the next change once the project was
opened in the desktop app. Claude Code, Codex and Grok read these files on
their own, so a resumed session catches up even when it never loaded a
HyperFrames skill: a resumed Claude Code session that had not, asked to
change the title, ran catch-up first.

catch-up also prints each reply on one line.

* fix(cli): read and write .hyperframes records without following links, mark seen after showing

Review (Jerrai, Rames):
- One helper (projectRecords.ts) now reads and writes every record in
  .hyperframes/. A linked folder is refused. A read opens once with
  O_NOFOLLOW and checks the open file is a plain file, which clears the
  CodeQL race. A write goes to a temp file renamed over the record, so a
  link is replaced, never written through. agent-handoff.json, the seen
  record and the history all go through it.
- catch-up marks a turn seen only after showing it, and only up to the
  newest turn shown, so a turn written just after the read shows next
  time. Changed files have their own "checked" time.
- Turns show only once the project was handed over from this machine, so
  a history file shipped in a cloned repo is never shown as the person's
  words. The end-of-command check skips reading when the history is
  older than the seen record.
- The skills mention catch-up only when the CLI printed a line naming it,
  so an older CLI never meets an unknown command.
- The notice test uses basename, which fixes Windows.

* feat(cli): add the catch-up line to an older project's instructions on hand-off

A project scaffolded before catch-up existed has no line telling its agent
to catch up, and a resumed session that never loaded a HyperFrames skill
then edits without knowing what happened in the app. When `hyperframes open`
or Edit with Framey hands such a project over, its scaffolded CLAUDE.md and
AGENTS.md (they start "# HyperFrames Composition Project") gain the
template's line, in the template's place. A file of the person's own, a
link, a missing file, or one that has the line is left alone. The file is
rewritten through a temp file with its mode kept. A test keeps the line the
same as the template's.

* fix(cli): keep catch-up's cursor in the person's home, never reset it, read without links on Windows

Review (Rames, Jerrai):
- The seen record moved from the project to ~/.hyperframes/catch-up/,
  keyed by the project's real path. A seen file a cloned repo commits
  can no longer make its history read as the person's words.
- A second hand-off (`open` again, or another Edit with Framey click)
  keeps the cursor, so turns the app recorded meanwhile still show.
- Reads lstat the path, open it once, and refuse a file whose dev/ino
  differs from what lstat saw. That holds where O_NOFOLLOW doesn't
  exist, fixing the Windows test. The note writer reads the same way,
  which clears the new CodeQL alert.
- Writes use core's replaceFileAtomically, which retries a rename that
  Windows briefly refuses.
- catch-up names every changed file, and the cursor is the newest `at`
  shown, not the last line's.
- `open --json` carries `catchUp` after a hand-off.
- The bin scripts are back to mode 644.

* fix(cli): open before checking for CodeQL, never show a turn dated ahead

Review (Rames, Jerrai):
- readPlainFile opens the file first, then requires lstat to find the same
  plain file (dev/ino) at the path, and reads only through the fd. That is
  just as safe, and it clears CodeQL's check-then-use alert.
- catch-up skips turns dated after the moment it reads. So a cloned history
  can't show a far-future turn as the person's words or park the cursor
  past real ones.
- The seen key uses core's realpath (realpathSync.native), so letter case
  can't split a project in two. ~/.hyperframes/catch-up is made 0700, and
  a hand-off that can't note itself says so.
- Tests stub HOME with vi.stubEnv and restore it.
2026-10-06 13:20:27 +00:00
Miguel Ángel f6e419eb29 fix(cli): use a healthy pinned Sherpa copy beside the CLI (#5095)
* docs(cli): map the pinned Sherpa runtime loading contract

* fix(cli): use a healthy pinned Sherpa copy beside the CLI

* chore(cli): keep runtime loader comments within the ratchet

* docs(cli): wrap the runtime contract palette declarations

* chore(cli): keep the Sherpa contract note out of the repo
2026-10-06 05:43:09 +00:00
Miguel Ángel e7b69785ea chore: release v0.8.137 2026-10-06 00:39:59 -04:00
Miguel Ángel 1db44c6595 fix(cli): report transcription language and progress (#5103)
* docs(cli): record transcription output contract

* fix(cli): report transcription language and progress
2026-10-06 03:42:43 +00:00
Miguel Angel Simon Sierra 3026723aa1 chore: release v0.8.136 2026-10-05 19:11:20 -07:00
Miguel Ángel d45cb34e2f fix(cli): init refuses a name too long for a folder in one plain sentence (#5071)
* fix(cli): init refuses a name too long for a folder in one plain sentence

* fix(cli): validate resolved init paths and cover interactive names
2026-10-06 01:52:44 +00:00
Miguel Angel Simon Sierra 2f9c7dba32 chore: release v0.8.135 2026-10-05 16:49:32 -07:00
Miguel Ángel 60fc69d4f4 chore: release v0.8.134 2026-10-05 12:05:03 -04:00
Miguel Ángel f5e9c4bea4 chore: release v0.8.133 2026-10-05 08:25:49 -04:00
Miguel Ángel 46d7237f39 chore: release v0.8.132 2026-10-05 07:27:54 -04:00
Miguel Ángel 07d05cc459 chore: release v0.8.131 2026-10-05 07:02:33 -04:00
Miguel Ángel afb266d3fe chore: release v0.8.130 2026-10-05 05:40:59 -04:00
Miguel Ángel 198eb10f59 chore: release v0.8.129 2026-10-05 04:53:17 -04:00
Miguel Ángel c6478e404c fix(studio): undo takes back the first edit of a film whose media is still being copied (#5043)
* fix(studio): undo takes back the first edit of a film whose media is still being copied

* fix(studio): the first undo of a film repaints the preview in place

* fix(studio): background media copies keep the media ledger and survive a history prune

* fix(studio): a busy project history never makes writes wait for its lock again

* refactor(studio): name the restore stamping instead of commenting it

* perf(studio): undo stamps only a restore side that has no stamps

* fix(studio): closing a project history finishes its media copy first

* fix(studio): media copies stay resumable until recorded and skip a replaced folder

* fix(studio): the history lock wait comes back after a successful open
2026-10-05 08:05:14 +00:00
Miguel Ángel 5765c63244 chore: release v0.8.128 2026-10-05 03:55:42 -04:00
Miguel Ángel b3d2b82fe9 feat(core): the renderer's font pipeline is importable from @hyperframes/core/fonts/embed (#5041)
* feat(core): the renderer's font pipeline is importable from @hyperframes/core/fonts/embed

* style(core): format the font pipeline and state the bundled-subset invariant

* test(producer): repin the test lane map after the font tests moved to core

* fix(core): images build fonts once, CLI bundles one pipeline, font tests pass on Windows

* test(core): font retry tests get a budget no slow machine can exhaust

* test(core): the Retry-After budget test fails when the budget is ignored
2026-10-05 04:54:53 +00:00
Miguel Angel Simon Sierra da1ac75718 chore: release v0.8.127 2026-10-04 17:25:23 -07:00
Miguel Angel Simon Sierra a9787836e8 chore: release v0.8.126 2026-10-04 15:36:19 -07:00
Miguel Angel Simon Sierra b6c89b5583 chore: release v0.8.125 2026-10-04 13:35:42 -07:00
Miguel Angel Simon Sierra 706118aa1a chore: release v0.8.124 2026-10-04 10:06:35 -07:00
Miguel Ángel dcc721062b fix(cli): a project .env cannot set the feedback email in another letter case (#5018)
* fix(cli): a project .env cannot set the feedback email in another letter case

* fix(cli): capture loads a project .env through the same rules as the CLI
2026-10-04 15:54:34 +00:00
WaterrrForever 6a27a34f21 feat(cli,studio): hand off on macOS now, and open the app on Windows and Linux (#4999)
* feat(studio,cli): add an Edit with Framey button and `hyperframes open` for HyperFrames Studio

Point people from the CLI and the Studio preview to HyperFrames Studio, the desktop app.

- Studio header: an "Edit with Framey" button with Framey, the app's cursor mascot (the eye
  follows the pointer, a wiggle on hover, honours reduced motion). Until the app opens
  handed-over projects (HANDOFF_READY), a press shows a "Meet Framey" card with the download.
  The button shows only where the preview server answers GET /api/open-in-desktop on macOS,
  so the desktop app's own embedded Studio never shows it.
- `hyperframes open [dir]`: hands the project folder to the app with `open -b` (released app,
  then Canary) and points to the download when neither is installed. When run by Claude Code
  or Codex it leaves .hyperframes/agent-handoff.json (CLAUDE_CODE_SESSION_ID / CODEX_THREAD_ID)
  so the app's first chat can pick up that conversation.
- render and preview print one line about the app: the download until HANDOFF_READY, then
  `hyperframes open` when the app is installed. Nothing for a draft render, a batch row, or a
  run inside the app (HYPERFRAMES_DESKTOP_PROJECT).

* fix(studio,cli): gate `hyperframes open` too, guard the open-in-desktop POST, keep one gate

Review of #4951 at 58934a34.

- One gate: HANDOFF_READY lives only in the CLI. `GET /api/open-in-desktop` now says
  `handoff`, and Studio's button reads it, so the gate cannot be half-flipped.
- While gated nothing opens and nothing is written: `openInDesktop` returns
  `handoff-unavailable` (so `hyperframes open` prints the download line and the POST route
  answers with it), and no `.hyperframes/agent-handoff.json` can sit waiting for a later release.
- The POST answers only Studio's own same-origin request (loopback Host, matching Origin,
  Sec-Fetch-Site same-origin); a cross-site or rebound-host POST gets 403. Routes moved to
  server/desktopRoutes.ts with their own tests.
- `hyperframes open`: exits 1 when nothing opened, answers in JSON under --json for a bad
  directory, and tells a missing app from one macOS could not open (`open-failed`). A project
  that cannot be written still opens, with nothing handed over.
- Tests drive the live path through `ready`, are path-portable for Windows, and cover the draft
  and batch-row hint skips (`wantsDesktopHint`, `batchRowRenderOptions`).
- The load smoke mocks the route.

* feat(cli,studio): hand off on macos now, and open the app on windows and linux

The released Mac app (b254) takes a handed-over folder and its conversation, so
HANDOFF_READY turns on for macOS. Windows and Linux stay on the download until the
desktop change that reads a folder from the command line ships.

- `hyperframes open` finds the app off macOS: the per-user install on Windows, and
  on Linux the AppImage the app records in launcher.json; it starts it with the folder
- download links follow the machine: the DMG on macOS, the AppImage on Linux, and
  nothing on Windows, which has no public download yet
- the Studio button shows wherever there is a download or an installed app; the card
  no longer says "for macOS"
- every render prints the app line except batch rows: music-to-video delivers a draft
- the success line names the app that took the folder (Canary included)

* fix(studio): the edit-with-framey button asks its route through studioApiFetch

* fix(cli,studio): name the app the hyperframes desktop app, apart from the studio preview

* fix(cli,studio): fewer comment lines, so no file's comment share rises
2026-10-04 12:54:17 +00:00
Miguel Angel Simon Sierra 638e6b7b33 chore: release v0.8.123 2026-10-04 05:06:49 -07:00
WaterrrForever bd0dc8c3ae feat(studio,cli): add an Edit with Framey button and hyperframes open for HyperFrames Studio (#4951)
* feat(studio,cli): add an Edit with Framey button and `hyperframes open` for HyperFrames Studio

Point people from the CLI and the Studio preview to HyperFrames Studio, the desktop app.

- Studio header: an "Edit with Framey" button with Framey, the app's cursor mascot (the eye
  follows the pointer, a wiggle on hover, honours reduced motion). Until the app opens
  handed-over projects (HANDOFF_READY), a press shows a "Meet Framey" card with the download.
  The button shows only where the preview server answers GET /api/open-in-desktop on macOS,
  so the desktop app's own embedded Studio never shows it.
- `hyperframes open [dir]`: hands the project folder to the app with `open -b` (released app,
  then Canary) and points to the download when neither is installed. When run by Claude Code
  or Codex it leaves .hyperframes/agent-handoff.json (CLAUDE_CODE_SESSION_ID / CODEX_THREAD_ID)
  so the app's first chat can pick up that conversation.
- render and preview print one line about the app: the download until HANDOFF_READY, then
  `hyperframes open` when the app is installed. Nothing for a draft render, a batch row, or a
  run inside the app (HYPERFRAMES_DESKTOP_PROJECT).

* fix(studio,cli): gate `hyperframes open` too, guard the open-in-desktop POST, keep one gate

Review of #4951 at 58934a34.

- One gate: HANDOFF_READY lives only in the CLI. `GET /api/open-in-desktop` now says
  `handoff`, and Studio's button reads it, so the gate cannot be half-flipped.
- While gated nothing opens and nothing is written: `openInDesktop` returns
  `handoff-unavailable` (so `hyperframes open` prints the download line and the POST route
  answers with it), and no `.hyperframes/agent-handoff.json` can sit waiting for a later release.
- The POST answers only Studio's own same-origin request (loopback Host, matching Origin,
  Sec-Fetch-Site same-origin); a cross-site or rebound-host POST gets 403. Routes moved to
  server/desktopRoutes.ts with their own tests.
- `hyperframes open`: exits 1 when nothing opened, answers in JSON under --json for a bad
  directory, and tells a missing app from one macOS could not open (`open-failed`). A project
  that cannot be written still opens, with nothing handed over.
- Tests drive the live path through `ready`, are path-portable for Windows, and cover the draft
  and batch-row hint skips (`wantsDesktopHint`, `batchRowRenderOptions`).
- The load smoke mocks the route.

* fix(studio): the edit-with-framey button asks its route through studioApiFetch
2026-10-04 10:52:23 +00:00
Miguel Angel Simon Sierra b7baa5fde1 chore: release v0.8.122 2026-10-04 03:39:06 -07:00
Miguel Ángel 44b5836250 fix(core): composition scripts start after web fonts are ready, so renders repeat (#4980)
* fix(core): composition scripts start after web fonts are ready

A scene script that measures text (offsetHeight, getBoundingClientRect) ran while
the page parsed, often before its web font loaded, so it laid the scene out with
fallback metrics. Renders of the same film differed run to run, and preview could
differ from export.

The compilers now give every body script a type the browser does not run
(text/hf-after-fonts, or +module), through one helper in scriptRuns.ts called
at the end of the bundler, the producer compile and the sub-composition preview.
The Studio motion script is emitted the same way so it still runs after them.

The runtime entry owns the wait: on DOMContentLoaded it forces a layout, waits for
document.fonts.ready or FONT_WAIT_TIMEOUT_MS (5 s), then re-creates each script
in its own place in parser order (classic in order, awaiting src loads; then
module and defer scripts in insertion order), boots as before, and calls the
DOMContentLoaded and load listeners the scripts added. On timeout the scripts
run anyway and runtime_font_wait_timeout names the families still loading.
Scene swaps and the composition loader use the same wait.

* fix(core): a page whose runtime predates the font gate still runs its scripts

A bundle can be served with a runtime loaded from HYPERFRAME_RUNTIME_URL. If that
runtime is older than the font gate, nothing would run the deferred composition
scripts and the film would be blank.

Whenever the marking helper defers scripts, it also puts one small inline script
first in the head. At DOMContentLoaded it runs the deferred scripts in parser
order itself, without waiting for fonts and with a console warning, unless a
runtime has claimed them. The gate's runtime claims them when it loads, before
DOMContentLoaded, so the scripts run exactly once either way.

* fix(studio-server): motion paths animate in Studio preview when gsap loads in the body

The MotionPathPlugin tag Studio inserts after a body gsap script now carries
that script's type, so the runtime runs it right after gsap once fonts are
ready, instead of before gsap exists.

* fix(cli): layout, motion-shot and validate wait for the composition scripts to run

The three commands waited on the timeline registry, which exists as soon as the
runtime file loads, or on a flat sleep. They now wait for the runtime's
render-ready flag through one shared helper, keeping each command's timeout and
proceeding as before when it passes.

* fix(core): a script that wraps addEventListener keeps its wrapper after its scripts run

While composition scripts run, the runtime patches addEventListener to hold
load events that already passed. Putting the original back now happens only if
the patch is still in place, so a library wrapper installed meanwhile survives.

* docs: comments on script timing match scripts that wait for web fonts

The early stub's flush and the player's iframe-load handler described page
scripts as done by DOMContentLoaded and the runtime as ready before load.
Composition scripts now wait for web fonts, so both can come later. Comment-only.

* test(core): pin script order after an external script and the scene swap's font wait

An inline composition script that follows an external one sees its global, and
a scene swap runs the new scene's script only once web fonts are ready.

* docs(producer): the early stub comment names what drains late font-gated work

* fix(studio): motion paths add without a flash when gsap loads after web fonts

Studio preloads MotionPathPlugin on iframe load. A body gsap now runs after web fonts, often after that load, so the preload retries once the runtime reports ready.

* test(cli): pin the runtime wait before each command first samples the page

* fix(studio): the motion path preload stops waiting once the preview runtime has booted

A composition with no global gsap posts ready before the preload runs; the retry listener then never detached and kept each retired preview document alive. It now waits only while the runtime has not booted, and the tests pin the cleanup and the ready filter.

* fix(studio): a preview frame that never boots is not kept alive by the motion path preload

Only the latest preview waits for the ready message of its runtime; a newer load drops the older wait, so a crashed or missing composition frame can be collected.

* style: comments added by the font gate move into names
2026-10-04 10:24:42 +00:00
Miguel Ángel 18a922f433 fix(media-use): a slow download no longer leaves an empty asset that undo brings back (#4989)
* fix(media-use): a slow download no longer leaves an empty asset that undo brings back

* docs(media-use): tell the person an OAuth sign-in makes voice and avatar video free

* docs(media-use): the OAuth free usage line names the monthly allowance

* docs(media-use): the OAuth line says a free allowance
2026-10-04 08:40:46 +00:00
Miguel Angel Simon Sierra a6fc2592e2 chore: release v0.8.121 2026-10-04 01:30:55 -07:00
Miguel Angel Simon Sierra ccd80ea621 chore: release v0.8.120 2026-10-03 23:34:55 -07:00
Miguel Ángel 0ca69472d9 feat(cli): feedback says whether a person or an agent wrote it, with the email an app attaches (#4984)
* feat(cli): feedback says whether a person or an agent wrote it, with the email an app attaches

* refactor(cli): split the .env loader so each part stays simple

* fix(cli): a broken .env loader fails loudly; only a missing .env is ignored
2026-10-04 05:07:49 +00:00