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.
| const { preview } = result; | ||
| if (!preview) return c.json({ error: "Registry preview not found" }, 404); | ||
| return new Response(new Uint8Array(preview.content), { | ||
| headers: { | ||
| "Content-Type": preview.contentType, | ||
| "Cache-Control": "no-cache", | ||
| }, | ||
| }); |
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script> |
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script> |
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script> |
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
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.