hyperframes 0.6.97 → 0.6.99
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/beat-analyzer.global.js +326 -0
- package/dist/cli.js +12428 -4680
- package/dist/commands/layout-audit.browser.js +86 -0
- package/dist/hyperframe-runtime.js +22 -22
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +22 -22
- package/dist/skills/hyperframes-cli/SKILL.md +67 -103
- package/dist/skills/hyperframes-cli/references/doctor-browser.md +45 -0
- package/dist/skills/hyperframes-cli/references/init-and-scaffold.md +51 -0
- package/dist/skills/hyperframes-cli/references/lambda.md +132 -0
- package/dist/skills/hyperframes-cli/references/lint-validate-inspect.md +93 -0
- package/dist/skills/hyperframes-cli/references/preview-render.md +107 -0
- package/dist/skills/hyperframes-cli/references/upgrade-info-misc.md +75 -0
- package/dist/studio/assets/hyperframes-player-DgsMQSvV.js +418 -0
- package/dist/studio/assets/index-B62bDCQv.css +1 -0
- package/dist/studio/assets/{index-HveJ0MuV.js → index-C52IT_lp.js} +1 -1
- package/dist/studio/assets/index-DOh7E1uj.js +1 -0
- package/dist/studio/assets/index-DrwSRbsl.js +252 -0
- package/dist/studio/index.html +2 -2
- package/dist/templates/_shared/AGENTS.md +46 -21
- package/dist/templates/_shared/CLAUDE.md +16 -14
- package/package.json +3 -2
- package/dist/pngDecodeBlitWorker.js +0 -239
- package/dist/skills/gsap/SKILL.md +0 -240
- package/dist/skills/gsap/references/effects.md +0 -297
- package/dist/skills/gsap/scripts/extract-audio-data.py +0 -188
- package/dist/skills/hyperframes/SKILL.md +0 -491
- package/dist/skills/hyperframes/data-in-motion.md +0 -19
- package/dist/skills/hyperframes/house-style.md +0 -73
- package/dist/skills/hyperframes/palettes/bold-energetic.md +0 -14
- package/dist/skills/hyperframes/palettes/clean-corporate.md +0 -14
- package/dist/skills/hyperframes/palettes/dark-premium.md +0 -14
- package/dist/skills/hyperframes/palettes/jewel-rich.md +0 -14
- package/dist/skills/hyperframes/palettes/monochrome.md +0 -14
- package/dist/skills/hyperframes/palettes/nature-earth.md +0 -14
- package/dist/skills/hyperframes/palettes/neon-electric.md +0 -14
- package/dist/skills/hyperframes/palettes/pastel-soft.md +0 -14
- package/dist/skills/hyperframes/palettes/warm-editorial.md +0 -14
- package/dist/skills/hyperframes/patterns.md +0 -191
- package/dist/skills/hyperframes/references/audio-reactive.md +0 -76
- package/dist/skills/hyperframes/references/beat-direction.md +0 -171
- package/dist/skills/hyperframes/references/captions.md +0 -163
- package/dist/skills/hyperframes/references/css-patterns.md +0 -373
- package/dist/skills/hyperframes/references/design-picker.md +0 -117
- package/dist/skills/hyperframes/references/dynamic-techniques.md +0 -102
- package/dist/skills/hyperframes/references/html-in-canvas-patterns.md +0 -507
- package/dist/skills/hyperframes/references/motion-principles.md +0 -150
- package/dist/skills/hyperframes/references/narration.md +0 -92
- package/dist/skills/hyperframes/references/prompt-expansion.md +0 -68
- package/dist/skills/hyperframes/references/techniques.md +0 -525
- package/dist/skills/hyperframes/references/text-effects.md +0 -64
- package/dist/skills/hyperframes/references/transcript-guide.md +0 -107
- package/dist/skills/hyperframes/references/transitions/catalog.md +0 -117
- package/dist/skills/hyperframes/references/transitions/css-3d.md +0 -12
- package/dist/skills/hyperframes/references/transitions/css-blur.md +0 -51
- package/dist/skills/hyperframes/references/transitions/css-cover.md +0 -43
- package/dist/skills/hyperframes/references/transitions/css-destruction.md +0 -95
- package/dist/skills/hyperframes/references/transitions/css-dissolve.md +0 -66
- package/dist/skills/hyperframes/references/transitions/css-distortion.md +0 -45
- package/dist/skills/hyperframes/references/transitions/css-grid.md +0 -10
- package/dist/skills/hyperframes/references/transitions/css-light.md +0 -49
- package/dist/skills/hyperframes/references/transitions/css-mechanical.md +0 -30
- package/dist/skills/hyperframes/references/transitions/css-other.md +0 -25
- package/dist/skills/hyperframes/references/transitions/css-push.md +0 -41
- package/dist/skills/hyperframes/references/transitions/css-radial.md +0 -37
- package/dist/skills/hyperframes/references/transitions/css-scale.md +0 -24
- package/dist/skills/hyperframes/references/transitions.md +0 -138
- package/dist/skills/hyperframes/references/typography.md +0 -175
- package/dist/skills/hyperframes/references/video-composition.md +0 -62
- package/dist/skills/hyperframes/scripts/animation-map.mjs +0 -601
- package/dist/skills/hyperframes/scripts/contrast-report.mjs +0 -348
- package/dist/skills/hyperframes/scripts/package-loader.mjs +0 -269
- package/dist/skills/hyperframes/templates/design-picker.html +0 -1432
- package/dist/skills/hyperframes/visual-styles.md +0 -443
- package/dist/studio/assets/hyperframes-player-Daj5djxa.js +0 -418
- package/dist/studio/assets/index-B0twsRu0.css +0 -1
- package/dist/studio/assets/index-Cfye9xzo.js +0 -251
package/dist/studio/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
|
6
6
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
7
7
|
<title>HyperFrames Studio</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-DrwSRbsl.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-B62bDCQv.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root"></div>
|
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
# HyperFrames Composition Project
|
|
2
2
|
|
|
3
|
-
## Skills
|
|
3
|
+
## Skills — USE THESE FIRST
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**Always invoke the relevant skill before writing or modifying compositions.** Skills encode framework-specific patterns (e.g., `window.__timelines` registration, `data-*` attribute semantics, shader-compatible CSS rules) that are NOT in generic web docs. Skipping them produces broken compositions.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
**Doing anything with HyperFrames?** Start at `/hyperframes-read-first` — it tells you what HyperFrames can do and which skill or workflow handles your intent (make a video, TTS / BGM, prep footage, author / animate, render, install blocks), and routes every "make me a video" request to the right workflow. Read it first, especially when there's no project context to orient you. The video workflows it routes to:
|
|
8
|
+
|
|
9
|
+
- `/product-launch-video` — a **product** URL or brief / script → 60-90s product launch / SaaS / promo video.
|
|
10
|
+
- `/website-to-video` — a **general** website / URL → a video _of_ the site (tour / showcase / social clip from captured visuals); a product **launch / promo** is `/product-launch-video`.
|
|
11
|
+
- `/faceless-explainer` — arbitrary text (topic / article / notes), **no URL, no website capture** → 60-90s faceless explainer.
|
|
12
|
+
- `/embedded-captions` — an existing talking-head video (MP4) → the same footage with captions / subtitles added (rail + embed, or pure-cinematic embed); the footage itself is untouched.
|
|
13
|
+
- `/graphic-overlays` — an existing talking-head / interview / podcast video (MP4) → the same footage **packaged with designed graphic overlays** (kinetic titles, lower-thirds, data callouts, pull-quotes, side panels, pip) synced to the transcript; the clip plays unchanged underneath. (Plain captions/subtitles → `/embedded-captions`.)
|
|
14
|
+
- `/pr-to-video` — a GitHub PR (URL / `owner/repo#N` / "this PR") → 30-90s code-change explainer (changelog / feature reveal / fix / refactor).
|
|
15
|
+
- `/motion-graphics` — a short (typically under 10s) design-led **motion graphic**, motion-is-the-message, no narration: kinetic type, a stat / number count-up, a chart, a logo sting, a lower-third / overlay, or an animated tweet / headline / captured-page highlight; rendered to MP4 or a transparent overlay. Longer / narrated / custom → `/general-video`.
|
|
16
|
+
- `/general-video` — fallback for any other video (title card, longer brand / sizzle reel, multi-scene montage, static loop, custom composition); the original hyperframes authoring flow, any length.
|
|
17
|
+
|
|
18
|
+
**Porting an existing composition?** `/remotion-to-hyperframes` translates a Remotion (React) composition into HyperFrames HTML — a source migration, separate from the creation workflows above.
|
|
19
|
+
|
|
20
|
+
The domain skills (`/hyperframes-core`, `/hyperframes-animation`, `/hyperframes-creative`, `/hyperframes-cli`, `/hyperframes-media`, `/hyperframes-registry`) and the full capability map live inside `/hyperframes-read-first` — it is the single source of truth for which skill handles which intent.
|
|
21
|
+
|
|
22
|
+
> **Tailwind v4 projects** (`hyperframes init --tailwind`): see `/hyperframes-core` → `references/tailwind.md`.
|
|
10
23
|
|
|
11
|
-
Skills
|
|
24
|
+
> **Skills not available?** Ask the user to run `npx hyperframes skills` and restart their
|
|
25
|
+
> agent session, or install manually: `npx skills add heygen-com/hyperframes`.
|
|
12
26
|
|
|
13
27
|
## Commands
|
|
14
28
|
|
|
@@ -17,46 +31,57 @@ npm run dev # start the preview server (long-running — keep it alive
|
|
|
17
31
|
npm run check # lint + validate + inspect
|
|
18
32
|
npm run render # render to MP4
|
|
19
33
|
npm run publish # publish and get a shareable link
|
|
34
|
+
npx hyperframes lint --verbose # include info-level findings
|
|
35
|
+
npx hyperframes lint --json # machine-readable output for CI
|
|
20
36
|
npx hyperframes docs <topic> # reference docs in terminal
|
|
21
37
|
```
|
|
22
38
|
|
|
23
39
|
> **`npm run dev` is a long-running server, not a one-shot command.** It blocks until stopped.
|
|
24
|
-
>
|
|
25
|
-
>
|
|
40
|
+
> In Claude Code, always run it with `run_in_background: true`. Never run it as a foreground
|
|
41
|
+
> command — it will time out and the server will die, breaking the browser preview.
|
|
42
|
+
|
|
43
|
+
## Documentation
|
|
44
|
+
|
|
45
|
+
**For quick reference**, use the local CLI docs command (no network required):
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npx hyperframes docs <topic>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Topics: `data-attributes`, `gsap`, `compositions`, `rendering`, `examples`, `troubleshooting`
|
|
52
|
+
|
|
53
|
+
**For full documentation**, discover pages via the machine-readable index — do NOT guess URLs:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
https://hyperframes.heygen.com/llms.txt
|
|
57
|
+
```
|
|
26
58
|
|
|
27
59
|
## Project Structure
|
|
28
60
|
|
|
29
61
|
- `index.html` — main composition (root timeline)
|
|
30
62
|
- `compositions/` — sub-compositions referenced via `data-composition-src`
|
|
31
|
-
- `assets/` — media files (video, audio, images)
|
|
32
63
|
- `meta.json` — project metadata (id, name)
|
|
33
64
|
- `transcript.json` — whisper word-level transcript (if generated)
|
|
34
65
|
|
|
35
|
-
## Linting —
|
|
66
|
+
## Linting — ALWAYS RUN AFTER CHANGES
|
|
36
67
|
|
|
37
|
-
After creating or editing any `.html` composition, run the full check before considering the task complete:
|
|
68
|
+
After creating or editing any `.html` composition, **always** run the full check before considering the task complete:
|
|
38
69
|
|
|
39
70
|
```bash
|
|
40
71
|
npm run check
|
|
41
72
|
```
|
|
42
73
|
|
|
43
|
-
Fix all errors before presenting the result.
|
|
74
|
+
Fix all errors before presenting the result. Inspect warnings should be reviewed before rendering.
|
|
44
75
|
|
|
45
76
|
## Key Rules
|
|
46
77
|
|
|
47
78
|
1. Every timed element needs `data-start`, `data-duration`, and `data-track-index`
|
|
48
|
-
2.
|
|
49
|
-
3.
|
|
79
|
+
2. Elements with timing **MUST** have `class="clip"` — the framework uses this for visibility control
|
|
80
|
+
3. Timelines must be paused and registered on `window.__timelines`:
|
|
50
81
|
```js
|
|
51
82
|
window.__timelines = window.__timelines || {};
|
|
52
83
|
window.__timelines["composition-id"] = gsap.timeline({ paused: true });
|
|
53
84
|
```
|
|
54
85
|
4. Videos use `muted` with a separate `<audio>` element for the audio track
|
|
55
|
-
5. Sub-compositions use `data-composition-src="compositions/file.html"`
|
|
86
|
+
5. Sub-compositions use `data-composition-src="compositions/file.html"` to reference other HTML files
|
|
56
87
|
6. Only deterministic logic — no `Date.now()`, no `Math.random()`, no network fetches
|
|
57
|
-
|
|
58
|
-
## Documentation
|
|
59
|
-
|
|
60
|
-
Full docs: https://hyperframes.heygen.com/introduction
|
|
61
|
-
|
|
62
|
-
Machine-readable index for AI tools: https://hyperframes.heygen.com/llms.txt
|
|
@@ -4,20 +4,22 @@
|
|
|
4
4
|
|
|
5
5
|
**Always invoke the relevant skill before writing or modifying compositions.** Skills encode framework-specific patterns (e.g., `window.__timelines` registration, `data-*` attribute semantics, shader-compatible CSS rules) that are NOT in generic web docs. Skipping them produces broken compositions.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
7
|
+
**Doing anything with HyperFrames?** Start at `/hyperframes-read-first` — it tells you what HyperFrames can do and which skill or workflow handles your intent (make a video, TTS / BGM, prep footage, author / animate, render, install blocks), and routes every "make me a video" request to the right workflow. Read it first, especially when there's no project context to orient you. The video workflows it routes to:
|
|
8
|
+
|
|
9
|
+
- `/product-launch-video` — a **product** URL or brief / script → 60-90s product launch / SaaS / promo video.
|
|
10
|
+
- `/website-to-video` — a **general** website / URL → a video _of_ the site (tour / showcase / social clip from captured visuals); a product **launch / promo** is `/product-launch-video`.
|
|
11
|
+
- `/faceless-explainer` — arbitrary text (topic / article / notes), **no URL, no website capture** → 60-90s faceless explainer.
|
|
12
|
+
- `/embedded-captions` — an existing talking-head video (MP4) → the same footage with captions / subtitles added (rail + embed, or pure-cinematic embed); the footage itself is untouched.
|
|
13
|
+
- `/graphic-overlays` — an existing talking-head / interview / podcast video (MP4) → the same footage **packaged with designed graphic overlays** (kinetic titles, lower-thirds, data callouts, pull-quotes, side panels, pip) synced to the transcript; the clip plays unchanged underneath. (Plain captions/subtitles → `/embedded-captions`.)
|
|
14
|
+
- `/pr-to-video` — a GitHub PR (URL / `owner/repo#N` / "this PR") → 30-90s code-change explainer (changelog / feature reveal / fix / refactor).
|
|
15
|
+
- `/motion-graphics` — a short (typically under 10s) design-led **motion graphic**, motion-is-the-message, no narration: kinetic type, a stat / number count-up, a chart, a logo sting, a lower-third / overlay, or an animated tweet / headline / captured-page highlight; rendered to MP4 or a transparent overlay. Longer / narrated / custom → `/general-video`.
|
|
16
|
+
- `/general-video` — fallback for any other video (title card, longer brand / sizzle reel, multi-scene montage, static loop, custom composition); the original hyperframes authoring flow, any length.
|
|
17
|
+
|
|
18
|
+
**Porting an existing composition?** `/remotion-to-hyperframes` translates a Remotion (React) composition into HyperFrames HTML — a source migration, separate from the creation workflows above.
|
|
19
|
+
|
|
20
|
+
The domain skills (`/hyperframes-core`, `/hyperframes-animation`, `/hyperframes-creative`, `/hyperframes-cli`, `/hyperframes-media`, `/hyperframes-registry`) and the full capability map live inside `/hyperframes-read-first` — it is the single source of truth for which skill handles which intent.
|
|
21
|
+
|
|
22
|
+
> **Tailwind v4 projects** (`hyperframes init --tailwind`): see `/hyperframes-core` → `references/tailwind.md`.
|
|
21
23
|
|
|
22
24
|
> **Skills not available?** Ask the user to run `npx hyperframes skills` and restart their
|
|
23
25
|
> agent session, or install manually: `npx skills add heygen-com/hyperframes`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hyperframes",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.99",
|
|
4
4
|
"description": "HyperFrames CLI — create, preview, and render HTML video compositions",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,9 +17,10 @@
|
|
|
17
17
|
"scripts": {
|
|
18
18
|
"test": "vitest run",
|
|
19
19
|
"dev": "tsx src/cli.ts",
|
|
20
|
-
"build": "bun run build:fonts && tsup && bun run build:runtime && bun run build:copy",
|
|
20
|
+
"build": "bun run build:fonts && tsup && bun run build:runtime && bun run build:beat-analyzer && bun run build:copy",
|
|
21
21
|
"build:fonts": "node scripts/build-fonts.mjs",
|
|
22
22
|
"build:runtime": "tsx scripts/build-runtime.ts",
|
|
23
|
+
"build:beat-analyzer": "node scripts/build-beat-analyzer.mjs",
|
|
23
24
|
"build:copy": "node scripts/build-copy.mjs",
|
|
24
25
|
"typecheck": "tsc --noEmit"
|
|
25
26
|
},
|
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
import { createRequire as __hf_createRequire } from "node:module";
|
|
2
|
-
import { fileURLToPath as __hf_fileURLToPath } from "node:url";
|
|
3
|
-
import { dirname as __hf_dirname } from "node:path";
|
|
4
|
-
var require = __hf_createRequire(import.meta.url);
|
|
5
|
-
var __filename = __hf_fileURLToPath(import.meta.url);
|
|
6
|
-
var __dirname = __hf_dirname(__filename);
|
|
7
|
-
|
|
8
|
-
// ../producer/src/services/pngDecodeBlitWorker.ts
|
|
9
|
-
import { parentPort } from "worker_threads";
|
|
10
|
-
|
|
11
|
-
// ../engine/src/utils/alphaBlit.ts
|
|
12
|
-
import { inflateSync } from "zlib";
|
|
13
|
-
function paeth(a, b, c) {
|
|
14
|
-
const p = a + b - c;
|
|
15
|
-
const pa = Math.abs(p - a);
|
|
16
|
-
const pb = Math.abs(p - b);
|
|
17
|
-
const pc = Math.abs(p - c);
|
|
18
|
-
if (pa <= pb && pa <= pc) return a;
|
|
19
|
-
if (pb <= pc) return b;
|
|
20
|
-
return c;
|
|
21
|
-
}
|
|
22
|
-
function decodePngRaw(buf, caller) {
|
|
23
|
-
if (buf[0] !== 137 || buf[1] !== 80 || buf[2] !== 78 || buf[3] !== 71 || buf[4] !== 13 || buf[5] !== 10 || buf[6] !== 26 || buf[7] !== 10) {
|
|
24
|
-
throw new Error(`${caller}: not a PNG file`);
|
|
25
|
-
}
|
|
26
|
-
let pos = 8;
|
|
27
|
-
let width = 0;
|
|
28
|
-
let height = 0;
|
|
29
|
-
let bitDepth = 0;
|
|
30
|
-
let colorType = 0;
|
|
31
|
-
let interlace = 0;
|
|
32
|
-
let sawIhdr = false;
|
|
33
|
-
const idatChunks = [];
|
|
34
|
-
while (pos + 12 <= buf.length) {
|
|
35
|
-
const chunkLen = buf.readUInt32BE(pos);
|
|
36
|
-
const chunkType = buf.toString("ascii", pos + 4, pos + 8);
|
|
37
|
-
const chunkData = buf.subarray(pos + 8, pos + 8 + chunkLen);
|
|
38
|
-
if (chunkType === "IHDR") {
|
|
39
|
-
width = chunkData.readUInt32BE(0);
|
|
40
|
-
height = chunkData.readUInt32BE(4);
|
|
41
|
-
bitDepth = chunkData[8] ?? 0;
|
|
42
|
-
colorType = chunkData[9] ?? 0;
|
|
43
|
-
interlace = chunkData[12] ?? 0;
|
|
44
|
-
sawIhdr = true;
|
|
45
|
-
} else if (chunkType === "IDAT") {
|
|
46
|
-
idatChunks.push(Buffer.from(chunkData));
|
|
47
|
-
} else if (chunkType === "IEND") {
|
|
48
|
-
break;
|
|
49
|
-
}
|
|
50
|
-
pos += 12 + chunkLen;
|
|
51
|
-
}
|
|
52
|
-
if (!sawIhdr) {
|
|
53
|
-
throw new Error(`${caller}: PNG missing IHDR chunk`);
|
|
54
|
-
}
|
|
55
|
-
if (colorType !== 2 && colorType !== 6) {
|
|
56
|
-
throw new Error(`${caller}: unsupported color type ${colorType} (expected 2=RGB or 6=RGBA)`);
|
|
57
|
-
}
|
|
58
|
-
if (interlace !== 0) {
|
|
59
|
-
throw new Error(
|
|
60
|
-
`${caller}: Adam7-interlaced PNGs are not supported (interlace method ${interlace})`
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
const channels = colorType === 6 ? 4 : 3;
|
|
64
|
-
const bpp = channels * (bitDepth / 8);
|
|
65
|
-
const stride = width * bpp;
|
|
66
|
-
const compressed = Buffer.concat(idatChunks);
|
|
67
|
-
const decompressed = inflateSync(compressed);
|
|
68
|
-
const rawPixels = Buffer.allocUnsafe(height * stride);
|
|
69
|
-
const prevRow = new Uint8Array(stride);
|
|
70
|
-
const currRow = new Uint8Array(stride);
|
|
71
|
-
let srcPos = 0;
|
|
72
|
-
for (let y = 0; y < height; y++) {
|
|
73
|
-
const filterType = decompressed[srcPos++] ?? 0;
|
|
74
|
-
const rawRow = decompressed.subarray(srcPos, srcPos + stride);
|
|
75
|
-
srcPos += stride;
|
|
76
|
-
switch (filterType) {
|
|
77
|
-
case 0:
|
|
78
|
-
currRow.set(rawRow);
|
|
79
|
-
break;
|
|
80
|
-
case 1:
|
|
81
|
-
for (let x = 0; x < stride; x++) {
|
|
82
|
-
currRow[x] = (rawRow[x] ?? 0) + (x >= bpp ? currRow[x - bpp] ?? 0 : 0) & 255;
|
|
83
|
-
}
|
|
84
|
-
break;
|
|
85
|
-
case 2:
|
|
86
|
-
for (let x = 0; x < stride; x++) {
|
|
87
|
-
currRow[x] = (rawRow[x] ?? 0) + (prevRow[x] ?? 0) & 255;
|
|
88
|
-
}
|
|
89
|
-
break;
|
|
90
|
-
case 3:
|
|
91
|
-
for (let x = 0; x < stride; x++) {
|
|
92
|
-
const left = x >= bpp ? currRow[x - bpp] ?? 0 : 0;
|
|
93
|
-
const up = prevRow[x] ?? 0;
|
|
94
|
-
currRow[x] = (rawRow[x] ?? 0) + Math.floor((left + up) / 2) & 255;
|
|
95
|
-
}
|
|
96
|
-
break;
|
|
97
|
-
case 4:
|
|
98
|
-
for (let x = 0; x < stride; x++) {
|
|
99
|
-
const left = x >= bpp ? currRow[x - bpp] ?? 0 : 0;
|
|
100
|
-
const up = prevRow[x] ?? 0;
|
|
101
|
-
const upLeft = x >= bpp ? prevRow[x - bpp] ?? 0 : 0;
|
|
102
|
-
currRow[x] = (rawRow[x] ?? 0) + paeth(left, up, upLeft) & 255;
|
|
103
|
-
}
|
|
104
|
-
break;
|
|
105
|
-
default:
|
|
106
|
-
throw new Error(`${caller}: unknown filter type ${filterType} at row ${y}`);
|
|
107
|
-
}
|
|
108
|
-
rawPixels.set(currRow, y * stride);
|
|
109
|
-
prevRow.set(currRow);
|
|
110
|
-
}
|
|
111
|
-
return { width, height, bitDepth, colorType, rawPixels };
|
|
112
|
-
}
|
|
113
|
-
function decodePng(buf) {
|
|
114
|
-
const { width, height, bitDepth, colorType, rawPixels } = decodePngRaw(buf, "decodePng");
|
|
115
|
-
if (bitDepth !== 8) {
|
|
116
|
-
throw new Error(`decodePng: unsupported bit depth ${bitDepth} (expected 8)`);
|
|
117
|
-
}
|
|
118
|
-
const output = new Uint8Array(width * height * 4);
|
|
119
|
-
if (colorType === 6) {
|
|
120
|
-
output.set(rawPixels);
|
|
121
|
-
} else {
|
|
122
|
-
for (let i = 0; i < width * height; i++) {
|
|
123
|
-
output[i * 4 + 0] = rawPixels[i * 3 + 0] ?? 0;
|
|
124
|
-
output[i * 4 + 1] = rawPixels[i * 3 + 1] ?? 0;
|
|
125
|
-
output[i * 4 + 2] = rawPixels[i * 3 + 2] ?? 0;
|
|
126
|
-
output[i * 4 + 3] = 255;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
return { width, height, data: output };
|
|
130
|
-
}
|
|
131
|
-
function buildSrgbToSignalLut(transfer) {
|
|
132
|
-
const lut = new Uint16Array(256);
|
|
133
|
-
const hlgA = 0.17883277;
|
|
134
|
-
const hlgB = 1 - 4 * hlgA;
|
|
135
|
-
const hlgC = 0.5 - hlgA * Math.log(4 * hlgA);
|
|
136
|
-
const pqM1 = 0.1593017578125;
|
|
137
|
-
const pqM2 = 78.84375;
|
|
138
|
-
const pqC1 = 0.8359375;
|
|
139
|
-
const pqC2 = 18.8515625;
|
|
140
|
-
const pqC3 = 18.6875;
|
|
141
|
-
const pqMaxNits = 1e4;
|
|
142
|
-
const sdrNits = 203;
|
|
143
|
-
for (let i = 0; i < 256; i++) {
|
|
144
|
-
if (transfer === "srgb") {
|
|
145
|
-
lut[i] = i * 257;
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
const v = i / 255;
|
|
149
|
-
const linear = v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
|
|
150
|
-
let signal;
|
|
151
|
-
if (transfer === "hlg") {
|
|
152
|
-
signal = linear <= 1 / 12 ? Math.sqrt(3 * linear) : hlgA * Math.log(12 * linear - hlgB) + hlgC;
|
|
153
|
-
} else {
|
|
154
|
-
const Lp = Math.max(0, linear * sdrNits / pqMaxNits);
|
|
155
|
-
const Lm1 = Math.pow(Lp, pqM1);
|
|
156
|
-
signal = Math.pow((pqC1 + pqC2 * Lm1) / (1 + pqC3 * Lm1), pqM2);
|
|
157
|
-
}
|
|
158
|
-
lut[i] = Math.min(65535, Math.round(signal * 65535));
|
|
159
|
-
}
|
|
160
|
-
return lut;
|
|
161
|
-
}
|
|
162
|
-
var SRGB_TO_SRGB_16 = buildSrgbToSignalLut("srgb");
|
|
163
|
-
var SRGB_TO_HLG = buildSrgbToSignalLut("hlg");
|
|
164
|
-
var SRGB_TO_PQ = buildSrgbToSignalLut("pq");
|
|
165
|
-
function getSrgbToSignalLut(transfer) {
|
|
166
|
-
if (transfer === "pq") return SRGB_TO_PQ;
|
|
167
|
-
if (transfer === "hlg") return SRGB_TO_HLG;
|
|
168
|
-
return SRGB_TO_SRGB_16;
|
|
169
|
-
}
|
|
170
|
-
function blitRgba8OverRgb48le(domRgba, canvas, width, height, transfer = "hlg") {
|
|
171
|
-
const pixelCount = width * height;
|
|
172
|
-
const lut = getSrgbToSignalLut(transfer);
|
|
173
|
-
for (let i = 0; i < pixelCount; i++) {
|
|
174
|
-
const da = domRgba[i * 4 + 3] ?? 0;
|
|
175
|
-
if (da === 0) {
|
|
176
|
-
continue;
|
|
177
|
-
} else if (da === 255) {
|
|
178
|
-
const r16 = lut[domRgba[i * 4 + 0] ?? 0] ?? 0;
|
|
179
|
-
const g16 = lut[domRgba[i * 4 + 1] ?? 0] ?? 0;
|
|
180
|
-
const b16 = lut[domRgba[i * 4 + 2] ?? 0] ?? 0;
|
|
181
|
-
canvas.writeUInt16LE(r16, i * 6);
|
|
182
|
-
canvas.writeUInt16LE(g16, i * 6 + 2);
|
|
183
|
-
canvas.writeUInt16LE(b16, i * 6 + 4);
|
|
184
|
-
} else {
|
|
185
|
-
const alpha = da / 255;
|
|
186
|
-
const invAlpha = 1 - alpha;
|
|
187
|
-
const hdrR = (canvas[i * 6 + 0] ?? 0) | (canvas[i * 6 + 1] ?? 0) << 8;
|
|
188
|
-
const hdrG = (canvas[i * 6 + 2] ?? 0) | (canvas[i * 6 + 3] ?? 0) << 8;
|
|
189
|
-
const hdrB = (canvas[i * 6 + 4] ?? 0) | (canvas[i * 6 + 5] ?? 0) << 8;
|
|
190
|
-
const domR = lut[domRgba[i * 4 + 0] ?? 0] ?? 0;
|
|
191
|
-
const domG = lut[domRgba[i * 4 + 1] ?? 0] ?? 0;
|
|
192
|
-
const domB = lut[domRgba[i * 4 + 2] ?? 0] ?? 0;
|
|
193
|
-
canvas.writeUInt16LE(Math.round(domR * alpha + hdrR * invAlpha), i * 6);
|
|
194
|
-
canvas.writeUInt16LE(Math.round(domG * alpha + hdrG * invAlpha), i * 6 + 2);
|
|
195
|
-
canvas.writeUInt16LE(Math.round(domB * alpha + hdrB * invAlpha), i * 6 + 4);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// ../producer/src/services/pngDecodeBlitWorker.ts
|
|
201
|
-
if (!parentPort) {
|
|
202
|
-
console.warn("[pngDecodeBlitWorker] no parentPort; module loaded on main thread");
|
|
203
|
-
} else {
|
|
204
|
-
parentPort.on("message", (msg) => {
|
|
205
|
-
const { png, pngOffset, pngLength, dest, destOffset, destLength, width, height, transfer } = msg;
|
|
206
|
-
const pngBuf = Buffer.from(png, pngOffset, pngLength);
|
|
207
|
-
const destBuf = Buffer.from(dest, destOffset, destLength);
|
|
208
|
-
try {
|
|
209
|
-
const decodeStart = Date.now();
|
|
210
|
-
const { data: rgba } = decodePng(pngBuf);
|
|
211
|
-
const decodeMs = Date.now() - decodeStart;
|
|
212
|
-
const blitStart = Date.now();
|
|
213
|
-
blitRgba8OverRgb48le(
|
|
214
|
-
rgba,
|
|
215
|
-
destBuf,
|
|
216
|
-
width,
|
|
217
|
-
height,
|
|
218
|
-
transfer
|
|
219
|
-
);
|
|
220
|
-
const blitMs = Date.now() - blitStart;
|
|
221
|
-
const reply = {
|
|
222
|
-
ok: true,
|
|
223
|
-
png,
|
|
224
|
-
dest,
|
|
225
|
-
decodeMs,
|
|
226
|
-
blitMs
|
|
227
|
-
};
|
|
228
|
-
parentPort.postMessage(reply, [png, dest]);
|
|
229
|
-
} catch (err) {
|
|
230
|
-
const reply = {
|
|
231
|
-
ok: false,
|
|
232
|
-
error: err instanceof Error ? err.message : String(err),
|
|
233
|
-
png,
|
|
234
|
-
dest
|
|
235
|
-
};
|
|
236
|
-
parentPort.postMessage(reply, [png, dest]);
|
|
237
|
-
}
|
|
238
|
-
});
|
|
239
|
-
}
|
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: gsap
|
|
3
|
-
description: GSAP animation reference for HyperFrames. Covers gsap.to(), from(), fromTo(), easing, stagger, defaults, timelines (gsap.timeline(), position parameter, labels, nesting, playback), and performance (transforms, will-change, quickTo). Use when writing GSAP animations in HyperFrames compositions.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# GSAP
|
|
7
|
-
|
|
8
|
-
## HyperFrames Contract
|
|
9
|
-
|
|
10
|
-
HyperFrames controls GSAP through its `gsap` runtime adapter. Create a paused timeline synchronously, register it on `window.__timelines` with the exact `data-composition-id`, and let HyperFrames seek it.
|
|
11
|
-
|
|
12
|
-
```html
|
|
13
|
-
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
|
14
|
-
<script>
|
|
15
|
-
window.__timelines = window.__timelines || {};
|
|
16
|
-
const tl = gsap.timeline({ paused: true });
|
|
17
|
-
|
|
18
|
-
tl.from(".title", { y: 48, opacity: 0, duration: 0.6, ease: "power3.out" }, 0);
|
|
19
|
-
tl.to(".accent", { scaleX: 1, duration: 0.5, ease: "power2.out" }, 0.25);
|
|
20
|
-
|
|
21
|
-
window.__timelines["main"] = tl; // key must equal data-composition-id on the composition root
|
|
22
|
-
</script>
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
- The registry key must match the composition root's `data-composition-id`.
|
|
26
|
-
- Do not call `tl.play()` for render-critical motion.
|
|
27
|
-
- Do not build timelines inside async code, timers, or event handlers.
|
|
28
|
-
- Keep loops finite. HyperFrames renders finite video durations.
|
|
29
|
-
|
|
30
|
-
## Core Tween Methods
|
|
31
|
-
|
|
32
|
-
- **gsap.to(targets, vars)** — animate from current state to `vars`. Most common.
|
|
33
|
-
- **gsap.from(targets, vars)** — animate from `vars` to current state (entrances).
|
|
34
|
-
- **gsap.fromTo(targets, fromVars, toVars)** — explicit start and end.
|
|
35
|
-
- **gsap.set(targets, vars)** — apply immediately (duration 0).
|
|
36
|
-
|
|
37
|
-
Always use **camelCase** property names (e.g. `backgroundColor`, `rotationX`).
|
|
38
|
-
|
|
39
|
-
## Common vars
|
|
40
|
-
|
|
41
|
-
- **duration** — seconds (default 0.5).
|
|
42
|
-
- **delay** — seconds before start.
|
|
43
|
-
- **ease** — `"power1.out"` (default), `"power3.inOut"`, `"back.out(1.7)"`, `"elastic.out(1, 0.3)"`, `"none"`.
|
|
44
|
-
- **stagger** — number `0.1` or object: `{ amount: 0.3, from: "center" }`, `{ each: 0.1, from: "random" }`.
|
|
45
|
-
- **overwrite** — `false` (default), `true`, or `"auto"`.
|
|
46
|
-
- **repeat** — finite number; never `-1` in HyperFrames. Compute repeats from the visible duration. **yoyo** — alternates direction with repeat.
|
|
47
|
-
- **onComplete**, **onStart**, **onUpdate** — callbacks.
|
|
48
|
-
- **immediateRender** — default `true` for from()/fromTo(). Set `false` on later tweens targeting the same property+element to avoid overwrite.
|
|
49
|
-
|
|
50
|
-
## Transforms and CSS
|
|
51
|
-
|
|
52
|
-
Prefer GSAP's **transform aliases** over raw `transform` string:
|
|
53
|
-
|
|
54
|
-
| GSAP property | Equivalent |
|
|
55
|
-
| --------------------------- | ------------------- |
|
|
56
|
-
| `x`, `y`, `z` | translateX/Y/Z (px) |
|
|
57
|
-
| `xPercent`, `yPercent` | translateX/Y in % |
|
|
58
|
-
| `scale`, `scaleX`, `scaleY` | scale |
|
|
59
|
-
| `rotation` | rotate (deg) |
|
|
60
|
-
| `rotationX`, `rotationY` | 3D rotate |
|
|
61
|
-
| `skewX`, `skewY` | skew |
|
|
62
|
-
| `transformOrigin` | transform-origin |
|
|
63
|
-
|
|
64
|
-
- **autoAlpha** — prefer over `opacity`. At 0: also sets `visibility: hidden`.
|
|
65
|
-
- **CSS variables** — `"--hue": 180`.
|
|
66
|
-
- **svgOrigin** _(SVG only)_ — global SVG coordinate space origin. Don't combine with `transformOrigin`.
|
|
67
|
-
- **Directional rotation** — `"360_cw"`, `"-170_short"`, `"90_ccw"`.
|
|
68
|
-
- **clearProps** — `"all"` or comma-separated; removes inline styles on complete.
|
|
69
|
-
- **Relative values** — `"+=20"`, `"-=10"`, `"*=2"`.
|
|
70
|
-
|
|
71
|
-
## Function-Based Values
|
|
72
|
-
|
|
73
|
-
```javascript
|
|
74
|
-
gsap.to(".item", {
|
|
75
|
-
x: (i, target, targets) => i * 50,
|
|
76
|
-
stagger: 0.1,
|
|
77
|
-
});
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
## Easing
|
|
81
|
-
|
|
82
|
-
Built-in eases: `power1`–`power4`, `back`, `bounce`, `circ`, `elastic`, `expo`, `sine`. Each has `.in`, `.out`, `.inOut`.
|
|
83
|
-
|
|
84
|
-
## Defaults
|
|
85
|
-
|
|
86
|
-
```javascript
|
|
87
|
-
gsap.defaults({ duration: 0.6, ease: "power2.out" });
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
## Controlling Tweens
|
|
91
|
-
|
|
92
|
-
```javascript
|
|
93
|
-
const tween = gsap.to(".box", { x: 100 });
|
|
94
|
-
tween.pause();
|
|
95
|
-
tween.play();
|
|
96
|
-
tween.reverse();
|
|
97
|
-
tween.kill();
|
|
98
|
-
tween.progress(0.5);
|
|
99
|
-
tween.time(0.2);
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
## gsap.matchMedia() (Responsive + Accessibility)
|
|
103
|
-
|
|
104
|
-
Runs setup only when a media query matches; auto-reverts when it stops matching.
|
|
105
|
-
|
|
106
|
-
```javascript
|
|
107
|
-
let mm = gsap.matchMedia();
|
|
108
|
-
mm.add(
|
|
109
|
-
{
|
|
110
|
-
isDesktop: "(min-width: 800px)",
|
|
111
|
-
reduceMotion: "(prefers-reduced-motion: reduce)",
|
|
112
|
-
},
|
|
113
|
-
(context) => {
|
|
114
|
-
const { isDesktop, reduceMotion } = context.conditions;
|
|
115
|
-
gsap.to(".box", {
|
|
116
|
-
rotation: isDesktop ? 360 : 180,
|
|
117
|
-
duration: reduceMotion ? 0 : 2,
|
|
118
|
-
});
|
|
119
|
-
},
|
|
120
|
-
);
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
---
|
|
124
|
-
|
|
125
|
-
## Timelines
|
|
126
|
-
|
|
127
|
-
### Creating a Timeline
|
|
128
|
-
|
|
129
|
-
```javascript
|
|
130
|
-
const tl = gsap.timeline({ defaults: { duration: 0.5, ease: "power2.out" } });
|
|
131
|
-
tl.to(".a", { x: 100 }).to(".b", { y: 50 }).to(".c", { opacity: 0 });
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
### Position Parameter
|
|
135
|
-
|
|
136
|
-
Third argument controls placement:
|
|
137
|
-
|
|
138
|
-
- **Absolute**: `1` — at 1s
|
|
139
|
-
- **Relative**: `"+=0.5"` — after end; `"-=0.2"` — before end
|
|
140
|
-
- **Label**: `"intro"`, `"intro+=0.3"`
|
|
141
|
-
- **Alignment**: `"<"` — same start as previous; `">"` — after previous ends; `"<0.2"` — 0.2s after previous starts
|
|
142
|
-
|
|
143
|
-
```javascript
|
|
144
|
-
tl.to(".a", { x: 100 }, 0);
|
|
145
|
-
tl.to(".b", { y: 50 }, "<"); // same start as .a
|
|
146
|
-
tl.to(".c", { opacity: 0 }, "<0.2"); // 0.2s after .b starts
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
### Labels
|
|
150
|
-
|
|
151
|
-
```javascript
|
|
152
|
-
tl.addLabel("intro", 0);
|
|
153
|
-
tl.to(".a", { x: 100 }, "intro");
|
|
154
|
-
tl.addLabel("outro", "+=0.5");
|
|
155
|
-
tl.play("outro");
|
|
156
|
-
tl.tweenFromTo("intro", "outro");
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
### Timeline Options
|
|
160
|
-
|
|
161
|
-
- **paused: true** — create paused; call `.play()` to start.
|
|
162
|
-
- **repeat**, **yoyo** — apply to whole timeline.
|
|
163
|
-
- **defaults** — vars merged into every child tween.
|
|
164
|
-
|
|
165
|
-
### Nesting Timelines
|
|
166
|
-
|
|
167
|
-
```javascript
|
|
168
|
-
const master = gsap.timeline();
|
|
169
|
-
const child = gsap.timeline();
|
|
170
|
-
child.to(".a", { x: 100 }).to(".b", { y: 50 });
|
|
171
|
-
master.add(child, 0);
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
### Playback Control
|
|
175
|
-
|
|
176
|
-
`tl.play()`, `tl.pause()`, `tl.reverse()`, `tl.restart()`, `tl.time(2)`, `tl.progress(0.5)`, `tl.kill()`.
|
|
177
|
-
|
|
178
|
-
---
|
|
179
|
-
|
|
180
|
-
## Performance
|
|
181
|
-
|
|
182
|
-
### Prefer Transform and Opacity
|
|
183
|
-
|
|
184
|
-
Animating `x`, `y`, `scale`, `rotation`, `opacity` stays on the compositor. Avoid `width`, `height`, `top`, `left` when transforms achieve the same effect.
|
|
185
|
-
|
|
186
|
-
### will-change
|
|
187
|
-
|
|
188
|
-
```css
|
|
189
|
-
will-change: transform;
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
Only on elements that actually animate.
|
|
193
|
-
|
|
194
|
-
### gsap.quickTo() for Frequent Updates
|
|
195
|
-
|
|
196
|
-
```javascript
|
|
197
|
-
let xTo = gsap.quickTo("#id", "x", { duration: 0.4, ease: "power3" }),
|
|
198
|
-
yTo = gsap.quickTo("#id", "y", { duration: 0.4, ease: "power3" });
|
|
199
|
-
container.addEventListener("mousemove", (e) => {
|
|
200
|
-
xTo(e.pageX);
|
|
201
|
-
yTo(e.pageY);
|
|
202
|
-
});
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
### Stagger > Many Tweens
|
|
206
|
-
|
|
207
|
-
Use `stagger` instead of separate tweens with manual delays.
|
|
208
|
-
|
|
209
|
-
### Cleanup
|
|
210
|
-
|
|
211
|
-
Pause or kill off-screen animations.
|
|
212
|
-
|
|
213
|
-
---
|
|
214
|
-
|
|
215
|
-
## References (loaded on demand)
|
|
216
|
-
|
|
217
|
-
- **[references/effects.md](references/effects.md)** — Drop-in effects: typewriter text, audio visualizer. Read when needing ready-made effect patterns for HyperFrames.
|
|
218
|
-
|
|
219
|
-
## Best Practices
|
|
220
|
-
|
|
221
|
-
- Use camelCase property names; prefer transform aliases and autoAlpha.
|
|
222
|
-
- Prefer timelines over chaining with delay; use the position parameter.
|
|
223
|
-
- Add labels with `addLabel()` for readable sequencing.
|
|
224
|
-
- Pass defaults into timeline constructor.
|
|
225
|
-
- Store tween/timeline return value when controlling playback.
|
|
226
|
-
|
|
227
|
-
## Do Not
|
|
228
|
-
|
|
229
|
-
- Animate layout properties (width/height/top/left) when transforms suffice.
|
|
230
|
-
- Use both svgOrigin and transformOrigin on the same SVG element.
|
|
231
|
-
- Chain animations with delay when a timeline can sequence them.
|
|
232
|
-
- Create tweens before the DOM exists.
|
|
233
|
-
- Skip cleanup — always kill tweens when no longer needed.
|
|
234
|
-
- Use infinite repeat values in HyperFrames compositions. Use finite repeat counts computed from the visible duration.
|
|
235
|
-
|
|
236
|
-
## Credits And References
|
|
237
|
-
|
|
238
|
-
- HyperFrames adapter source: `packages/core/src/runtime/adapters/gsap.ts`.
|
|
239
|
-
- GSAP documentation: https://gsap.com/docs/v3/
|
|
240
|
-
- GSAP timeline pause and seek behavior: https://gsap.com/docs/v3/GSAP/Timeline/pause%28%29/
|