reframe-video 0.6.23 → 0.6.25

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/labels.js CHANGED
@@ -325,7 +325,67 @@ function compileScene(ir) {
325
325
  };
326
326
  }
327
327
 
328
+ // ../core/src/interpolate.ts
329
+ var BACK_C1 = 1.70158;
330
+ var BACK_C2 = BACK_C1 * 1.525;
331
+ var BACK_C3 = BACK_C1 + 1;
332
+ var ELASTIC_C4 = 2 * Math.PI / 3;
333
+ var ELASTIC_C5 = 2 * Math.PI / 4.5;
334
+ function springEase(stiffness, damping, velocity) {
335
+ const K = 5;
336
+ const zeta = Math.min(0.999, Math.max(0.05, damping / (2 * Math.sqrt(Math.max(1e-6, stiffness)))));
337
+ const wd = K / zeta * Math.sqrt(1 - zeta * zeta);
338
+ const coef = (K - velocity) / wd;
339
+ return (u) => {
340
+ if (u <= 0) return 0;
341
+ if (u >= 1) return 1;
342
+ return 1 - Math.exp(-K * u) * (Math.cos(wd * u) + coef * Math.sin(wd * u));
343
+ };
344
+ }
345
+ function easeOutBounce(u) {
346
+ const n1 = 7.5625;
347
+ const d1 = 2.75;
348
+ if (u < 1 / d1) return n1 * u * u;
349
+ if (u < 2 / d1) return n1 * (u -= 1.5 / d1) * u + 0.75;
350
+ if (u < 2.5 / d1) return n1 * (u -= 2.25 / d1) * u + 0.9375;
351
+ return n1 * (u -= 2.625 / d1) * u + 0.984375;
352
+ }
353
+ var EASE_TABLE = {
354
+ linear: (u) => u,
355
+ easeInQuad: (u) => u * u,
356
+ easeOutQuad: (u) => 1 - (1 - u) * (1 - u),
357
+ easeInOutQuad: (u) => u < 0.5 ? 2 * u * u : 1 - (-2 * u + 2) ** 2 / 2,
358
+ easeInCubic: (u) => u ** 3,
359
+ easeOutCubic: (u) => 1 - (1 - u) ** 3,
360
+ easeInOutCubic: (u) => u < 0.5 ? 4 * u ** 3 : 1 - (-2 * u + 2) ** 3 / 2,
361
+ easeInQuart: (u) => u ** 4,
362
+ easeOutQuart: (u) => 1 - (1 - u) ** 4,
363
+ easeInOutQuart: (u) => u < 0.5 ? 8 * u ** 4 : 1 - (-2 * u + 2) ** 4 / 2,
364
+ easeInExpo: (u) => u === 0 ? 0 : 2 ** (10 * u - 10),
365
+ easeOutExpo: (u) => u === 1 ? 1 : 1 - 2 ** (-10 * u),
366
+ easeInOutExpo: (u) => u === 0 ? 0 : u === 1 ? 1 : u < 0.5 ? 2 ** (20 * u - 10) / 2 : (2 - 2 ** (-20 * u + 10)) / 2,
367
+ // --- expressive eases (GSAP's signature feel) — standard Penner equations ---
368
+ // back: overshoots past the target then settles (pop / snap)
369
+ easeInBack: (u) => BACK_C3 * u ** 3 - BACK_C1 * u * u,
370
+ easeOutBack: (u) => 1 + BACK_C3 * (u - 1) ** 3 + BACK_C1 * (u - 1) ** 2,
371
+ easeInOutBack: (u) => u < 0.5 ? (2 * u) ** 2 * ((BACK_C2 + 1) * 2 * u - BACK_C2) / 2 : ((2 * u - 2) ** 2 * ((BACK_C2 + 1) * (2 * u - 2) + BACK_C2) + 2) / 2,
372
+ // elastic: rings around the target before settling (playful spring)
373
+ easeInElastic: (u) => u === 0 ? 0 : u === 1 ? 1 : -(2 ** (10 * u - 10)) * Math.sin((u * 10 - 10.75) * ELASTIC_C4),
374
+ easeOutElastic: (u) => u === 0 ? 0 : u === 1 ? 1 : 2 ** (-10 * u) * Math.sin((u * 10 - 0.75) * ELASTIC_C4) + 1,
375
+ easeInOutElastic: (u) => u === 0 ? 0 : u === 1 ? 1 : u < 0.5 ? -(2 ** (20 * u - 10) * Math.sin((20 * u - 11.125) * ELASTIC_C5)) / 2 : 2 ** (-20 * u + 10) * Math.sin((20 * u - 11.125) * ELASTIC_C5) / 2 + 1,
376
+ // bounce: drops and bounces to rest (lands without overshoot)
377
+ easeInBounce: (u) => 1 - easeOutBounce(1 - u),
378
+ easeOutBounce,
379
+ easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2,
380
+ // damped-spring presets (ζ from damping/(2√stiffness)): 0.5 / 0.30 / 0.90
381
+ spring: springEase(100, 10, 0),
382
+ springBouncy: springEase(180, 8, 0),
383
+ springStiff: springEase(210, 26, 0)
384
+ };
385
+ var EASE_NAMES = Object.keys(EASE_TABLE);
386
+
328
387
  // ../core/src/validate.ts
388
+ var EASE_SET = new Set(EASE_NAMES);
329
389
  var FX_PROPS = ["blur", "shadowColor", "shadowBlur", "shadowX", "shadowY", "blend"];
330
390
  var BLEND_MODES = /* @__PURE__ */ new Set([
331
391
  "normal",
@@ -457,6 +517,26 @@ function validateScene(ir) {
457
517
  );
458
518
  }
459
519
  const labels = /* @__PURE__ */ new Set();
520
+ const checkEase = (path3, ease) => {
521
+ if (ease === void 0) return;
522
+ if (typeof ease === "string") {
523
+ if (!EASE_SET.has(ease)) {
524
+ problems.push(`${path3}: unknown ease "${ease}" \u2014 valid: ${EASE_NAMES.join(", ")} (note: there are no *Sine eases)`);
525
+ }
526
+ return;
527
+ }
528
+ if (typeof ease === "object" && ease !== null) {
529
+ const o = ease;
530
+ if ("spring" in o) return;
531
+ if ("cubicBezier" in o) {
532
+ if (!Array.isArray(o.cubicBezier) || o.cubicBezier.length !== 4) {
533
+ problems.push(`${path3}: ease cubicBezier must be [x1, y1, x2, y2]`);
534
+ }
535
+ return;
536
+ }
537
+ }
538
+ problems.push(`${path3}: invalid ease \u2014 use a name, { spring }, or { cubicBezier: [x1,y1,x2,y2] }`);
539
+ };
460
540
  const checkTimeline = (tl, path3) => {
461
541
  if ("label" in tl && tl.label !== void 0) {
462
542
  if (labels.has(tl.label)) {
@@ -484,6 +564,7 @@ function validateScene(ir) {
484
564
  if (tl.duration !== void 0 && tl.duration <= 0) {
485
565
  problems.push(`${path3}: to("${tl.state}") duration must be > 0`);
486
566
  }
567
+ checkEase(path3, tl.ease);
487
568
  for (const id of tl.filter ?? []) {
488
569
  if (!nodeById.has(id)) problems.push(`${path3}: filter contains unknown node "${id}"`);
489
570
  }
@@ -493,6 +574,7 @@ function validateScene(ir) {
493
574
  if (tl.duration !== void 0 && tl.duration <= 0) {
494
575
  problems.push(`${path3}: tween duration must be > 0`);
495
576
  }
577
+ checkEase(path3, tl.ease);
496
578
  break;
497
579
  case "motionPath": {
498
580
  const node = nodeById.get(tl.target);
@@ -513,6 +595,7 @@ function validateScene(ir) {
513
595
  if (tl.curviness !== void 0 && tl.curviness < 0) {
514
596
  problems.push(`${path3}: motionPath "${tl.target}" curviness must be >= 0`);
515
597
  }
598
+ checkEase(path3, tl.ease);
516
599
  break;
517
600
  }
518
601
  case "wait":
@@ -609,65 +692,6 @@ function validateScene(ir) {
609
692
  // ../core/src/presets.ts
610
693
  var SET = 1 / 120;
611
694
 
612
- // ../core/src/interpolate.ts
613
- var BACK_C1 = 1.70158;
614
- var BACK_C2 = BACK_C1 * 1.525;
615
- var BACK_C3 = BACK_C1 + 1;
616
- var ELASTIC_C4 = 2 * Math.PI / 3;
617
- var ELASTIC_C5 = 2 * Math.PI / 4.5;
618
- function springEase(stiffness, damping, velocity) {
619
- const K = 5;
620
- const zeta = Math.min(0.999, Math.max(0.05, damping / (2 * Math.sqrt(Math.max(1e-6, stiffness)))));
621
- const wd = K / zeta * Math.sqrt(1 - zeta * zeta);
622
- const coef = (K - velocity) / wd;
623
- return (u) => {
624
- if (u <= 0) return 0;
625
- if (u >= 1) return 1;
626
- return 1 - Math.exp(-K * u) * (Math.cos(wd * u) + coef * Math.sin(wd * u));
627
- };
628
- }
629
- function easeOutBounce(u) {
630
- const n1 = 7.5625;
631
- const d1 = 2.75;
632
- if (u < 1 / d1) return n1 * u * u;
633
- if (u < 2 / d1) return n1 * (u -= 1.5 / d1) * u + 0.75;
634
- if (u < 2.5 / d1) return n1 * (u -= 2.25 / d1) * u + 0.9375;
635
- return n1 * (u -= 2.625 / d1) * u + 0.984375;
636
- }
637
- var EASE_TABLE = {
638
- linear: (u) => u,
639
- easeInQuad: (u) => u * u,
640
- easeOutQuad: (u) => 1 - (1 - u) * (1 - u),
641
- easeInOutQuad: (u) => u < 0.5 ? 2 * u * u : 1 - (-2 * u + 2) ** 2 / 2,
642
- easeInCubic: (u) => u ** 3,
643
- easeOutCubic: (u) => 1 - (1 - u) ** 3,
644
- easeInOutCubic: (u) => u < 0.5 ? 4 * u ** 3 : 1 - (-2 * u + 2) ** 3 / 2,
645
- easeInQuart: (u) => u ** 4,
646
- easeOutQuart: (u) => 1 - (1 - u) ** 4,
647
- easeInOutQuart: (u) => u < 0.5 ? 8 * u ** 4 : 1 - (-2 * u + 2) ** 4 / 2,
648
- easeInExpo: (u) => u === 0 ? 0 : 2 ** (10 * u - 10),
649
- easeOutExpo: (u) => u === 1 ? 1 : 1 - 2 ** (-10 * u),
650
- easeInOutExpo: (u) => u === 0 ? 0 : u === 1 ? 1 : u < 0.5 ? 2 ** (20 * u - 10) / 2 : (2 - 2 ** (-20 * u + 10)) / 2,
651
- // --- expressive eases (GSAP's signature feel) — standard Penner equations ---
652
- // back: overshoots past the target then settles (pop / snap)
653
- easeInBack: (u) => BACK_C3 * u ** 3 - BACK_C1 * u * u,
654
- easeOutBack: (u) => 1 + BACK_C3 * (u - 1) ** 3 + BACK_C1 * (u - 1) ** 2,
655
- easeInOutBack: (u) => u < 0.5 ? (2 * u) ** 2 * ((BACK_C2 + 1) * 2 * u - BACK_C2) / 2 : ((2 * u - 2) ** 2 * ((BACK_C2 + 1) * (2 * u - 2) + BACK_C2) + 2) / 2,
656
- // elastic: rings around the target before settling (playful spring)
657
- easeInElastic: (u) => u === 0 ? 0 : u === 1 ? 1 : -(2 ** (10 * u - 10)) * Math.sin((u * 10 - 10.75) * ELASTIC_C4),
658
- easeOutElastic: (u) => u === 0 ? 0 : u === 1 ? 1 : 2 ** (-10 * u) * Math.sin((u * 10 - 0.75) * ELASTIC_C4) + 1,
659
- easeInOutElastic: (u) => u === 0 ? 0 : u === 1 ? 1 : u < 0.5 ? -(2 ** (20 * u - 10) * Math.sin((20 * u - 11.125) * ELASTIC_C5)) / 2 : 2 ** (-20 * u + 10) * Math.sin((20 * u - 11.125) * ELASTIC_C5) / 2 + 1,
660
- // bounce: drops and bounces to rest (lands without overshoot)
661
- easeInBounce: (u) => 1 - easeOutBounce(1 - u),
662
- easeOutBounce,
663
- easeInOutBounce: (u) => u < 0.5 ? (1 - easeOutBounce(1 - 2 * u)) / 2 : (1 + easeOutBounce(2 * u - 1)) / 2,
664
- // damped-spring presets (ζ from damping/(2√stiffness)): 0.5 / 0.30 / 0.90
665
- spring: springEase(100, 10, 0),
666
- springBouncy: springEase(180, 8, 0),
667
- springStiff: springEase(210, 26, 0)
668
- };
669
- var EASE_NAMES = Object.keys(EASE_TABLE);
670
-
671
695
  // ../core/src/evaluate.ts
672
696
  var DEG = Math.PI / 180;
673
697
 
package/dist/trace-cli.js CHANGED
@@ -5,37 +5,6 @@ import { writeFile as writeFile2 } from "node:fs/promises";
5
5
  import { resolve as resolve2 } from "node:path";
6
6
  import { pathToFileURL } from "node:url";
7
7
 
8
- // ../core/src/validate.ts
9
- var FX_PROPS = ["blur", "shadowColor", "shadowBlur", "shadowX", "shadowY", "blend"];
10
- var COMMON_PROPS = ["x", "y", "opacity", "rotation", "scale", "scaleX", "scaleY", "skewX", "skewY", "z", "rotateX", "rotateY", "anchor", "fixed", ...FX_PROPS];
11
- var PROPS_BY_TYPE = {
12
- rect: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth", "radius"],
13
- ellipse: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth"],
14
- line: ["x1", "y1", "x2", "y2", "stroke", "strokeWidth", "opacity", "progress", ...FX_PROPS],
15
- text: [...COMMON_PROPS, "content", "contentDecimals", "contentThousands", "prefix", "suffix", "fontFamily", "fontSize", "fontWeight", "fill", "letterSpacing"],
16
- image: [...COMMON_PROPS, "src", "width", "height", "fit"],
17
- video: [...COMMON_PROPS, "src", "width", "height", "fit", "start", "rate", "clipStart", "volume", "fadeIn", "pan"],
18
- path: [...COMMON_PROPS, "d", "fill", "stroke", "strokeWidth", "progress", "originX", "originY"],
19
- group: COMMON_PROPS
20
- };
21
-
22
- // ../core/src/dsl.ts
23
- function seq(...children) {
24
- return { kind: "seq", children };
25
- }
26
- function par(...children) {
27
- return { kind: "par", children };
28
- }
29
- function tween(target, props, opts = {}) {
30
- return { kind: "tween", target, props, ...opts };
31
- }
32
- function wait(duration, label) {
33
- return { kind: "wait", duration, ...label !== void 0 && { label } };
34
- }
35
-
36
- // ../core/src/presets.ts
37
- var SET = 1 / 120;
38
-
39
8
  // ../core/src/interpolate.ts
40
9
  var BACK_C1 = 1.70158;
41
10
  var BACK_C2 = BACK_C1 * 1.525;
@@ -95,6 +64,38 @@ var EASE_TABLE = {
95
64
  };
96
65
  var EASE_NAMES = Object.keys(EASE_TABLE);
97
66
 
67
+ // ../core/src/validate.ts
68
+ var EASE_SET = new Set(EASE_NAMES);
69
+ var FX_PROPS = ["blur", "shadowColor", "shadowBlur", "shadowX", "shadowY", "blend"];
70
+ var COMMON_PROPS = ["x", "y", "opacity", "rotation", "scale", "scaleX", "scaleY", "skewX", "skewY", "z", "rotateX", "rotateY", "anchor", "fixed", ...FX_PROPS];
71
+ var PROPS_BY_TYPE = {
72
+ rect: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth", "radius"],
73
+ ellipse: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth"],
74
+ line: ["x1", "y1", "x2", "y2", "stroke", "strokeWidth", "opacity", "progress", ...FX_PROPS],
75
+ text: [...COMMON_PROPS, "content", "contentDecimals", "contentThousands", "prefix", "suffix", "fontFamily", "fontSize", "fontWeight", "fill", "letterSpacing"],
76
+ image: [...COMMON_PROPS, "src", "width", "height", "fit"],
77
+ video: [...COMMON_PROPS, "src", "width", "height", "fit", "start", "rate", "clipStart", "volume", "fadeIn", "pan"],
78
+ path: [...COMMON_PROPS, "d", "fill", "stroke", "strokeWidth", "progress", "originX", "originY"],
79
+ group: COMMON_PROPS
80
+ };
81
+
82
+ // ../core/src/dsl.ts
83
+ function seq(...children) {
84
+ return { kind: "seq", children };
85
+ }
86
+ function par(...children) {
87
+ return { kind: "par", children };
88
+ }
89
+ function tween(target, props, opts = {}) {
90
+ return { kind: "tween", target, props, ...opts };
91
+ }
92
+ function wait(duration, label) {
93
+ return { kind: "wait", duration, ...label !== void 0 && { label } };
94
+ }
95
+
96
+ // ../core/src/presets.ts
97
+ var SET = 1 / 120;
98
+
98
99
  // ../core/src/evaluate.ts
99
100
  var DEG = Math.PI / 180;
100
101
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reframe-video",
3
- "version": "0.6.23",
3
+ "version": "0.6.25",
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",
@@ -11,9 +11,12 @@ runtime needs ffmpeg on PATH and a one-time `npx playwright install chromium`
11
11
 
12
12
  ## Creating a scene
13
13
 
14
- 1. **Read the guide first** — it is the complete, current syntax (~1,700
15
- tokens) and one read is enough to write valid scenes:
16
- `npx -y reframe-video guide`
14
+ 1. **Read the guide first** — `npx -y reframe-video guide` is the complete,
15
+ current syntax. It's sectioned and sizable: read the core (Nodes, States,
16
+ Timeline) closely, and **skim the specialized sections by heading**
17
+ (perspective, track mattes, group effects, video, montage, character rig,
18
+ device frames, cursor, audio) — read one in full only when your scene uses
19
+ it. You don't need to re-read it for follow-up edits; it's already in context.
17
20
  2. Write a single self-contained `<name>.ts` in the user's directory
18
21
  (`npx -y reframe-video new <name>` scaffolds a documented starter).
19
22
  Scenes must be pure functions of time: no `Math.random()`/`Date` — use