Commit Graph
4002 Commits
Author SHA1 Message Date
Vance IngallsandClaude Fable 5 0e58344dca feat(engine): frame-capture core — fast-capture routing, worker-encode, dedup extension (#1919)
* feat(engine): drawElementImage capture service

* chore(ci): ignore drawElementService exports pending upstack consumers

Fallow's per-PR audit diffs against the merge base, so the bottom of the
fast-capture stack (#1917) sees drawElementService's exports as unused —
their consumers (frameCapture) land in #1919, two PRs upstack. ignoreExports
entry documents this and can be dropped once #1919 merges.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(engine): 3D projection + compositor-effect risk gate

* fix(engine): gate filter drop-shadow wherever blur gates (review)

detectCssEffectRisk documented drop-shadow as a ~29dB damage case but only
detected blur( in its three scan paths — a drop-shadow comp stayed on the
fast path despite the gate's own correctness contract. Detect drop-shadow(
in computed styles, stylesheet rules, and GSAP tween vars, pinned by a
focused test that runs the real page-side closure against a DOM shim
(computed / stylesheet / tween coverage + blur regression + effect-free
null).

Addresses miguel-heygen's blocker on #1918.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(engine): frame-capture core — fast-capture routing, worker-encode, dedup extension

# Conflicts:
#	packages/engine/src/services/screenshotService.ts

* fix(engine): document HF_FORCE_DRAWELEMENT as diagnostic-only; make armStaticDedup idempotent (review)

Addresses miguel-heygen's blockers on #1919:

- HF_FORCE_DRAWELEMENT promoted from a stale "SCRATCH/Uncommitted" comment to
  a documented diagnostic flag: it exists for upstream-Chromium repro work
  (gate-vs-API isolation, crbug 521861819 149-vs-151) and R&D on gated effect
  classes; renders under it may be damaged BY DESIGN since it bypasses gates
  whose thresholds encode measured damage. Never production; the safety-net
  blank guard also stands down under it so diagnostic frames arrive unmodified.
- armStaticDedup is now idempotent: the drawElement init path arms dedup
  before canvas injection, then initializeSession called it again — the
  second run overwrote the armed state with skipReason="capture_mode"
  (captureMode is "drawelement" by then), producing contradictory telemetry
  (armed frames + a skip reason), and re-ran the verification seeks on the
  fallback path. It now no-ops once staticFrames or a skip decision exists.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 16:21:12 -07:00
Vance IngallsandClaude Fable 5 4749fe5716 feat(engine): 3D projection + compositor-effect risk gate (#1918)
* feat(engine): drawElementImage capture service

* chore(ci): ignore drawElementService exports pending upstack consumers

Fallow's per-PR audit diffs against the merge base, so the bottom of the
fast-capture stack (#1917) sees drawElementService's exports as unused —
their consumers (frameCapture) land in #1919, two PRs upstack. ignoreExports
entry documents this and can be dropped once #1919 merges.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(engine): 3D projection + compositor-effect risk gate

* fix(engine): gate filter drop-shadow wherever blur gates (review)

detectCssEffectRisk documented drop-shadow as a ~29dB damage case but only
detected blur( in its three scan paths — a drop-shadow comp stayed on the
fast path despite the gate's own correctness contract. Detect drop-shadow(
in computed styles, stylesheet rules, and GSAP tween vars, pinned by a
focused test that runs the real page-side closure against a DOM shim
(computed / stylesheet / tween coverage + blur regression + effect-free
null).

Addresses miguel-heygen's blocker on #1918.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 16:19:44 -07:00
Vance IngallsandClaude Fable 5 795934adba feat(engine): drawElementImage capture service (#1917)
* feat(engine): drawElementImage capture service

* chore(ci): ignore drawElementService exports pending upstack consumers

Fallow's per-PR audit diffs against the merge base, so the bottom of the
fast-capture stack (#1917) sees drawElementService's exports as unused —
their consumers (frameCapture) land in #1919, two PRs upstack. ignoreExports
entry documents this and can be dropped once #1919 merges.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 16:19:17 -07:00
Vance Ingalls cd1adcb581 feat(studio): harden ui primitives — focus rings, keyboard tooltips, dialog hook (#1962)
## Summary

Base of the studio UX-review stack (148 findings audited across the studio; 13 critical). This PR hardens the shared `components/ui` primitives that every later PR in the stack builds on.

## Changes

- **Button / IconButton**: visible `focus-visible` outline (studio accent); `disabled:pointer-events-none` removed (replaced with `disabled:cursor-not-allowed`, hover/active gated behind `enabled:`) so disabled buttons can host explain-why tooltips.
- **Tooltip**: keyboard support (`onFocus`/`onBlur` triggers), `role="tooltip"`, Escape-to-hide, viewport flip (top↔bottom) + horizontal clamping. API unchanged — all ~28 call sites unaffected.
- **HyperframesLoader**: `role="status"` on the loader; determinate track is a real `role="progressbar"` with `aria-valuenow/min/max` (was `aria-hidden`).
- **VideoFrameThumbnail**: error event resolves to a static fallback-label tile instead of an infinite shimmer; `motion-reduce` guard.
- **NEW `useDialogBehavior`**: shared modal contract — document-level Escape, Tab focus trap, focus-first-on-open, focus-restore-on-close, `canClose()` veto for dirty-draft guards. Adopted by every modal later in the stack.
- **NEW `SearchInput`**: shared search primitive with required `aria-label`, panel-input token style (kills the two-divergent-search-styles inconsistency in the sidebar).
- **studio.css**: `hf-toast-in/out` + `hf-backdrop-in` keyframes with `prefers-reduced-motion` guards (the previous `animate-in fade-in` classes were dead — no tailwindcss-animate plugin exists).

## Verification

- oxlint 0 errors, oxfmt clean, `tsc --noEmit` clean at stack top
- Full studio suite at stack top: 1189 tests pass

## Stack

PR 1/7 of the studio UX-review fixes. Merges bottom-up; the stack top is fully green (tsc + 1189 tests). Some shared-file edits span PRs, so intermediate branches may not typecheck in isolation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-06 16:07:44 -07:00
Vance Ingalls e04f6dda37 feat(engine,cli): drawElement fast-capture config + CLI flag (#1916)
## drawElement fast-capture — config + CLI flag (stack 1/6)

Foundation layer for the drawElement fast-capture feature: the config surface and CLI/Docker plumbing that the rest of the stack builds on.

### What this adds
- **`packages/engine/src/config.ts`** — new config fields for fast capture: `useDrawElement` / `enableDrawElementWorkerEncode` (macOS-GPU `drawElementImage` capture + worker-offloaded JPEG encode), resolved from env in `resolveConfig` (env `HF_DE_WORKER_ENCODE`). Wired alongside main's existing `staticFrameDedup` (unified downstream in 4/6).
- **`packages/cli/src/commands/render.ts`** — `--experimental-fast-capture` flag → sets `experimentalFastCapture`; `--debug` passthrough.
- **`packages/cli/src/utils/dockerRunArgs.ts`** — pass the fast-capture env through to the container.
- **`.github/workflows/fast-video-validation.yml`** — CI job validating fast-capture renders.
- `.oxlintrc.json` / `.fallowrc.jsonc` — ignore-pattern housekeeping for the new paths.

### Notes
- Config-only + entrypoint; no capture behavior yet (that's 2/6–4/6).
- Tests: `config.test.ts`, `dockerRunArgs.test.ts` added.

---
**Stack (drawElement fast-capture, rebased onto current `main`, supersedes #1295 + #1444):**
1. **#1916 config + CLI** ← you are here
2. #1917 drawElementImage capture service
3. #1918 3D projection + compositor-effect risk gate
4. #1919 frame-capture core (routing, worker-encode, static-dedup unification)
5. #1920 producer render stages + remote bg-image localizer
6. #1921 lint rule + player media sync

⚠️ Intermediate PRs (1–5) are split by package boundary for review and **do not each compile independently** (cross-file deps); the complete feature is green at the stack tip (#1921) — tsc-clean on engine + producer, 231 tests pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-06 15:57:30 -07:00
Ular Kimsanov 972bedf062 Merge pull request #1988 from heygen-com/feat/color-grading-docs
docs: document Studio Color Grading
2026-07-06 15:40:46 -07:00
ukimsanov b38c054baf docs: document Studio Color Grading 2026-07-06 15:29:37 -07:00
Ular Kimsanov c1aa35d069 Merge pull request #1987 from heygen-com/feat/studio-color-grading-inspector
feat(studio): add color grading inspector
2026-07-06 15:24:50 -07:00
Miguel Ángel 1a3330a972 fix(compiler): don't scan media tags inside comments/scripts; DOM-check auto-start (#1938) (#1940)
The timing compiler scanned raw HTML with tag regexes that weren't
comment-aware, so a comment or script merely mentioning `<video>`/`<audio>`
was rewritten as a real element — injecting id/data-start/data-hf-auto-start
into the comment text. That phantom attribute then tripped the probe stage's
substring check (`html.includes("data-hf-auto-start")`), launching an
unnecessary browser probe on every render with an unexplained empty reasons list.

- Mask comments, <script>, and <style> regions before the tag scan, then
  restore them verbatim (compileTimingAttrs, extractResolvedMedia).
- Replace the probe's substring match with a DOM query
  (video[data-hf-auto-start]) and add "auto-start video(s)" to the reasons list.
2026-07-06 18:19:35 -04:00
Miguel Ángel afa7f292fb feat(lint): warn when <html dir> can blank render output (#1893)
* feat(lint): flag dir="rtl" on <html> as a confirmed silent render failure

Two independent reports diagnosed the same exact bug: dir="rtl" (or any
non-ltr value) on <html> renders correctly in preview/snapshot but
produces a fully blank/black video from render, with no other
lint/validate/inspect check catching it - output file size (far smaller
than expected) was the only tell for both reporters. Both independently
confirmed the same fix: drop dir from <html>, keep lang, and scope
direction: rtl to individual text-containing elements via CSS instead.

Could not empirically verify the render pipeline's own root cause in this
session (headless Chrome screenshot capture is unreliable in this
sandboxed environment - even a baseline, non-RTL capture timed out), so
this ships the safe, already-confirmed advisory rather than guessing at a
runtime fix. Both reporters explicitly asked for exactly this: "deserves
a lint rule or render-time warning."

* fix(lint): only flag valid non-ltr html dir values
2026-07-06 18:16:40 -04:00
ukimsanov 04f96da169 fix(studio): share safe media path cleanup 2026-07-06 14:35:41 -07:00
ukimsanov e0090efbf6 fix(studio): avoid regex in media asset path cleanup 2026-07-06 14:15:30 -07:00
Miguel Ángel 769dc702c1 feat(cli): emit sign-in lifecycle telemetry (#2000)
* feat(cli): emit sign-in lifecycle telemetry

The CLI tracks command and render lifecycles but emits nothing for
`auth login`, so sign-in outcomes are invisible on the observability
dashboards — a completed sign-in, an abandoned browser flow, and a
rejected key all look identical (absent). This leaves a blind spot in
the same funnel the render events already cover.

Add three events mirroring the existing `trackX` pattern:
  - auth_login_started    (method: oauth | api_key)
  - auth_login_completed  (method)
  - auth_login_failed     (method, reason)

`reason` is a fixed low-cardinality enum (flow_error / no_credential /
rejected / invalid_input). No token, key, identity, email, or free text
is ever attached — consistent with the existing anonymous telemetry and
the `telemetry disable` opt-out. Wired into both the OAuth and
--api-key paths in `auth login`, with unit coverage for the new events.

* fix(cli): close sign-in telemetry funnel dropout gaps

Follow-up so `started` reconciles to `completed + failed` on the common
abandonment paths, which the first cut missed:

- Interactive prompt cancel (Ctrl-C) now surfaces as a throw that the
  single catch in the api-key path records as `aborted`, instead of a
  bare exit with no event.
- A stdin read that times out in non-TTY `--api-key` mode now records
  `aborted` before the error propagates, rather than exiting silently.
- OAuth split: a timed-out browser callback (user closed the tab) is
  tagged `flow_timeout`, separated from real `flow_error` (IdP/network),
  since the walk-away timeout is the dominant non-error dropout.

Also pre-plumb an optional `distinctId` on the three trackers, mirroring
trackRenderComplete/trackRenderError. Unused today; it lets a later
identity-level attribution be a one-line callsite change rather than a
signature sweep. Coverage added for the new reasons and forwarding.
2026-07-06 16:59:47 -04:00
ukimsanov 30a944de34 fix(studio): split color grading inspector files 2026-07-06 13:56:37 -07:00
ukimsanov 139cf568b4 feat(studio): add color grading inspector 2026-07-06 13:40:20 -07:00
Ular Kimsanov a4989dbe75 Merge pull request #1986 from heygen-com/feat/studio-media-processing-routes
feat(studio-server): add media processing routes
2026-07-06 13:39:01 -07:00
ukimsanov 413ee07da5 fix(studio-server): share background removal job runner 2026-07-06 13:27:45 -07:00
ukimsanov a3bf7eb995 feat(studio-server): add media processing routes 2026-07-06 13:25:04 -07:00
Ular Kimsanov 2757d32092 Merge pull request #1985 from heygen-com/feat/studio-color-grading-runtime
feat(runtime): render media color grading shaders
2026-07-06 13:18:06 -07:00
ukimsanov 644ae384a5 fix(runtime): satisfy color grading audit 2026-07-06 12:49:34 -07:00
ukimsanov 870964b0cf feat(runtime): render media color grading shaders 2026-07-06 12:44:43 -07:00
Ular Kimsanov e01ec16194 Merge pull request #1984 from heygen-com/feat/studio-color-grading-core
feat(core): add media color grading contract
2026-07-06 11:30:31 -07:00
jbernard077andClaude Fable 5 264f2f1401 feat(registry): add hw-scribble-transition — scribble transition
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:59 -04:00
jbernard077andClaude Fable 5 4467a8d61c feat(registry): add hw-frame — sketched frame
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:59 -04:00
jbernard077andClaude Fable 5 6624023374 feat(registry): add hw-path-text — text along a path
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:59 -04:00
jbernard077andClaude Fable 5 b77a163845 feat(registry): add hw-pipeline — hand-drawn flowchart
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:59 -04:00
jbernard077andClaude Fable 5 b0f6cc7b9b feat(registry): add hw-underline — squiggle marks
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:59 -04:00
jbernard077andClaude Fable 5 0ba613e571 feat(registry): add hw-box-label — hand-drawn box label
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:59 -04:00
jbernard077andClaude Fable 5 fae6560766 feat(registry): add hw-title — handwritten title
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:59 -04:00
jbernard077andClaude Fable 5 3beed70e56 feat(registry): add hw-text-cloud — speech bubble
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:59 -04:00
jbernard077andClaude Fable 5 f1a506eb4b feat(registry): add hw-callout-circle — scribble callout circle
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:59 -04:00
jbernard077andClaude Fable 5 95a2ae0a49 feat(registry): add hw-arrow — hand-drawn arrow
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:59 -04:00
jbernard077andClaude Fable 5 d4f1ad0c37 feat(registry): add hw-boil — hand-drawn boil
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:59 -04:00
jbernard077 e9d2792824 feat(registry): add yt-doc-lower-third — documentary lower third 2026-07-06 12:50:58 -04:00
jbernard077andClaude Fable 5 cc98f5fd6d feat(registry): add mk-emphasis-type — background emphasis type
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:58 -04:00
jbernard077andClaude Fable 5 1ead7a768a feat(registry): add yt-screen-warp — on-screen display effect
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:58 -04:00
jbernard077andClaude Fable 5 44cd520253 feat(registry): add mk-logo-sting — outline logo sting
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:58 -04:00
jbernard077andClaude Fable 5 ba774de05c feat(registry): add yt-vertical-fill — vertical media filler
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:58 -04:00
jbernard077andClaude Fable 5 4ce35b7cba feat(registry): add mk-line-graph — minimal line graph
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:58 -04:00
jbernard077andClaude Fable 5 c4425bfb40 feat(registry): add yt-circle-pointer — pointer circle + countdown
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:58 -04:00
jbernard077andClaude Fable 5 2039bf5f44 feat(registry): add mk-progress-stat — count-up stat card
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:58 -04:00
jbernard077andClaude Fable 5 4e0a7dda88 feat(registry): add yt-feather-highlight — feathered spotlight
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:58 -04:00
jbernard077andClaude Fable 5 63c6cd02ea feat(registry): add mk-usage-arc — hairline arc gauge
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:58 -04:00
jbernard077andClaude Fable 5 8948612359 feat(registry): add yt-logo-intro — logo intro sequence
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:58 -04:00
jbernard077andClaude Fable 5 eed6adafb0 feat(registry): add mk-cta-button — pill cta button
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:58 -04:00
jbernard077andClaude Fable 5 9036e3c76c feat(registry): add yt-prism-title — chromatic fringe title
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:58 -04:00
jbernard077andClaude Fable 5 fc40c882e1 feat(registry): add mk-clone-wall-transition — text clone wall transition
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:58 -04:00
jbernard077andClaude Fable 5 d924630f23 feat(registry): add yt-avatar-pip — circle pip window
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:58 -04:00
jbernard077andClaude Fable 5 ac8d34fb9d feat(registry): add mk-placeholder-grid — rounded media grid
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:58 -04:00
jbernard077andClaude Fable 5 a4ef52fcb0 feat(registry): add yt-comment-card — comment cards
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:58 -04:00