Skip to content

feat(registry): add VFX and Captions block categories#643

Merged
jrusso1020 merged 11 commits intomainfrom
feat/vfx-catalog-blocks
May 6, 2026
Merged

feat(registry): add VFX and Captions block categories#643
jrusso1020 merged 11 commits intomainfrom
feat/vfx-catalog-blocks

Conversation

@miguel-heygen
Copy link
Copy Markdown
Collaborator

@miguel-heygen miguel-heygen commented May 6, 2026

Summary

Add 12 new registry blocks across two categories — HTML-in-Canvas (7 blocks) and Captions (5 blocks) — plus renderer support and a bulk-install CLI feature.

HTML-in-Canvas Blocks

Install all at once: hyperframes add html-in-canvas

Block Description
vfx-text-cursor Dramatic text reveal with cursor glow, chromatic shadow rays, directional lighting
vfx-liquid-background Organic liquid simulation with vertex displacement
vfx-iphone-device Real GLTF iPhone 15 Pro + MacBook Pro with live HTML screens, glass lens, 360° turntable
vfx-magnetic Magnetic field particle visualization
vfx-portal Dimension breach portal with volumetric light rays
vfx-liquid-glass Voronoi glass fracture (54 shards) with MeshPhysicalMaterial transmission and parallax reveal
vfx-shatter HTML content shatters into physics-driven glass fragments

Caption Blocks

Install all at once: hyperframes add captions

Block Description
captions-slam Bold uppercase scale-pop with back.out(1.7) overshoot, per-word accent colors
captions-karaoke Word-by-word color reveal on frosted glass pill
captions-minimal Clean subtitle style with soft fade
captions-bounce Playful elastic bounce per word with colorful pills
captions-cinematic Elegant slow fade with letter-spacing animation and line accent

Renderer Changes

Cherry-picked from #611:

  • Enable --enable-features=CanvasDrawElement in Chrome browser args
  • Add native drawElementImage() capture path for shader transitions
  • Reuse renderer Chrome args in hyperframes validate

CLI: Bulk Install by Tag

hyperframes add now auto-detects whether the argument is a single block name or a tag:

hyperframes add html-in-canvas   # installs all 7 HTML-in-Canvas blocks
hyperframes add captions          # installs all 5 caption blocks
hyperframes add vfx-shatter       # installs one specific block

When the name doesn't match a registry item, the CLI falls back to tag-based resolution and bulk-installs all matching blocks.

Test plan

  • hyperframes add html-in-canvas installs all 7 blocks
  • hyperframes add captions installs all 5 caption blocks
  • Each block previews in Studio without errors
  • Each block renders via CLI without errors
  • registry.json schema validates
  • Format, lint, typecheck pass

Add 12 new registry blocks across two new categories:

**VFX Blocks (7):**
- vfx-text-cursor: dramatic text reveal with cursor glow and chromatic shadows
- vfx-liquid-background: organic liquid simulation with vertex displacement
- vfx-iphone-device: real GLTF iPhone + MacBook with live HTML screens
- vfx-magnetic: magnetic field particle visualization
- vfx-portal: dimension breach portal transition
- vfx-liquid-glass: Voronoi glass fracture with parallax reveal
- vfx-shatter: glass shatter with physics-driven fragments

**Caption Blocks (5):**
- captions-slam: bold uppercase scale-pop with back.out overshoot
- captions-karaoke: word-by-word color reveal on frosted glass pill
- captions-minimal: clean Netflix-subtitle style fade
- captions-bounce: playful elastic bounce with colorful word pills
- captions-cinematic: elegant slow fade with letter-spacing animation

All blocks include registry-item.json and are registered in registry.json.
VFX blocks use experimental stability, caption blocks use stable.
Cherry-picked from feat/html-in-canvas-launch (PR #611):

- Enable --enable-features=CanvasDrawElement in Chrome browser args
  so HTML-in-canvas compositions render correctly
- Add native drawElementImage() capture path for shader transitions
  with existing fallback preserved
- Reuse renderer Chrome args in hyperframes validate for consistent
  WebGL/CanvasDrawElement environment
- Add capture.test.ts for the new shader transition capture path
Install all registry blocks matching a tag in one command:

  hyperframes add --tag vfx       # installs all 7 VFX blocks
  hyperframes add --tag captions  # installs all 5 caption blocks

The resolver loads each item's full manifest to check tags, then
installs matching blocks sequentially. Failed items are skipped
with a warning so one broken block doesn't abort the batch.

Also supports JSON output for CI: hyperframes add --tag vfx --json
All 12 blocks now share the 'html-in-canvas' tag, enabling:

  hyperframes add --tag html-in-canvas

This installs all VFX + caption blocks in one command.
hyperframes add now auto-detects whether the argument is a single
block name or a tag. No --tag flag needed:

  hyperframes add html-in-canvas  # installs all 7 html-in-canvas blocks
  hyperframes add captions        # installs all 5 caption blocks
  hyperframes add vfx-shatter     # installs one block

When the name doesn't match a registry item, the CLI falls back to
tag-based resolution and bulk-installs all matching blocks.

Also fixes tag assignments:
- VFX blocks tagged "html-in-canvas" (not "vfx")
- Caption blocks tagged "captions" only (no html-in-canvas)
Merges main's refactored capture (CaptureSceneOptions, forceVisible,
stabilizeTransformedBoxShadows, foreignObjectRendering fallback) with
our HTML-in-Canvas drawElementImage capture path. The native capture
tries first and falls back to html2canvas on failure.
Copy link
Copy Markdown
Collaborator

@jrusso1020 jrusso1020 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — focused infra review (per-block content trusted)

8143 LoC across 53 files is at the edge of what I can credibly read in a single pass — and 28 of those files are per-block content (HTML/CSS/JS/TS for the 12 new blocks) where the per-block correctness is something you (and Vai's parallel review) are better positioned to judge than I am for one review pass. So I'm being explicit about scope: I deeply audited the infrastructure changes (CLI, registry resolver, renderer args, shader-transitions capture), trusting your manual smoke tests + the parallel review on the per-block content.

Audited

1. Bulk-install-by-tag in add.ts is correctly scoped. The fallback gate at line 232:

if (!(singleErr instanceof AddError) || singleErr.code !== "unknown-item") {
  // re-throw real failures, don't try tag fallback
}

Means only "no item with that name exists" triggers the tag-based bulk install. Real install failures (manifest fetch error, file conflict, etc.) propagate as before. Without that error-code check, any single-item failure would silently fall through to tag resolution and the user would see confusing "0 blocks installed" output instead of the actual error. Right discipline.

2. Per-item failures in the bulk loop don't abort. Each runAdd call is wrapped in try/catch with a console.log("✗ skipped"). The aggregate summary (✓ Installed X/N blocks) reflects partial success accurately. Sensible — better than fail-fast on the first conflict.

3. resolveItemsByTag correctly loads full manifests. Tags live in the per-item manifest, not the top-level registry index, so the resolver has to fetch each item's manifest to filter. The onWarn: () => {} silencing is right for the bulk path (warnings would be noise per-item), and the "tags" in item && Array.isArray(item.tags) && item.tags.includes(tag) check defensively narrows the type before the includes call.

4. Renderer Chrome flag (--enable-features=CanvasDrawElement) is unconditional. Module-level constant in browserManager.ts:268, added to the default args list at line 287. Means every render — not just compositions that use HTML-in-Canvas or shader transitions — gets the flag. Chromium's response when an enabled-but-unused feature flag is set should be a no-op, but worth confirming there's no side effect on existing render paths (HDR / regular DOM / PNG sequences). The feature-detection in capture.ts (isHtmlInCanvasCaptureSupported) gates use of the feature, so this is belt-and-suspenders — fine.

5. validate now reuses buildChromeArgs instead of inline 3-flag list. Good consistency move. One subtle behavior shift worth knowing about: the old inline list had --disable-gpu (3rd flag); the new buildChromeArgs defaults browserGpuMode = "hardware" unless PRODUCER_BROWSER_GPU_MODE === "software". So hyperframes validate is now using hardware GPU by default — different from before where it was explicitly disabled. If validate is expected to work in headless/CI environments without GPU access, the PRODUCER_BROWSER_GPU_MODE=software env var becomes load-bearing. Worth a doc note in the README, but probably fine since getBrowserGpuArgs("hardware", platform) falls back to SwiftShader on Linux servers without GPU.

6. isHtmlInCanvasCaptureSupported feature detection is layered correctly. Three checks: typeof document !== "undefined" (browser only), "layoutSubtree" in probe (DOM property exists), typeof ctx.drawElementImage === "function" (method available). The new tests pin the three branches. The captureScene fallback chain is also right — feature-detected path falls back to html2canvas on any error, html2canvas remains the universal fallback.

7. captureSceneWithHtmlInCanvas cleanup is leak-safe. Both the try/explicit-remove and catch/remove-and-rethrow paths cover canvas removal. The double-rAF (requestAnimationFrame(() => requestAnimationFrame(() => r()))) before drawElementImage is the correct "wait for layout + paint" pattern.

Non-blocking observations

A. Tag-vs-item-name collision is a documented priority but not commented in code. If someone names a block captions (as a single block), that single-item resolution wins and the tag bulk-install would never fire for that name. This is the intended priority order ("single name first") but worth a code comment in add.ts:202 clarifying for future readers. Cheap.

B. No unit tests for add.ts bulk-install logic. The new branching (unknown-item discrimination, tag fallback, partial-success aggregation, JSON output shape) has zero test coverage. Test plan checkboxes are all unchecked manual tests. Given this is the CLI's primary install command, a few tests (e.g. "tag matches no items → exits with original error", "tag matches 3 items, one fails → exits 0 with installed: 2", "JSON mode emits correct shape") would be cheap regression guards.

C. resolveItemsByTag is O(n) network fetches. Loads every item's full manifest to filter by tag. For the current ~30-block registry, fine. As the catalog grows (12 new blocks here, likely more later), this scales. Worth a comment that future versions could push the tag list into the top-level registry.json (avoiding the per-item fetch) when the registry gets bigger. Out of scope for this PR.

D. UX model around tag bulk-install. hyperframes add captions installs all 5 caption blocks. Is the user expected to use one at a time, or is the catalog meant as a "browse and pick"? I assume the latter — these are templates the user customizes one at a time — but a one-line README note clarifying the model would help users not feel surprised by getting 5 caption files when they typed a single command. Especially relevant since hyperframes add html-in-canvas installs 7 visual effect blocks.

E. JSON-mode output includes successful installs only. When bulk-install partially fails (say 5 of 7 succeed), the JSON includes installed: ["a", "b", "c", "d", "e"] but no field for the 2 that failed. Users scripting against the CLI would have no programmatic way to detect partial failure. Worth either: (a) failed: [...] field, or (b) ok: false when any item fails. Current behavior is ok: true even on partial failure, which could lead silent script breaks.

F. Failed bulk blocks are logged as ✗ <name> (skipped) without a reason. Useful for the user to see WHY each one failed (manifest 404, file conflict, etc.). Could surface err.message in non-JSON mode if the catch had one. Minor.

G. No verification of the _ placeholder block in the registry. I noticed registry/registry.json +48 -0. With 12 new blocks plus 2 tag-only entries (or however the schema represents tags), the structure should validate against whatever schema the loader uses. PR test plan says "registry.json schema validates" but isn't checked. Worth running before merge.

Bottom line

Strong PR. The architectural changes are clean and well-scoped, the feature-detection-with-fallback pattern in captureScene is right, the bulk-install error discrimination is correctly narrow. The 28 per-block content files I'm trusting your manual smoke + Vai's parallel pass to verify.

Three follow-ups worth picking up if you have a spare moment: (B) test coverage on the bulk-install branching, (E) JSON output for partial failures, (G) confirm the registry.json schema actually validates. None gating.

Stamping.

— Review by Rames Jusso (pr-review)

@jrusso1020 jrusso1020 merged commit 1cdf86a into main May 6, 2026
38 checks passed
@jrusso1020 jrusso1020 deleted the feat/vfx-catalog-blocks branch May 6, 2026 16:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants