feat(registry): add media treatment overlays#2754
Conversation
There was a problem hiding this comment.
Pull request overview
Adds new “media treatment overlay” Registry components and enables Studio to serve locally-generated preview posters/videos for Registry catalog cards via the Studio server API.
Changes:
- Added four Registry overlay components (Camcorder HUD, Editorial Flash Overlay, Freeze-Frame Cutout, Organic Light Leak Overlay) including manifests, snippets, demos, and an owned MP4 asset for the light leak.
- Extended Studio’s adapter + routes to optionally serve local Registry preview media, and updated the Studio Vite adapter to prefer local generated previews when present.
- Added route-level tests covering success, validation, and adapter failure behavior for Registry preview serving.
Reviewed changes
Copilot reviewed 17 out of 23 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| registry/registry.json | Registers the new overlay components in the top-level Registry catalog list. |
| registry/components/organic-light-leak-overlay/registry-item.json | Defines the component manifest, install targets, tags, and CDN preview metadata. |
| registry/components/organic-light-leak-overlay/organic-light-leak-overlay.html | Provides the installable styling/snippet guidance for the owned light leak asset usage. |
| registry/components/organic-light-leak-overlay/demo.html | Demo composition showing how to drive the overlay via a paused GSAP timeline. |
| registry/components/freeze-frame-cutout/registry-item.json | Defines the component manifest, tags, and preview metadata. |
| registry/components/freeze-frame-cutout/freeze-frame-cutout.html | Provides the overlay HTML/CSS snippet for the freeze-frame cutout look. |
| registry/components/freeze-frame-cutout/demo.html | Demo composition showing the cutout/tape/flash driven via GSAP. |
| registry/components/editorial-flash-overlay/registry-item.json | Defines the component manifest, tags, and preview metadata. |
| registry/components/editorial-flash-overlay/editorial-flash-overlay.html | Provides the static flash overlay layers and CSS-variable customization surface. |
| registry/components/editorial-flash-overlay/demo.html | Demo composition showing a flash-cut transition driven via GSAP. |
| registry/components/camcorder-hud/registry-item.json | Defines the component manifest, tags, and preview metadata. |
| registry/components/camcorder-hud/demo.html | Demo composition embedding the HUD as a sub-composition. |
| registry/components/camcorder-hud/camcorder-hud.html | Installable HUD snippet with a seek-safe counter/REC blink timeline. |
| packages/studio/vite.adapter.ts | Overrides Registry preview URLs to a local /api/registry/previews/... route when generated preview media exists on disk; implements adapter read for preview bytes. |
| packages/studio-server/src/types.ts | Adds optional readRegistryPreview adapter API for preview bytes + content-type. |
| packages/studio-server/src/routes/registry.ts | Adds a GET route to serve local Registry poster/video previews via the adapter. |
| packages/studio-server/src/routes/registry.test.ts | Adds tests for preview serving success, validation errors, missing previews, and adapter failures. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
miguel-heygen
left a comment
There was a problem hiding this comment.
Audited: all four installable component snippets/manifests; Studio Registry route, adapter contract, Vite adapter implementation, route tests, runtime timeline binding/seeking contract, existing reviews, and CI at exact head f02ecc6b.
Trusting: demo visual composition and binary preview/media assets were inspected for integration shape but not pixel-reviewed; generated registry/registry.json ordering was not regenerated independently.
The three static host-driven overlays keep animation ownership explicit, Registry names/kinds are validated before adapter access, and local preview fallback preserves remote manifest URLs when a generated variant is absent. Copilot already covers Range support and the three GSAP version mismatches; I did not repeat them.
Blocker: registry/components/camcorder-hud/camcorder-hud.html:114-148 tells users to paste the HUD inside an existing composition, but creates a separate timeline under the fixed key camcorder-hud. Runtime standalone seeking only seeks a registered timeline when a matching [data-composition-id="camcorder-hud"] exists (packages/core/src/runtime/init.ts:2544-2550). In the advertised installed shape the host has a different ID and already owns its root timeline, so this HUD timeline is not composition-time-seeked; the counter/REC state can stay frozen or advance from wall-clock sibling activation instead of matching scrub/render time. The demo masks the defect by making its root composition ID camcorder-hud. Either make the installed HUD a real subcomposition with a matching ID/timing contract, or attach its tween to the actual host timeline; add a regression using a differently named host composition.
Ponytail: packages/studio-server/src/routes/registry.ts:25-33: shrink: the promise is boxed into { preview } only to distinguish rejection from a legitimate null. A direct try/catch around await adapter.readRegistryPreview(...) plus the existing null check is clearer. net: -3 lines possible.
— Magi
Verdict: REQUEST CHANGES
Reasoning: The installed Camcorder HUD violates the runtime timeline key contract in its documented usage shape, and the self-contained demo does not exercise that shape.
jrusso1020
left a comment
There was a problem hiding this comment.
Additive review at f02ecc6b. @magi has CHANGES_REQUESTED (the Camcorder HUD timeline-seek gap) and @Copilot commented (preview Range handling; gsap@3.15.0 vs 3.14.2 in the demo files). Layering my render/registry angle; not repeating those.
Concur with @magi's blocker — and I verified the blast radius is Camcorder HUD only. Magi's finding is right: camcorder-hud.html reads the host's composition id (var host = hud.closest("[data-composition-id]"), :122) but registers its paused timeline under the fixed key window.__timelines["camcorder-hud"] (:148). The runtime composition-seeks a registered timeline only when a matching data-composition-id exists, so the demo (whose root id is literally camcorder-hud) masks it; installed under a normally-named host the REC/counter timeline is never deterministically seeked → frozen or timing-dependent in capture. Additive scoping: I grepped the other three installed overlays — editorial-flash-overlay, freeze-frame-cutout, organic-light-leak-overlay register zero __timelines entries; each documents that the host's paused timeline owns its opacity/reveal, so they ride the host seek and are seek-safe. So Magi's fix is confined to Camcorder HUD, and the host-owned pattern the other three use is the correct one to mirror (register under the host's actual composition id, or drive the HUD from the host timeline) — plus the differently-named-host regression Magi asks for.
Good (verified): the preview route's path-safety is solid — registry.ts:22 rejects any itemName not matching ^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$ before the adapter touches disk, so ../ traversal can't reach readRegistryPreview's join(previewRoot, subdir, itemName + ext). Installed components pin the correct gsap@3.14.2 (the 3.15.0 drift Copilot flagged is demo-only). The four overlays are genuinely orthogonal to the #2751/#2752 color-grading contract (HUD copy / flash / cutout / light-leak are DOM dressing, not shader effects) — no contract duplication.
Nits (mine):
vite.adapter.tsrecomputesresolve(__dirname, "../../docs/images/catalog")and iterates["blocks", "components"]in bothlistRegistryCatalogandreadRegistryPreview— worth a shared const so the preview root + subdir list have one owner.readRegistryPreviewleans entirely on the route's slug regex for path-safety and doesn't re-validateitemNameitself. Fine for the dev-only Studio server today, but a defense-in-depth guard in the adapter would keep it safe if a second (non-route) caller ever appears.
Merge gate: the PR's own draft blocker — the 8 preview CDN URLs returning 403 until a maintainer with the HeyGen AWS profile uploads the generated previews — is the real pre-merge item, independent of the code review.
Verdict: COMMENT — I concur with @magi's CHANGES_REQUESTED (verified + scoped to Camcorder HUD); the CDN-upload draft blocker gates merge regardless. (Deferring any stamp to the maintainer.)
Review by Jerrai (hyperframes)
d91c2f4 to
a96419f
Compare
f02ecc6 to
6b7bb56
Compare
a96419f to
d0d9c38
Compare
6b7bb56 to
9b79f5b
Compare
d0d9c38 to
ba10d66
Compare
9b79f5b to
1a12cf4
Compare
jrusso1020
left a comment
There was a problem hiding this comment.
Re-review at 1a12cf41. @magi re-reviewing in parallel; independent verdict.
Magi's camcorder-hud blocker is resolved. The overlay (now registry/blocks/camcorder-hud/camcorder-hud.html) reads the host's data-composition-id at runtime, guards a missing host (:131 throws a clear error), and registers a paused seek-driven timeline under that dynamic compositionId (:160) — no more fixed __timelines["camcorder-hud"] key. The registration key now always matches the seek lookup, so it's deterministic on any host, not just the demo. gsap pinned to 3.14.2.
The CDN-preview draft-blocker is resolved by removal. The /registry/previews/:name/:kind studio-server route was deleted (route + tests + adapter type together), and the overlays moved to registry/blocks/ with local demo.html + backdrop assets — so nothing depends on the 8 CDN preview URLs that were 403'ing.
The restructure is clean. components/ → blocks/ (and freeze-frame-cutout → freeze-frame-dressing): registry.json registers all four under the new paths, no dangling components/ paths or old names remain in the registry, and CI is CLEAN at 1a12cf41.
important — the freeze-frame-cutout → freeze-frame-dressing rename is incomplete in two references:
skills/media-use/references/media-treatment-recipes.md:829—npx hyperframes add freeze-frame-cutout --no-clipboardstill uses the old name; post-merge thataddfails (no block by that name). Functional — a loud failure, not silent.docs/guides/color-grading.mdx:325— still names thefreeze-frame-cutoutblock (cosmetic).
Update both tofreeze-frame-dressingso the rename is complete.
Verdict: COMMENT — ready on the merits; Magi's blocker and the CDN issue are resolved and the restructure is clean. The only open item is the two dangling freeze-frame-cutout references above (important, but not blocking — loud failure). Deferring the stamp; happy to APPROVE on @jrusso1020's go.
Review by Jerrai (hyperframes)
miguel-heygen
left a comment
There was a problem hiding this comment.
Audited: exact head 1a12cf41; all four installable overlay blocks/manifests and demos; Camcorder HUD composition ownership and bundling regression; Registry catalog updates; preview-generation routing; all eight published poster/video URLs; current reviews/threads; and exact-head checks.
Trusting: preview visuals were not pixel-reviewed frame by frame; generated catalog ordering was inspected rather than regenerated locally; the focused registry bundling regression is covered by exact-head CI rather than a separate local dependency install.
My prior blocker is fully resolved. Camcorder HUD is now a real data-composition-id="camcorder-hud" composition with a dynamically keyed paused timeline, while its demo uses a differently named camcorder-hud-demo host and embeds the HUD via data-composition-src. The bundling regression verifies the differently named host survives and the inlined subcomposition registers under camcorder-hud, matching runtime seek semantics.
The prior publish gate is also cleared: all four poster URLs and all four MP4 URLs return HTTP 200. The old Studio Range and demo GSAP comments are outdated because that route delta was removed and current demos pin GSAP 3.14.2. Exact-head checks are green. No new correctness, registry-contract, render-seam, duplication, or unnecessary-complexity blocker found.
— Magi
Verdict: APPROVE
Reasoning: The HUD now obeys the runtime subcomposition/timeline contract with a differently named-host regression, published previews are live, and exact-head CI is green.
1a12cf4 to
32918fc
Compare
ba10d66 to
e65276f
Compare
32918fc to
7ae3dea
Compare
jrusso1020
left a comment
There was a problem hiding this comment.
APPROVE at 7ae3dea6 per @jrusso1020's go. Re-verified since my COMMENT at 1a12cf41:
- My one open finding is fixed. The
freeze-frame-cutout→freeze-frame-dressingrename is now complete —git grep freeze-frame-cutoutis empty across docs + skills, and the two dangling refs I flagged are corrected:docs/guides/color-grading.mdx(block name) andskills/media-use/references/media-treatment-recipes.md(npx hyperframes add freeze-frame-dressing, which would have failed post-merge under the old name). - The registry/overlay content I reviewed is unchanged — the camcorder-hud blocker resolution (timeline registered under the host's runtime
data-composition-id, with a null-host guard) still holds; the delta since1a12cf41is docs + base-movement only (the merged #2752init.tsvisibility fix and other merged PRs'proxyTranscoder/previewchurn), not this PR's own overlay code. - Full required suite green at this head; @magi also approved.
(Note: this is currently stacked on #2753's branch; if #2753 merges and this retargets onto main, I'll re-confirm the rebased head.)
Review by Jerrai (hyperframes)
What
Adds four editable Registry overlays: Camcorder HUD, Editorial Flash, Freeze-Frame Cutout, and Organic Light Leak. It also adds the read-only local Registry preview route used by Studio cards.
Why
Color and shader effects cannot express timeline dressing such as HUD copy, flashes, cutouts, or light-leak layers. These remain independent deterministic composition layers.
How
Three overlays are HTML/CSS/paused-GSAP components; Organic Light Leak installs one media asset. Local development serves generated preview posters/videos through the existing Studio adapter while published manifests retain CDN URLs.
Test plan
Draft blocker
The eight new preview CDN URLs currently return 403. The generated previews must be uploaded by a maintainer with the required HeyGen AWS profile before this PR is merge-ready. Installed overlay components themselves work locally.
Stack 4/6. Base: #2753.