Commit Graph

3576 Commits

Author SHA1 Message Date
ukimsanov e2b66d23cf docs: trim generated project scaffolding 2026-08-03 15:43:07 -07:00
ukimsanov 27a349155a docs: remove generated reference artifacts 2026-08-03 15:38:41 -07:00
ukimsanov 8429a43a9e docs: serve documentation media from CDN 2026-08-03 15:24:54 -07:00
ukimsanov 0111bc3b89 docs: preserve the weekly update archive 2026-08-03 15:03:23 -07:00
ukimsanov 958000b2e3 docs: simplify the reference project example 2026-08-03 15:02:59 -07:00
ukimsanov 350ede57e8 docs: remove redundant and unverified guide claims 2026-08-03 15:02:59 -07:00
ukimsanov ea57b740a5 docs: complete Guides task endings 2026-08-03 15:02:59 -07:00
ukimsanov 9d8da15190 docs: expose Studio follow-ups and reference project 2026-08-03 15:02:59 -07:00
ukimsanov 41ba576dd2 docs: clarify Guides and Studio navigation 2026-08-03 15:02:59 -07:00
ukimsanov 4290057562 refactor(docs): remove unused reference story 2026-08-03 15:02:59 -07:00
ukimsanov a7ba5455b0 docs: simplify examples and idle video controls 2026-08-03 15:02:58 -07:00
ukimsanov a7bde7444b docs: polish navigation and video controls 2026-08-03 15:02:58 -07:00
ukimsanov 948c37a773 docs: prepare related topics for core hubs 2026-08-03 15:02:58 -07:00
ukimsanov bbdcbe29d8 fix(docs): restore native code block styling 2026-08-03 15:02:58 -07:00
ukimsanov 109ba1f41e docs: polish navigation and video presentation 2026-08-03 15:02:58 -07:00
ukimsanov e1e240edcf fix(docs): tighten journey release details 2026-08-03 15:02:58 -07:00
ukimsanov 63ac0b4a48 refactor(docs): trim journey assets and components 2026-08-03 15:02:58 -07:00
ukimsanov 24eb051809 fix: exclude compiled docs embed from CodeQL 2026-08-03 15:02:57 -07:00
ukimsanov 167e372aad chore: scope CodeQL for generated docs embed 2026-08-03 15:02:57 -07:00
ukimsanov cb5a3b7332 chore: exclude generated reference html from formatting 2026-08-03 15:02:57 -07:00
ukimsanov ead05f5ec7 docs: make journey pages interactive 2026-08-03 15:02:57 -07:00
ukimsanov eee496b4a8 docs: add canonical reference project 2026-08-03 15:02:57 -07:00
ukimsanov 0a7432bcf5 docs: rebuild journeys guides and studio 2026-08-03 15:02:57 -07:00
James Russo 1d0d4d8939 docs(changelog): weekly digest 2026-07-27–2026-08-03 (#2969)
* docs(changelog): weekly digest 2026-07-27–2026-08-03

* docs(changelog): embed weekly video for 2026-07-27 to 2026-08-03
2026-08-03 15:08:01 -04:00
WaterrrForever bc6dbc7e21 fix(cli): stop dropping queued telemetry when process.exit races the final flush (#2970)
* fix(cli): stop dropping queued telemetry when process.exit races the final flush

Two exit-path defects introduced by the 0.7.65 process-lifecycle refactor:

1. The 'exit' handler returned early once finalizeCli had started, which
   also skipped the flushSync() fallback. When an agent-pipe EPIPE killed
   the process mid-flush (the NORMAL teardown under Claude Code / Codex),
   the still-queued render_complete was silently dropped — fleet delivery
   fell from ~90% (0.7.55-0.7.64) to ~35%. flushSync() is now
   unconditional: empty queue is a no-op, event uuids dedupe re-sends.

2. The EPIPE handlers set commandFailed unconditionally, so every piped
   successful render scored success:false in cli_command_result (fleet
   success rate collapsed 89% -> 5-25%). EPIPE now only marks failure
   when the pipe died before the render artifact was validated, matching
   the existing isRenderSucceeded() exemption on the uncaughtException
   path.

Regression tests cover both: flushSync-after-finalize, and EPIPE
before/after artifact validation.

* fix(cli): don't score a validated render as failed due to pre-artifact noise

Review follow-up: commandFailed can be set by noise that precedes artifact
validation — a stray unhandledRejection mid-render, or an EPIPE firing
before markRenderSucceeded on a run that still completes. Once the
artifact validates, that earlier noise must not flip the run's
cli_command_result to success:false. Genuine failures keep a non-zero
exit code and are still caught by the exitCode check.

Extracted commandSucceededForTelemetry() and applied it at both tracking
sites (finalizeCli and the exit handler), with a regression test.

* test(cli): pin the production-reachable producer of the stale-failure override

Review note: the pre-artifact-noise test drives the scenario with an
EPIPE, which only reaches 'render validates afterwards' because
process.exit is mocked — that sequence can't occur in production. Add a
test for the reachable producer: an unhandledRejection before validation
(the handler deliberately does not exit), followed by a validated render,
must score success:true at exit code 0. Verified red on the pre-override
cli.ts.
2026-08-04 02:02:55 +08:00
Miguel Ángel 3cf268e1e5 Merge pull request #2960 from heygen-com/fix/registry-transform-motion
fix(registry): animate mk card offsets with transforms, not top/left
2026-08-03 10:16:02 +02:00
Miguel Angel Simon Sierra ce7d75dbaa fix(registry): animate mk card offsets with transforms, not top/left
mk-background and mk-clone-wall-transition tween the card's `top`/`left`.
Layout properties snap to integer device pixels, so the move stutters under
the seek-by-frame capture engine (lint: gsap_non_transform_motion). Both
cards sit at top:0/left:0 in CSS, so the values carry straight over to x/y,
and in clone-wall the later scale composes cleanly with the translate.
Re-rendered both and diffed frames against the previous output — identical,
as intended: this changes how the motion is computed, not how it looks.

Adds scripts/lint-registry-items.mjs (bun run lint:registry-items), which
mounts each item into a throwaway project and lints it. Registry items ship
as `<name>.html`, so `hyperframes lint <dir>` fails with "No composition
found" and these items had never actually been linted — which is how both
errors reached main. Verified the script reproduces the original failure on
the pre-fix source.

Left as a local command rather than a CI gate for now; wiring it up needs
two prior fixes, noted in the PR.
2026-08-03 10:14:37 +02:00
Miguel Ángel 7e95630060 Merge pull request #2939 from heygen-com/feat/registry-theme-families
feat(registry): mk / yt / hw theme families + beat-freeze-cut — 30 items
2026-08-03 09:53:54 +02:00
Miguel Angel Simon Sierra d3d286a1a5 feat(registry): add beat-freeze-cut
Beat-driven speed ramp, freeze-frame hit, and hard cut for music-led
promos and montages. Contributed as #2957; applied here as content so the
whole catalog set lands together.

Verified by rendering: 6s, clean, deterministic (the one Math.random hit
is a comment above a fixed pattern array). Opaque root, so no demo.html
is needed for its catalog preview.

Co-authored-by: akiyoshisan1218 <akiyoshisan1218@users.noreply.github.com>
2026-08-03 09:32:12 +02:00
James Russo 1e51eaec2c chore: release v0.7.90 (#2958) v0.7.90 2026-08-02 21:57:19 -07:00
James Russo d6191965cf fix: pin release publishing to merge commit (#2959) 2026-08-02 21:32:08 -07:00
James Russo 2339757377 fix: bound HDR and video extraction resources (#2955)
* fix: bound HDR and video extraction resources

* fix: trim negative video extraction preroll

* fix: skip invisible video extraction windows

* fix: preserve negative-start loop and held tails

* fix: cap finite video slots to source duration

* fix: bound held-tail frame extraction

* fix: plan from playable video duration

* fix: preserve open-ended held video tails

* fix: resolve held tails from decoded frames

* fix: normalize final-frame probe timestamps

* fix: handle unseekable final-frame sources

* fix: dedupe final-frame probes per render

* refactor: clarify output dynamic range contract
2026-08-02 20:21:54 -07:00
Miguel Ángel 67ffafb11c docs(contributing): refresh the catalog contribution guide (#2954)
The guide still described a 52-block registry, told contributors to run the
deprecated `validate` command, and listed gaps that have since shipped.

- Correct the counts: 113 blocks, 25 components
- `validate` -> `check` in the quick version
- Document the `demo.html` requirement for components (CI fails without it)
- Document `params` (drives the Studio customization panel) and the other
  optional registry-item fields
- Add the monospace caption floor and `fitTextFontSize()` to the quality bar
- Add a motion-review checklist: rules paired with a self-check question
- Replace the manual preview-MP4 step with what catalog-previews CI does
- Rewrite "What's Needed Right Now" by the job a shot does in a video, and
  drop the gaps that have shipped (karaoke, lower thirds, maps, news ticker)
2026-08-02 22:02:55 +02:00
Miguel Ángel 411ada0d90 chore: release v0.7.89 (#2953) v0.7.89 2026-08-02 20:25:14 +02:00
Miguel Ángel 59fe32b633 Merge pull request #2952 from heygen-com/fix/2951-ignore-generated-cache-watcher
fix(cli): ignore generated caches in project watcher
2026-08-02 19:51:43 +02:00
Miguel Ángel 1d01b9f2cf fix(studio): exclude generated caches from project metadata 2026-08-02 17:37:51 +00:00
Miguel Ángel 3eb7b1ffd5 test(cli): clarify watcher exclusion coverage 2026-08-02 17:25:17 +00:00
Miguel Ángel bf739a4db2 fix(cli): ignore waveform cache in project watcher 2026-08-02 17:19:51 +00:00
Miguel Ángel 7ea8250f50 fix(cli): ignore generated caches in project watcher 2026-08-02 17:13:19 +00:00
Vance Ingalls 74fadf69c4 chore: release v0.7.88 v0.7.88 2026-08-01 16:53:08 -07:00
Vance Ingalls 3f30de7f09 Merge pull request #2911 from heygen-com/docs/color-grading-chapter
docs(prompting): add colour grading and film effects chapter
2026-08-01 16:50:50 -07:00
Vance Ingalls 0c7c9bbdb0 docs: make the intensity explanation order-agnostic
Both pages explained intensity's independence by saying details and
effects are applied after the u_intensity mix. The conclusion was right
but the mechanism was wrong for several families.

Verified in the shader: applyCrtWarp runs on the uv before sampling;
sampleMedia itself carries pixelate, chromaBleed and the tape family;
sampleChromaticMedia and applyDigitalGlitch shape sampleColor — all
before the mix at runtime/colorGrading.ts:1234. Only grain,
filmArtifacts, monoScreen, engraving, crosshatch, halftone, twoInkPrint,
bloom, scanlines and vignette run after it.

Pre-mix effects are already present on both sides of
mix(sampleColor.rgb, applyColorGrade(sampleColor.rgb), u_intensity), so
intensity does not scale them either. Both pages now say details and
effects sit outside the mix — some before, some after — without making
the ordering the reason.

Reported by miguel-heygen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 16:36:12 -07:00
Vance Ingalls 343c025188 Merge pull request #2916 from heygen-com/ffprobe-5-invocation
fix(engine): reject stdin input, decode stdout correctly, bound its size
2026-07-31 20:07:03 -07:00
Vance Ingalls b5640a5464 Merge pull request #2915 from heygen-com/ffprobe-4-audio
fix(engine): make the AAC duration refinement safe, cancellable and LC-only
2026-07-31 19:58:27 -07:00
Vance Ingalls 97564b0268 Merge pull request #2914 from heygen-com/ffprobe-3-framerate
fix(engine): reject non-finite, negative and malformed frame rates
2026-07-31 19:58:13 -07:00
Vance Ingalls e794227908 Merge pull request #2913 from heygen-com/ffprobe-2-metadata
fix(engine): merge colour per field, detect alpha correctly, defer PNG parse
2026-07-31 19:57:33 -07:00
Vance Ingalls 9e275423e2 fix(engine): reject stdin input, decode stdout correctly, bound its size
Three issues in runFfprobe's process and stream handling.

A filePath of exactly "-" hung for 30 seconds. `--` stops option parsing,
so "-intro.mp4" is safe, but ffprobe rewrites "-" to `fd:` AFTER option
parsing and reads stdin — and stdin was an inherited pipe the parent
never writes to and never ends. The probe ran to the deadline and failed
with an empty diagnostic, because ffprobe never errored so stderr was
blank: 30010 ms and no message, against 28 ms for a normal missing-file
error. Rejected up front, and the child now gets stdio ["ignore", ...]
so no future invocation can block on stdin either.

stdout was decoded per chunk. `stdout += data.toString()` decodes each
64 KiB pipe chunk independently, so a multi-byte character straddling a
boundary became U+FFFD on both sides — verified: 200 KB of 3-byte
characters produced 15 replacements and a string 9 characters longer
than the source. -show_format output above ~64 KiB with non-ASCII tag
text returns silently mangled values, since JSON.parse still succeeds.
Now accumulated through StringDecoder.

Note on testing that one: U+FFFD is valid JSON string content, and
nothing on extractMediaMetadata's public surface exposes a tag value, so
there is no assertion that fails against the old implementation. Rather
than add a test that cannot fail, it is stated here and the bound below
is what the new test covers.

stdout was unbounded. stderr is capped by ManagedChildProcess but stdout
was not, and analyzeKeyframeIntervals emits one line per frame — an
all-intra ProRes proxy can produce an arbitrarily large string. Capped
at 8M characters, which real -show_streams JSON is nowhere near.

Tests: "-" rejected without spawning, the stdio shape, and the size
bound. Reverting the stdin guards fails 1. The first draft of the bound
checked before appending, so a single oversized chunk passed — the test
caught it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 19:53:01 -07:00
Vance Ingalls 361fd49926 fix(engine): allowlist AAC-LC for the packet refinement, not deny HE-AAC
The previous gate was a HE-AAC DENYLIST, so every other profile still
got the 1024-sample formula. ffprobe reports codec_name "aac" for all of
them; the framing lives in the profile:

  LC            1024 samples/frame   <- the only one this maths fits
  HE-AAC v1/v2  2048 output samples against a doubled sample_rate
  LD            512
  ELD           480
  Main/SSR/LTP  1024 nominally, unverified here
  xHE-AAC       variable

LD and ELD therefore had their already-correct container duration
overwritten with a value 2x / ~2.13x too large, and an unknown or
missing profile fell through — so an unrecognised HE spelling preserved
the exact truncation the previous commit set out to close.

Now an affirmative match on LC. Skipping the refinement is harmless:
format.duration is already correct before it runs.

Tests: 11 non-LC profiles (including LD, ELD, xHE-AAC, empty and
unrecognised) assert the container duration is kept AND that the second
probe is not launched; LC still refines, with whitespace tolerated. The
pre-existing duration table asserted that an UNPROFILED "aac" stream
refines — the behaviour under review — so it now states LC explicitly
and adds an unprofiled row that must not refine.

Also strengthened the `--` separator test while it was failing: it
compared a flattened count of 3 across three spawns, which one call
emitting three terminators would satisfy. Now asserts the last two argv
entries per call.

Reverting the allowlist fails 8.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 19:52:26 -07:00
Vance Ingalls 242a42f6c9 fix(engine): make the AAC duration refinement safe, cancellable and LC-only
The packet-count probe is a refinement — durationSeconds is already
correct from format.duration before it runs — but it was written as if
it were load-bearing.

It could fail the whole call. No try/catch, and `-count_packets` demuxes
the entire container against runFfprobe's fixed 30s deadline, so a long
AAC file on slow or network storage timed out and extractAudioMetadata
rejected. htmlCompiler catches that under the comment "Source file has
no audio stream", returns duration 0, drops the audio element, and the
render ships silent with no warning. Now caught, keeping the container
duration.

It ignored the caller's AbortSignal. Only the first probe received it,
so aborting during the packet probe let the child run to completion and
the call resolved with full metadata after cancellation — while
audioPadTrim's comment claims the wrapper preserves cancellation. The
signal is forwarded, and an abort still propagates rather than being
swallowed as a refinement failure.

It halved HE-AAC durations. ffprobe reports codec_name "aac" for
HE-AAC v1/v2 as well — the marker is in the profile field — and with SBR
each packet carries 2048 output samples against the doubled output
sample_rate, so the 1024 assumption computed exactly half. A 10:00
podcast became 5:00 and htmlCompiler truncated the audio there. Gated on
profile, with `profile` added to FFProbeStream.

Tests: probe failure, junk output, three HE-AAC profile spellings (which
also assert the second probe is not attempted), and that plain AAC-LC is
still refined. Reverting the guards fails 5.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 19:52:25 -07:00
Vance Ingalls 4d563fa752 fix(engine): guard the rounded frame rate and strict-parse rationals
Two paths the previous guards still let through.

Rounding could recreate Infinity after the finite check. `raw * 100`
overflows for a finite-but-huge rate — "1e307", "1e307/1" — so `rounded`
became Infinity and passed the positivity check, reaching exactly the
`-r Infinity` failure the finite guard exists to prevent. The rounded
result is now checked too.

The rational operands still used parseFloat. The plain-number path
switched to Number() so trailing garbage fails the whole string, but the
numerator and denominator did not, so "60fps/1", "60/1fps" and
"30garbage/1garbage" returned valid rates while the contract says
malformed frame rates fail closed. Both operands are now parsed strictly,
and an empty operand ("/", "/1", "30/") is rejected rather than coerced.

Tests: 8 malformed inputs and 3 overflow cases in the direct table.
Reverting either fix fails 5.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 19:52:17 -07:00