reframe-video 0.6.33 → 0.6.39
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/.claude-plugin/plugin.json +1 -1
- package/dist/assemble.js +138 -0
- package/dist/bin.js +248 -36
- package/dist/browserEntry.js +62 -3
- package/dist/cli.js +162 -18
- package/dist/compile-api.js +20 -0
- package/dist/compile.js +20 -0
- package/dist/diff.js +81 -3
- package/dist/frame.js +81 -3
- package/dist/index.js +1537 -1111
- package/dist/labels.js +80 -2
- package/dist/lint.js +1060 -0
- package/dist/manifest.js +1065 -0
- package/dist/types/compose.d.ts +6 -0
- package/dist/types/devicePreset.d.ts +36 -3
- package/dist/types/dsl.d.ts +4 -3
- package/dist/types/index.d.ts +4 -2
- package/dist/types/ir.d.ts +13 -5
- package/dist/types/manifest.d.ts +92 -0
- package/dist/types/titles.d.ts +71 -0
- package/dist/verifyOverlay.js +1152 -0
- package/guides/edsl-guide.md +58 -0
- package/guides/regen-contract.md +14 -0
- package/package.json +1 -1
- package/skills/reframe/SKILL.md +26 -0
package/guides/edsl-guide.md
CHANGED
|
@@ -135,6 +135,16 @@ them with normal TS (`Object.fromEntries`, `.map`) for data-driven scenes.
|
|
|
135
135
|
`curviness` shapes the path: `1` smooth (default), `0` sharp corners, `>1` loopier.
|
|
136
136
|
- `wait(seconds, label?)` — hold; the optional `label` names the hold so audio
|
|
137
137
|
cues and overlay retiming can address it.
|
|
138
|
+
- `beat(name, opts, children)` — a named, retimable, reorderable span (the unit
|
|
139
|
+
humans/AI revise; its `name` is a stable overlay address). `opts`: `parallel`,
|
|
140
|
+
`at` (absolute start — a NUMBER, or a **label string to anchor to**), `gap`,
|
|
141
|
+
`scale`/`duration` (time-stretch), `order` (reorder within a `seq`), `nodes`.
|
|
142
|
+
**Label anchor**: `beat("caption", { at: "shot-2" }, [...])` starts the beat at
|
|
143
|
+
the `shot-2` label's time (with `gap` as the offset), so a title/lower-third/
|
|
144
|
+
caption laid over a montage stays locked to its shot when the cut is retimed
|
|
145
|
+
(via an overlay or AI regen) — the same retime-survival `audio.cues` get. Put
|
|
146
|
+
anchored beats in a `par` branch (an overlay layer), not inside a sequential
|
|
147
|
+
flow. See `examples/scenes/media-story.ts`.
|
|
138
148
|
|
|
139
149
|
Eases: `linear`, `easeIn/Out/InOutQuad`, `easeIn/Out/InOutCubic`,
|
|
140
150
|
`easeIn/Out/InOutQuart`, `easeIn/Out/InOutExpo`, or `{ cubicBezier: [x1,y1,x2,y2] }`.
|
|
@@ -421,6 +431,37 @@ scene({ size, nodes: [...m.nodes, ...titles], timeline: par(m.timeline, titleTra
|
|
|
421
431
|
- Seeded + pure (same `(shots, opts)` → identical IR). Note: image/video sources do
|
|
422
432
|
not render in `reframe player` / artifacts — montage ships as mp4. See
|
|
423
433
|
`examples/scenes/video-montage.ts`.
|
|
434
|
+
- **Assemble from files**: `reframe assemble <media...> [-o name] [--title "…"]
|
|
435
|
+
[--bgm <synth>] [--hold s] [--seed N]` probes each clip's real duration (so a
|
|
436
|
+
video shot's `hold` = its actual length, never a freeze) and scaffolds an editable
|
|
437
|
+
scene `.ts` wiring `photoMontage` + an optional `title` + a bed. The probed
|
|
438
|
+
numbers are baked in, so the emitted scene is a normal deterministic scene — edit
|
|
439
|
+
it (reorder, retime, swap a `src`), then `reframe render` it.
|
|
440
|
+
|
|
441
|
+
## Titles & lower-thirds (`title` / `lowerThird`)
|
|
442
|
+
|
|
443
|
+
The motion-graphic overlay vocabulary for a media piece — generators that return
|
|
444
|
+
`{ nodes, timeline }` to compose over a montage (or anything). Stable ids so overlays
|
|
445
|
+
address them; pure + deterministic.
|
|
446
|
+
|
|
447
|
+
- `title({ text, id?, x?, y?, fontSize?, fontWeight?, fill?, letterSpacing?,
|
|
448
|
+
entrance?, exit?, speed?, seed?, hold? })` → `{ nodes, timeline, block }`. A kinetic
|
|
449
|
+
headline built on `splitText` + `textIn` (entrance presets: `cascade` `rise`
|
|
450
|
+
`bounce` `typewriter` `assemble` `decode`). Set `exit` (a `textOut` preset) and it
|
|
451
|
+
plays in, holds `hold`s, then exits. Glyph ids `${id}-${i}`; labels `${id}-in` /
|
|
452
|
+
`${id}-out`. `block` is returned so you can add `textLoop` behaviors or extra tweens.
|
|
453
|
+
- `lowerThird({ name, role?, id?, x?, y?, accent?, fill?, subFill?, fontSize?, hold? })`
|
|
454
|
+
→ `{ nodes, timeline }`. A name/role strap: an accent bar grows in, the text slides +
|
|
455
|
+
fades. Ids `${id}` (group) / `${id}-bar` / `${id}-name` / `${id}-role`; labels
|
|
456
|
+
`${id}-in` / `${id}-out`. Defaults to a bottom-left title-safe position.
|
|
457
|
+
|
|
458
|
+
```ts
|
|
459
|
+
const ttl = title({ text: "OUR YEAR", id: "ttl", x: 960, y: 540, fontSize: 132, entrance: "rise", exit: "dissolve", hold: 1.6 });
|
|
460
|
+
const lt = lowerThird({ name: "Nantes, France", role: "spring 2026", id: "lt" });
|
|
461
|
+
// nodes: [...m.nodes, ...ttl.nodes, ...lt.nodes]
|
|
462
|
+
// timeline: par(m.timeline, ttl.timeline, seq(wait(6.6), lt.timeline))
|
|
463
|
+
```
|
|
464
|
+
See `examples/scenes/media-story.ts`.
|
|
424
465
|
|
|
425
466
|
## Video clips (`video`)
|
|
426
467
|
|
|
@@ -437,6 +478,9 @@ tween("clip", { scale: 1.08 }, { duration: 5 }) // transform composes with play
|
|
|
437
478
|
`fit` (`"cover"` like the image node), `start` (scene-time playback begins), `rate`
|
|
438
479
|
(speed), `clipStart` (source in-point s), `volume` (clip-audio gain, default 1; `0` mutes).
|
|
439
480
|
Transform/opacity/effects compose as usual.
|
|
481
|
+
- **`start` can be a label** (not just a number): `start: "shot-2"` anchors playback to that
|
|
482
|
+
timeline label's time (like `beat.at`), so the clip **ripples** when its shot is retimed (by an
|
|
483
|
+
overlay or AI regen) instead of desyncing. `photoMontage` does this automatically for video shots.
|
|
440
484
|
- **Deterministic by frame extraction**: render-cli runs `ffmpeg -vf fps=<sceneFps>` to pull
|
|
441
485
|
the clip's frames, and the renderer draws frame `round(t·fps)` — no live `<video>` seek, so
|
|
442
486
|
it stays byte-identical (same machine).
|
|
@@ -506,6 +550,20 @@ vector frame (bezel, rounded body, phone notch / dynamic island, browser chrome)
|
|
|
506
550
|
(overlay/regen addresses) — keep `id` across rewrites.
|
|
507
551
|
- It's one node: animate the device group for the float/entrance (`tween`/
|
|
508
552
|
`motionPath` its `x`/`y`/`scale`/`rotation`, `oscillate` for an idle drift).
|
|
553
|
+
- **Premium by default** — `material:"premium"` (the default) gives a gradient
|
|
554
|
+
body, an ambient screen glow, a soft contact shadow and (glass) a sheen; the
|
|
555
|
+
`style` knob picks `"glass"` (realistic glass/metal, default) or `"neon"` (flat
|
|
556
|
+
body + additive accent edge-glow, graphic punch). `material:"flat"` opts back to
|
|
557
|
+
clean solid fills. All of this is purely cosmetic — the screen rect, the clip,
|
|
558
|
+
and the stable ids are identical across materials/styles, so `deviceScreen`
|
|
559
|
+
coords and existing `content`/overlays are unaffected.
|
|
560
|
+
- **Auto-varied per instance** — each device's look (bezel, corner, glare angle,
|
|
561
|
+
neon hue) is derived deterministically from its `id`, so two devices differ
|
|
562
|
+
while staying on-model. Pass `seed` to pin or explore a variation; same `seed`
|
|
563
|
+
→ identical, different `seed` → same family. Reproducible (no `Math.random`).
|
|
564
|
+
- `notch?: "island" | "notch" | "punch" | "none"` selects the phone front-camera
|
|
565
|
+
treatment (default `"island"` — keep it explicit for an iOS vs Android read).
|
|
566
|
+
- See `examples/scenes/device-gallery.ts` for glass/neon + seed variation.
|
|
509
567
|
|
|
510
568
|
```ts
|
|
511
569
|
// a phone floating centre, a chat bubble inside the screen:
|
package/guides/regen-contract.md
CHANGED
|
@@ -27,3 +27,17 @@ each joint is `${id}-${jointName}` (e.g. `hero-armUpperR`) and its bone art is
|
|
|
27
27
|
joint `name`s** for any character/device that survives the redesign — overlay
|
|
28
28
|
edits (a retimed wave, a nudged limb angle) reference those exact ids. Renaming a
|
|
29
29
|
joint orphans the edit, exactly like renaming a hand-authored node id.
|
|
30
|
+
|
|
31
|
+
## Tooling
|
|
32
|
+
|
|
33
|
+
Three read-only commands make the address namespace queryable and the contract
|
|
34
|
+
checkable (no render):
|
|
35
|
+
|
|
36
|
+
- `reframe manifest <scene> [--json]` — list every editable address (nodes +
|
|
37
|
+
their editable/animated props, states, timeline labels with patchable params,
|
|
38
|
+
beats, behaviors). Read it before patching so you target real, stable addresses.
|
|
39
|
+
- `reframe lint <scene> [--strict]` — flag motion with no `label` (timing an
|
|
40
|
+
overlay can't reach and a regen can silently drop) + a `motionAddressableRatio`.
|
|
41
|
+
- `reframe verify-overlay <base> <overlay>...` — compose the overlay onto a base
|
|
42
|
+
and report applied vs orphaned. Run it against the regenerated base to prove
|
|
43
|
+
every edit survived; it exits non-zero if any address broke.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reframe-video",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.39",
|
|
4
4
|
"description": "Declarative motion graphics that AI can write and humans can tweak — human edits survive AI regeneration. Deterministic mp4 renders from a plain-data scene format.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"motion-graphics",
|
package/skills/reframe/SKILL.md
CHANGED
|
@@ -72,8 +72,34 @@ to handle explicitly:
|
|
|
72
72
|
mask (update the scene AND remove/update the superseded overlay entry) and
|
|
73
73
|
tell them why.
|
|
74
74
|
|
|
75
|
+
Addressability tooling (read-only, no render — use it when editing):
|
|
76
|
+
|
|
77
|
+
- `npx -y reframe-video manifest <scene> [--json]` — list the scene's editable
|
|
78
|
+
surface (every node + its editable/animated props, states, timeline labels
|
|
79
|
+
with patchable params, beats, behaviors, each with its overlay address). Read
|
|
80
|
+
this BEFORE patching so you target real, stable addresses instead of guessing.
|
|
81
|
+
- `npx -y reframe-video lint <scene> [--strict]` — flag motion with no `label`
|
|
82
|
+
(timing a later overlay can't reach, and a regen can silently drop) plus a
|
|
83
|
+
`motionAddressableRatio`. When authoring motion the user may want to tweak,
|
|
84
|
+
give the step a stable `label`.
|
|
85
|
+
- `npx -y reframe-video verify-overlay <base> <overlay>... ` — after you rewrite
|
|
86
|
+
a base that has overlays, run this to confirm every edit still applies (it
|
|
87
|
+
reports orphans and exits non-zero if any address broke). The regen-survival
|
|
88
|
+
check, without a full render.
|
|
89
|
+
|
|
75
90
|
## Other capabilities
|
|
76
91
|
|
|
92
|
+
- **Assemble media → scene**: when the user hands you images/videos for a piece,
|
|
93
|
+
`npx -y reframe-video assemble <media...> [-o name] [--title "…"] [--bgm <synth>]`
|
|
94
|
+
probes each clip's real duration and scaffolds an editable montage scene `.ts`
|
|
95
|
+
(clip-aware holds, so a short clip never freezes) wiring `photoMontage` + an
|
|
96
|
+
optional `title` + a bed. Then edit the `.ts` (reorder shots, retime, swap a
|
|
97
|
+
`src`) and `render` it. For motion-graphic overlays use the `title()` (kinetic
|
|
98
|
+
headline) and `lowerThird()` (name/role strap) generators — both return
|
|
99
|
+
`{ nodes, timeline }` you compose over the montage; see the guide. **Anchor an
|
|
100
|
+
overlay beat to a shot label** — `beat("cap", { at: "shot-2" }, [lt.timeline])`
|
|
101
|
+
in a `par` branch — so the caption stays synced to its shot if the cut is
|
|
102
|
+
retimed (don't pin it to a fixed `wait`).
|
|
77
103
|
- **Batch**: `npx -y reframe-video batch scene.ts data.json` — one mp4 per
|
|
78
104
|
data row; row keys are overlay addresses (`nodes.<id>.<prop>`,
|
|
79
105
|
`timeline.<label>.duration`, ...). CSV works too (headers = addresses).
|