feat(registry): add VFX and Captions block categories#643
Conversation
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.
jrusso1020
left a comment
There was a problem hiding this comment.
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)
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-canvasvfx-text-cursorvfx-liquid-backgroundvfx-iphone-devicevfx-magneticvfx-portalvfx-liquid-glassvfx-shatterCaption Blocks
Install all at once:
hyperframes add captionscaptions-slamback.out(1.7)overshoot, per-word accent colorscaptions-karaokecaptions-minimalcaptions-bouncecaptions-cinematicRenderer Changes
Cherry-picked from #611:
--enable-features=CanvasDrawElementin Chrome browser argsdrawElementImage()capture path for shader transitionshyperframes validateCLI: Bulk Install by Tag
hyperframes addnow auto-detects whether the argument is a single block name or a tag: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-canvasinstalls all 7 blockshyperframes add captionsinstalls all 5 caption blocksregistry.jsonschema validates