Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions docs/packages/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ npx hyperframes <command>
- Preview compositions with live hot reload (`preview`)
- Render compositions to MP4 locally or in Docker (`render`)
- Lint compositions for structural issues (`lint`)
- Audit rendered layout for text overflow and clipped containers (`layout`)
- Capture key frames as PNG screenshots (`snapshot`)
- Check your environment for missing dependencies (`doctor`)

Expand Down Expand Up @@ -445,6 +446,40 @@ This is suppressed in CI environments, non-TTY shells, and when `HYPERFRAMES_NO_

The linter detects missing attributes, missing adapter libraries (GSAP, Lottie, Three.js), structural problems, and more. See [Common Mistakes](/guides/common-mistakes) for details on each rule.

### `layout`

Audit rendered layout across the composition timeline:

```bash
npx hyperframes layout [dir]
npx hyperframes layout [dir] --json
npx hyperframes layout [dir] --samples 15
npx hyperframes layout [dir] --at 1.5,4,7.25
```

```
◆ Auditing layout for my-project (9 timeline samples)

✗ text_box_overflow t=3.25s #headline inside .bubble overflowed right 18px — "Quarterly plan"
Fix: Increase the bubble/container size or padding, reduce font-size/letter-spacing, or set a max-width that allows wrapping inside the container.

◇ 1 error(s), 0 warning(s)
```

`layout` bundles the project, serves it locally, opens headless Chrome, seeks through the composition, and reports text or elements that escape their intended boxes. It is designed for agent workflows: each finding includes a timestamp, selector, nearest container selector, measured bounding boxes, overflow sides, and a fix hint.

| Flag | Description |
|------|-------------|
| `--json` | Output agent-readable findings with `samples`, `issues`, bounding boxes, and summary counts |
| `--samples` | Number of midpoint samples across the composition duration (default: 9) |
| `--at` | Comma-separated timestamps in seconds for explicit hero-frame checks |
| `--tolerance` | Allowed pixel overflow before reporting an issue (default: 2) |
| `--timeout` | Ms to wait for runtime initialization (default: 5000) |
| `--max-issues` | Maximum findings to print or return (default: 80) |
| `--strict` | Exit non-zero on warnings as well as errors |

Use `data-layout-allow-overflow` on an element or ancestor when overflow is intentional, such as a planned off-canvas entrance. Use `data-layout-ignore` for decorative elements that should not be audited.

### `snapshot`

Capture key frames from a composition as PNG screenshots — verify visual output without a full render:
Expand Down
6 changes: 6 additions & 0 deletions packages/cli/scripts/build-copy.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ async function main() {
for (const sub of ["studio", "docs", "templates", "skills", "docker"]) {
mkdirSync(join(DIST, sub), { recursive: true });
}
mkdirSync(join(DIST, "commands"), { recursive: true });

const studioDist = resolve(CLI_ROOT, "..", "studio", "dist");
await waitForStudioDist(studioDist);
Expand All @@ -76,6 +77,11 @@ async function main() {
cpSync(dockerfile, join(DIST, "docker", "Dockerfile.render"));
}

const layoutAuditScript = join(CLI_ROOT, "src", "commands", "layout-audit.browser.js");
if (existsSync(layoutAuditScript)) {
cpSync(layoutAuditScript, join(DIST, "commands", "layout-audit.browser.js"));
}

copyMdFiles(join(CLI_ROOT, "src", "docs"), join(DIST, "docs"));

console.log("[build-copy] done");
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const subCommands = {
publish: () => import("./commands/publish.js").then((m) => m.default),
render: () => import("./commands/render.js").then((m) => m.default),
lint: () => import("./commands/lint.js").then((m) => m.default),
layout: () => import("./commands/layout.js").then((m) => m.default),
info: () => import("./commands/info.js").then((m) => m.default),
compositions: () => import("./commands/compositions.js").then((m) => m.default),
benchmark: () => import("./commands/benchmark.js").then((m) => m.default),
Expand Down
Loading
Loading