reframe-video 0.1.3 → 0.2.0
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/assets/sfx/LICENSE.md +1 -1
- package/assets/sfx/bong_001.ogg +0 -0
- package/assets/sfx/click_001.ogg +0 -0
- package/assets/sfx/confirmation_002.ogg +0 -0
- package/assets/sfx/confirmation_003.ogg +0 -0
- package/assets/sfx/confirmation_004.ogg +0 -0
- package/assets/sfx/glass_001.ogg +0 -0
- package/assets/sfx/maximize_001.ogg +0 -0
- package/assets/sfx/maximize_002.ogg +0 -0
- package/assets/sfx/maximize_005.ogg +0 -0
- package/assets/sfx/maximize_009.ogg +0 -0
- package/assets/sfx/open_001.ogg +0 -0
- package/assets/sfx/pluck_001.ogg +0 -0
- package/assets/sfx/pluck_002.ogg +0 -0
- package/assets/sfx/select_001.ogg +0 -0
- package/assets/sfx/select_002.ogg +0 -0
- package/assets/sfx/select_003.ogg +0 -0
- package/dist/bin.js +240 -47
- package/dist/browserEntry.js +130 -68
- package/dist/cli.js +445 -85
- package/dist/index.js +674 -86
- package/dist/labels.js +606 -0
- package/dist/renderer-canvas.js +15 -0
- package/dist/trace-cli.js +9 -9
- package/dist/types/audio.d.ts +9 -0
- package/dist/types/compile.d.ts +1 -0
- package/dist/types/compose.d.ts +18 -2
- package/dist/types/composeComposition.d.ts +27 -0
- package/dist/types/devicePreset.d.ts +65 -0
- package/dist/types/dsl.d.ts +12 -1
- package/dist/types/evaluate.d.ts +32 -0
- package/dist/types/index.d.ts +6 -3
- package/dist/types/ir.d.ts +68 -0
- package/dist/types/motionOps.d.ts +36 -0
- package/dist/types/path.d.ts +7 -3
- package/dist/types/validate.d.ts +4 -1
- package/guides/edsl-guide.md +2 -1
- package/package.json +1 -1
- package/preview/index.html +56 -3
- package/preview/src/main.ts +1132 -46
- package/preview/src/panel.ts +478 -8
- package/preview/src/store.ts +323 -6
package/assets/sfx/LICENSE.md
CHANGED
|
@@ -5,7 +5,7 @@ Verified against each asset page's license field on 2026-06-11.
|
|
|
5
5
|
| files | source | author | license |
|
|
6
6
|
|---|---|---|---|
|
|
7
7
|
| keypress-001/004/007/010/014.wav | [Keyboard Soundpack #1](https://opengameart.org/content/keyboard-soundpack-1-typing-and-single-keystrokes) (Cherry KC 1000 recordings) | unicaegames | CC0 |
|
|
8
|
-
|
|
|
8
|
+
| click_001/002/003/004.ogg, confirmation_001/002/003/004.ogg, maximize_001/002/005/009.ogg, open_001.ogg, pluck_001/002.ogg, select_001/002/003.ogg, bong_001.ogg, glass_001.ogg | [Interface Sounds](https://kenney.nl/assets/interface-sounds) | Kenney (kenney.nl) | CC0 |
|
|
9
9
|
| tick.wav (tick_001), pop.wav (drop_002), shimmer.wav (glass_002 + echo tail) | [Interface Sounds](https://opengameart.org/content/interface-sounds) | Kenney (kenney.nl) | CC0 |
|
|
10
10
|
| whoosh.wav (wind body), rise.wav (reversed slice) | [Air whoosh](https://opengameart.org/content/air-whoosh) | qubodup | CC0 |
|
|
11
11
|
| whoosh.wav (transient layer: swish-9) | [Swishes Sound Pack](https://opengameart.org/content/swishes-sound-pack) | artisticdude | CC0 |
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/bin.js
CHANGED
|
@@ -42,7 +42,7 @@ function segCountOf(points, closed) {
|
|
|
42
42
|
if (n < 2) return 0;
|
|
43
43
|
return closed ? n : n - 1;
|
|
44
44
|
}
|
|
45
|
-
function pathPoint(points, closed, u) {
|
|
45
|
+
function pathPoint(points, closed, u, curviness = 1) {
|
|
46
46
|
const n = points.length;
|
|
47
47
|
if (n === 0) return [0, 0];
|
|
48
48
|
if (n === 1) return [points[0][0], points[0][1]];
|
|
@@ -51,19 +51,41 @@ function pathPoint(points, closed, u) {
|
|
|
51
51
|
const [p0, p1, p2, p3] = controls(points, closed, i);
|
|
52
52
|
const t2 = t * t;
|
|
53
53
|
const t3 = t2 * t;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
if (curviness === 1) {
|
|
55
|
+
const f = (a, b, c, d) => 0.5 * (2 * b + (-a + c) * t + (2 * a - 5 * b + 4 * c - d) * t2 + (-a + 3 * b - 3 * c + d) * t3);
|
|
56
|
+
return [f(p0[0], p1[0], p2[0], p3[0]), f(p0[1], p1[1], p2[1], p3[1])];
|
|
57
|
+
}
|
|
58
|
+
const h00 = 2 * t3 - 3 * t2 + 1;
|
|
59
|
+
const h10 = t3 - 2 * t2 + t;
|
|
60
|
+
const h01 = -2 * t3 + 3 * t2;
|
|
61
|
+
const h11 = t3 - t2;
|
|
62
|
+
const k = curviness * 0.5;
|
|
63
|
+
const H = (a, b, c, d) => h00 * b + h10 * k * (c - a) + h01 * c + h11 * k * (d - b);
|
|
64
|
+
return [H(p0[0], p1[0], p2[0], p3[0]), H(p0[1], p1[1], p2[1], p3[1])];
|
|
65
|
+
}
|
|
66
|
+
function pathTangentAngle(points, closed, u, curviness = 1) {
|
|
58
67
|
const n = points.length;
|
|
59
68
|
if (n < 2) return 0;
|
|
60
69
|
const segs = segCountOf(points, closed);
|
|
61
70
|
const { i, t } = locate(segs, u);
|
|
62
71
|
const [p0, p1, p2, p3] = controls(points, closed, i);
|
|
63
72
|
const t2 = t * t;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
73
|
+
let dx;
|
|
74
|
+
let dy;
|
|
75
|
+
if (curviness === 1) {
|
|
76
|
+
const d = (a, b, c, e) => 0.5 * (-a + c + 2 * (2 * a - 5 * b + 4 * c - e) * t + 3 * (-a + 3 * b - 3 * c + e) * t2);
|
|
77
|
+
dx = d(p0[0], p1[0], p2[0], p3[0]);
|
|
78
|
+
dy = d(p0[1], p1[1], p2[1], p3[1]);
|
|
79
|
+
} else {
|
|
80
|
+
const g00 = 6 * t2 - 6 * t;
|
|
81
|
+
const g10 = 3 * t2 - 4 * t + 1;
|
|
82
|
+
const g01 = -6 * t2 + 6 * t;
|
|
83
|
+
const g11 = 3 * t2 - 2 * t;
|
|
84
|
+
const k = curviness * 0.5;
|
|
85
|
+
const D = (a, b, c, e) => g00 * b + g10 * k * (c - a) + g01 * c + g11 * k * (e - b);
|
|
86
|
+
dx = D(p0[0], p1[0], p2[0], p3[0]);
|
|
87
|
+
dy = D(p0[1], p1[1], p2[1], p3[1]);
|
|
88
|
+
}
|
|
67
89
|
if (dx === 0 && dy === 0) return 0;
|
|
68
90
|
return Math.atan2(dy, dx) * 180 / Math.PI;
|
|
69
91
|
}
|
|
@@ -144,8 +166,8 @@ function compileScene(ir) {
|
|
|
144
166
|
const currentValue = (target, prop) => {
|
|
145
167
|
const v = current.get(key(target, prop));
|
|
146
168
|
if (v !== void 0) return v;
|
|
147
|
-
if (prop === "opacity" || prop === "scale" || prop === "progress") return 1;
|
|
148
|
-
if (prop === "rotation") return 0;
|
|
169
|
+
if (prop === "opacity" || prop === "scale" || prop === "progress" || prop === "scaleX" || prop === "scaleY") return 1;
|
|
170
|
+
if (prop === "rotation" || prop === "skewX" || prop === "skewY") return 0;
|
|
149
171
|
throw new Error(`cannot animate "${prop}" of "${target}": no base value to start from`);
|
|
150
172
|
};
|
|
151
173
|
const labelTimes = /* @__PURE__ */ new Map();
|
|
@@ -248,16 +270,17 @@ function compileScene(ir) {
|
|
|
248
270
|
const duration = tl.duration ?? DEFAULT_MOTIONPATH_DURATION;
|
|
249
271
|
const points = tl.points;
|
|
250
272
|
const closed = tl.closed ?? false;
|
|
273
|
+
const curviness = tl.curviness ?? 1;
|
|
251
274
|
const autoRotate = tl.autoRotate ?? false;
|
|
252
275
|
const rotateOffset = tl.rotateOffset ?? 0;
|
|
253
276
|
let list = motionPaths.get(tl.target);
|
|
254
277
|
if (!list) motionPaths.set(tl.target, list = []);
|
|
255
|
-
list.push({ t0: start, t1: start + duration, points, closed, autoRotate, rotateOffset, ...tl.ease !== void 0 && { ease: tl.ease } });
|
|
278
|
+
list.push({ t0: start, t1: start + duration, points, closed, curviness, autoRotate, rotateOffset, ...tl.ease !== void 0 && { ease: tl.ease } });
|
|
256
279
|
if (points.length > 0) {
|
|
257
|
-
const [ex, ey] = pathPoint(points, closed, 1);
|
|
280
|
+
const [ex, ey] = pathPoint(points, closed, 1, curviness);
|
|
258
281
|
current.set(key(tl.target, "x"), ex);
|
|
259
282
|
current.set(key(tl.target, "y"), ey);
|
|
260
|
-
if (autoRotate) current.set(key(tl.target, "rotation"), pathTangentAngle(points, closed, 1) + rotateOffset);
|
|
283
|
+
if (autoRotate) current.set(key(tl.target, "rotation"), pathTangentAngle(points, closed, 1, curviness) + rotateOffset);
|
|
261
284
|
}
|
|
262
285
|
return start + duration;
|
|
263
286
|
}
|
|
@@ -322,7 +345,18 @@ function validateScene(ir) {
|
|
|
322
345
|
problems.push(`duplicate node id "${node.id}" \u2014 every node id must be unique`);
|
|
323
346
|
}
|
|
324
347
|
nodeById.set(node.id, node);
|
|
325
|
-
if (node.type === "group")
|
|
348
|
+
if (node.type === "group") {
|
|
349
|
+
const clip = node.props.clip;
|
|
350
|
+
if (clip) {
|
|
351
|
+
if (clip.kind !== "rect" && clip.kind !== "ellipse") {
|
|
352
|
+
problems.push(`group "${node.id}" clip: unknown kind "${clip.kind}" \u2014 use "rect" or "ellipse"`);
|
|
353
|
+
}
|
|
354
|
+
if (!(clip.width > 0) || !(clip.height > 0)) {
|
|
355
|
+
problems.push(`group "${node.id}" clip: width and height must be > 0`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
collect(node.children);
|
|
359
|
+
}
|
|
326
360
|
}
|
|
327
361
|
};
|
|
328
362
|
collect(ir.nodes);
|
|
@@ -405,6 +439,9 @@ function validateScene(ir) {
|
|
|
405
439
|
if (tl.duration !== void 0 && tl.duration <= 0) {
|
|
406
440
|
problems.push(`${path2}: motionPath "${tl.target}" duration must be > 0`);
|
|
407
441
|
}
|
|
442
|
+
if (tl.curviness !== void 0 && tl.curviness < 0) {
|
|
443
|
+
problems.push(`${path2}: motionPath "${tl.target}" curviness must be >= 0`);
|
|
444
|
+
}
|
|
408
445
|
break;
|
|
409
446
|
}
|
|
410
447
|
case "wait":
|
|
@@ -423,6 +460,13 @@ function validateScene(ir) {
|
|
|
423
460
|
if (tl.scale !== void 0 && tl.scale <= 0) {
|
|
424
461
|
problems.push(`${path2}: beat "${tl.name}" scale must be > 0`);
|
|
425
462
|
}
|
|
463
|
+
for (const id of tl.nodes ?? []) {
|
|
464
|
+
if (!nodeById.has(id)) {
|
|
465
|
+
problems.push(
|
|
466
|
+
`${path2}: beat "${tl.name}" owns unknown node "${id}" \u2014 known ids: ${[...nodeById.keys()].join(", ")}`
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
426
470
|
tl.children.forEach((c, i) => checkTimeline(c, `${path2}.beat(${tl.name})[${i}]`));
|
|
427
471
|
break;
|
|
428
472
|
}
|
|
@@ -463,11 +507,40 @@ function validateScene(ir) {
|
|
|
463
507
|
}
|
|
464
508
|
if (problems.length > 0) throw new SceneValidationError(problems);
|
|
465
509
|
}
|
|
466
|
-
|
|
510
|
+
function validateComposition(comp) {
|
|
511
|
+
const problems = [];
|
|
512
|
+
if (comp.scenes.length === 0) problems.push("composition has no scenes");
|
|
513
|
+
const seen = /* @__PURE__ */ new Set();
|
|
514
|
+
for (const [i, entry] of comp.scenes.entries()) {
|
|
515
|
+
const where = `scenes[${i}]`;
|
|
516
|
+
try {
|
|
517
|
+
validateScene(entry.scene);
|
|
518
|
+
} catch (err) {
|
|
519
|
+
if (err instanceof SceneValidationError) {
|
|
520
|
+
for (const p of err.problems) problems.push(`${where} (scene "${entry.scene.id}"): ${p}`);
|
|
521
|
+
} else throw err;
|
|
522
|
+
}
|
|
523
|
+
if (seen.has(entry.scene.id)) {
|
|
524
|
+
problems.push(`${where}: duplicate scene id "${entry.scene.id}" \u2014 scene ids must be unique in a composition`);
|
|
525
|
+
}
|
|
526
|
+
seen.add(entry.scene.id);
|
|
527
|
+
if (entry.transition !== void 0 && !TRANSITIONS.includes(entry.transition)) {
|
|
528
|
+
problems.push(`${where}: unknown transition "${entry.transition}" \u2014 valid: ${TRANSITIONS.join(", ")}`);
|
|
529
|
+
}
|
|
530
|
+
if (typeof entry.at === "string" && Number.isNaN(Number(entry.at))) {
|
|
531
|
+
problems.push(`${where}: "at" string "${entry.at}" is not a number (use "-0.5"/"+0.5" or a number)`);
|
|
532
|
+
}
|
|
533
|
+
if (typeof entry.at === "number" && entry.at < 0) {
|
|
534
|
+
problems.push(`${where}: absolute "at" must be >= 0`);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
if (problems.length > 0) throw new SceneValidationError(problems);
|
|
538
|
+
}
|
|
539
|
+
var COMMON_PROPS, PROPS_BY_TYPE, SceneValidationError, TRANSITIONS;
|
|
467
540
|
var init_validate = __esm({
|
|
468
541
|
"../core/src/validate.ts"() {
|
|
469
542
|
"use strict";
|
|
470
|
-
COMMON_PROPS = ["x", "y", "opacity", "rotation", "scale", "anchor"];
|
|
543
|
+
COMMON_PROPS = ["x", "y", "opacity", "rotation", "scale", "scaleX", "scaleY", "skewX", "skewY", "anchor"];
|
|
471
544
|
PROPS_BY_TYPE = {
|
|
472
545
|
rect: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth", "radius"],
|
|
473
546
|
ellipse: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth"],
|
|
@@ -486,6 +559,7 @@ ${problems.map((p) => ` - ${p}`).join("\n")}`);
|
|
|
486
559
|
}
|
|
487
560
|
problems;
|
|
488
561
|
};
|
|
562
|
+
TRANSITIONS = ["cut", "crossfade"];
|
|
489
563
|
}
|
|
490
564
|
});
|
|
491
565
|
|
|
@@ -543,10 +617,27 @@ var init_dsl = __esm({
|
|
|
543
617
|
}
|
|
544
618
|
});
|
|
545
619
|
|
|
620
|
+
// ../core/src/composeComposition.ts
|
|
621
|
+
var init_composeComposition = __esm({
|
|
622
|
+
"../core/src/composeComposition.ts"() {
|
|
623
|
+
"use strict";
|
|
624
|
+
init_compile();
|
|
625
|
+
init_ir();
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
|
|
546
629
|
// ../core/src/compose.ts
|
|
547
630
|
function composeScene(base, ...overlays) {
|
|
548
631
|
const ir = structuredClone(base);
|
|
549
632
|
const report = { applied: [], orphans: [], warnings: [] };
|
|
633
|
+
const baseNodeIds = /* @__PURE__ */ new Set();
|
|
634
|
+
const collectBase = (nodes) => {
|
|
635
|
+
for (const node of nodes) {
|
|
636
|
+
baseNodeIds.add(node.id);
|
|
637
|
+
if (node.type === "group") collectBase(node.children);
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
collectBase(base.nodes);
|
|
550
641
|
overlays.forEach((overlay, index) => {
|
|
551
642
|
const layer = overlay.name ?? `overlay-${index}`;
|
|
552
643
|
if (overlay.target !== void 0 && overlay.target !== ir.id) {
|
|
@@ -554,12 +645,12 @@ function composeScene(base, ...overlays) {
|
|
|
554
645
|
`${layer}: authored against scene "${overlay.target}" but composing onto "${ir.id}"`
|
|
555
646
|
);
|
|
556
647
|
}
|
|
557
|
-
applyOverlay(ir, overlay, layer, report);
|
|
648
|
+
applyOverlay(ir, overlay, layer, report, baseNodeIds);
|
|
558
649
|
});
|
|
559
650
|
validateScene(ir);
|
|
560
651
|
return { ir, report };
|
|
561
652
|
}
|
|
562
|
-
function applyOverlay(ir, overlay, layer, report) {
|
|
653
|
+
function applyOverlay(ir, overlay, layer, report, baseNodeIds) {
|
|
563
654
|
const nodeById = /* @__PURE__ */ new Map();
|
|
564
655
|
const collect = (nodes) => {
|
|
565
656
|
for (const node of nodes) {
|
|
@@ -674,7 +765,7 @@ function applyOverlay(ir, overlay, layer, report) {
|
|
|
674
765
|
to: ["duration", "ease", "stagger"],
|
|
675
766
|
tween: ["duration", "ease"],
|
|
676
767
|
wait: ["duration"],
|
|
677
|
-
motionPath: ["points", "duration", "ease"],
|
|
768
|
+
motionPath: ["points", "duration", "ease", "curviness", "autoRotate"],
|
|
678
769
|
beat: ["at", "gap", "scale", "duration", "order"]
|
|
679
770
|
};
|
|
680
771
|
let timingPatched = false;
|
|
@@ -712,6 +803,49 @@ function applyOverlay(ir, overlay, layer, report) {
|
|
|
712
803
|
nodeById.set(node.id, node);
|
|
713
804
|
applied(`addNodes.${node.id}`, "add-node");
|
|
714
805
|
}
|
|
806
|
+
for (const id of overlay.removeNodes ?? []) {
|
|
807
|
+
if (baseNodeIds.has(id)) {
|
|
808
|
+
orphan(
|
|
809
|
+
`removeNodes.${id}`,
|
|
810
|
+
`"${id}" is a base scene node \u2014 the scene owns it; hide it with opacity: 0 instead of removing`
|
|
811
|
+
);
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
814
|
+
const index = ir.nodes.findIndex((n) => n.id === id);
|
|
815
|
+
if (index < 0) {
|
|
816
|
+
orphan(
|
|
817
|
+
`removeNodes.${id}`,
|
|
818
|
+
`unknown overlay-added node "${id}" \u2014 nothing to remove`
|
|
819
|
+
);
|
|
820
|
+
continue;
|
|
821
|
+
}
|
|
822
|
+
ir.nodes.splice(index, 1);
|
|
823
|
+
nodeById.delete(id);
|
|
824
|
+
applied(`removeNodes.${id}`, "remove-node");
|
|
825
|
+
}
|
|
826
|
+
if (overlay.addTimeline && overlay.addTimeline.length > 0) {
|
|
827
|
+
const collectTargets = (tl, out) => {
|
|
828
|
+
if (tl.kind === "tween" || tl.kind === "motionPath") out.add(tl.target);
|
|
829
|
+
if ("children" in tl) tl.children.forEach((c) => collectTargets(c, out));
|
|
830
|
+
};
|
|
831
|
+
const valid = [];
|
|
832
|
+
overlay.addTimeline.forEach((frag, i) => {
|
|
833
|
+
const targets = /* @__PURE__ */ new Set();
|
|
834
|
+
collectTargets(frag, targets);
|
|
835
|
+
const missing = [...targets].filter((id) => !nodeById.has(id));
|
|
836
|
+
if (missing.length > 0) {
|
|
837
|
+
orphan(`addTimeline[${i}]`, `targets unknown node(s) ${missing.join(", ")} \u2014 known ids: ${knownIds()}`);
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
valid.push(structuredClone(frag));
|
|
841
|
+
applied(`addTimeline[${i}]`, "add-timeline");
|
|
842
|
+
});
|
|
843
|
+
if (valid.length > 0) {
|
|
844
|
+
ir.timeline = ir.timeline ? { kind: "par", children: [ir.timeline, ...valid] } : valid.length === 1 ? valid[0] : { kind: "par", children: valid };
|
|
845
|
+
delete ir.duration;
|
|
846
|
+
ir.duration = compileScene(ir).duration;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
715
849
|
}
|
|
716
850
|
var SCENE_PATCHABLE;
|
|
717
851
|
var init_compose = __esm({
|
|
@@ -902,6 +1036,22 @@ var init_presets = __esm({
|
|
|
902
1036
|
}
|
|
903
1037
|
});
|
|
904
1038
|
|
|
1039
|
+
// ../core/src/devicePreset.ts
|
|
1040
|
+
var init_devicePreset = __esm({
|
|
1041
|
+
"../core/src/devicePreset.ts"() {
|
|
1042
|
+
"use strict";
|
|
1043
|
+
init_dsl();
|
|
1044
|
+
}
|
|
1045
|
+
});
|
|
1046
|
+
|
|
1047
|
+
// ../core/src/motionOps.ts
|
|
1048
|
+
var init_motionOps = __esm({
|
|
1049
|
+
"../core/src/motionOps.ts"() {
|
|
1050
|
+
"use strict";
|
|
1051
|
+
init_dsl();
|
|
1052
|
+
}
|
|
1053
|
+
});
|
|
1054
|
+
|
|
905
1055
|
// ../core/src/audio.ts
|
|
906
1056
|
function resolveAudioPlan(compiled) {
|
|
907
1057
|
const audio = compiled.ir.audio;
|
|
@@ -938,6 +1088,15 @@ function resolveAudioPlan(compiled) {
|
|
|
938
1088
|
});
|
|
939
1089
|
}
|
|
940
1090
|
cues.sort((a, b) => a.t - b.t);
|
|
1091
|
+
return {
|
|
1092
|
+
duration,
|
|
1093
|
+
bgm: resolveBgm(audio.bgm),
|
|
1094
|
+
cues,
|
|
1095
|
+
duckWindows: mergeDuckWindows(cues, duration),
|
|
1096
|
+
warnings
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
function mergeDuckWindows(cues, duration) {
|
|
941
1100
|
const duckWindows = [];
|
|
942
1101
|
for (const cue of cues) {
|
|
943
1102
|
const window2 = { t0: cue.t, t1: Math.min(duration, cue.t + cue.duration) };
|
|
@@ -945,23 +1104,22 @@ function resolveAudioPlan(compiled) {
|
|
|
945
1104
|
if (last && window2.t0 <= last.t1 + 0.1) last.t1 = Math.max(last.t1, window2.t1);
|
|
946
1105
|
else duckWindows.push(window2);
|
|
947
1106
|
}
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
}
|
|
964
|
-
return { duration, bgm, cues, duckWindows, warnings };
|
|
1107
|
+
return duckWindows;
|
|
1108
|
+
}
|
|
1109
|
+
function resolveBgm(b) {
|
|
1110
|
+
if (!b) return null;
|
|
1111
|
+
const duck = b.duck === false ? null : {
|
|
1112
|
+
depth: b.duck?.depth ?? 0.5,
|
|
1113
|
+
attack: b.duck?.attack ?? 0.05,
|
|
1114
|
+
release: b.duck?.release ?? 0.25
|
|
1115
|
+
};
|
|
1116
|
+
return {
|
|
1117
|
+
source: b.file ? { kind: "file", path: b.file } : { kind: "synth", name: b.synth ?? "ambient-pad" },
|
|
1118
|
+
gain: b.gain ?? 0.5,
|
|
1119
|
+
fadeIn: b.fadeIn ?? 0,
|
|
1120
|
+
fadeOut: b.fadeOut ?? 0,
|
|
1121
|
+
duck
|
|
1122
|
+
};
|
|
965
1123
|
}
|
|
966
1124
|
var SFX_DURATION, FILE_CUE_DURATION;
|
|
967
1125
|
var init_audio = __esm({
|
|
@@ -1098,10 +1256,13 @@ var init_src = __esm({
|
|
|
1098
1256
|
init_ir();
|
|
1099
1257
|
init_dsl();
|
|
1100
1258
|
init_validate();
|
|
1259
|
+
init_composeComposition();
|
|
1101
1260
|
init_compose();
|
|
1102
1261
|
init_compile();
|
|
1103
1262
|
init_path();
|
|
1104
1263
|
init_presets();
|
|
1264
|
+
init_devicePreset();
|
|
1265
|
+
init_motionOps();
|
|
1105
1266
|
init_audio();
|
|
1106
1267
|
init_evaluate();
|
|
1107
1268
|
init_interpolate();
|
|
@@ -2042,18 +2203,16 @@ var init_batch = __esm({
|
|
|
2042
2203
|
// ../render-cli/src/loadScene.ts
|
|
2043
2204
|
var loadScene_exports = {};
|
|
2044
2205
|
__export(loadScene_exports, {
|
|
2206
|
+
isComposition: () => isComposition,
|
|
2207
|
+
loadModule: () => loadModule,
|
|
2045
2208
|
loadScene: () => loadScene
|
|
2046
2209
|
});
|
|
2047
2210
|
import { build as build2 } from "esbuild";
|
|
2048
2211
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
2049
2212
|
import { dirname as dirname6, resolve as resolve3 } from "node:path";
|
|
2050
2213
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
2051
|
-
async function
|
|
2052
|
-
if (path2.endsWith(".json"))
|
|
2053
|
-
const ir = JSON.parse(await readFile5(path2, "utf8"));
|
|
2054
|
-
validateScene(ir);
|
|
2055
|
-
return ir;
|
|
2056
|
-
}
|
|
2214
|
+
async function loadDefault(path2) {
|
|
2215
|
+
if (path2.endsWith(".json")) return JSON.parse(await readFile5(path2, "utf8"));
|
|
2057
2216
|
let code;
|
|
2058
2217
|
try {
|
|
2059
2218
|
const out = await build2({
|
|
@@ -2070,15 +2229,33 @@ async function loadScene(path2) {
|
|
|
2070
2229
|
});
|
|
2071
2230
|
code = out.outputFiles[0].text;
|
|
2072
2231
|
} catch (err) {
|
|
2073
|
-
throw new Error(
|
|
2074
|
-
|
|
2075
|
-
${err instanceof Error ? err.message : String(err)}`
|
|
2076
|
-
);
|
|
2232
|
+
throw new Error(`failed to bundle ${path2}:
|
|
2233
|
+
${err instanceof Error ? err.message : String(err)}`);
|
|
2077
2234
|
}
|
|
2078
2235
|
const mod = await import(`data:text/javascript;base64,${Buffer.from(code).toString("base64")}`);
|
|
2079
|
-
if (
|
|
2236
|
+
if (mod.default === void 0) throw new Error(`${path2} must default-export a scene or composition`);
|
|
2080
2237
|
return mod.default;
|
|
2081
2238
|
}
|
|
2239
|
+
function isComposition(def) {
|
|
2240
|
+
return typeof def === "object" && def !== null && Array.isArray(def.scenes);
|
|
2241
|
+
}
|
|
2242
|
+
async function loadScene(path2) {
|
|
2243
|
+
const def = await loadDefault(path2);
|
|
2244
|
+
if (isComposition(def)) {
|
|
2245
|
+
throw new Error(`${path2} is a composition \u2014 render it directly, not as a single scene`);
|
|
2246
|
+
}
|
|
2247
|
+
validateScene(def);
|
|
2248
|
+
return def;
|
|
2249
|
+
}
|
|
2250
|
+
async function loadModule(path2) {
|
|
2251
|
+
const def = await loadDefault(path2);
|
|
2252
|
+
if (isComposition(def)) {
|
|
2253
|
+
validateComposition(def);
|
|
2254
|
+
return { kind: "composition", ir: def };
|
|
2255
|
+
}
|
|
2256
|
+
validateScene(def);
|
|
2257
|
+
return { kind: "scene", ir: def };
|
|
2258
|
+
}
|
|
2082
2259
|
var HERE, CORE_ENTRY;
|
|
2083
2260
|
var init_loadScene = __esm({
|
|
2084
2261
|
"../render-cli/src/loadScene.ts"() {
|
|
@@ -2101,6 +2278,7 @@ var HERE2 = dirname7(fileURLToPath5(import.meta.url));
|
|
|
2101
2278
|
var ROOT2 = PACKAGED ? resolve4(HERE2, "..") : resolve4(HERE2, "..", "..", "..");
|
|
2102
2279
|
var USER_CWD = process.env.INIT_CWD ?? process.cwd();
|
|
2103
2280
|
var RENDER_CLI = PACKAGED ? join6(ROOT2, "dist", "cli.js") : join6(ROOT2, "packages", "render-cli", "src", "cli.ts");
|
|
2281
|
+
var LABELS = PACKAGED ? join6(ROOT2, "dist", "labels.js") : join6(ROOT2, "packages", "render-cli", "src", "labels.ts");
|
|
2104
2282
|
var ANALYZE = PACKAGED ? join6(ROOT2, "dist", "analyze.js") : join6(ROOT2, "benchmark", "harness", "motion", "analyze.ts");
|
|
2105
2283
|
var TRACE = PACKAGED ? join6(ROOT2, "dist", "trace-cli.js") : join6(ROOT2, "benchmark", "harness", "motion", "trace-cli.ts");
|
|
2106
2284
|
var CMD = PACKAGED ? "reframe" : "pnpm reframe";
|
|
@@ -2114,6 +2292,7 @@ usage:
|
|
|
2114
2292
|
rise-settle, slide-bank, reveal-orbit, spin-forge)
|
|
2115
2293
|
${CMD} preview open the scrub/edit UI (lists scenes in your directory)
|
|
2116
2294
|
${CMD} new <scene-name> scaffold <scene-name>.ts in your directory
|
|
2295
|
+
${CMD} labels <scene.ts|.json> print the event clock (label \u2192 exact seconds; for sound design / timing)
|
|
2117
2296
|
${CMD} motion <mp4|framesDir> motion-profile a rendered clip
|
|
2118
2297
|
${CMD} trace <ref.mp4> [--apply scene.ts] extract a video's motion structure \u2192 MotionSketch / timeline
|
|
2119
2298
|
${CMD} guide [--regen] print the scene-authoring guide (for you or your AI)
|
|
@@ -2244,6 +2423,17 @@ ${USAGE}`);
|
|
|
2244
2423
|
await (PACKAGED ? run(process.execPath, [RENDER_CLI, mode, inputPath, ...outArgs]) : run("npx", ["tsx", RENDER_CLI, mode, inputPath, ...outArgs]))
|
|
2245
2424
|
);
|
|
2246
2425
|
}
|
|
2426
|
+
case "labels": {
|
|
2427
|
+
const input = rest[0];
|
|
2428
|
+
if (!input || input.startsWith("-")) fail(`labels needs a scene file
|
|
2429
|
+
|
|
2430
|
+
${USAGE}`);
|
|
2431
|
+
const inputPath = userPath(input);
|
|
2432
|
+
if (!existsSync4(inputPath)) fail(`no such file: ${inputPath}`);
|
|
2433
|
+
process.exit(
|
|
2434
|
+
await (PACKAGED ? run(process.execPath, [LABELS, inputPath]) : run("npx", ["tsx", LABELS, inputPath]))
|
|
2435
|
+
);
|
|
2436
|
+
}
|
|
2247
2437
|
case "logo": {
|
|
2248
2438
|
const positional = [];
|
|
2249
2439
|
const flags = {};
|
|
@@ -2332,6 +2522,9 @@ ${results.length - failed} rendered (${orphaned} with orphans), ${failed} failed
|
|
|
2332
2522
|
process.exit(failed > 0 ? 1 : 0);
|
|
2333
2523
|
}
|
|
2334
2524
|
case "preview": {
|
|
2525
|
+
console.log(
|
|
2526
|
+
"preview: drag waypoints/nodes; double-click a path to add a waypoint or a handle to remove it;\n \u270E reshapes an ease curve; 'vary \xD74' proposes motion variants.\ndeep-link a scene + time: http://localhost:5173/?scene=<scene-name>&t=<seconds>"
|
|
2527
|
+
);
|
|
2335
2528
|
if (PACKAGED) {
|
|
2336
2529
|
const { createRequire } = await import("node:module");
|
|
2337
2530
|
const vitePkg = createRequire(import.meta.url).resolve("vite/package.json");
|