reframe-video 0.6.4 → 0.6.6

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 CHANGED
@@ -380,6 +380,7 @@ function validateScene(ir) {
380
380
  if (typeof props.blur === "number" && props.blur < 0) problems.push(`node "${node.id}": blur must be >= 0`);
381
381
  if (typeof props.shadowBlur === "number" && props.shadowBlur < 0) problems.push(`node "${node.id}": shadowBlur must be >= 0`);
382
382
  if (typeof props.blend === "string" && !BLEND_MODES.has(props.blend)) problems.push(`node "${node.id}": unknown blend "${props.blend}" \u2014 use ${[...BLEND_MODES].join(", ")}`);
383
+ if (typeof props.fit === "string" && !IMAGE_FITS.has(props.fit)) problems.push(`node "${node.id}": unknown fit "${props.fit}" \u2014 use ${[...IMAGE_FITS].join(", ")}`);
383
384
  if (node.type === "group") {
384
385
  const clip = node.props.clip;
385
386
  if (clip) {
@@ -594,7 +595,7 @@ function validateComposition(comp) {
594
595
  }
595
596
  if (problems.length > 0) throw new SceneValidationError(problems);
596
597
  }
597
- var FX_PROPS, BLEND_MODES, COMMON_PROPS, CAMERA_PROPS, PROPS_BY_TYPE, SceneValidationError, TRANSITIONS;
598
+ var FX_PROPS, BLEND_MODES, IMAGE_FITS, COMMON_PROPS, CAMERA_PROPS, PROPS_BY_TYPE, SceneValidationError, TRANSITIONS;
598
599
  var init_validate = __esm({
599
600
  "../core/src/validate.ts"() {
600
601
  "use strict";
@@ -612,6 +613,7 @@ var init_validate = __esm({
612
613
  "hard-light",
613
614
  "difference"
614
615
  ]);
616
+ IMAGE_FITS = /* @__PURE__ */ new Set(["fill", "cover"]);
615
617
  COMMON_PROPS = ["x", "y", "opacity", "rotation", "scale", "scaleX", "scaleY", "skewX", "skewY", "anchor", "fixed", ...FX_PROPS];
616
618
  CAMERA_PROPS = ["x", "y", "zoom", "rotation"];
617
619
  PROPS_BY_TYPE = {
@@ -619,7 +621,8 @@ var init_validate = __esm({
619
621
  ellipse: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth"],
620
622
  line: ["x1", "y1", "x2", "y2", "stroke", "strokeWidth", "opacity", "progress", ...FX_PROPS],
621
623
  text: [...COMMON_PROPS, "content", "contentDecimals", "contentThousands", "fontFamily", "fontSize", "fontWeight", "fill", "letterSpacing"],
622
- image: [...COMMON_PROPS, "src", "width", "height"],
624
+ image: [...COMMON_PROPS, "src", "width", "height", "fit"],
625
+ video: [...COMMON_PROPS, "src", "width", "height", "fit", "start", "rate", "clipStart"],
623
626
  path: [...COMMON_PROPS, "d", "fill", "stroke", "strokeWidth", "progress", "originX", "originY"],
624
627
  group: COMMON_PROPS
625
628
  };
@@ -1361,13 +1364,13 @@ var init_evaluate = __esm({
1361
1364
  });
1362
1365
 
1363
1366
  // ../core/src/assets.ts
1364
- function collectImageSrcs(ir) {
1367
+ function collectSrcs(ir, type) {
1365
1368
  const srcs = /* @__PURE__ */ new Set();
1366
- const imageIds = /* @__PURE__ */ new Set();
1369
+ const ids = /* @__PURE__ */ new Set();
1367
1370
  const walkNodes = (nodes) => {
1368
1371
  for (const node of nodes) {
1369
- if (node.type === "image") {
1370
- imageIds.add(node.id);
1372
+ if (node.type === type) {
1373
+ ids.add(node.id);
1371
1374
  srcs.add(node.props.src);
1372
1375
  }
1373
1376
  if (node.type === "group") walkNodes(node.children);
@@ -1376,14 +1379,14 @@ function collectImageSrcs(ir) {
1376
1379
  walkNodes(ir.nodes);
1377
1380
  for (const overrides of Object.values(ir.states ?? {})) {
1378
1381
  for (const [nodeId, props] of Object.entries(overrides)) {
1379
- if (imageIds.has(nodeId) && typeof props.src === "string") srcs.add(props.src);
1382
+ if (ids.has(nodeId) && typeof props.src === "string") srcs.add(props.src);
1380
1383
  }
1381
1384
  }
1382
1385
  const walkTimeline = (step) => {
1383
1386
  if (!step) return;
1384
1387
  if (step.kind === "seq" || step.kind === "par" || step.kind === "stagger") {
1385
1388
  for (const child of step.children) walkTimeline(child);
1386
- } else if (step.kind === "tween" && imageIds.has(step.target)) {
1389
+ } else if (step.kind === "tween" && ids.has(step.target)) {
1387
1390
  const src = step.props.src;
1388
1391
  if (typeof src === "string") srcs.add(src);
1389
1392
  }
@@ -1391,6 +1394,12 @@ function collectImageSrcs(ir) {
1391
1394
  walkTimeline(ir.timeline);
1392
1395
  return [...srcs];
1393
1396
  }
1397
+ function collectImageSrcs(ir) {
1398
+ return collectSrcs(ir, "image");
1399
+ }
1400
+ function collectVideoSrcs(ir) {
1401
+ return collectSrcs(ir, "video");
1402
+ }
1394
1403
  var init_assets = __esm({
1395
1404
  "../core/src/assets.ts"() {
1396
1405
  "use strict";
@@ -1955,12 +1964,12 @@ async function encodeMp4(framesDir, fps, outFile) {
1955
1964
  "+faststart",
1956
1965
  outFile
1957
1966
  ];
1958
- await new Promise((resolve5, reject) => {
1967
+ await new Promise((resolve6, reject) => {
1959
1968
  const proc = spawn2("ffmpeg", args, { stdio: ["ignore", "ignore", "pipe"] });
1960
1969
  let stderr = "";
1961
1970
  proc.stderr.on("data", (d) => stderr += d.toString());
1962
1971
  proc.on("close", (code) => {
1963
- if (code === 0) resolve5();
1972
+ if (code === 0) resolve6();
1964
1973
  else reject(new Error(`ffmpeg exited with ${code}:
1965
1974
  ${stderr.slice(-2e3)}`));
1966
1975
  });
@@ -2042,6 +2051,103 @@ var init_images = __esm({
2042
2051
  }
2043
2052
  });
2044
2053
 
2054
+ // ../render-cli/src/videos.ts
2055
+ import { spawn as spawn3 } from "node:child_process";
2056
+ import { mkdtemp as mkdtemp2, readFile as readFile4, readdir, rm as rm2 } from "node:fs/promises";
2057
+ import { existsSync as existsSync4 } from "node:fs";
2058
+ import { tmpdir as tmpdir3 } from "node:os";
2059
+ import { extname as extname2, isAbsolute as isAbsolute3, join as join4, resolve as resolve3 } from "node:path";
2060
+ function runFfmpeg(args) {
2061
+ return new Promise((resolve6, reject) => {
2062
+ const proc = spawn3("ffmpeg", args, { stdio: ["ignore", "ignore", "pipe"] });
2063
+ let stderr = "";
2064
+ proc.stderr.on("data", (d) => stderr += d.toString());
2065
+ proc.on(
2066
+ "close",
2067
+ (code) => code === 0 ? resolve6() : reject(new Error(`ffmpeg exited ${code}:
2068
+ ${stderr.slice(-2e3)}`))
2069
+ );
2070
+ proc.on("error", reject);
2071
+ });
2072
+ }
2073
+ function neededSeconds(node, duration) {
2074
+ const start = node.props.start ?? 0;
2075
+ const rate = node.props.rate ?? 1;
2076
+ const clipStart = node.props.clipStart ?? 0;
2077
+ return clipStart + Math.max(0, duration - start) * Math.max(0, rate) + 1 / 30;
2078
+ }
2079
+ function videoNodes(ir) {
2080
+ const out = [];
2081
+ const walk = (nodes) => {
2082
+ for (const n of nodes) {
2083
+ if (n.type === "video") out.push(n);
2084
+ if (n.type === "group") walk(n.children);
2085
+ }
2086
+ };
2087
+ walk(ir.nodes);
2088
+ return out;
2089
+ }
2090
+ async function buildVideoFrameAssets(ir, sceneDir, fps, duration) {
2091
+ const srcs = collectVideoSrcs(ir);
2092
+ if (srcs.length === 0) return {};
2093
+ const nodes = videoNodes(ir);
2094
+ const reachBySrc = /* @__PURE__ */ new Map();
2095
+ for (const n of nodes) {
2096
+ const reach = neededSeconds(n, duration);
2097
+ reachBySrc.set(n.props.src, Math.max(reachBySrc.get(n.props.src) ?? 0, reach));
2098
+ }
2099
+ const assets = {};
2100
+ for (const src of srcs) {
2101
+ if (!VIDEO_EXT.has(extname2(src).toLowerCase())) {
2102
+ throw new Error(
2103
+ `video "${src}": unsupported format "${extname2(src)}" \u2014 supported: ${[...VIDEO_EXT].join(" ")}`
2104
+ );
2105
+ }
2106
+ const candidates = [isAbsolute3(src) ? src : null, resolve3(sceneDir, src)].filter(
2107
+ (c) => c !== null
2108
+ );
2109
+ const found = candidates.find((c) => existsSync4(c));
2110
+ if (!found) throw new Error(`video "${src}" not found (tried: ${candidates.join(", ")})`);
2111
+ const dir = await mkdtemp2(join4(tmpdir3(), "reframe-vframes-"));
2112
+ try {
2113
+ const seconds = Math.max(1 / fps, reachBySrc.get(src) ?? duration);
2114
+ await runFfmpeg([
2115
+ "-y",
2116
+ "-i",
2117
+ found,
2118
+ "-t",
2119
+ seconds.toFixed(3),
2120
+ "-vf",
2121
+ `fps=${fps},scale='min(iw,1280)':-2`,
2122
+ "-q:v",
2123
+ "4",
2124
+ join4(dir, "%05d.jpg")
2125
+ ]);
2126
+ const files = (await readdir(dir)).filter((f) => f.endsWith(".jpg")).sort();
2127
+ assets[src] = await Promise.all(
2128
+ files.map(async (f) => `data:image/jpeg;base64,${(await readFile4(join4(dir, f))).toString("base64")}`)
2129
+ );
2130
+ if (assets[src].length === 0) throw new Error(`video "${src}": ffmpeg extracted no frames`);
2131
+ } finally {
2132
+ await rm2(dir, { recursive: true, force: true });
2133
+ }
2134
+ }
2135
+ return assets;
2136
+ }
2137
+ function resolveTiming(ir, opts) {
2138
+ const fps = opts.fps ?? ir.fps ?? 30;
2139
+ const duration = opts.duration ?? compileScene(ir).duration;
2140
+ return { fps, duration };
2141
+ }
2142
+ var VIDEO_EXT;
2143
+ var init_videos = __esm({
2144
+ "../render-cli/src/videos.ts"() {
2145
+ "use strict";
2146
+ init_src();
2147
+ VIDEO_EXT = /* @__PURE__ */ new Set([".mp4", ".mov", ".webm", ".m4v", ".mkv"]);
2148
+ }
2149
+ });
2150
+
2045
2151
  // ../render-cli/src/vclock.ts
2046
2152
  var VCLOCK_SOURCE;
2047
2153
  var init_vclock = __esm({
@@ -2122,7 +2228,7 @@ var init_reframeGlobal = __esm({
2122
2228
 
2123
2229
  // ../render-cli/src/frameLoop.ts
2124
2230
  import { mkdir as mkdir2, writeFile as writeFile3 } from "node:fs/promises";
2125
- import { join as join4, dirname as dirname4 } from "node:path";
2231
+ import { join as join5, dirname as dirname4 } from "node:path";
2126
2232
  import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
2127
2233
  import { build } from "esbuild";
2128
2234
  import { chromium } from "playwright";
@@ -2147,14 +2253,14 @@ async function withPage(size, fn) {
2147
2253
  async function browserBundle() {
2148
2254
  if (bundleCache) return bundleCache;
2149
2255
  if (true) {
2150
- const { readFile: readFile6 } = await import("node:fs/promises");
2151
- bundleCache = await readFile6(
2152
- join4(dirname4(fileURLToPath3(import.meta.url)), "browserEntry.js"),
2256
+ const { readFile: readFile7 } = await import("node:fs/promises");
2257
+ bundleCache = await readFile7(
2258
+ join5(dirname4(fileURLToPath3(import.meta.url)), "browserEntry.js"),
2153
2259
  "utf8"
2154
2260
  );
2155
2261
  return bundleCache;
2156
2262
  }
2157
- const entry = join4(dirname4(fileURLToPath3(import.meta.url)), "browserEntry.ts");
2263
+ const entry = join5(dirname4(fileURLToPath3(import.meta.url)), "browserEntry.ts");
2158
2264
  const result = await build({
2159
2265
  entryPoints: [entry],
2160
2266
  bundle: true,
@@ -2167,7 +2273,10 @@ async function browserBundle() {
2167
2273
  }
2168
2274
  async function captureIr(ir, opts) {
2169
2275
  await mkdir2(opts.framesDir, { recursive: true });
2170
- const assets = await buildImageAssets(ir, opts.sceneDir ?? process.cwd());
2276
+ const sceneDir = opts.sceneDir ?? process.cwd();
2277
+ const assets = await buildImageAssets(ir, sceneDir);
2278
+ const { fps, duration } = resolveTiming(ir, opts);
2279
+ const videoAssets = await buildVideoFrameAssets(ir, sceneDir, fps, duration);
2171
2280
  const bundle = await browserBundle();
2172
2281
  return withPage(ir.size, async (page) => {
2173
2282
  await page.setContent(
@@ -2175,12 +2284,10 @@ async function captureIr(ir, opts) {
2175
2284
  );
2176
2285
  await injectFonts(page);
2177
2286
  await page.addScriptTag({ content: bundle });
2178
- const info = await page.evaluate(
2179
- ([sceneIr, imageAssets]) => window.__reframe.init(sceneIr, imageAssets),
2180
- [ir, assets]
2287
+ await page.evaluate(
2288
+ ([sceneIr, imageAssets, vAssets]) => window.__reframe.init(sceneIr, imageAssets, vAssets),
2289
+ [ir, assets, videoAssets]
2181
2290
  );
2182
- const fps = opts.fps ?? info.fps;
2183
- const duration = opts.duration ?? info.duration;
2184
2291
  const frameCount = Math.max(1, Math.round(duration * fps));
2185
2292
  for (let f = 0; f < frameCount; f++) {
2186
2293
  const dataUrl = await page.evaluate((t) => window.__reframe.renderFrame(t), f / fps);
@@ -2195,9 +2302,10 @@ var init_frameLoop = __esm({
2195
2302
  "use strict";
2196
2303
  init_fonts();
2197
2304
  init_images();
2305
+ init_videos();
2198
2306
  init_vclock();
2199
2307
  init_reframeGlobal();
2200
- framePath = (dir, i) => join4(dir, `${String(i).padStart(5, "0")}.png`);
2308
+ framePath = (dir, i) => join5(dir, `${String(i).padStart(5, "0")}.png`);
2201
2309
  bundleCache = null;
2202
2310
  }
2203
2311
  });
@@ -2210,9 +2318,9 @@ __export(batch_exports, {
2210
2318
  parseCsv: () => parseCsv,
2211
2319
  runBatch: () => runBatch
2212
2320
  });
2213
- import { mkdir as mkdir3, mkdtemp as mkdtemp2, readFile as readFile4, rm as rm2, writeFile as writeFile4 } from "node:fs/promises";
2214
- import { tmpdir as tmpdir3 } from "node:os";
2215
- import { join as join5, dirname as dirname5 } from "node:path";
2321
+ import { mkdir as mkdir3, mkdtemp as mkdtemp3, readFile as readFile5, rm as rm3, writeFile as writeFile4 } from "node:fs/promises";
2322
+ import { tmpdir as tmpdir4 } from "node:os";
2323
+ import { join as join6, dirname as dirname5 } from "node:path";
2216
2324
  function overlayFromFlat(row, name) {
2217
2325
  const doc = { reframeOverlay: 1, name };
2218
2326
  for (const [key2, raw] of Object.entries(row)) {
@@ -2286,7 +2394,7 @@ function parseCsv(text2) {
2286
2394
  });
2287
2395
  }
2288
2396
  async function loadRows(path2) {
2289
- const text2 = await readFile4(path2, "utf8");
2397
+ const text2 = await readFile5(path2, "utf8");
2290
2398
  if (path2.endsWith(".csv")) return parseCsv(text2);
2291
2399
  const parsed = JSON.parse(text2);
2292
2400
  if (!Array.isArray(parsed)) throw new Error(`${path2}: expected a JSON array of row objects`);
@@ -2306,8 +2414,8 @@ async function runBatch(scene2, rows, opts) {
2306
2414
  try {
2307
2415
  const rowOverlay = overlayFromFlat(row, name);
2308
2416
  const { ir, report } = composeScene(scene2, ...opts.baseOverlays, rowOverlay);
2309
- const framesDir = await mkdtemp2(join5(tmpdir3(), `reframe-batch-${index}-`));
2310
- const output = join5(opts.outDir, `${name}.mp4`);
2417
+ const framesDir = await mkdtemp3(join6(tmpdir4(), `reframe-batch-${index}-`));
2418
+ const output = join6(opts.outDir, `${name}.mp4`);
2311
2419
  const plan = opts.noAudio ? null : resolveAudioPlan(compileScene(ir));
2312
2420
  try {
2313
2421
  const captured = await captureIr(ir, {
@@ -2319,12 +2427,12 @@ async function runBatch(scene2, rows, opts) {
2319
2427
  const videoTmp = `${output}.video.mp4`;
2320
2428
  await encodeMp4(captured.framesDir, captured.fps, videoTmp);
2321
2429
  await buildAudioTrack(plan, opts.scenePath ?? output, videoTmp, output);
2322
- await rm2(videoTmp, { force: true });
2430
+ await rm3(videoTmp, { force: true });
2323
2431
  } else {
2324
2432
  await encodeMp4(captured.framesDir, captured.fps, output);
2325
2433
  }
2326
2434
  } finally {
2327
- await rm2(framesDir, { recursive: true, force: true });
2435
+ await rm3(framesDir, { recursive: true, force: true });
2328
2436
  }
2329
2437
  result = {
2330
2438
  name,
@@ -2348,7 +2456,7 @@ async function runBatch(scene2, rows, opts) {
2348
2456
  };
2349
2457
  await Promise.all(Array.from({ length: Math.max(1, opts.concurrency) }, worker));
2350
2458
  await writeFile4(
2351
- join5(opts.outDir, "batch-report.json"),
2459
+ join6(opts.outDir, "batch-report.json"),
2352
2460
  JSON.stringify({ rows: results }, null, 2)
2353
2461
  );
2354
2462
  return results;
@@ -2373,11 +2481,11 @@ __export(loadScene_exports, {
2373
2481
  loadScene: () => loadScene
2374
2482
  });
2375
2483
  import { build as build2 } from "esbuild";
2376
- import { readFile as readFile5 } from "node:fs/promises";
2377
- import { dirname as dirname6, resolve as resolve3 } from "node:path";
2484
+ import { readFile as readFile6 } from "node:fs/promises";
2485
+ import { dirname as dirname6, resolve as resolve4 } from "node:path";
2378
2486
  import { fileURLToPath as fileURLToPath4 } from "node:url";
2379
2487
  async function loadDefault(path2) {
2380
- if (path2.endsWith(".json")) return JSON.parse(await readFile5(path2, "utf8"));
2488
+ if (path2.endsWith(".json")) return JSON.parse(await readFile6(path2, "utf8"));
2381
2489
  let code;
2382
2490
  try {
2383
2491
  const out = await build2({
@@ -2427,26 +2535,26 @@ var init_loadScene = __esm({
2427
2535
  "use strict";
2428
2536
  init_src();
2429
2537
  HERE = dirname6(fileURLToPath4(import.meta.url));
2430
- CORE_ENTRY = true ? resolve3(HERE, "index.js") : resolve3(HERE, "..", "..", "core", "src", "index.ts");
2538
+ CORE_ENTRY = true ? resolve4(HERE, "index.js") : resolve4(HERE, "..", "..", "core", "src", "index.ts");
2431
2539
  }
2432
2540
  });
2433
2541
 
2434
2542
  // ../render-cli/src/reframe.ts
2435
- import { spawn as spawn3, spawnSync } from "node:child_process";
2436
- import { existsSync as existsSync4 } from "node:fs";
2543
+ import { spawn as spawn4, spawnSync } from "node:child_process";
2544
+ import { existsSync as existsSync5 } from "node:fs";
2437
2545
  import { mkdir as mkdir4, writeFile as writeFile5 } from "node:fs/promises";
2438
- import { basename, isAbsolute as isAbsolute3, join as join6, resolve as resolve4 } from "node:path";
2546
+ import { basename, isAbsolute as isAbsolute4, join as join7, resolve as resolve5 } from "node:path";
2439
2547
  import { dirname as dirname7 } from "node:path";
2440
2548
  import { fileURLToPath as fileURLToPath5 } from "node:url";
2441
2549
  var PACKAGED = true;
2442
2550
  var HERE2 = dirname7(fileURLToPath5(import.meta.url));
2443
- var ROOT2 = PACKAGED ? resolve4(HERE2, "..") : resolve4(HERE2, "..", "..", "..");
2551
+ var ROOT2 = PACKAGED ? resolve5(HERE2, "..") : resolve5(HERE2, "..", "..", "..");
2444
2552
  var USER_CWD = process.env.INIT_CWD ?? process.cwd();
2445
- var RENDER_CLI = PACKAGED ? join6(ROOT2, "dist", "cli.js") : join6(ROOT2, "packages", "render-cli", "src", "cli.ts");
2446
- var LABELS = PACKAGED ? join6(ROOT2, "dist", "labels.js") : join6(ROOT2, "packages", "render-cli", "src", "labels.ts");
2447
- var PLAYER = PACKAGED ? join6(ROOT2, "dist", "player.js") : join6(ROOT2, "packages", "render-cli", "src", "player.ts");
2448
- var ANALYZE = PACKAGED ? join6(ROOT2, "dist", "analyze.js") : join6(ROOT2, "benchmark", "harness", "motion", "analyze.ts");
2449
- var TRACE = PACKAGED ? join6(ROOT2, "dist", "trace-cli.js") : join6(ROOT2, "benchmark", "harness", "motion", "trace-cli.ts");
2553
+ var RENDER_CLI = PACKAGED ? join7(ROOT2, "dist", "cli.js") : join7(ROOT2, "packages", "render-cli", "src", "cli.ts");
2554
+ var LABELS = PACKAGED ? join7(ROOT2, "dist", "labels.js") : join7(ROOT2, "packages", "render-cli", "src", "labels.ts");
2555
+ var PLAYER = PACKAGED ? join7(ROOT2, "dist", "player.js") : join7(ROOT2, "packages", "render-cli", "src", "player.ts");
2556
+ var ANALYZE = PACKAGED ? join7(ROOT2, "dist", "analyze.js") : join7(ROOT2, "benchmark", "harness", "motion", "analyze.ts");
2557
+ var TRACE = PACKAGED ? join7(ROOT2, "dist", "trace-cli.js") : join7(ROOT2, "benchmark", "harness", "motion", "trace-cli.ts");
2450
2558
  var CMD = PACKAGED ? "reframe" : "pnpm reframe";
2451
2559
  var USAGE = `reframe \u2014 declarative motion graphics
2452
2560
 
@@ -2466,7 +2574,7 @@ usage:
2466
2574
  ${CMD} guide [--regen] print the scene-authoring guide (for you or your AI)
2467
2575
  ${CMD} demo run the edit-survival demo (3 mp4s into out/)
2468
2576
  `;
2469
- var userPath = (p) => isAbsolute3(p) ? p : resolve4(USER_CWD, p);
2577
+ var userPath = (p) => isAbsolute4(p) ? p : resolve5(USER_CWD, p);
2470
2578
  function fail(message) {
2471
2579
  console.error(`error: ${message}`);
2472
2580
  process.exit(2);
@@ -2478,7 +2586,7 @@ function preflightFfmpeg() {
2478
2586
  }
2479
2587
  function run(cmd, args, opts = {}) {
2480
2588
  return new Promise((res) => {
2481
- const proc = spawn3(cmd, args, {
2589
+ const proc = spawn4(cmd, args, {
2482
2590
  cwd: opts.cwd ?? (PACKAGED ? USER_CWD : ROOT2),
2483
2591
  stdio: ["inherit", "inherit", "pipe"],
2484
2592
  ...opts.env && { env: { ...process.env, ...opts.env } }
@@ -2568,7 +2676,7 @@ async function main() {
2568
2676
 
2569
2677
  ${USAGE}`);
2570
2678
  const inputPath = userPath(input);
2571
- if (!existsSync4(inputPath)) fail(`no such file: ${inputPath}`);
2679
+ if (!existsSync5(inputPath)) fail(`no such file: ${inputPath}`);
2572
2680
  const mode = /\.(ts|json)$/.test(input) ? "ir" : /\.html$/.test(input) ? "html" : null;
2573
2681
  if (!mode) {
2574
2682
  fail(`cannot infer render mode from "${input}" \u2014 expected .ts/.json (reframe scene) or .html (GSAP page)`);
@@ -2578,11 +2686,11 @@ ${USAGE}`);
2578
2686
  fail("html render requires --duration <seconds> (the page does not declare its own length)");
2579
2687
  }
2580
2688
  preflightFfmpeg();
2581
- const outBase = PACKAGED ? join6(USER_CWD, "out") : join6(ROOT2, "out");
2689
+ const outBase = PACKAGED ? join7(USER_CWD, "out") : join7(ROOT2, "out");
2582
2690
  let outArgs = args;
2583
2691
  if (!args.includes("-o")) {
2584
2692
  await mkdir4(outBase, { recursive: true });
2585
- outArgs = [...args, "-o", join6(outBase, `${basename(input).replace(/\.[^.]+$/, "")}.mp4`)];
2693
+ outArgs = [...args, "-o", join7(outBase, `${basename(input).replace(/\.[^.]+$/, "")}.mp4`)];
2586
2694
  }
2587
2695
  outArgs = outArgs.map(
2588
2696
  (a, i) => outArgs[i - 1] === "--overlay" || outArgs[i - 1] === "-o" ? userPath(a) : a
@@ -2597,7 +2705,7 @@ ${USAGE}`);
2597
2705
 
2598
2706
  ${USAGE}`);
2599
2707
  const inputPath = userPath(input);
2600
- if (!existsSync4(inputPath)) fail(`no such file: ${inputPath}`);
2708
+ if (!existsSync5(inputPath)) fail(`no such file: ${inputPath}`);
2601
2709
  process.exit(
2602
2710
  await (PACKAGED ? run(process.execPath, [LABELS, inputPath]) : run("npx", ["tsx", LABELS, inputPath]))
2603
2711
  );
@@ -2608,10 +2716,10 @@ ${USAGE}`);
2608
2716
 
2609
2717
  ${USAGE}`);
2610
2718
  const inputPath = userPath(input);
2611
- if (!existsSync4(inputPath)) fail(`no such file: ${inputPath}`);
2719
+ if (!existsSync5(inputPath)) fail(`no such file: ${inputPath}`);
2612
2720
  const oIdx = rest.indexOf("-o");
2613
- const outBase = PACKAGED ? join6(USER_CWD, "out") : join6(ROOT2, "out");
2614
- const outPath = oIdx >= 0 && rest[oIdx + 1] ? userPath(rest[oIdx + 1]) : join6(outBase, `${basename(input).replace(/\.[^.]+$/, "")}.html`);
2721
+ const outBase = PACKAGED ? join7(USER_CWD, "out") : join7(ROOT2, "out");
2722
+ const outPath = oIdx >= 0 && rest[oIdx + 1] ? userPath(rest[oIdx + 1]) : join7(outBase, `${basename(input).replace(/\.[^.]+$/, "")}.html`);
2615
2723
  await mkdir4(dirname7(outPath), { recursive: true });
2616
2724
  process.exit(
2617
2725
  await (PACKAGED ? run(process.execPath, [PLAYER, inputPath, outPath]) : run("npx", ["tsx", PLAYER, inputPath, outPath]))
@@ -2631,7 +2739,7 @@ ${USAGE}`);
2631
2739
  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]`);
2632
2740
  }
2633
2741
  preflightFfmpeg();
2634
- const { tmpdir: tmpdir4 } = await import("node:os");
2742
+ const { tmpdir: tmpdir5 } = await import("node:os");
2635
2743
  const { resolveLogo: resolveLogo2, buildLogoSting: buildLogoSting2 } = await Promise.resolve().then(() => (init_logoSting(), logoSting_exports));
2636
2744
  const num = (k) => flags[k] !== void 0 ? Number(flags[k]) : void 0;
2637
2745
  console.log(`loading logo: ${arg} \u2026`);
@@ -2644,10 +2752,10 @@ ${USAGE}`);
2644
2752
  seed: num("seed")
2645
2753
  });
2646
2754
  const sceneIR = buildLogoSting2(data);
2647
- const tmp = join6(tmpdir4(), `reframe-logo-${slug}-${process.pid}.json`);
2755
+ const tmp = join7(tmpdir5(), `reframe-logo-${slug}-${process.pid}.json`);
2648
2756
  await writeFile5(tmp, JSON.stringify(sceneIR));
2649
- const outBase = PACKAGED ? join6(USER_CWD, "out") : join6(ROOT2, "out");
2650
- const out = flags.o ? userPath(flags.o) : join6(outBase, `logo-${slug}.mp4`);
2757
+ const outBase = PACKAGED ? join7(USER_CWD, "out") : join7(ROOT2, "out");
2758
+ const out = flags.o ? userPath(flags.o) : join7(outBase, `logo-${slug}.mp4`);
2651
2759
  await mkdir4(dirname7(out), { recursive: true });
2652
2760
  console.log(`rendering ${data.name} (${data.paths.length} path${data.paths.length > 1 ? "s" : ""}, motion: ${data.motion ?? "reveal-orbit"}) \u2192 ${out}`);
2653
2761
  process.exit(
@@ -2659,9 +2767,9 @@ ${USAGE}`);
2659
2767
  if (!sceneArg || !dataArg) fail(`usage: ${CMD} batch <scene.ts> <data.json|csv> [...]`);
2660
2768
  const scenePath = userPath(sceneArg);
2661
2769
  const dataPath = userPath(dataArg);
2662
- for (const p of [scenePath, dataPath]) if (!existsSync4(p)) fail(`no such file: ${p}`);
2770
+ for (const p of [scenePath, dataPath]) if (!existsSync5(p)) fail(`no such file: ${p}`);
2663
2771
  preflightFfmpeg();
2664
- let outDir = PACKAGED ? join6(USER_CWD, "out", "batch") : join6(ROOT2, "out", "batch");
2772
+ let outDir = PACKAGED ? join7(USER_CWD, "out", "batch") : join7(ROOT2, "out", "batch");
2665
2773
  let concurrency = 3;
2666
2774
  let fps;
2667
2775
  const baseOverlayPaths = [];
@@ -2674,10 +2782,10 @@ ${USAGE}`);
2674
2782
  }
2675
2783
  const { loadRows: loadRows2, runBatch: runBatch2 } = await Promise.resolve().then(() => (init_batch(), batch_exports));
2676
2784
  const { loadScene: loadScene2 } = await Promise.resolve().then(() => (init_loadScene(), loadScene_exports));
2677
- const { readFile: readFile6 } = await import("node:fs/promises");
2785
+ const { readFile: readFile7 } = await import("node:fs/promises");
2678
2786
  const scene2 = await loadScene2(scenePath);
2679
2787
  const baseOverlays = await Promise.all(
2680
- baseOverlayPaths.map(async (p) => JSON.parse(await readFile6(p, "utf8")))
2788
+ baseOverlayPaths.map(async (p) => JSON.parse(await readFile7(p, "utf8")))
2681
2789
  );
2682
2790
  const rows = await loadRows2(dataPath);
2683
2791
  if (rows.length === 0) fail(`${dataPath}: no data rows`);
@@ -2700,7 +2808,7 @@ ${USAGE}`);
2700
2808
  const orphaned = results.filter((r) => !r.error && r.orphans.length > 0).length;
2701
2809
  console.log(
2702
2810
  `
2703
- ${results.length - failed} rendered (${orphaned} with orphans), ${failed} failed \u2014 report: ${join6(outDir, "batch-report.json")}`
2811
+ ${results.length - failed} rendered (${orphaned} with orphans), ${failed} failed \u2014 report: ${join7(outDir, "batch-report.json")}`
2704
2812
  );
2705
2813
  process.exit(failed > 0 ? 1 : 0);
2706
2814
  }
@@ -2711,9 +2819,9 @@ ${results.length - failed} rendered (${orphaned} with orphans), ${failed} failed
2711
2819
  if (PACKAGED) {
2712
2820
  const { createRequire } = await import("node:module");
2713
2821
  const vitePkg = createRequire(import.meta.url).resolve("vite/package.json");
2714
- const viteBin = join6(dirname7(vitePkg), "bin", "vite.js");
2822
+ const viteBin = join7(dirname7(vitePkg), "bin", "vite.js");
2715
2823
  process.exit(
2716
- await run(process.execPath, [viteBin, join6(ROOT2, "preview")], {
2824
+ await run(process.execPath, [viteBin, join7(ROOT2, "preview")], {
2717
2825
  env: { REFRAME_SCENE_DIR: USER_CWD }
2718
2826
  })
2719
2827
  );
@@ -2731,10 +2839,10 @@ ${results.length - failed} rendered (${orphaned} with orphans), ${failed} failed
2731
2839
  fail(`scene name must be kebab-case (a-z, 0-9, -): got "${name}"`);
2732
2840
  }
2733
2841
  const inRepo = USER_CWD === ROOT2 || USER_CWD.startsWith(ROOT2 + "/");
2734
- const targetDir = inRepo ? join6(ROOT2, "examples", "scenes") : USER_CWD;
2735
- const target = join6(targetDir, `${name}.ts`);
2842
+ const targetDir = inRepo ? join7(ROOT2, "examples", "scenes") : USER_CWD;
2843
+ const target = join7(targetDir, `${name}.ts`);
2736
2844
  const shown = inRepo ? `examples/scenes/${name}.ts` : `${name}.ts`;
2737
- if (existsSync4(target)) fail(`${shown} already exists`);
2845
+ if (existsSync5(target)) fail(`${shown} already exists`);
2738
2846
  const id = name.split("-")[0] ?? name;
2739
2847
  await writeFile5(target, SCENE_TEMPLATE(name, id));
2740
2848
  console.log(`created ${shown}
@@ -2763,9 +2871,9 @@ ${results.length - failed} rendered (${orphaned} with orphans), ${failed} failed
2763
2871
  );
2764
2872
  }
2765
2873
  case "guide": {
2766
- 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");
2767
- const { readFile: readFile6 } = await import("node:fs/promises");
2768
- process.stdout.write(await readFile6(file, "utf8"));
2874
+ const file = rest.includes("--regen") ? PACKAGED ? join7(ROOT2, "guides", "regen-contract.md") : join7(ROOT2, "docs", "regen-contract.md") : PACKAGED ? join7(ROOT2, "guides", "edsl-guide.md") : join7(ROOT2, "benchmark", "guides", "edsl-guide.md");
2875
+ const { readFile: readFile7 } = await import("node:fs/promises");
2876
+ process.stdout.write(await readFile7(file, "utf8"));
2769
2877
  return;
2770
2878
  }
2771
2879
  case "demo":
@@ -2776,7 +2884,7 @@ ${results.length - failed} rendered (${orphaned} with orphans), ${failed} failed
2776
2884
  }
2777
2885
  preflightFfmpeg();
2778
2886
  process.exit(
2779
- await run("npx", ["tsx", join6(ROOT2, "examples", "scripts", "demo-edit-survival.ts")])
2887
+ await run("npx", ["tsx", join7(ROOT2, "examples", "scripts", "demo-edit-survival.ts")])
2780
2888
  );
2781
2889
  default:
2782
2890
  console.log(USAGE);