Adds a Blocks tab to the Studio left sidebar with the full 78-item registry
catalog (58 blocks + 20 components). Users can browse by category, search by
title/description, preview CDN-hosted poster thumbnails with video-on-hover,
and install items on-demand with one click or drag-to-timeline.
Core changes:
- BlockCategory type + resolveBlockCategory() for 7 categories (Captions, VFX,
Transitions, Effects, Social, Data, Scenes)
- Registry API routes: GET /api/registry/blocks (catalog) + POST install
- StudioApiAdapter extended with listRegistryCatalog + installRegistryBlock
- Vite adapter reads from disk; CLI adapter fetches from GitHub (24h cache)
- BlockParam interface + params on 6 blocks for future parameter controls
Studio UI:
- 4th sidebar tab "Blocks" with responsive grid, category pills, search bar
- BlockCard: CDN poster thumbnail, video autoplay on hover, duration + WebGL badges
- On-demand install: blocks append as sub-compositions on timeline; components
overlay at start=0 spanning full duration with transparent background patching
- TIMELINE_BLOCK_MIME drag-and-drop to timeline
- BlockParamsPanel (Phase 3 scaffold) auto-opens for parameterized blocks
Registry manifests:
- All 58 blocks backfilled with preview: { video, poster } CDN URLs
- All 20 components normalized to object format + poster URLs added
- 6 blocks annotated with params (Liquid Glass/Background, Portal, Chart,
Logo Outro, Magnetic)
- flowchart-vertical preview generated and uploaded to CDN
Configure fallow via .fallowrc.jsonc so its analysis reflects this repo's
real entry surface, then fix the genuine issues it found.
Fallow noise reduction (601 → 276 dead-code findings):
- Ignore docs/, test fixtures, skill test-corpora, registry/, examples/
- Declare worker entry points loaded dynamically by file path
(pngDecodeBlitWorker.ts, shaderTransitionWorker.ts)
- Declare runtime IIFE entry (core/src/runtime/entry.ts) built outside the
import graph by build-hyperframes-runtime-artifact.ts
- Declare bun:test files in producer + aws-lambda as test entries
- Ignore dynamically-resolved deps: tsup external (puppeteer-core, esbuild,
giget), peer/static-file (gsap in player perf tests), workspace deps
hoisted by bun (happy-dom, @hyperframes/*), and @fontsource/* packages
read via readFileSync in generate-font-data.ts
Extract inline build:fonts scripts:
- packages/{cli,producer}/package.json had multi-line `node -e ...` blobs
containing braces that fallow mis-parsed as glob alternate groups. Moved
to dedicated build-fonts.mjs scripts.
Fix duplicate exports:
- Remove dead FileIcon alias in studio/SystemIcons.tsx (FileTreeIcons.tsx
has the real, used one)
- Consolidate ValidationResult: drop the identical duplicate in
gsapParser.ts; both parsers now import from core.types
- Suppress intentional namespace patterns (per-namespace ML manager
exports; CLI per-command 'examples' convention; fileServer.ts test-only
isPathInside which has different symlink semantics from utils/paths.ts)
Break circular dep (studio/components/editor):
- manualEditsDom.ts re-exported clearStudioPathOffset / clearStudioRotation
/ clearStudioBoxSize from manualEditsSnapshot.ts, which imports four
helpers from manualEditsDom.ts — back-edge cycle
- Re-export moved to manualEdits.ts (the package-public barrel) where the
rest of the snapshot re-exports already live; underlying files now form
a clean DAG
Remove genuinely unused deps:
- studio: motion (no imports anywhere), codemirror (umbrella package; the
@codemirror/* sub-packages are used directly)
- cli: mime-types (plus its only consumer src/utils/mime.ts, which was a
hardcoded mime table that didn't use the package), and its now-stale
tsup external entry
Verified: typecheck across core/cli/producer/studio is clean, oxlint
+ oxfmt pass, manualEdits.test.ts (18 tests) and core parser tests (69
tests) still pass.
Deferred follow-ups (real findings, separate PRs):
- 8 circular deps in producer/services/render/stages/ — renderOrchestrator
↔ captureHdr* / captureStage / extractVideosStage form a hub cycle
- ~14 unused files in producer/src/services/ that look like dead
re-export shims to @hyperframes/engine, but aren't in the public
exports map — need to confirm no deep-import consumers before deletion
- waveform.ts complexity hotspot
* feat(producer): add --mode=lambda-local to the regression harness
Third harness mode that drives the OSS @hyperframes/aws-lambda handler
through the exact event sequence Step Functions produces in
production:
handler({Action: "plan"}) → planDir tarball on fake S3
handler({Action: "renderChunk"}) × N → chunk artifacts on fake S3
handler({Action: "assemble"}) → final mp4/mov/png-sequence
The S3 client is a filesystem-backed fake (every s3://<bucket>/<key>
URI maps to <tempRoot>/s3/<key>), so the harness exercises the
handler's event-parsing + tar/S3 conventions + dispatch logic on top
of the underlying producer primitives. Regressions in event JSON
shape, S3 key layout, or plan-hash boundary checks now surface in
the same CI run as the in-process and distributed-simulated modes
without paying for a real AWS round-trip.
Deliberately NOT a Docker/RIE invocation — that would gate the
producer test suite on Docker-in-Docker support which most CI
runners lack. Real-ZIP-via-RIE tests live in
packages/aws-lambda/scripts/ (probe:beginframe) and the
maintainer-run smoke.sh.
Wired up via:
- HarnessMode union extended to include "lambda-local"
- parseHarnessModeFlag accepts --mode=lambda-local
- regression-harness.ts dispatches to runLambdaLocalRender for
the new mode, sharing the distributed-support gate +
pathology-floor threshold with distributed-simulated mode
- package.json scripts: test:lambda-local + docker:test:lambda-local
- producer.devDependencies += @hyperframes/aws-lambda (workspace)
- producer/tsconfig.json gains path mappings to self so the type
cycle through aws-lambda's source resolves at typecheck time
without needing producer to be pre-built
Tests: 3 new unit tests on parseHarnessModeFlag + resolveMinPsnrForMode
cover the new mode. End-to-end PSNR contract still runs through
Dockerfile.test (manual + CI).
* refactor(producer): /simplify pass on lambda-local harness imports
Three small cleanups on top of the lambda-local harness:
- Drop the unused createReadStream import + its `void` workaround
comment. The aws-lambda handler's tar / S3 transport pulls
createReadStream from its own imports; this file never references
it directly.
- Hoist the dynamic `await import("node:fs")` calls for
writeFileSync out of FilesystemBackedFakeS3.send into the static
import block. Repeated PutObject calls don't need to repay the
dynamic-import cost.
- Hoist the dynamic `await import("@hyperframes/aws-lambda")` call
for untarDirectory similarly. Drops the now-redundant duplicate
aws-lambda import statement.
The PutObject body branch also collapses: `body instanceof Buffer`
and `typeof body === "string"` both call writeFileSync identically,
so they share one branch.
No behavior changes.
* fix(producer): lazy-import lambda-local harness module
The static import of regression-harness-lambda-local.ts pulled
@hyperframes/aws-lambda (and its @aws-sdk/* + @sparticuz/chromium
transitive deps) at module-load time. Dockerfile.test only copies
the producer's own files into the container, so aws-lambda's src
isn't present at runtime — and even `--mode=in-process` failed:
Error [ERR_MODULE_NOT_FOUND]: Cannot find module
'/app/packages/producer/node_modules/@hyperframes/aws-lambda/src/index.ts'
imported from /app/packages/producer/src/regression-harness-lambda-local.ts
Load the module on demand instead. `--mode=lambda-local` callers
pay the import cost; the existing in-process and distributed-
simulated modes don't.
* fix(producer): address PR review on lambda-local harness
Three review items from Vai:
- `Config.width`/`Config.height` are now plumbed through
RunLambdaLocalInput rather than hardcoded inside
runLambdaLocalRender. Lambda-local's whole point is to catch
event-shape drift; if the handler ever starts honouring
Config.width/height (e.g. for canvas sizing), having those
values flow from the caller means the harness sees what the
fixture authored. The interface change makes the eventual
upgrade-to-real-fixture-resolution a one-line dispatch swap.
- Drop the dead `export type { Fps }` and its unused import
from @hyperframes/core. The module never re-exports it.
- The dispatch site in regression-harness.ts now passes 1920×1080
explicitly with a comment marking it as a placeholder until
the harness compiles the composition HTML up-front to surface
the authored data-width/data-height. distributed-simulated
mode uses the same placeholder internally, kept for parity.
No behavior change in the existing modes; lambda-local now has a
clear extension point for honouring fixture dimensions.