reframe-video 0.1.2 → 0.1.3
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/bin.js +490 -90
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -355,11 +355,11 @@ function validateScene(ir) {
|
|
|
355
355
|
);
|
|
356
356
|
}
|
|
357
357
|
const labels = /* @__PURE__ */ new Set();
|
|
358
|
-
const checkTimeline = (tl,
|
|
358
|
+
const checkTimeline = (tl, path2) => {
|
|
359
359
|
if ("label" in tl && tl.label !== void 0) {
|
|
360
360
|
if (labels.has(tl.label)) {
|
|
361
361
|
problems.push(
|
|
362
|
-
`${
|
|
362
|
+
`${path2}: duplicate timeline label "${tl.label}" \u2014 labels are overlay addresses and must be unique`
|
|
363
363
|
);
|
|
364
364
|
}
|
|
365
365
|
labels.add(tl.label);
|
|
@@ -367,63 +367,63 @@ function validateScene(ir) {
|
|
|
367
367
|
switch (tl.kind) {
|
|
368
368
|
case "seq":
|
|
369
369
|
case "par":
|
|
370
|
-
tl.children.forEach((c, i) => checkTimeline(c, `${
|
|
370
|
+
tl.children.forEach((c, i) => checkTimeline(c, `${path2}.${tl.kind}[${i}]`));
|
|
371
371
|
break;
|
|
372
372
|
case "stagger":
|
|
373
|
-
if (tl.interval < 0) problems.push(`${
|
|
374
|
-
tl.children.forEach((c, i) => checkTimeline(c, `${
|
|
373
|
+
if (tl.interval < 0) problems.push(`${path2}: stagger interval must be >= 0`);
|
|
374
|
+
tl.children.forEach((c, i) => checkTimeline(c, `${path2}.stagger[${i}]`));
|
|
375
375
|
break;
|
|
376
376
|
case "to":
|
|
377
377
|
if (!(tl.state in states)) {
|
|
378
378
|
problems.push(
|
|
379
|
-
`${
|
|
379
|
+
`${path2}: to("${tl.state}") references an undefined state \u2014 defined states: ${Object.keys(states).join(", ") || "(none)"}`
|
|
380
380
|
);
|
|
381
381
|
}
|
|
382
382
|
if (tl.duration !== void 0 && tl.duration <= 0) {
|
|
383
|
-
problems.push(`${
|
|
383
|
+
problems.push(`${path2}: to("${tl.state}") duration must be > 0`);
|
|
384
384
|
}
|
|
385
385
|
for (const id of tl.filter ?? []) {
|
|
386
|
-
if (!nodeById.has(id)) problems.push(`${
|
|
386
|
+
if (!nodeById.has(id)) problems.push(`${path2}: filter contains unknown node "${id}"`);
|
|
387
387
|
}
|
|
388
388
|
break;
|
|
389
389
|
case "tween":
|
|
390
|
-
checkProps(
|
|
390
|
+
checkProps(path2, tl.target, tl.props);
|
|
391
391
|
if (tl.duration !== void 0 && tl.duration <= 0) {
|
|
392
|
-
problems.push(`${
|
|
392
|
+
problems.push(`${path2}: tween duration must be > 0`);
|
|
393
393
|
}
|
|
394
394
|
break;
|
|
395
395
|
case "motionPath": {
|
|
396
396
|
const node = nodeById.get(tl.target);
|
|
397
397
|
if (!node) {
|
|
398
398
|
problems.push(
|
|
399
|
-
`${
|
|
399
|
+
`${path2}: motionPath targets unknown node "${tl.target}" \u2014 known ids: ${[...nodeById.keys()].join(", ")}`
|
|
400
400
|
);
|
|
401
401
|
} else if (node.type === "line") {
|
|
402
|
-
problems.push(`${
|
|
402
|
+
problems.push(`${path2}: motionPath cannot target a line (no x/y) \u2014 "${tl.target}"`);
|
|
403
403
|
}
|
|
404
|
-
if (tl.points.length < 1) problems.push(`${
|
|
404
|
+
if (tl.points.length < 1) problems.push(`${path2}: motionPath "${tl.target}" needs at least 1 point`);
|
|
405
405
|
if (tl.duration !== void 0 && tl.duration <= 0) {
|
|
406
|
-
problems.push(`${
|
|
406
|
+
problems.push(`${path2}: motionPath "${tl.target}" duration must be > 0`);
|
|
407
407
|
}
|
|
408
408
|
break;
|
|
409
409
|
}
|
|
410
410
|
case "wait":
|
|
411
|
-
if (tl.duration < 0) problems.push(`${
|
|
411
|
+
if (tl.duration < 0) problems.push(`${path2}: wait duration must be >= 0`);
|
|
412
412
|
break;
|
|
413
413
|
case "beat":
|
|
414
414
|
if (labels.has(tl.name)) {
|
|
415
415
|
problems.push(
|
|
416
|
-
`${
|
|
416
|
+
`${path2}: duplicate timeline label "${tl.name}" (beat name) \u2014 labels are overlay addresses and must be unique`
|
|
417
417
|
);
|
|
418
418
|
}
|
|
419
419
|
labels.add(tl.name);
|
|
420
420
|
if (tl.duration !== void 0 && tl.duration <= 0) {
|
|
421
|
-
problems.push(`${
|
|
421
|
+
problems.push(`${path2}: beat "${tl.name}" duration must be > 0`);
|
|
422
422
|
}
|
|
423
423
|
if (tl.scale !== void 0 && tl.scale <= 0) {
|
|
424
|
-
problems.push(`${
|
|
424
|
+
problems.push(`${path2}: beat "${tl.name}" scale must be > 0`);
|
|
425
425
|
}
|
|
426
|
-
tl.children.forEach((c, i) => checkTimeline(c, `${
|
|
426
|
+
tl.children.forEach((c, i) => checkTimeline(c, `${path2}.beat(${tl.name})[${i}]`));
|
|
427
427
|
break;
|
|
428
428
|
}
|
|
429
429
|
};
|
|
@@ -490,6 +490,51 @@ ${problems.map((p) => ` - ${p}`).join("\n")}`);
|
|
|
490
490
|
});
|
|
491
491
|
|
|
492
492
|
// ../core/src/dsl.ts
|
|
493
|
+
function scene(input) {
|
|
494
|
+
const ir = { version: 1, ...input };
|
|
495
|
+
validateScene(ir);
|
|
496
|
+
if (ir.duration === void 0 && ir.timeline) {
|
|
497
|
+
ir.duration = compileScene(ir).duration;
|
|
498
|
+
}
|
|
499
|
+
return ir;
|
|
500
|
+
}
|
|
501
|
+
function rect(props) {
|
|
502
|
+
const { id, ...rest } = props;
|
|
503
|
+
return { type: "rect", id, props: rest };
|
|
504
|
+
}
|
|
505
|
+
function text(props) {
|
|
506
|
+
const { id, ...rest } = props;
|
|
507
|
+
return { type: "text", id, props: rest };
|
|
508
|
+
}
|
|
509
|
+
function path(props) {
|
|
510
|
+
const { id, ...rest } = props;
|
|
511
|
+
return { type: "path", id, props: rest };
|
|
512
|
+
}
|
|
513
|
+
function group(props, children) {
|
|
514
|
+
const { id, ...rest } = props;
|
|
515
|
+
return { type: "group", id, props: rest, children };
|
|
516
|
+
}
|
|
517
|
+
function seq(...children) {
|
|
518
|
+
return { kind: "seq", children };
|
|
519
|
+
}
|
|
520
|
+
function par(...children) {
|
|
521
|
+
return { kind: "par", children };
|
|
522
|
+
}
|
|
523
|
+
function stagger(interval, ...children) {
|
|
524
|
+
return { kind: "stagger", interval, children };
|
|
525
|
+
}
|
|
526
|
+
function beat(name, opts, children) {
|
|
527
|
+
return { kind: "beat", name, children, ...opts };
|
|
528
|
+
}
|
|
529
|
+
function tween(target, props, opts = {}) {
|
|
530
|
+
return { kind: "tween", target, props, ...opts };
|
|
531
|
+
}
|
|
532
|
+
function wait(duration, label) {
|
|
533
|
+
return { kind: "wait", duration, ...label !== void 0 && { label } };
|
|
534
|
+
}
|
|
535
|
+
function motionPath(target, points, opts = {}) {
|
|
536
|
+
return { kind: "motionPath", target, points, ...opts };
|
|
537
|
+
}
|
|
493
538
|
var init_dsl = __esm({
|
|
494
539
|
"../core/src/dsl.ts"() {
|
|
495
540
|
"use strict";
|
|
@@ -679,12 +724,181 @@ var init_compose = __esm({
|
|
|
679
724
|
});
|
|
680
725
|
|
|
681
726
|
// ../core/src/presets.ts
|
|
682
|
-
|
|
727
|
+
function makeRng(seed) {
|
|
728
|
+
let a = seed >>> 0 || 2654435769;
|
|
729
|
+
return () => {
|
|
730
|
+
a = a + 1831565813 | 0;
|
|
731
|
+
let t = Math.imul(a ^ a >>> 15, 1 | a);
|
|
732
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
733
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
function ctx(o) {
|
|
737
|
+
const rand = makeRng((o.seed ?? 0) + 1);
|
|
738
|
+
return {
|
|
739
|
+
e: clamp01(o.energy ?? 0.5),
|
|
740
|
+
sp: Math.max(0.25, o.speed ?? 1),
|
|
741
|
+
it: clamp01(o.intensity ?? 0.5),
|
|
742
|
+
from: o.from,
|
|
743
|
+
rand,
|
|
744
|
+
jit: (amp) => (rand() - 0.5) * 2 * amp,
|
|
745
|
+
g: o.target.group,
|
|
746
|
+
cx: o.target.center[0],
|
|
747
|
+
cy: o.target.center[1],
|
|
748
|
+
s: o.target.baseScale,
|
|
749
|
+
fills: o.target.fills,
|
|
750
|
+
inks: o.target.inks
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
function settleEase(e) {
|
|
754
|
+
return e < 0.34 ? "easeOutCubic" : e < 0.67 ? "easeOutBack" : "easeOutElastic";
|
|
755
|
+
}
|
|
756
|
+
function fromVec(from, dist) {
|
|
757
|
+
switch (from) {
|
|
758
|
+
case "left":
|
|
759
|
+
return [-dist, 0];
|
|
760
|
+
case "right":
|
|
761
|
+
return [dist, 0];
|
|
762
|
+
case "top":
|
|
763
|
+
return [0, -dist];
|
|
764
|
+
default:
|
|
765
|
+
return [0, dist];
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
function fadeFills(c, base = 0.4, gap = 0.06) {
|
|
769
|
+
return stagger(
|
|
770
|
+
gap / c.sp,
|
|
771
|
+
...c.fills.map(
|
|
772
|
+
(id, i) => tween(id, { opacity: 1 }, { duration: dur(base, c.sp), ease: "easeOutQuad", ...i === 0 && { label: "reveal" } })
|
|
773
|
+
)
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
function drawInks(c) {
|
|
777
|
+
return stagger(
|
|
778
|
+
0.15 / c.sp,
|
|
779
|
+
...c.inks.map(
|
|
780
|
+
(id, i) => tween(id, { progress: 1 }, { duration: dur(1.3 + c.jit(0.2), c.sp), ease: "easeInOutQuad", ...i === 0 && { label: "draw" } })
|
|
781
|
+
)
|
|
782
|
+
);
|
|
783
|
+
}
|
|
784
|
+
function motionPreset(name, opts) {
|
|
785
|
+
const c = ctx(opts);
|
|
786
|
+
switch (name) {
|
|
787
|
+
case "draw-bloom":
|
|
788
|
+
return beat("draw-bloom", {}, [
|
|
789
|
+
drawInks(c),
|
|
790
|
+
fadeFills(c, 0.45),
|
|
791
|
+
tween(c.g, { scale: c.s * (1.02 + 0.05 * c.e) }, { duration: dur(2.4, c.sp), ease: "easeInOutQuad", label: "settle" })
|
|
792
|
+
]);
|
|
793
|
+
case "punch-in": {
|
|
794
|
+
const peak = c.s * (1 + 0.06 + 0.24 * c.e + c.jit(0.02));
|
|
795
|
+
return beat("punch-in", {}, [
|
|
796
|
+
par(
|
|
797
|
+
fadeFills(c, 0.25),
|
|
798
|
+
seq(
|
|
799
|
+
tween(c.g, { scale: peak }, { duration: dur(0.45 + c.jit(0.05), c.sp), ease: "easeOutCubic", label: "punch" }),
|
|
800
|
+
tween(c.g, { scale: c.s }, { duration: dur(0.5, c.sp), ease: settleEase(c.e) })
|
|
801
|
+
)
|
|
802
|
+
)
|
|
803
|
+
]);
|
|
804
|
+
}
|
|
805
|
+
case "rise-settle": {
|
|
806
|
+
const es = 0.65 + c.rand() * 0.7;
|
|
807
|
+
const dist = (220 + 260 * c.it) * es;
|
|
808
|
+
const [dx, dy] = fromVec(c.from ?? "bottom", dist);
|
|
809
|
+
const jx = c.jit(110);
|
|
810
|
+
return beat("rise-settle", {}, [
|
|
811
|
+
par(
|
|
812
|
+
motionPath(
|
|
813
|
+
c.g,
|
|
814
|
+
[
|
|
815
|
+
[c.cx + dx + jx, c.cy + dy],
|
|
816
|
+
[c.cx + dx * 0.4 - jx * 0.6, c.cy + dy * 0.4],
|
|
817
|
+
[c.cx, c.cy]
|
|
818
|
+
],
|
|
819
|
+
{ duration: dur(1.1, c.sp), ease: settleEase(c.e), label: "rise" }
|
|
820
|
+
),
|
|
821
|
+
fadeFills(c, 0.4)
|
|
822
|
+
)
|
|
823
|
+
]);
|
|
824
|
+
}
|
|
825
|
+
case "slide-bank": {
|
|
826
|
+
const es = 0.65 + c.rand() * 0.7;
|
|
827
|
+
const dist = (420 + 240 * c.it) * es;
|
|
828
|
+
const [dx, dy] = fromVec(c.from ?? "left", dist);
|
|
829
|
+
const arc = c.jit(140);
|
|
830
|
+
const midx = c.jit(120);
|
|
831
|
+
const move = dur(1.2, c.sp);
|
|
832
|
+
return beat("slide-bank", {}, [
|
|
833
|
+
par(
|
|
834
|
+
motionPath(
|
|
835
|
+
c.g,
|
|
836
|
+
[
|
|
837
|
+
[c.cx + dx, c.cy + dy],
|
|
838
|
+
[c.cx + dx * 0.4 + midx, c.cy + dy * 0.4 - 70 - arc],
|
|
839
|
+
[c.cx, c.cy]
|
|
840
|
+
],
|
|
841
|
+
{ duration: move, ease: settleEase(c.e), autoRotate: true, label: "slide" }
|
|
842
|
+
),
|
|
843
|
+
// level the bank out once it lands (authored after the path → wins for rotation)
|
|
844
|
+
seq(wait(move), tween(c.g, { rotation: 0 }, { duration: dur(0.5, c.sp), ease: "easeOutCubic" })),
|
|
845
|
+
fadeFills(c, 0.4)
|
|
846
|
+
)
|
|
847
|
+
]);
|
|
848
|
+
}
|
|
849
|
+
case "reveal-orbit": {
|
|
850
|
+
const es = 0.65 + c.rand() * 0.7;
|
|
851
|
+
const orbit = (180 + 160 * c.it) * es;
|
|
852
|
+
const jx = c.jit(0.4);
|
|
853
|
+
const jy = c.jit(0.4);
|
|
854
|
+
return beat("reveal-orbit", {}, [
|
|
855
|
+
drawInks(c),
|
|
856
|
+
fadeFills(c, 0.45),
|
|
857
|
+
par(
|
|
858
|
+
motionPath(
|
|
859
|
+
c.g,
|
|
860
|
+
[
|
|
861
|
+
[c.cx, c.cy],
|
|
862
|
+
[c.cx - orbit * (1 + jx), c.cy - orbit * 0.8],
|
|
863
|
+
[c.cx + orbit * (1 + jy), c.cy - orbit],
|
|
864
|
+
[c.cx, c.cy]
|
|
865
|
+
],
|
|
866
|
+
{ duration: dur(1.7, c.sp), ease: "easeInOutCubic", label: "orbit" }
|
|
867
|
+
),
|
|
868
|
+
seq(
|
|
869
|
+
tween(c.g, { scale: c.s * (1.12 + 0.1 * c.e) }, { duration: dur(0.85, c.sp), ease: "easeOutBack" }),
|
|
870
|
+
tween(c.g, { scale: c.s }, { duration: dur(0.85, c.sp), ease: "easeInOutQuad" })
|
|
871
|
+
)
|
|
872
|
+
)
|
|
873
|
+
]);
|
|
874
|
+
}
|
|
875
|
+
case "spin-forge": {
|
|
876
|
+
const turns = 1 + Math.round(c.it);
|
|
877
|
+
const dir = c.rand() < 0.5 ? -1 : 1;
|
|
878
|
+
const startRot = dir * 360 * turns;
|
|
879
|
+
const peak = c.s * (1 + 0.05 + 0.2 * c.e);
|
|
880
|
+
return beat("spin-forge", {}, [
|
|
881
|
+
par(
|
|
882
|
+
seq(
|
|
883
|
+
tween(c.g, { scale: c.s * 0.2, rotation: startRot }, { duration: SET }),
|
|
884
|
+
// establish (invisible)
|
|
885
|
+
tween(c.g, { scale: peak, rotation: 0 }, { duration: dur(0.9, c.sp), ease: "easeOutBack", label: "spin" }),
|
|
886
|
+
tween(c.g, { scale: c.s }, { duration: dur(0.3, c.sp), ease: "easeInOutQuad" })
|
|
887
|
+
),
|
|
888
|
+
seq(wait(SET), fadeFills(c, 0.3))
|
|
889
|
+
)
|
|
890
|
+
]);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
var clamp01, SET, dur;
|
|
683
895
|
var init_presets = __esm({
|
|
684
896
|
"../core/src/presets.ts"() {
|
|
685
897
|
"use strict";
|
|
686
898
|
init_dsl();
|
|
899
|
+
clamp01 = (x) => Math.max(0, Math.min(1, x));
|
|
687
900
|
SET = 1 / 120;
|
|
901
|
+
dur = (base, sp) => base / sp;
|
|
688
902
|
}
|
|
689
903
|
});
|
|
690
904
|
|
|
@@ -897,6 +1111,152 @@ var init_src = __esm({
|
|
|
897
1111
|
}
|
|
898
1112
|
});
|
|
899
1113
|
|
|
1114
|
+
// ../render-cli/src/logoSting.ts
|
|
1115
|
+
var logoSting_exports = {};
|
|
1116
|
+
__export(logoSting_exports, {
|
|
1117
|
+
LOGO_PRESETS: () => LOGO_PRESETS,
|
|
1118
|
+
buildLogoSting: () => buildLogoSting,
|
|
1119
|
+
resolveLogo: () => resolveLogo
|
|
1120
|
+
});
|
|
1121
|
+
import { existsSync } from "node:fs";
|
|
1122
|
+
import { readFile } from "node:fs/promises";
|
|
1123
|
+
async function loadSvg(arg) {
|
|
1124
|
+
if (existsSync(arg)) {
|
|
1125
|
+
return { svg: await readFile(arg, "utf8"), name: arg.split("/").pop().replace(/\.svg$/i, "") };
|
|
1126
|
+
}
|
|
1127
|
+
const slug = arg.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1128
|
+
const r = await fetch(`https://cdn.simpleicons.org/${slug}`);
|
|
1129
|
+
if (!r.ok) throw new Error(`no local file "${arg}", and simple-icons has no "${slug}" (${r.status})`);
|
|
1130
|
+
return { svg: await r.text(), name: arg };
|
|
1131
|
+
}
|
|
1132
|
+
function tooDark(hex) {
|
|
1133
|
+
const h = hex.replace("#", "");
|
|
1134
|
+
const n = h.length === 3 ? h.split("").map((c) => c + c).join("") : h;
|
|
1135
|
+
const r = parseInt(n.slice(0, 2), 16);
|
|
1136
|
+
const g = parseInt(n.slice(2, 4), 16);
|
|
1137
|
+
const b = parseInt(n.slice(4, 6), 16);
|
|
1138
|
+
return 0.299 * r + 0.587 * g + 0.114 * b < 40;
|
|
1139
|
+
}
|
|
1140
|
+
function parseSvg(svg) {
|
|
1141
|
+
let viewBox = { minX: 0, minY: 0, w: 100, h: 100 };
|
|
1142
|
+
const vb = svg.match(/viewBox\s*=\s*"([\d.\-\s]+)"/i);
|
|
1143
|
+
if (vb) {
|
|
1144
|
+
const [a, b, c, d] = vb[1].trim().split(/\s+/).map(Number);
|
|
1145
|
+
viewBox = { minX: a, minY: b, w: c, h: d };
|
|
1146
|
+
} else {
|
|
1147
|
+
const w = svg.match(/\bwidth\s*=\s*"([\d.]+)/i);
|
|
1148
|
+
const h = svg.match(/\bheight\s*=\s*"([\d.]+)/i);
|
|
1149
|
+
if (w && h) viewBox = { minX: 0, minY: 0, w: +w[1], h: +h[1] };
|
|
1150
|
+
}
|
|
1151
|
+
const rootFill = svg.match(/<svg[^>]*\bfill\s*=\s*"(#[0-9a-fA-F]{3,8})"/)?.[1];
|
|
1152
|
+
const fallback = rootFill && !tooDark(rootFill) ? rootFill : "#E6EDF3";
|
|
1153
|
+
const paths = [];
|
|
1154
|
+
const re = /<path\b[^>]*>/g;
|
|
1155
|
+
let m;
|
|
1156
|
+
while (m = re.exec(svg)) {
|
|
1157
|
+
const tag = m[0];
|
|
1158
|
+
const d = tag.match(/\bd\s*=\s*"([^"]+)"/)?.[1];
|
|
1159
|
+
if (!d) continue;
|
|
1160
|
+
let fill = tag.match(/\bfill\s*=\s*"(#[0-9a-fA-F]{3,8})"/)?.[1] ?? fallback;
|
|
1161
|
+
if (tooDark(fill)) fill = fallback;
|
|
1162
|
+
paths.push({ d, fill });
|
|
1163
|
+
}
|
|
1164
|
+
return { paths, viewBox };
|
|
1165
|
+
}
|
|
1166
|
+
async function resolveLogo(arg, displayName, opts) {
|
|
1167
|
+
if (opts.motion && !LOGO_PRESETS.includes(opts.motion)) {
|
|
1168
|
+
throw new Error(`unknown --motion "${opts.motion}". options: ${LOGO_PRESETS.join(", ")}`);
|
|
1169
|
+
}
|
|
1170
|
+
const { svg, name } = await loadSvg(arg);
|
|
1171
|
+
const { paths, viewBox } = parseSvg(svg);
|
|
1172
|
+
if (paths.length === 0) throw new Error("no <path> elements found \u2014 logo stings need a path-based SVG");
|
|
1173
|
+
const from = FROMS.includes(opts.from) ? opts.from : void 0;
|
|
1174
|
+
const data = {
|
|
1175
|
+
name: displayName ?? titleCase(name),
|
|
1176
|
+
paths,
|
|
1177
|
+
viewBox,
|
|
1178
|
+
...opts.motion && { motion: opts.motion },
|
|
1179
|
+
...opts.energy !== void 0 && { energy: opts.energy },
|
|
1180
|
+
...opts.speed !== void 0 && { speed: opts.speed },
|
|
1181
|
+
...opts.intensity !== void 0 && { intensity: opts.intensity },
|
|
1182
|
+
...from && { from },
|
|
1183
|
+
...opts.seed !== void 0 && { seed: opts.seed }
|
|
1184
|
+
};
|
|
1185
|
+
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "logo";
|
|
1186
|
+
return { data, slug };
|
|
1187
|
+
}
|
|
1188
|
+
function buildLogoSting(d) {
|
|
1189
|
+
const W = 1080;
|
|
1190
|
+
const H = 1080;
|
|
1191
|
+
const CX = 540;
|
|
1192
|
+
const CY = 500;
|
|
1193
|
+
const vcx = d.viewBox.minX + d.viewBox.w / 2;
|
|
1194
|
+
const vcy = d.viewBox.minY + d.viewBox.h / 2;
|
|
1195
|
+
const fit = LOGO_PX / Math.max(d.viewBox.w, d.viewBox.h);
|
|
1196
|
+
const sw = 2.2 / fit;
|
|
1197
|
+
const fills = d.paths.map(
|
|
1198
|
+
(p, i) => path({ id: `fill-${i}`, d: p.d, originX: vcx, originY: vcy, x: 0, y: 0, fill: p.fill, opacity: 0 })
|
|
1199
|
+
);
|
|
1200
|
+
const inks = d.paths.map(
|
|
1201
|
+
(p, i) => path({ id: `ink-${i}`, d: p.d, originX: vcx, originY: vcy, x: 0, y: 0, stroke: p.fill, strokeWidth: sw, progress: 0 })
|
|
1202
|
+
);
|
|
1203
|
+
const rig = {
|
|
1204
|
+
group: "logo",
|
|
1205
|
+
center: [CX, CY],
|
|
1206
|
+
baseScale: fit,
|
|
1207
|
+
fills: fills.map((n) => n.id),
|
|
1208
|
+
inks: inks.map((n) => n.id)
|
|
1209
|
+
};
|
|
1210
|
+
return scene({
|
|
1211
|
+
id: "logo-sting",
|
|
1212
|
+
size: { width: W, height: H },
|
|
1213
|
+
fps: 30,
|
|
1214
|
+
background: BG,
|
|
1215
|
+
nodes: [
|
|
1216
|
+
rect({ id: "bg", x: 0, y: 0, width: W, height: H, fill: BG }),
|
|
1217
|
+
group({ id: "logo", x: CX, y: CY, scale: fit }, [...fills, ...inks]),
|
|
1218
|
+
text({ id: "word", x: CX, y: 905, anchor: "center", content: d.name, fontFamily: "Inter", fontSize: 56, fontWeight: 800, fill: FG, opacity: 0 }),
|
|
1219
|
+
text({ id: "made", x: CX, y: 968, anchor: "center", content: "made with reframe", fontFamily: "Inter", fontSize: 20, fill: MUTED, opacity: 0 })
|
|
1220
|
+
],
|
|
1221
|
+
timeline: seq(
|
|
1222
|
+
motionPreset(d.motion ?? "reveal-orbit", {
|
|
1223
|
+
target: rig,
|
|
1224
|
+
...d.energy !== void 0 && { energy: d.energy },
|
|
1225
|
+
...d.speed !== void 0 && { speed: d.speed },
|
|
1226
|
+
...d.intensity !== void 0 && { intensity: d.intensity },
|
|
1227
|
+
...d.from !== void 0 && { from: d.from },
|
|
1228
|
+
...d.seed !== void 0 && { seed: d.seed }
|
|
1229
|
+
}),
|
|
1230
|
+
par(
|
|
1231
|
+
tween("word", { opacity: 1 }, { duration: 0.5, ease: "easeOutQuad", label: "word" }),
|
|
1232
|
+
seq(wait(0.2), tween("made", { opacity: 1 }, { duration: 0.5, ease: "easeOutQuad" }))
|
|
1233
|
+
),
|
|
1234
|
+
wait(0.8, "hold")
|
|
1235
|
+
)
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1238
|
+
var LOGO_PRESETS, titleCase, FROMS, BG, FG, MUTED, LOGO_PX;
|
|
1239
|
+
var init_logoSting = __esm({
|
|
1240
|
+
"../render-cli/src/logoSting.ts"() {
|
|
1241
|
+
"use strict";
|
|
1242
|
+
init_src();
|
|
1243
|
+
LOGO_PRESETS = [
|
|
1244
|
+
"draw-bloom",
|
|
1245
|
+
"punch-in",
|
|
1246
|
+
"rise-settle",
|
|
1247
|
+
"slide-bank",
|
|
1248
|
+
"reveal-orbit",
|
|
1249
|
+
"spin-forge"
|
|
1250
|
+
];
|
|
1251
|
+
titleCase = (s) => s.replace(/[-_]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()).trim();
|
|
1252
|
+
FROMS = ["left", "right", "top", "bottom"];
|
|
1253
|
+
BG = "#0D1117";
|
|
1254
|
+
FG = "#E6EDF3";
|
|
1255
|
+
MUTED = "#8B949E";
|
|
1256
|
+
LOGO_PX = 520;
|
|
1257
|
+
}
|
|
1258
|
+
});
|
|
1259
|
+
|
|
900
1260
|
// ../render-cli/src/audio/wav.ts
|
|
901
1261
|
function encodeWavMono16(samples, sampleRate = SAMPLE_RATE) {
|
|
902
1262
|
const dataBytes = samples.length * 2;
|
|
@@ -941,52 +1301,52 @@ function buffer(duration) {
|
|
|
941
1301
|
return { out: new Float32Array(n), n };
|
|
942
1302
|
}
|
|
943
1303
|
function whoosh(seed) {
|
|
944
|
-
const
|
|
945
|
-
const { out, n } = buffer(
|
|
1304
|
+
const dur2 = 0.35;
|
|
1305
|
+
const { out, n } = buffer(dur2);
|
|
946
1306
|
let lp = 0;
|
|
947
1307
|
let lp2 = 0;
|
|
948
1308
|
for (let i = 0; i < n; i++) {
|
|
949
1309
|
const t = i / SAMPLE_RATE;
|
|
950
|
-
const u = t /
|
|
1310
|
+
const u = t / dur2;
|
|
951
1311
|
const center = 1200 * Math.pow(300 / 1200, u);
|
|
952
1312
|
const alpha = Math.min(1, TAU * center / SAMPLE_RATE);
|
|
953
1313
|
lp += alpha * (noise(i, seed) - lp);
|
|
954
1314
|
lp2 += alpha * 0.5 * (lp - lp2);
|
|
955
|
-
const env = u < 0.3 ? u / 0.3 : expDecay(t - 0.3 *
|
|
1315
|
+
const env = u < 0.3 ? u / 0.3 : expDecay(t - 0.3 * dur2, dur2 * 0.7, 4);
|
|
956
1316
|
out[i] = (lp - lp2) * env * 2.2;
|
|
957
1317
|
}
|
|
958
1318
|
return out;
|
|
959
1319
|
}
|
|
960
1320
|
function pop(seed) {
|
|
961
|
-
const
|
|
962
|
-
const { out, n } = buffer(
|
|
1321
|
+
const dur2 = 0.12;
|
|
1322
|
+
const { out, n } = buffer(dur2);
|
|
963
1323
|
let phase = 0;
|
|
964
1324
|
for (let i = 0; i < n; i++) {
|
|
965
1325
|
const t = i / SAMPLE_RATE;
|
|
966
1326
|
const freq = 600 * Math.pow(150 / 600, t / 0.08);
|
|
967
1327
|
phase += TAU * freq / SAMPLE_RATE;
|
|
968
1328
|
const transient = t < 2e-3 ? noise(i, seed) * 0.5 : 0;
|
|
969
|
-
out[i] = (Math.sin(phase) + transient) * expDecay(t,
|
|
1329
|
+
out[i] = (Math.sin(phase) + transient) * expDecay(t, dur2, 6) * 0.8;
|
|
970
1330
|
}
|
|
971
1331
|
return out;
|
|
972
1332
|
}
|
|
973
1333
|
function tick(seed) {
|
|
974
|
-
const
|
|
975
|
-
const { out, n } = buffer(
|
|
1334
|
+
const dur2 = 0.03;
|
|
1335
|
+
const { out, n } = buffer(dur2);
|
|
976
1336
|
for (let i = 0; i < n; i++) {
|
|
977
1337
|
const t = i / SAMPLE_RATE;
|
|
978
1338
|
const sine = t < 4e-3 ? Math.sin(TAU * 4e3 * t) : 0;
|
|
979
|
-
out[i] = (sine * 0.6 + noise(i, seed) * 0.35) * expDecay(t,
|
|
1339
|
+
out[i] = (sine * 0.6 + noise(i, seed) * 0.35) * expDecay(t, dur2, 8);
|
|
980
1340
|
}
|
|
981
1341
|
return out;
|
|
982
1342
|
}
|
|
983
1343
|
function rise(seed) {
|
|
984
|
-
const
|
|
985
|
-
const { out, n } = buffer(
|
|
1344
|
+
const dur2 = 0.5;
|
|
1345
|
+
const { out, n } = buffer(dur2);
|
|
986
1346
|
let phase = 0;
|
|
987
1347
|
for (let i = 0; i < n; i++) {
|
|
988
1348
|
const t = i / SAMPLE_RATE;
|
|
989
|
-
const u = t /
|
|
1349
|
+
const u = t / dur2;
|
|
990
1350
|
const freq = 220 * Math.pow(880 / 220, u);
|
|
991
1351
|
phase += TAU * freq / SAMPLE_RATE;
|
|
992
1352
|
const env = Math.sin(Math.PI * Math.min(1, u * 1.05)) ** 1.5;
|
|
@@ -995,8 +1355,8 @@ function rise(seed) {
|
|
|
995
1355
|
return out;
|
|
996
1356
|
}
|
|
997
1357
|
function shimmer(seed) {
|
|
998
|
-
const
|
|
999
|
-
const { out, n } = buffer(
|
|
1358
|
+
const dur2 = 0.9;
|
|
1359
|
+
const { out, n } = buffer(dur2);
|
|
1000
1360
|
const partials = Array.from({ length: 5 }, (_, p) => ({
|
|
1001
1361
|
freq: 2e3 + hash01(p, seed + 7) * 2e3,
|
|
1002
1362
|
am: 0.5 + hash01(p, seed + 8) * 1.5,
|
|
@@ -1004,7 +1364,7 @@ function shimmer(seed) {
|
|
|
1004
1364
|
}));
|
|
1005
1365
|
for (let i = 0; i < n; i++) {
|
|
1006
1366
|
const t = i / SAMPLE_RATE;
|
|
1007
|
-
const u = t /
|
|
1367
|
+
const u = t / dur2;
|
|
1008
1368
|
const env = Math.sin(Math.PI * u) ** 1.2;
|
|
1009
1369
|
let s = 0;
|
|
1010
1370
|
for (const part of partials) {
|
|
@@ -1015,8 +1375,8 @@ function shimmer(seed) {
|
|
|
1015
1375
|
return out;
|
|
1016
1376
|
}
|
|
1017
1377
|
function thud(seed) {
|
|
1018
|
-
const
|
|
1019
|
-
const { out, n } = buffer(
|
|
1378
|
+
const dur2 = 0.25;
|
|
1379
|
+
const { out, n } = buffer(dur2);
|
|
1020
1380
|
let phase = 0;
|
|
1021
1381
|
let lp = 0;
|
|
1022
1382
|
for (let i = 0; i < n; i++) {
|
|
@@ -1025,7 +1385,7 @@ function thud(seed) {
|
|
|
1025
1385
|
phase += TAU * freq / SAMPLE_RATE;
|
|
1026
1386
|
lp += 0.02 * (noise(i, seed) - lp);
|
|
1027
1387
|
const attack = t < 0.01 ? lp * 3 : 0;
|
|
1028
|
-
out[i] = (Math.sin(phase) * 0.9 + attack) * expDecay(t,
|
|
1388
|
+
out[i] = (Math.sin(phase) * 0.9 + attack) * expDecay(t, dur2, 5);
|
|
1029
1389
|
}
|
|
1030
1390
|
return out;
|
|
1031
1391
|
}
|
|
@@ -1060,7 +1420,7 @@ var init_synth = __esm({
|
|
|
1060
1420
|
init_wav();
|
|
1061
1421
|
noise = (n, seed) => hash01(n, seed) * 2 - 1;
|
|
1062
1422
|
TAU = Math.PI * 2;
|
|
1063
|
-
expDecay = (t,
|
|
1423
|
+
expDecay = (t, dur2, k = 5) => Math.exp(-k * t / dur2);
|
|
1064
1424
|
RECIPES = {
|
|
1065
1425
|
whoosh,
|
|
1066
1426
|
pop,
|
|
@@ -1074,26 +1434,26 @@ var init_synth = __esm({
|
|
|
1074
1434
|
|
|
1075
1435
|
// ../render-cli/src/audio/sfx.ts
|
|
1076
1436
|
import { mkdir, rename, writeFile } from "node:fs/promises";
|
|
1077
|
-
import { existsSync } from "node:fs";
|
|
1437
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
1078
1438
|
import { tmpdir } from "node:os";
|
|
1079
1439
|
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
1080
1440
|
import { fileURLToPath } from "node:url";
|
|
1081
|
-
function fnv1a(
|
|
1441
|
+
function fnv1a(text2) {
|
|
1082
1442
|
let h = 2166136261;
|
|
1083
|
-
for (let i = 0; i <
|
|
1084
|
-
h ^=
|
|
1443
|
+
for (let i = 0; i < text2.length; i++) {
|
|
1444
|
+
h ^= text2.charCodeAt(i);
|
|
1085
1445
|
h = Math.imul(h, 16777619);
|
|
1086
1446
|
}
|
|
1087
1447
|
return (h >>> 0).toString(16);
|
|
1088
1448
|
}
|
|
1089
1449
|
async function writeCached(key2, make) {
|
|
1090
|
-
const
|
|
1091
|
-
if (
|
|
1450
|
+
const path2 = join(CACHE, `${key2}.wav`);
|
|
1451
|
+
if (existsSync2(path2)) return path2;
|
|
1092
1452
|
await mkdir(CACHE, { recursive: true });
|
|
1093
|
-
const temp = `${
|
|
1453
|
+
const temp = `${path2}.${process.pid}.${fnv1a(String(performance.now()))}.tmp`;
|
|
1094
1454
|
await writeFile(temp, encodeWavMono16(make()));
|
|
1095
|
-
await rename(temp,
|
|
1096
|
-
return
|
|
1455
|
+
await rename(temp, path2);
|
|
1456
|
+
return path2;
|
|
1097
1457
|
}
|
|
1098
1458
|
async function resolveCueFile(cue, sceneDir) {
|
|
1099
1459
|
if (cue.source.kind === "file") {
|
|
@@ -1103,14 +1463,14 @@ async function resolveCueFile(cue, sceneDir) {
|
|
|
1103
1463
|
resolve(sceneDir, p),
|
|
1104
1464
|
join(VENDORED, p)
|
|
1105
1465
|
]) {
|
|
1106
|
-
if (candidate &&
|
|
1466
|
+
if (candidate && existsSync2(candidate)) return candidate;
|
|
1107
1467
|
}
|
|
1108
1468
|
throw new Error(
|
|
1109
1469
|
`audio cue file "${p}" not found (tried absolute, scene-relative, assets/sfx/)`
|
|
1110
1470
|
);
|
|
1111
1471
|
}
|
|
1112
1472
|
const vendored = join(VENDORED, `${cue.source.name}.wav`);
|
|
1113
|
-
if (
|
|
1473
|
+
if (existsSync2(vendored)) return vendored;
|
|
1114
1474
|
const { name, params } = cue.source;
|
|
1115
1475
|
return writeCached(`${name}-${fnv1a(JSON.stringify(params))}`, () => synthSfx(name, params));
|
|
1116
1476
|
}
|
|
@@ -1118,7 +1478,7 @@ async function resolveBgmFile(source, duration, sceneDir) {
|
|
|
1118
1478
|
if (source.kind === "file") {
|
|
1119
1479
|
const p = source.path;
|
|
1120
1480
|
for (const candidate of [isAbsolute(p) ? p : null, resolve(sceneDir, p), join(VENDORED, p)]) {
|
|
1121
|
-
if (candidate &&
|
|
1481
|
+
if (candidate && existsSync2(candidate)) return candidate;
|
|
1122
1482
|
}
|
|
1123
1483
|
throw new Error(`bgm file "${p}" not found`);
|
|
1124
1484
|
}
|
|
@@ -1288,14 +1648,14 @@ var init_encode = __esm({
|
|
|
1288
1648
|
});
|
|
1289
1649
|
|
|
1290
1650
|
// ../render-cli/src/fonts.ts
|
|
1291
|
-
import { readFile } from "node:fs/promises";
|
|
1651
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
1292
1652
|
import { dirname as dirname3, join as join3 } from "node:path";
|
|
1293
1653
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1294
1654
|
async function fontFaceCss() {
|
|
1295
1655
|
if (cssCache) return cssCache;
|
|
1296
1656
|
const rules = await Promise.all(
|
|
1297
1657
|
WEIGHTS.map(async (weight) => {
|
|
1298
|
-
const data = await
|
|
1658
|
+
const data = await readFile2(join3(FONTS_DIR, `inter-${weight}.woff2`));
|
|
1299
1659
|
return `@font-face {
|
|
1300
1660
|
font-family: "Inter";
|
|
1301
1661
|
font-style: normal;
|
|
@@ -1318,8 +1678,8 @@ var init_fonts = __esm({
|
|
|
1318
1678
|
});
|
|
1319
1679
|
|
|
1320
1680
|
// ../render-cli/src/images.ts
|
|
1321
|
-
import { readFile as
|
|
1322
|
-
import { existsSync as
|
|
1681
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
1682
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
1323
1683
|
import { extname, isAbsolute as isAbsolute2, resolve as resolve2 } from "node:path";
|
|
1324
1684
|
async function buildImageAssets(ir, sceneDir) {
|
|
1325
1685
|
const assets = {};
|
|
@@ -1333,11 +1693,11 @@ async function buildImageAssets(ir, sceneDir) {
|
|
|
1333
1693
|
const candidates = [isAbsolute2(src) ? src : null, resolve2(sceneDir, src)].filter(
|
|
1334
1694
|
(c) => c !== null
|
|
1335
1695
|
);
|
|
1336
|
-
const found = candidates.find((c) =>
|
|
1696
|
+
const found = candidates.find((c) => existsSync3(c));
|
|
1337
1697
|
if (!found) {
|
|
1338
1698
|
throw new Error(`image "${src}" not found (tried: ${candidates.join(", ")})`);
|
|
1339
1699
|
}
|
|
1340
|
-
const data = await
|
|
1700
|
+
const data = await readFile3(found);
|
|
1341
1701
|
assets[src] = `data:${mime};base64,${data.toString("base64")}`;
|
|
1342
1702
|
}
|
|
1343
1703
|
return assets;
|
|
@@ -1461,8 +1821,8 @@ async function withPage(size, fn) {
|
|
|
1461
1821
|
async function browserBundle() {
|
|
1462
1822
|
if (bundleCache) return bundleCache;
|
|
1463
1823
|
if (true) {
|
|
1464
|
-
const { readFile:
|
|
1465
|
-
bundleCache = await
|
|
1824
|
+
const { readFile: readFile6 } = await import("node:fs/promises");
|
|
1825
|
+
bundleCache = await readFile6(
|
|
1466
1826
|
join4(dirname4(fileURLToPath3(import.meta.url)), "browserEntry.js"),
|
|
1467
1827
|
"utf8"
|
|
1468
1828
|
);
|
|
@@ -1524,7 +1884,7 @@ __export(batch_exports, {
|
|
|
1524
1884
|
parseCsv: () => parseCsv,
|
|
1525
1885
|
runBatch: () => runBatch
|
|
1526
1886
|
});
|
|
1527
|
-
import { mkdir as mkdir3, mkdtemp as mkdtemp2, readFile as
|
|
1887
|
+
import { mkdir as mkdir3, mkdtemp as mkdtemp2, readFile as readFile4, rm as rm2, writeFile as writeFile4 } from "node:fs/promises";
|
|
1528
1888
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
1529
1889
|
import { join as join5, dirname as dirname5 } from "node:path";
|
|
1530
1890
|
function overlayFromFlat(row, name) {
|
|
@@ -1563,8 +1923,8 @@ function overlayFromFlat(row, name) {
|
|
|
1563
1923
|
}
|
|
1564
1924
|
return doc;
|
|
1565
1925
|
}
|
|
1566
|
-
function parseCsv(
|
|
1567
|
-
const lines =
|
|
1926
|
+
function parseCsv(text2) {
|
|
1927
|
+
const lines = text2.split(/\r?\n/).filter((l) => l.trim().length > 0);
|
|
1568
1928
|
if (lines.length < 2) return [];
|
|
1569
1929
|
const split = (line) => {
|
|
1570
1930
|
const out = [];
|
|
@@ -1599,14 +1959,14 @@ function parseCsv(text) {
|
|
|
1599
1959
|
return row;
|
|
1600
1960
|
});
|
|
1601
1961
|
}
|
|
1602
|
-
async function loadRows(
|
|
1603
|
-
const
|
|
1604
|
-
if (
|
|
1605
|
-
const parsed = JSON.parse(
|
|
1606
|
-
if (!Array.isArray(parsed)) throw new Error(`${
|
|
1962
|
+
async function loadRows(path2) {
|
|
1963
|
+
const text2 = await readFile4(path2, "utf8");
|
|
1964
|
+
if (path2.endsWith(".csv")) return parseCsv(text2);
|
|
1965
|
+
const parsed = JSON.parse(text2);
|
|
1966
|
+
if (!Array.isArray(parsed)) throw new Error(`${path2}: expected a JSON array of row objects`);
|
|
1607
1967
|
return parsed;
|
|
1608
1968
|
}
|
|
1609
|
-
async function runBatch(
|
|
1969
|
+
async function runBatch(scene2, rows, opts) {
|
|
1610
1970
|
await mkdir3(opts.outDir, { recursive: true });
|
|
1611
1971
|
const results = new Array(rows.length);
|
|
1612
1972
|
let next = 0;
|
|
@@ -1619,7 +1979,7 @@ async function runBatch(scene, rows, opts) {
|
|
|
1619
1979
|
let result;
|
|
1620
1980
|
try {
|
|
1621
1981
|
const rowOverlay = overlayFromFlat(row, name);
|
|
1622
|
-
const { ir, report } = composeScene(
|
|
1982
|
+
const { ir, report } = composeScene(scene2, ...opts.baseOverlays, rowOverlay);
|
|
1623
1983
|
const framesDir = await mkdtemp2(join5(tmpdir3(), `reframe-batch-${index}-`));
|
|
1624
1984
|
const output = join5(opts.outDir, `${name}.mp4`);
|
|
1625
1985
|
const plan = opts.noAudio ? null : resolveAudioPlan(compileScene(ir));
|
|
@@ -1685,19 +2045,19 @@ __export(loadScene_exports, {
|
|
|
1685
2045
|
loadScene: () => loadScene
|
|
1686
2046
|
});
|
|
1687
2047
|
import { build as build2 } from "esbuild";
|
|
1688
|
-
import { readFile as
|
|
2048
|
+
import { readFile as readFile5 } from "node:fs/promises";
|
|
1689
2049
|
import { dirname as dirname6, resolve as resolve3 } from "node:path";
|
|
1690
2050
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
1691
|
-
async function loadScene(
|
|
1692
|
-
if (
|
|
1693
|
-
const ir = JSON.parse(await
|
|
2051
|
+
async function loadScene(path2) {
|
|
2052
|
+
if (path2.endsWith(".json")) {
|
|
2053
|
+
const ir = JSON.parse(await readFile5(path2, "utf8"));
|
|
1694
2054
|
validateScene(ir);
|
|
1695
2055
|
return ir;
|
|
1696
2056
|
}
|
|
1697
2057
|
let code;
|
|
1698
2058
|
try {
|
|
1699
2059
|
const out = await build2({
|
|
1700
|
-
entryPoints: [
|
|
2060
|
+
entryPoints: [path2],
|
|
1701
2061
|
bundle: true,
|
|
1702
2062
|
format: "esm",
|
|
1703
2063
|
platform: "neutral",
|
|
@@ -1711,12 +2071,12 @@ async function loadScene(path) {
|
|
|
1711
2071
|
code = out.outputFiles[0].text;
|
|
1712
2072
|
} catch (err) {
|
|
1713
2073
|
throw new Error(
|
|
1714
|
-
`failed to bundle ${
|
|
2074
|
+
`failed to bundle ${path2}:
|
|
1715
2075
|
${err instanceof Error ? err.message : String(err)}`
|
|
1716
2076
|
);
|
|
1717
2077
|
}
|
|
1718
2078
|
const mod = await import(`data:text/javascript;base64,${Buffer.from(code).toString("base64")}`);
|
|
1719
|
-
if (!mod.default) throw new Error(`${
|
|
2079
|
+
if (!mod.default) throw new Error(`${path2} must default-export a scene`);
|
|
1720
2080
|
return mod.default;
|
|
1721
2081
|
}
|
|
1722
2082
|
var HERE, CORE_ENTRY;
|
|
@@ -1731,7 +2091,7 @@ var init_loadScene = __esm({
|
|
|
1731
2091
|
|
|
1732
2092
|
// ../render-cli/src/reframe.ts
|
|
1733
2093
|
import { spawn as spawn3, spawnSync } from "node:child_process";
|
|
1734
|
-
import { existsSync as
|
|
2094
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
1735
2095
|
import { mkdir as mkdir4, writeFile as writeFile5 } from "node:fs/promises";
|
|
1736
2096
|
import { basename, isAbsolute as isAbsolute3, join as join6, resolve as resolve4 } from "node:path";
|
|
1737
2097
|
import { dirname as dirname7 } from "node:path";
|
|
@@ -1749,6 +2109,9 @@ var USAGE = `reframe \u2014 declarative motion graphics
|
|
|
1749
2109
|
usage:
|
|
1750
2110
|
${CMD} render <scene.ts|.json|.html> [--overlay edits.json]... [-o out.mp4] [--fps N] [--duration S] [--no-audio]
|
|
1751
2111
|
${CMD} batch <scene.ts> <data.json|csv> [-o outDir] [--overlay base.json]... [--concurrency N] [--fps N]
|
|
2112
|
+
${CMD} logo <logo.svg|brand-slug> ["Name"] [--motion <preset>] [--energy 0..1] [--seed N] [-o out.mp4]
|
|
2113
|
+
animate a logo into a sting (presets: draw-bloom, punch-in,
|
|
2114
|
+
rise-settle, slide-bank, reveal-orbit, spin-forge)
|
|
1752
2115
|
${CMD} preview open the scrub/edit UI (lists scenes in your directory)
|
|
1753
2116
|
${CMD} new <scene-name> scaffold <scene-name>.ts in your directory
|
|
1754
2117
|
${CMD} motion <mp4|framesDir> motion-profile a rendered clip
|
|
@@ -1775,9 +2138,9 @@ function run(cmd, args, opts = {}) {
|
|
|
1775
2138
|
});
|
|
1776
2139
|
let sawBrowserError = false;
|
|
1777
2140
|
proc.stderr.on("data", (d) => {
|
|
1778
|
-
const
|
|
1779
|
-
if (/Executable doesn't exist|browserType\.launch/.test(
|
|
1780
|
-
process.stderr.write(
|
|
2141
|
+
const text2 = d.toString();
|
|
2142
|
+
if (/Executable doesn't exist|browserType\.launch/.test(text2)) sawBrowserError = true;
|
|
2143
|
+
process.stderr.write(text2);
|
|
1781
2144
|
});
|
|
1782
2145
|
proc.on("close", (code) => {
|
|
1783
2146
|
if (code !== 0 && sawBrowserError) {
|
|
@@ -1858,7 +2221,7 @@ async function main() {
|
|
|
1858
2221
|
|
|
1859
2222
|
${USAGE}`);
|
|
1860
2223
|
const inputPath = userPath(input);
|
|
1861
|
-
if (!
|
|
2224
|
+
if (!existsSync4(inputPath)) fail(`no such file: ${inputPath}`);
|
|
1862
2225
|
const mode = /\.(ts|json)$/.test(input) ? "ir" : /\.html$/.test(input) ? "html" : null;
|
|
1863
2226
|
if (!mode) {
|
|
1864
2227
|
fail(`cannot infer render mode from "${input}" \u2014 expected .ts/.json (reframe scene) or .html (GSAP page)`);
|
|
@@ -1881,12 +2244,49 @@ ${USAGE}`);
|
|
|
1881
2244
|
await (PACKAGED ? run(process.execPath, [RENDER_CLI, mode, inputPath, ...outArgs]) : run("npx", ["tsx", RENDER_CLI, mode, inputPath, ...outArgs]))
|
|
1882
2245
|
);
|
|
1883
2246
|
}
|
|
2247
|
+
case "logo": {
|
|
2248
|
+
const positional = [];
|
|
2249
|
+
const flags = {};
|
|
2250
|
+
for (let i = 0; i < rest.length; i++) {
|
|
2251
|
+
const a = rest[i];
|
|
2252
|
+
if (a.startsWith("--")) flags[a.slice(2)] = rest[++i] ?? "";
|
|
2253
|
+
else if (a === "-o") flags.o = rest[++i] ?? "";
|
|
2254
|
+
else positional.push(a);
|
|
2255
|
+
}
|
|
2256
|
+
const arg = positional[0];
|
|
2257
|
+
if (!arg) {
|
|
2258
|
+
fail(`usage: ${CMD} logo <logo.svg | brand-slug> ["Display Name"] [--motion <preset>] [--energy 0..1] [--speed n] [--intensity 0..1] [--from left|right|top|bottom] [--seed n] [-o out.mp4]`);
|
|
2259
|
+
}
|
|
2260
|
+
preflightFfmpeg();
|
|
2261
|
+
const { tmpdir: tmpdir4 } = await import("node:os");
|
|
2262
|
+
const { resolveLogo: resolveLogo2, buildLogoSting: buildLogoSting2 } = await Promise.resolve().then(() => (init_logoSting(), logoSting_exports));
|
|
2263
|
+
const num = (k) => flags[k] !== void 0 ? Number(flags[k]) : void 0;
|
|
2264
|
+
console.log(`loading logo: ${arg} \u2026`);
|
|
2265
|
+
const { data, slug } = await resolveLogo2(arg, positional[1], {
|
|
2266
|
+
motion: flags.motion,
|
|
2267
|
+
energy: num("energy"),
|
|
2268
|
+
speed: num("speed"),
|
|
2269
|
+
intensity: num("intensity"),
|
|
2270
|
+
from: flags.from,
|
|
2271
|
+
seed: num("seed")
|
|
2272
|
+
});
|
|
2273
|
+
const sceneIR = buildLogoSting2(data);
|
|
2274
|
+
const tmp = join6(tmpdir4(), `reframe-logo-${slug}-${process.pid}.json`);
|
|
2275
|
+
await writeFile5(tmp, JSON.stringify(sceneIR));
|
|
2276
|
+
const outBase = PACKAGED ? join6(USER_CWD, "out") : join6(ROOT2, "out");
|
|
2277
|
+
const out = flags.o ? userPath(flags.o) : join6(outBase, `logo-${slug}.mp4`);
|
|
2278
|
+
await mkdir4(dirname7(out), { recursive: true });
|
|
2279
|
+
console.log(`rendering ${data.name} (${data.paths.length} path${data.paths.length > 1 ? "s" : ""}, motion: ${data.motion ?? "reveal-orbit"}) \u2192 ${out}`);
|
|
2280
|
+
process.exit(
|
|
2281
|
+
await (PACKAGED ? run(process.execPath, [RENDER_CLI, "ir", tmp, "-o", out, "--no-audio"]) : run("npx", ["tsx", RENDER_CLI, "ir", tmp, "-o", out, "--no-audio"]))
|
|
2282
|
+
);
|
|
2283
|
+
}
|
|
1884
2284
|
case "batch": {
|
|
1885
2285
|
const [sceneArg, dataArg, ...flags] = rest;
|
|
1886
2286
|
if (!sceneArg || !dataArg) fail(`usage: ${CMD} batch <scene.ts> <data.json|csv> [...]`);
|
|
1887
2287
|
const scenePath = userPath(sceneArg);
|
|
1888
2288
|
const dataPath = userPath(dataArg);
|
|
1889
|
-
for (const p of [scenePath, dataPath]) if (!
|
|
2289
|
+
for (const p of [scenePath, dataPath]) if (!existsSync4(p)) fail(`no such file: ${p}`);
|
|
1890
2290
|
preflightFfmpeg();
|
|
1891
2291
|
let outDir = PACKAGED ? join6(USER_CWD, "out", "batch") : join6(ROOT2, "out", "batch");
|
|
1892
2292
|
let concurrency = 3;
|
|
@@ -1901,15 +2301,15 @@ ${USAGE}`);
|
|
|
1901
2301
|
}
|
|
1902
2302
|
const { loadRows: loadRows2, runBatch: runBatch2 } = await Promise.resolve().then(() => (init_batch(), batch_exports));
|
|
1903
2303
|
const { loadScene: loadScene2 } = await Promise.resolve().then(() => (init_loadScene(), loadScene_exports));
|
|
1904
|
-
const { readFile:
|
|
1905
|
-
const
|
|
2304
|
+
const { readFile: readFile6 } = await import("node:fs/promises");
|
|
2305
|
+
const scene2 = await loadScene2(scenePath);
|
|
1906
2306
|
const baseOverlays = await Promise.all(
|
|
1907
|
-
baseOverlayPaths.map(async (p) => JSON.parse(await
|
|
2307
|
+
baseOverlayPaths.map(async (p) => JSON.parse(await readFile6(p, "utf8")))
|
|
1908
2308
|
);
|
|
1909
2309
|
const rows = await loadRows2(dataPath);
|
|
1910
2310
|
if (rows.length === 0) fail(`${dataPath}: no data rows`);
|
|
1911
2311
|
console.log(`batch: ${rows.length} rows \xD7 ${concurrency} workers \u2192 ${outDir}`);
|
|
1912
|
-
const results = await runBatch2(
|
|
2312
|
+
const results = await runBatch2(scene2, rows, {
|
|
1913
2313
|
outDir,
|
|
1914
2314
|
baseOverlays,
|
|
1915
2315
|
concurrency,
|
|
@@ -1958,7 +2358,7 @@ ${results.length - failed} rendered (${orphaned} with orphans), ${failed} failed
|
|
|
1958
2358
|
const targetDir = inRepo ? join6(ROOT2, "examples", "scenes") : USER_CWD;
|
|
1959
2359
|
const target = join6(targetDir, `${name}.ts`);
|
|
1960
2360
|
const shown = inRepo ? `examples/scenes/${name}.ts` : `${name}.ts`;
|
|
1961
|
-
if (
|
|
2361
|
+
if (existsSync4(target)) fail(`${shown} already exists`);
|
|
1962
2362
|
const id = name.split("-")[0] ?? name;
|
|
1963
2363
|
await writeFile5(target, SCENE_TEMPLATE(name, id));
|
|
1964
2364
|
console.log(`created ${shown}
|
|
@@ -1988,8 +2388,8 @@ ${results.length - failed} rendered (${orphaned} with orphans), ${failed} failed
|
|
|
1988
2388
|
}
|
|
1989
2389
|
case "guide": {
|
|
1990
2390
|
const file = rest.includes("--regen") ? PACKAGED ? join6(ROOT2, "guides", "regen-contract.md") : join6(ROOT2, "docs", "regen-contract.md") : PACKAGED ? join6(ROOT2, "guides", "edsl-guide.md") : join6(ROOT2, "benchmark", "guides", "edsl-guide.md");
|
|
1991
|
-
const { readFile:
|
|
1992
|
-
process.stdout.write(await
|
|
2391
|
+
const { readFile: readFile6 } = await import("node:fs/promises");
|
|
2392
|
+
process.stdout.write(await readFile6(file, "utf8"));
|
|
1993
2393
|
return;
|
|
1994
2394
|
}
|
|
1995
2395
|
case "demo":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reframe-video",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
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",
|