mirror of
https://github.com/hanzoai/frames.git
synced 2026-09-24 00:08:03 +00:00
@hanzo/frame is taken by the hero video primitive — one full-bleed video element — which has held that name on npm since it shipped. This repo is the video toolchain: a CLI, a renderer, a studio, a player. Plural is what it is, so the CLI publishes as @hanzo/frames and the workspace packages follow it (@hanzo/frames-core, @hanzo/frames-engine, and the rest). The binary stays `frames`, and now actually ships. The manifest mapped it to bin/frames.mjs while the file on disk was bin/hyperframes.mjs, so a global install produced no working command. The paths that name a package to fetch were pointing elsewhere. The update check read registry.npmjs.org/frames, a different author's package, and the npm, bun and pnpm installers would have pulled it over the CLI; the render image installed the upstream hyperframes; scaffolded projects and the docs pinned the unscoped name. All of them now name @hanzo/frames.
187 lines
6.1 KiB
Plaintext
187 lines
6.1 KiB
Plaintext
---
|
|
title: Contributing to the Catalog
|
|
description: How to add blocks and components to the Frames registry.
|
|
---
|
|
|
|
Your agent already knows how to build video components. It writes HTML. Frames renders it. The registry is the collection of everything that's been built — 52 blocks and counting.
|
|
|
|
This guide shows you how to add to it.
|
|
|
|
<Info>
|
|
**Quick version** — Fork the repo. Write one HTML file with a paused GSAP timeline. Add `registry-item.json`. Run `frames lint` + `validate`. Publish with `npx @hanzo/frames publish`. Open a PR.
|
|
</Info>
|
|
|
|
## Why Contribute?
|
|
|
|
Every block in the registry exists because someone needed it and built it. When you add a block, every Frames user gets it with one command:
|
|
|
|
```bash
|
|
npx @hanzo/frames add instagram-follow
|
|
```
|
|
|
|
The registry grows, Frames gets more useful, and your work ships to everyone.
|
|
|
|
## Two Paths
|
|
|
|
### Ideas (No Code)
|
|
|
|
You spot visual trends before anyone. That's the most valuable contribution.
|
|
|
|
- Screen-record a caption style from TikTok/YouTube that doesn't exist yet
|
|
- Sketch a lower-third in Figma with fonts, colors, and timing
|
|
- Install a component, preview it, report what feels off
|
|
|
|
Open an issue on [GitHub](https://github.com/hanzoai/frames/issues) with a visual reference. Tag it `component-request`.
|
|
|
|
<Tip>The bar for ideas is low. We'd rather have 100 and build the best 10.</Tip>
|
|
|
|
### Build It
|
|
|
|
Every block is a single HTML file. No build step, no framework.
|
|
|
|
If you use Claude Code with Frames skills:
|
|
|
|
> "I want to contribute a new transition that looks like \[description\]"
|
|
|
|
The `/frames-registry` skill scaffolds the structure, validates, renders a preview, publishes to [frames.hanzo.ai](https://frames.hanzo.ai), and prepares the PR.
|
|
|
|
## What Goes in the Registry
|
|
|
|
**Blocks** (`registry/blocks/`) — full standalone compositions. Fixed dimensions, fixed duration. Caption styles, VFX effects, title cards, transitions.
|
|
|
|
**Components** (`registry/components/`) — reusable snippets. No fixed size. CSS effects, text treatments, overlays that adapt to any composition.
|
|
|
|
### Structure
|
|
|
|
```
|
|
registry/blocks/my-block/
|
|
my-block.html ← the composition
|
|
registry-item.json ← metadata
|
|
```
|
|
|
|
### registry-item.json
|
|
|
|
```json
|
|
{
|
|
"$schema": "https://frames.hanzo.ai/schema/registry-item.json",
|
|
"name": "my-block",
|
|
"type": "frames:block",
|
|
"title": "My Block",
|
|
"description": "What this block does in one sentence",
|
|
"tags": ["category", "subcategory"],
|
|
"dimensions": { "width": 1920, "height": 1080 },
|
|
"duration": 5,
|
|
"files": [
|
|
{
|
|
"path": "my-block.html",
|
|
"target": "compositions/my-block.html",
|
|
"type": "frames:composition"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
## The Rules
|
|
|
|
Five things that must be true for every registry item:
|
|
|
|
<Steps>
|
|
<Step title="Deterministic">
|
|
No `Math.random()`, no `Date.now()`. Use seeded PRNG only.
|
|
</Step>
|
|
<Step title="Paused timeline">
|
|
`gsap.timeline({ paused: true })`. The player controls playback.
|
|
</Step>
|
|
<Step title="Register timeline">
|
|
`window.__timelines["id"]` must match `data-composition-id`.
|
|
</Step>
|
|
<Step title="No requestAnimationFrame">
|
|
Use `tl.eventCallback("onUpdate", render)` for Three.js/WebGL scenes.
|
|
</Step>
|
|
<Step title="Hard kills on captions">
|
|
`tl.set(el, { opacity: 0, visibility: "hidden" }, group.end)` — no lingering text.
|
|
</Step>
|
|
</Steps>
|
|
|
|
<Warning>
|
|
Break any of these and renders won't be reproducible. The renderer captures every frame by seeking the timeline — if your animation depends on real time or random state, it breaks.
|
|
</Warning>
|
|
|
|
## Quality Bar
|
|
|
|
Not everything belongs in the registry. The bar is production quality.
|
|
|
|
| Type | Minimum standard |
|
|
|------|-----------------|
|
|
| Captions | 96px+ font, text-stroke/shadow, overflow prevention |
|
|
| VFX | Solves a problem that takes 4+ hours from scratch |
|
|
| Transitions | Smoother than CSS — if opacity 0→1 works, it's not a transition |
|
|
| Blocks | Would a professional use this in a client project? |
|
|
|
|
### Common rejection reasons
|
|
|
|
1. **"Looks like a demo"** — a spinning cube is not a component
|
|
2. **"Text unreadable"** — font too small, no contrast treatment
|
|
3. **"Non-deterministic"** — `Math.random()` or `Date.now()`
|
|
4. **"Timeline not found"** — ID mismatch between HTML and JS
|
|
5. **"Breaks as sub-composition"** — element IDs collide (prefix everything)
|
|
|
|
## Workflow
|
|
|
|
<Steps>
|
|
<Step title="Fork and create">
|
|
Fork [hanzoai/frames](https://github.com/hanzoai/frames) and create your block directory:
|
|
```bash
|
|
mkdir -p registry/blocks/your-block
|
|
```
|
|
</Step>
|
|
<Step title="Write your block">
|
|
Create the HTML composition and `registry-item.json`. Use the templates above.
|
|
</Step>
|
|
<Step title="Validate">
|
|
```bash
|
|
frames lint
|
|
frames check
|
|
npx oxfmt your-block.html
|
|
```
|
|
</Step>
|
|
<Step title="Update registry">
|
|
```bash
|
|
# Add to registry index
|
|
# Update registry/registry.json
|
|
npx tsx scripts/generate-catalog-pages.ts
|
|
```
|
|
</Step>
|
|
<Step title="Render preview">
|
|
```bash
|
|
frames render -o preview.mp4
|
|
```
|
|
</Step>
|
|
<Step title="Publish and PR">
|
|
```bash
|
|
npx @hanzo/frames publish
|
|
```
|
|
Open a PR with your [frames.hanzo.ai](https://frames.hanzo.ai) preview link.
|
|
</Step>
|
|
</Steps>
|
|
|
|
**External contributors:** attach the preview MP4 to your PR. A maintainer handles the catalog image.
|
|
|
|
Previews are served from the docs site's own `/images/` path, so a regenerate is all that is needed — nothing is uploaded anywhere.
|
|
|
|
## What's Needed Right Now
|
|
|
|
These are gaps in the registry. If you're looking for something to build, start here.
|
|
|
|
| Category | Gap | Difficulty |
|
|
|----------|-----|-----------|
|
|
| Captions | Karaoke with clip-path sweep (CapCut style) | Medium |
|
|
| Captions | RTL language layouts (Arabic, Hebrew) | Medium |
|
|
| Lower thirds | 10 variations for podcasts/interviews | Easy |
|
|
| Lower thirds | News ticker / scrolling text bar | Easy |
|
|
| Maps | Animated route maps, region highlights, location pins | Medium |
|
|
| VFX | Product turntable with HDRI | Hard |
|
|
| VFX | Particle system with physics (collisions, gravity) | Hard |
|
|
| Transitions | Morphing shape transitions | Hard |
|
|
| Data viz | Sankey / flow diagrams | Medium |
|