hyperframes 0.4.23 → 0.4.24

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/cli.js CHANGED
@@ -54,7 +54,7 @@ var VERSION;
54
54
  var init_version = __esm({
55
55
  "src/version.ts"() {
56
56
  "use strict";
57
- VERSION = true ? "0.4.23" : "0.0.0-dev";
57
+ VERSION = true ? "0.4.24" : "0.0.0-dev";
58
58
  }
59
59
  });
60
60
 
@@ -9973,7 +9973,7 @@ import { get as httpsGet } from "https";
9973
9973
  import { pipeline } from "stream/promises";
9974
9974
  function downloadFile(url, dest) {
9975
9975
  const tmp = `${dest}.tmp`;
9976
- return new Promise((resolve38, reject) => {
9976
+ return new Promise((resolve39, reject) => {
9977
9977
  const follow = (u) => {
9978
9978
  httpsGet(u, (res) => {
9979
9979
  if (res.statusCode === 301 || res.statusCode === 302) {
@@ -9990,7 +9990,7 @@ function downloadFile(url, dest) {
9990
9990
  const file = createWriteStream(tmp);
9991
9991
  pipeline(res, file).then(() => {
9992
9992
  renameSync(tmp, dest);
9993
- resolve38();
9993
+ resolve39();
9994
9994
  }).catch((err) => {
9995
9995
  try {
9996
9996
  unlinkSync(tmp);
@@ -10708,7 +10708,7 @@ function hasNpx() {
10708
10708
  }
10709
10709
  }
10710
10710
  function runSkillsAdd(repo) {
10711
- return new Promise((resolve38, reject) => {
10711
+ return new Promise((resolve39, reject) => {
10712
10712
  const child = spawn("npx", ["skills", "add", repo, "--all"], {
10713
10713
  stdio: "inherit",
10714
10714
  timeout: 12e4,
@@ -10722,7 +10722,7 @@ function runSkillsAdd(repo) {
10722
10722
  env: { ...process.env, GIT_CLONE_PROTECTION_ACTIVE: "0" }
10723
10723
  });
10724
10724
  child.on("close", (code, signal) => {
10725
- if (code === 0) resolve38();
10725
+ if (code === 0) resolve39();
10726
10726
  else if (signal === "SIGINT" || code === 130) process.exit(0);
10727
10727
  else reject(new Error(`npx skills add exited with code ${code}`));
10728
10728
  });
@@ -14856,10 +14856,10 @@ function compareDocumentPosition(nodeA, nodeB) {
14856
14856
  function uniqueSort(nodes) {
14857
14857
  nodes = nodes.filter((node, i2, arr) => !arr.includes(node, i2 + 1));
14858
14858
  nodes.sort((a, b) => {
14859
- const relative6 = compareDocumentPosition(a, b);
14860
- if (relative6 & DocumentPosition.PRECEDING) {
14859
+ const relative7 = compareDocumentPosition(a, b);
14860
+ if (relative7 & DocumentPosition.PRECEDING) {
14861
14861
  return -1;
14862
- } else if (relative6 & DocumentPosition.FOLLOWING) {
14862
+ } else if (relative7 & DocumentPosition.FOLLOWING) {
14863
14863
  return 1;
14864
14864
  }
14865
14865
  return 0;
@@ -15321,8 +15321,8 @@ var init_custom_element_registry = __esm({
15321
15321
  } : (element) => element.localName === localName;
15322
15322
  registry.set(localName, { Class, check });
15323
15323
  if (waiting.has(localName)) {
15324
- for (const resolve38 of waiting.get(localName))
15325
- resolve38(Class);
15324
+ for (const resolve39 of waiting.get(localName))
15325
+ resolve39(Class);
15326
15326
  waiting.delete(localName);
15327
15327
  }
15328
15328
  ownerDocument.querySelectorAll(
@@ -15362,13 +15362,13 @@ var init_custom_element_registry = __esm({
15362
15362
  */
15363
15363
  whenDefined(localName) {
15364
15364
  const { registry, waiting } = this;
15365
- return new Promise((resolve38) => {
15365
+ return new Promise((resolve39) => {
15366
15366
  if (registry.has(localName))
15367
- resolve38(registry.get(localName).Class);
15367
+ resolve39(registry.get(localName).Class);
15368
15368
  else {
15369
15369
  if (!waiting.has(localName))
15370
15370
  waiting.set(localName, []);
15371
- waiting.get(localName).push(resolve38);
15371
+ waiting.get(localName).push(resolve39);
15372
15372
  }
15373
15373
  });
15374
15374
  }
@@ -25731,7 +25731,7 @@ async function pollPageExpression(page, expression, timeoutMs, intervalMs = 100)
25731
25731
  while (Date.now() < deadline) {
25732
25732
  const ready = Boolean(await page.evaluate(expression));
25733
25733
  if (ready) return true;
25734
- await new Promise((resolve38) => setTimeout(resolve38, intervalMs));
25734
+ await new Promise((resolve39) => setTimeout(resolve39, intervalMs));
25735
25735
  }
25736
25736
  return Boolean(await page.evaluate(expression));
25737
25737
  }
@@ -25753,8 +25753,12 @@ async function initializeSession(session) {
25753
25753
  }
25754
25754
  });
25755
25755
  page.on("pageerror", (err) => {
25756
- const text = `[Browser:PAGEERROR] ${err instanceof Error ? err.message : String(err)}`;
25757
- console.error(text);
25756
+ const message = err instanceof Error ? err.message : String(err);
25757
+ const text = `[Browser:PAGEERROR] ${message}`;
25758
+ const isPlayAbort = /^AbortError:/.test(message) && message.includes("play()") && message.includes("pause()");
25759
+ if (!isPlayAbort) {
25760
+ console.error(text);
25761
+ }
25758
25762
  session.browserConsoleBuffer.push(text);
25759
25763
  if (session.browserConsoleBuffer.length > BROWSER_CONSOLE_BUFFER_SIZE) {
25760
25764
  session.browserConsoleBuffer.shift();
@@ -26020,7 +26024,7 @@ var init_frameCapture = __esm({
26020
26024
  // ../engine/src/utils/gpuEncoder.ts
26021
26025
  import { spawn as spawn2 } from "child_process";
26022
26026
  async function detectGpuEncoder() {
26023
- return new Promise((resolve38) => {
26027
+ return new Promise((resolve39) => {
26024
26028
  const ffmpeg = spawn2("ffmpeg", ["-encoders"], {
26025
26029
  stdio: ["pipe", "pipe", "pipe"]
26026
26030
  });
@@ -26029,13 +26033,13 @@ async function detectGpuEncoder() {
26029
26033
  stdout2 += data.toString();
26030
26034
  });
26031
26035
  ffmpeg.on("close", () => {
26032
- if (stdout2.includes("h264_nvenc")) resolve38("nvenc");
26033
- else if (stdout2.includes("h264_videotoolbox")) resolve38("videotoolbox");
26034
- else if (stdout2.includes("h264_vaapi")) resolve38("vaapi");
26035
- else if (stdout2.includes("h264_qsv")) resolve38("qsv");
26036
- else resolve38(null);
26036
+ if (stdout2.includes("h264_nvenc")) resolve39("nvenc");
26037
+ else if (stdout2.includes("h264_videotoolbox")) resolve39("videotoolbox");
26038
+ else if (stdout2.includes("h264_vaapi")) resolve39("vaapi");
26039
+ else if (stdout2.includes("h264_qsv")) resolve39("qsv");
26040
+ else resolve39(null);
26037
26041
  });
26038
- ffmpeg.on("error", () => resolve38(null));
26042
+ ffmpeg.on("error", () => resolve39(null));
26039
26043
  });
26040
26044
  }
26041
26045
  async function getCachedGpuEncoder() {
@@ -26156,7 +26160,7 @@ async function runFfmpeg(args, opts) {
26156
26160
  const signal = opts?.signal;
26157
26161
  const timeout = opts?.timeout ?? DEFAULT_TIMEOUT;
26158
26162
  const onStderr = opts?.onStderr;
26159
- return new Promise((resolve38) => {
26163
+ return new Promise((resolve39) => {
26160
26164
  const ffmpeg = spawn3("ffmpeg", args);
26161
26165
  let stderr = "";
26162
26166
  const onAbort = () => {
@@ -26182,7 +26186,7 @@ async function runFfmpeg(args, opts) {
26182
26186
  ffmpeg.on("close", (code) => {
26183
26187
  clearTimeout(timer);
26184
26188
  if (signal) signal.removeEventListener("abort", onAbort);
26185
- resolve38({
26189
+ resolve39({
26186
26190
  success: !signal?.aborted && code === 0,
26187
26191
  exitCode: code,
26188
26192
  stderr,
@@ -26192,7 +26196,7 @@ async function runFfmpeg(args, opts) {
26192
26196
  ffmpeg.on("error", (err) => {
26193
26197
  clearTimeout(timer);
26194
26198
  if (signal) signal.removeEventListener("abort", onAbort);
26195
- resolve38({
26199
+ resolve39({
26196
26200
  success: false,
26197
26201
  exitCode: null,
26198
26202
  stderr: err.message,
@@ -26384,7 +26388,7 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
26384
26388
  const inputPath = join20(framesDir, framePattern);
26385
26389
  const inputArgs = ["-framerate", String(options.fps), "-i", inputPath];
26386
26390
  const args = buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder);
26387
- return new Promise((resolve38) => {
26391
+ return new Promise((resolve39) => {
26388
26392
  const ffmpeg = spawn4("ffmpeg", args);
26389
26393
  let stderr = "";
26390
26394
  const onAbort = () => {
@@ -26409,7 +26413,7 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
26409
26413
  if (signal) signal.removeEventListener("abort", onAbort);
26410
26414
  const durationMs = Date.now() - startTime;
26411
26415
  if (signal?.aborted) {
26412
- resolve38({
26416
+ resolve39({
26413
26417
  success: false,
26414
26418
  outputPath,
26415
26419
  durationMs,
@@ -26420,7 +26424,7 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
26420
26424
  return;
26421
26425
  }
26422
26426
  if (code !== 0) {
26423
- resolve38({
26427
+ resolve39({
26424
26428
  success: false,
26425
26429
  outputPath,
26426
26430
  durationMs,
@@ -26431,12 +26435,12 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
26431
26435
  return;
26432
26436
  }
26433
26437
  const fileSize = existsSync17(outputPath) ? statSync4(outputPath).size : 0;
26434
- resolve38({ success: true, outputPath, durationMs, framesEncoded: frameCount, fileSize });
26438
+ resolve39({ success: true, outputPath, durationMs, framesEncoded: frameCount, fileSize });
26435
26439
  });
26436
26440
  ffmpeg.on("error", (err) => {
26437
26441
  clearTimeout(timer);
26438
26442
  if (signal) signal.removeEventListener("abort", onAbort);
26439
- resolve38({
26443
+ resolve39({
26440
26444
  success: false,
26441
26445
  outputPath,
26442
26446
  durationMs: Date.now() - startTime,
@@ -26494,18 +26498,18 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
26494
26498
  let gpuEncoder = null;
26495
26499
  if (options.useGpu) gpuEncoder = await getCachedGpuEncoder();
26496
26500
  const args = buildEncoderArgs(options, inputArgs, chunkPath, gpuEncoder);
26497
- const chunkResult = await new Promise((resolve38) => {
26501
+ const chunkResult = await new Promise((resolve39) => {
26498
26502
  const ffmpeg = spawn4("ffmpeg", args);
26499
26503
  let stderr = "";
26500
26504
  ffmpeg.stderr.on("data", (d) => {
26501
26505
  stderr += d.toString();
26502
26506
  });
26503
26507
  ffmpeg.on("close", (code) => {
26504
- if (code === 0) resolve38({ success: true });
26505
- else resolve38({ success: false, error: `Chunk ${i2} encode failed: ${stderr.slice(-400)}` });
26508
+ if (code === 0) resolve39({ success: true });
26509
+ else resolve39({ success: false, error: `Chunk ${i2} encode failed: ${stderr.slice(-400)}` });
26506
26510
  });
26507
26511
  ffmpeg.on("error", (err) => {
26508
- resolve38({ success: false, error: `Chunk ${i2} encode error: ${err.message}` });
26512
+ resolve39({ success: false, error: `Chunk ${i2} encode error: ${err.message}` });
26509
26513
  });
26510
26514
  });
26511
26515
  if (!chunkResult.success) {
@@ -26535,18 +26539,18 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
26535
26539
  "-y",
26536
26540
  outputPath
26537
26541
  ];
26538
- const concatResult = await new Promise((resolve38) => {
26542
+ const concatResult = await new Promise((resolve39) => {
26539
26543
  const ffmpeg = spawn4("ffmpeg", concatArgs);
26540
26544
  let stderr = "";
26541
26545
  ffmpeg.stderr.on("data", (d) => {
26542
26546
  stderr += d.toString();
26543
26547
  });
26544
26548
  ffmpeg.on("close", (code) => {
26545
- if (code === 0) resolve38({ success: true });
26546
- else resolve38({ success: false, error: `Chunk concat failed: ${stderr.slice(-400)}` });
26549
+ if (code === 0) resolve39({ success: true });
26550
+ else resolve39({ success: false, error: `Chunk concat failed: ${stderr.slice(-400)}` });
26547
26551
  });
26548
26552
  ffmpeg.on("error", (err) => {
26549
- resolve38({ success: false, error: `Chunk concat error: ${err.message}` });
26553
+ resolve39({ success: false, error: `Chunk concat error: ${err.message}` });
26550
26554
  });
26551
26555
  });
26552
26556
  if (!concatResult.success) {
@@ -26646,37 +26650,37 @@ import { dirname as dirname7 } from "path";
26646
26650
  function createFrameReorderBuffer(startFrame, endFrame) {
26647
26651
  let cursor = startFrame;
26648
26652
  const pending = /* @__PURE__ */ new Map();
26649
- const enqueueAt = (frame, resolve38) => {
26653
+ const enqueueAt = (frame, resolve39) => {
26650
26654
  const list = pending.get(frame);
26651
26655
  if (list === void 0) {
26652
- pending.set(frame, [resolve38]);
26656
+ pending.set(frame, [resolve39]);
26653
26657
  } else {
26654
- list.push(resolve38);
26658
+ list.push(resolve39);
26655
26659
  }
26656
26660
  };
26657
26661
  const flushAt = (frame) => {
26658
26662
  const list = pending.get(frame);
26659
26663
  if (list === void 0) return;
26660
26664
  pending.delete(frame);
26661
- for (const resolve38 of list) resolve38();
26665
+ for (const resolve39 of list) resolve39();
26662
26666
  };
26663
- const waitForFrame = (frame) => new Promise((resolve38) => {
26667
+ const waitForFrame = (frame) => new Promise((resolve39) => {
26664
26668
  if (frame === cursor) {
26665
- resolve38();
26669
+ resolve39();
26666
26670
  return;
26667
26671
  }
26668
- enqueueAt(frame, resolve38);
26672
+ enqueueAt(frame, resolve39);
26669
26673
  });
26670
26674
  const advanceTo = (frame) => {
26671
26675
  cursor = frame;
26672
26676
  flushAt(frame);
26673
26677
  };
26674
- const waitForAllDone = () => new Promise((resolve38) => {
26678
+ const waitForAllDone = () => new Promise((resolve39) => {
26675
26679
  if (cursor >= endFrame) {
26676
- resolve38();
26680
+ resolve39();
26677
26681
  return;
26678
26682
  }
26679
- enqueueAt(endFrame, resolve38);
26683
+ enqueueAt(endFrame, resolve39);
26680
26684
  });
26681
26685
  return { waitForFrame, advanceTo, waitForAllDone };
26682
26686
  }
@@ -26838,7 +26842,7 @@ async function spawnStreamingEncoder(outputPath, options, signal, config) {
26838
26842
  let stderr = "";
26839
26843
  let exitCode = null;
26840
26844
  let exitPromiseResolve = null;
26841
- const exitPromise = new Promise((resolve38) => exitPromiseResolve = resolve38);
26845
+ const exitPromise = new Promise((resolve39) => exitPromiseResolve = resolve39);
26842
26846
  ffmpeg.stderr?.on("data", (data) => {
26843
26847
  stderr += data.toString();
26844
26848
  });
@@ -26884,8 +26888,8 @@ Process error: ${err.message}`;
26884
26888
  if (signal) signal.removeEventListener("abort", onAbort);
26885
26889
  const stdin = ffmpeg.stdin;
26886
26890
  if (stdin && !stdin.destroyed) {
26887
- await new Promise((resolve38) => {
26888
- stdin.end(() => resolve38());
26891
+ await new Promise((resolve39) => {
26892
+ stdin.end(() => resolve39());
26889
26893
  });
26890
26894
  }
26891
26895
  await exitPromise;
@@ -26928,7 +26932,7 @@ import { spawn as spawn6 } from "child_process";
26928
26932
  import { readFileSync as readFileSync15 } from "fs";
26929
26933
  import { extname as extname4 } from "path";
26930
26934
  function runFfprobe(args) {
26931
- return new Promise((resolve38, reject) => {
26935
+ return new Promise((resolve39, reject) => {
26932
26936
  const proc = spawn6("ffprobe", args);
26933
26937
  let stdout2 = "";
26934
26938
  let stderr = "";
@@ -26942,7 +26946,7 @@ function runFfprobe(args) {
26942
26946
  if (code !== 0) {
26943
26947
  reject(new Error(`[FFmpeg] ffprobe exited with code ${code}: ${stderr}`));
26944
26948
  } else {
26945
- resolve38(stdout2);
26949
+ resolve39(stdout2);
26946
26950
  }
26947
26951
  });
26948
26952
  proc.on("error", (err) => {
@@ -27484,7 +27488,7 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
27484
27488
  args.push("-q:v", format === "jpg" ? String(Math.ceil((100 - quality) / 3)) : "0");
27485
27489
  if (format === "png") args.push("-compression_level", "6");
27486
27490
  args.push("-y", outputPattern);
27487
- return new Promise((resolve38, reject) => {
27491
+ return new Promise((resolve39, reject) => {
27488
27492
  const ffmpeg = spawn7("ffmpeg", args);
27489
27493
  let stderr = "";
27490
27494
  const onAbort = () => {
@@ -27519,7 +27523,7 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
27519
27523
  files.forEach((file, index) => {
27520
27524
  framePaths.set(index, join23(videoOutputDir, file));
27521
27525
  });
27522
- resolve38({
27526
+ resolve39({
27523
27527
  videoId,
27524
27528
  srcPath: videoPath,
27525
27529
  outputDir: videoOutputDir,
@@ -28926,11 +28930,11 @@ function createFileServer(options) {
28926
28930
  headers: { "Content-Type": contentType }
28927
28931
  });
28928
28932
  });
28929
- return new Promise((resolve38) => {
28933
+ return new Promise((resolve39) => {
28930
28934
  const server = serve({ fetch: app.fetch, port }, (info) => {
28931
28935
  const actualPort = info.port;
28932
28936
  const url = `http://localhost:${actualPort}`;
28933
- resolve38({
28937
+ resolve39({
28934
28938
  url,
28935
28939
  port: actualPort,
28936
28940
  close: () => server.close()
@@ -31330,13 +31334,13 @@ function createFileServer2(options) {
31330
31334
  let filePath = null;
31331
31335
  if (compiledDir) {
31332
31336
  const candidate = join29(compiledDir, relativePath);
31333
- if (existsSync28(candidate) && isPathInside(candidate, compiledDir, { resolveSymlinks: true }) && statSync8(candidate).isFile()) {
31337
+ if (existsSync28(candidate) && isPathInside(candidate, compiledDir) && statSync8(candidate).isFile()) {
31334
31338
  filePath = candidate;
31335
31339
  }
31336
31340
  }
31337
31341
  if (!filePath) {
31338
31342
  const candidate = join29(projectDir, relativePath);
31339
- if (existsSync28(candidate) && isPathInside(candidate, projectDir, { resolveSymlinks: true }) && statSync8(candidate).isFile()) {
31343
+ if (existsSync28(candidate) && isPathInside(candidate, projectDir) && statSync8(candidate).isFile()) {
31340
31344
  filePath = candidate;
31341
31345
  }
31342
31346
  }
@@ -31364,10 +31368,10 @@ function createFileServer2(options) {
31364
31368
  headers: { "Content-Type": contentType }
31365
31369
  });
31366
31370
  });
31367
- return new Promise((resolve38) => {
31371
+ return new Promise((resolve39) => {
31368
31372
  const connections = /* @__PURE__ */ new Set();
31369
31373
  const server = serve2({ fetch: app.fetch, port }, (info) => {
31370
- resolve38({
31374
+ resolve39({
31371
31375
  url: `http://localhost:${info.port}`,
31372
31376
  port: info.port,
31373
31377
  close: () => {
@@ -35058,10 +35062,10 @@ var init_semaphore = __esm({
35058
35062
  this.active++;
35059
35063
  return () => this.release();
35060
35064
  }
35061
- return new Promise((resolve38) => {
35065
+ return new Promise((resolve39) => {
35062
35066
  this.queue.push(() => {
35063
35067
  this.active++;
35064
- resolve38(() => this.release());
35068
+ resolve39(() => this.release());
35065
35069
  });
35066
35070
  });
35067
35071
  }
@@ -36044,8 +36048,8 @@ async function runDevMode(dir, projectName) {
36044
36048
  }
36045
36049
  });
36046
36050
  }
36047
- return new Promise((resolve38) => {
36048
- child.on("close", () => resolve38());
36051
+ return new Promise((resolve39) => {
36052
+ child.on("close", () => resolve39());
36049
36053
  });
36050
36054
  }
36051
36055
  function hasLocalStudio(dir) {
@@ -36115,8 +36119,8 @@ async function runLocalStudioMode(dir, projectName) {
36115
36119
  }
36116
36120
  });
36117
36121
  }
36118
- return new Promise((resolve38) => {
36119
- child.on("close", () => resolve38());
36122
+ return new Promise((resolve39) => {
36123
+ child.on("close", () => resolve39());
36120
36124
  });
36121
36125
  }
36122
36126
  async function runEmbeddedMode(dir, startPort, projectName, forceNew = false) {
@@ -36183,7 +36187,29 @@ async function runEmbeddedMode(dir, startPort, projectName, forceNew = false) {
36183
36187
  console.log();
36184
36188
  import("open").then((mod) => mod.default(`${url}#project/${pName}`)).catch(() => {
36185
36189
  });
36186
- return new Promise(() => {
36190
+ let rl;
36191
+ if (process.platform === "win32") {
36192
+ const readline = await import("readline");
36193
+ rl = readline.createInterface({ input: process.stdin, output: process.stdout });
36194
+ rl.on("SIGINT", () => {
36195
+ process.emit("SIGINT", "SIGINT");
36196
+ });
36197
+ }
36198
+ return new Promise((resolveRun) => {
36199
+ let shuttingDown = false;
36200
+ const shutdown = () => {
36201
+ if (shuttingDown) return;
36202
+ shuttingDown = true;
36203
+ process.off("SIGINT", shutdown);
36204
+ process.off("SIGTERM", shutdown);
36205
+ rl?.close();
36206
+ console.log();
36207
+ console.log(` ${c.dim("Shutting down studio...")}`);
36208
+ result.server.close(() => resolveRun());
36209
+ setTimeout(() => process.exit(0), 2e3).unref();
36210
+ };
36211
+ process.once("SIGINT", shutdown);
36212
+ process.once("SIGTERM", shutdown);
36187
36213
  });
36188
36214
  }
36189
36215
  var examples, preview_default;
@@ -38551,6 +38577,558 @@ var init_lint3 = __esm({
38551
38577
  }
38552
38578
  });
38553
38579
 
38580
+ // src/utils/layoutAudit.ts
38581
+ function buildLayoutSampleTimes({ duration, samples, at: at2 }) {
38582
+ if (at2?.length) {
38583
+ return uniqueSortedTimes(
38584
+ at2.filter(
38585
+ (time) => Number.isFinite(time) && time >= 0 && (duration <= 0 || time <= duration)
38586
+ )
38587
+ );
38588
+ }
38589
+ if (!Number.isFinite(duration) || duration <= 0 || samples <= 0) return [];
38590
+ const count = Math.max(1, Math.floor(samples));
38591
+ return Array.from({ length: count }, (_2, index) => roundTime((index + 0.5) / count * duration));
38592
+ }
38593
+ function summarizeLayoutIssues(issues) {
38594
+ const errorCount = issues.filter((issue) => issue.severity === "error").length;
38595
+ const warningCount = issues.filter((issue) => issue.severity === "warning").length;
38596
+ const infoCount = issues.filter((issue) => issue.severity === "info").length;
38597
+ return {
38598
+ ok: errorCount === 0,
38599
+ errorCount,
38600
+ warningCount,
38601
+ infoCount,
38602
+ issueCount: issues.length
38603
+ };
38604
+ }
38605
+ function formatLayoutIssue(issue) {
38606
+ const timeLabel = issue.occurrences && issue.occurrences > 1 ? `t=${formatNumber(issue.firstSeen ?? issue.time)}-${formatNumber(issue.lastSeen ?? issue.time)}s (${issue.occurrences} samples)` : `t=${formatNumber(issue.time)}s`;
38607
+ const parts = [
38608
+ timeLabel,
38609
+ issue.code,
38610
+ issue.selector,
38611
+ issue.containerSelector ? `inside ${issue.containerSelector}` : "",
38612
+ issue.overflow ? `overflowed ${formatOverflow(issue.overflow)}` : "",
38613
+ issue.text ? quoteText(issue.text) : ""
38614
+ ].filter(Boolean);
38615
+ const line = `${parts.join(" ")} \u2014 ${issue.message}`;
38616
+ return issue.fixHint ? `${line}
38617
+ Fix: ${issue.fixHint}` : line;
38618
+ }
38619
+ function dedupeLayoutIssues(issues) {
38620
+ const seen = /* @__PURE__ */ new Set();
38621
+ const result = [];
38622
+ for (const issue of issues) {
38623
+ const key2 = [
38624
+ issue.code,
38625
+ issue.severity,
38626
+ issue.time.toFixed(3),
38627
+ issue.selector,
38628
+ issue.containerSelector ?? "",
38629
+ issue.text ?? "",
38630
+ issue.overflow ? formatOverflow(issue.overflow) : ""
38631
+ ].join("|");
38632
+ if (seen.has(key2)) continue;
38633
+ seen.add(key2);
38634
+ result.push(issue);
38635
+ }
38636
+ return result;
38637
+ }
38638
+ function collapseStaticLayoutIssues(issues) {
38639
+ const groups = /* @__PURE__ */ new Map();
38640
+ for (const issue of issues) {
38641
+ const key2 = staticIssueKey(issue);
38642
+ const existing = groups.get(key2);
38643
+ if (!existing) {
38644
+ groups.set(key2, {
38645
+ issue,
38646
+ firstSeen: issue.time,
38647
+ lastSeen: issue.time,
38648
+ occurrences: 1
38649
+ });
38650
+ continue;
38651
+ }
38652
+ existing.firstSeen = Math.min(existing.firstSeen, issue.time);
38653
+ existing.lastSeen = Math.max(existing.lastSeen, issue.time);
38654
+ existing.occurrences += 1;
38655
+ }
38656
+ return [...groups.values()].map(({ issue, firstSeen, lastSeen, occurrences }) => ({
38657
+ ...issue,
38658
+ time: firstSeen,
38659
+ firstSeen,
38660
+ lastSeen,
38661
+ occurrences
38662
+ }));
38663
+ }
38664
+ function limitLayoutIssues(issues, maxIssues) {
38665
+ const limit = Math.max(1, Math.floor(maxIssues));
38666
+ const sortedIssues = [...issues].sort((a, b) => {
38667
+ const severityDelta = severityRank(a.severity) - severityRank(b.severity);
38668
+ if (severityDelta !== 0) return severityDelta;
38669
+ return a.time - b.time;
38670
+ });
38671
+ return {
38672
+ issues: sortedIssues.slice(0, limit),
38673
+ totalIssueCount: issues.length,
38674
+ truncated: issues.length > limit
38675
+ };
38676
+ }
38677
+ function severityRank(severity) {
38678
+ if (severity === "error") return 0;
38679
+ if (severity === "warning") return 1;
38680
+ return 2;
38681
+ }
38682
+ function staticIssueKey(issue) {
38683
+ return [
38684
+ issue.code,
38685
+ issue.severity,
38686
+ issue.selector,
38687
+ issue.containerSelector ?? "",
38688
+ issue.text ?? "",
38689
+ issue.overflow ? formatOverflow(issue.overflow) : ""
38690
+ ].join("|");
38691
+ }
38692
+ function uniqueSortedTimes(times) {
38693
+ const rounded = times.map(roundTime);
38694
+ return [...new Set(rounded)].sort((a, b) => a - b);
38695
+ }
38696
+ function formatOverflow(overflow) {
38697
+ return ["left", "right", "top", "bottom"].flatMap((side) => {
38698
+ const value = overflow[side];
38699
+ return value == null ? [] : `${side} ${formatNumber(value)}px`;
38700
+ }).join(", ");
38701
+ }
38702
+ function quoteText(text) {
38703
+ const normalized = text.replace(/\s+/g, " ").trim();
38704
+ const truncated = normalized.length > 80 ? `${normalized.slice(0, 77)}...` : normalized;
38705
+ return `"${truncated}"`;
38706
+ }
38707
+ function formatNumber(value) {
38708
+ return Number.isInteger(value) ? String(value) : value.toFixed(2).replace(/0+$/, "").replace(/\.$/, "");
38709
+ }
38710
+ function roundTime(value) {
38711
+ return Math.round(value * 1e3) / 1e3;
38712
+ }
38713
+ var init_layoutAudit = __esm({
38714
+ "src/utils/layoutAudit.ts"() {
38715
+ "use strict";
38716
+ }
38717
+ });
38718
+
38719
+ // src/commands/layout.ts
38720
+ var layout_exports = {};
38721
+ __export(layout_exports, {
38722
+ createInspectCommand: () => createInspectCommand,
38723
+ default: () => layout_default,
38724
+ examples: () => examples9
38725
+ });
38726
+ import { createServer } from "http";
38727
+ import { existsSync as existsSync41, readFileSync as readFileSync29 } from "fs";
38728
+ import { dirname as dirname17, isAbsolute as isAbsolute6, join as join42, relative as relative5, resolve as resolve29 } from "path";
38729
+ import { fileURLToPath as fileURLToPath6 } from "url";
38730
+ async function getCompositionDuration2(page) {
38731
+ return page.evaluate(() => {
38732
+ const win = window;
38733
+ if (typeof win.__hf?.duration === "number" && win.__hf.duration > 0) return win.__hf.duration;
38734
+ const playerDuration = win.__player?.duration;
38735
+ if (typeof playerDuration === "function") return playerDuration();
38736
+ if (typeof playerDuration === "number" && playerDuration > 0) return playerDuration;
38737
+ const root = document.querySelector("[data-composition-id][data-duration]");
38738
+ const attrDuration = root ? parseFloat(root.getAttribute("data-duration") ?? "0") : 0;
38739
+ if (attrDuration > 0) return attrDuration;
38740
+ const timelines = win.__timelines;
38741
+ if (timelines) {
38742
+ for (const timeline of Object.values(timelines)) {
38743
+ const duration = timeline.duration;
38744
+ if (typeof duration === "function") return duration();
38745
+ if (typeof duration === "number" && duration > 0) return duration;
38746
+ }
38747
+ }
38748
+ return 0;
38749
+ });
38750
+ }
38751
+ async function seekTo(page, time) {
38752
+ await page.evaluate((t3) => {
38753
+ const win = window;
38754
+ if (typeof win.__hf?.seek === "function") {
38755
+ win.__hf.seek(t3);
38756
+ return;
38757
+ }
38758
+ if (typeof win.__player?.seek === "function") {
38759
+ win.__player.seek(t3);
38760
+ return;
38761
+ }
38762
+ const timelines = win.__timelines;
38763
+ if (timelines) {
38764
+ for (const timeline of Object.values(timelines)) {
38765
+ if (typeof timeline.pause === "function") timeline.pause();
38766
+ if (typeof timeline.seek === "function") timeline.seek(t3);
38767
+ }
38768
+ }
38769
+ }, time);
38770
+ await page.evaluate(
38771
+ () => new Promise(
38772
+ (resolveFrame) => requestAnimationFrame(() => requestAnimationFrame(() => resolveFrame()))
38773
+ )
38774
+ );
38775
+ await page.evaluate(() => {
38776
+ const fonts = document.fonts;
38777
+ if (!fonts?.ready) return Promise.resolve();
38778
+ return Promise.race([
38779
+ fonts.ready.then(() => void 0),
38780
+ new Promise((resolve39) => setTimeout(resolve39, 500))
38781
+ ]);
38782
+ }).catch(() => {
38783
+ });
38784
+ await new Promise((resolveSettle) => setTimeout(resolveSettle, SEEK_SETTLE_MS));
38785
+ }
38786
+ async function bundleProjectHtml(projectDir) {
38787
+ const { bundleToSingleHtml: bundleToSingleHtml2 } = await Promise.resolve().then(() => (init_compiler(), compiler_exports));
38788
+ let html = await bundleToSingleHtml2(projectDir);
38789
+ const runtimePath = resolve29(
38790
+ __dirname2,
38791
+ "..",
38792
+ "..",
38793
+ "..",
38794
+ "core",
38795
+ "dist",
38796
+ "hyperframe.runtime.iife.js"
38797
+ );
38798
+ if (existsSync41(runtimePath)) {
38799
+ const runtimeSource = readFileSync29(runtimePath, "utf-8");
38800
+ html = html.replace(
38801
+ /<script[^>]*data-hyperframes-preview-runtime[^>]*src="[^"]*"[^>]*><\/script>/,
38802
+ () => `<script data-hyperframes-preview-runtime="1">${runtimeSource}</script>`
38803
+ );
38804
+ }
38805
+ return html;
38806
+ }
38807
+ async function serveProject(projectDir, html) {
38808
+ const { getMimeType: getMimeType2 } = await Promise.resolve().then(() => (init_studio_api(), studio_api_exports));
38809
+ const server = createServer((req, res) => {
38810
+ const url = req.url ?? "/";
38811
+ if (url === "/" || url === "/index.html") {
38812
+ res.writeHead(200, { "Content-Type": "text/html" });
38813
+ res.end(html);
38814
+ return;
38815
+ }
38816
+ const filePath = resolve29(projectDir, decodeURIComponent(url).replace(/^\//, ""));
38817
+ const rel = relative5(projectDir, filePath);
38818
+ if (rel.startsWith("..") || isAbsolute6(rel)) {
38819
+ res.writeHead(403);
38820
+ res.end();
38821
+ return;
38822
+ }
38823
+ if (existsSync41(filePath)) {
38824
+ res.writeHead(200, { "Content-Type": getMimeType2(filePath) });
38825
+ res.end(readFileSync29(filePath));
38826
+ return;
38827
+ }
38828
+ res.writeHead(404);
38829
+ res.end();
38830
+ });
38831
+ const port = await new Promise((resolvePort, rejectPort) => {
38832
+ server.on("error", rejectPort);
38833
+ server.listen(0, () => {
38834
+ const addr = server.address();
38835
+ const resolvedPort = typeof addr === "object" && addr ? addr.port : 0;
38836
+ if (!resolvedPort) rejectPort(new Error("Failed to bind local layout audit server"));
38837
+ else resolvePort(resolvedPort);
38838
+ });
38839
+ });
38840
+ return {
38841
+ url: `http://127.0.0.1:${port}/`,
38842
+ close: () => new Promise((resolveClose) => {
38843
+ server.close(() => resolveClose());
38844
+ })
38845
+ };
38846
+ }
38847
+ async function alignViewportToComposition(page, url) {
38848
+ const size = await page.evaluate(() => {
38849
+ const root = document.querySelector("[data-composition-id][data-width][data-height]");
38850
+ const width = root ? parseInt(root.getAttribute("data-width") ?? "", 10) : 0;
38851
+ const height = root ? parseInt(root.getAttribute("data-height") ?? "", 10) : 0;
38852
+ return {
38853
+ width: Number.isFinite(width) && width > 0 ? Math.min(width, 4096) : 1920,
38854
+ height: Number.isFinite(height) && height > 0 ? Math.min(height, 4096) : 1080
38855
+ };
38856
+ });
38857
+ await page.setViewport(size);
38858
+ await page.goto(url, { waitUntil: "domcontentloaded", timeout: 1e4 });
38859
+ }
38860
+ async function runLayoutAudit(projectDir, opts) {
38861
+ const { ensureBrowser: ensureBrowser2 } = await Promise.resolve().then(() => (init_manager2(), manager_exports2));
38862
+ const puppeteer = await import("puppeteer-core");
38863
+ const html = await bundleProjectHtml(projectDir);
38864
+ const server = await serveProject(projectDir, html);
38865
+ let chromeBrowser;
38866
+ try {
38867
+ const browser = await ensureBrowser2();
38868
+ chromeBrowser = await puppeteer.default.launch({
38869
+ headless: true,
38870
+ executablePath: browser.executablePath,
38871
+ args: [
38872
+ "--no-sandbox",
38873
+ "--disable-gpu",
38874
+ "--disable-dev-shm-usage",
38875
+ "--enable-webgl",
38876
+ "--use-gl=angle",
38877
+ "--use-angle=swiftshader"
38878
+ ]
38879
+ });
38880
+ const page = await chromeBrowser.newPage();
38881
+ await page.setViewport({ width: 1920, height: 1080 });
38882
+ await page.goto(server.url, { waitUntil: "domcontentloaded", timeout: 1e4 });
38883
+ await alignViewportToComposition(page, server.url);
38884
+ await page.waitForFunction(() => !!window.__timelines, {
38885
+ timeout: opts.timeout
38886
+ }).catch(() => {
38887
+ });
38888
+ await page.evaluate(() => {
38889
+ const fonts = document.fonts;
38890
+ if (!fonts?.ready) return Promise.resolve();
38891
+ return Promise.race([
38892
+ fonts.ready.then(() => void 0),
38893
+ new Promise((resolve39) => setTimeout(resolve39, 750))
38894
+ ]);
38895
+ }).catch(() => {
38896
+ });
38897
+ await new Promise((resolveSettle) => setTimeout(resolveSettle, 250));
38898
+ const duration = await getCompositionDuration2(page);
38899
+ const samples = buildLayoutSampleTimes({ duration, samples: opts.samples, at: opts.at });
38900
+ if (samples.length === 0) return { duration, samples, rawIssues: [] };
38901
+ await page.addScriptTag({ content: loadLayoutAuditScript() });
38902
+ const issues = [];
38903
+ for (const time of samples) {
38904
+ await seekTo(page, time);
38905
+ const sampleIssues = await page.evaluate(
38906
+ (auditOptions) => {
38907
+ const win = window;
38908
+ return win.__hyperframesLayoutAudit?.(auditOptions) ?? [];
38909
+ },
38910
+ { time, tolerance: opts.tolerance }
38911
+ );
38912
+ issues.push(...sampleIssues);
38913
+ }
38914
+ return {
38915
+ duration,
38916
+ samples,
38917
+ rawIssues: dedupeLayoutIssues(issues)
38918
+ };
38919
+ } finally {
38920
+ await chromeBrowser?.close().catch(() => {
38921
+ });
38922
+ await server.close();
38923
+ }
38924
+ }
38925
+ function loadLayoutAuditScript() {
38926
+ const candidates = [
38927
+ join42(__dirname2, "layout-audit.browser.js"),
38928
+ join42(__dirname2, "commands", "layout-audit.browser.js")
38929
+ ];
38930
+ for (const candidate of candidates) {
38931
+ if (existsSync41(candidate)) return readFileSync29(candidate, "utf-8");
38932
+ }
38933
+ throw new Error("Missing layout audit browser script");
38934
+ }
38935
+ function parseAt(value) {
38936
+ if (!value) return void 0;
38937
+ const times = String(value).split(",").map((entry) => parseFloat(entry.trim())).filter((time) => Number.isFinite(time) && time >= 0);
38938
+ return times.length > 0 ? times : void 0;
38939
+ }
38940
+ function createInspectCommand(commandName) {
38941
+ return defineCommand({
38942
+ meta: {
38943
+ name: commandName,
38944
+ description: "Inspect rendered composition layout for text and container overflow"
38945
+ },
38946
+ args: {
38947
+ dir: { type: "positional", description: "Project directory", required: false },
38948
+ json: { type: "boolean", description: "Output agent-readable JSON", default: false },
38949
+ samples: {
38950
+ type: "string",
38951
+ description: "Number of midpoint samples across the duration (default: 9)",
38952
+ default: "9"
38953
+ },
38954
+ at: {
38955
+ type: "string",
38956
+ description: "Comma-separated timestamps in seconds (e.g., --at 1.5,4,7.25)"
38957
+ },
38958
+ tolerance: {
38959
+ type: "string",
38960
+ description: "Allowed pixel overflow before reporting an issue (default: 2)",
38961
+ default: "2"
38962
+ },
38963
+ timeout: {
38964
+ type: "string",
38965
+ description: "Ms to wait for runtime to initialize (default: 5000)",
38966
+ default: "5000"
38967
+ },
38968
+ "max-issues": {
38969
+ type: "string",
38970
+ description: "Maximum issues to print or return after static collapse (default: 80)",
38971
+ default: "80"
38972
+ },
38973
+ "collapse-static": {
38974
+ type: "boolean",
38975
+ description: "Collapse repeated static issues across samples (default: true)",
38976
+ default: true
38977
+ },
38978
+ strict: {
38979
+ type: "boolean",
38980
+ description: "Exit non-zero on warnings too",
38981
+ default: false
38982
+ }
38983
+ },
38984
+ async run({ args }) {
38985
+ const project = resolveProject(args.dir);
38986
+ const samples = Math.max(1, parseInt(args.samples, 10) || 9);
38987
+ const tolerance = Math.max(0, parseFloat(args.tolerance) || 2);
38988
+ const timeout = Math.max(500, parseInt(args.timeout, 10) || 5e3);
38989
+ const maxIssues = Math.max(1, parseInt(args["max-issues"], 10) || 80);
38990
+ const at2 = parseAt(args.at);
38991
+ const strict = !!args.strict;
38992
+ const collapseStatic = args["collapse-static"] !== false;
38993
+ if (!args.json) {
38994
+ const sampleLabel = at2 ? `${at2.length} explicit timestamp(s)` : `${samples} timeline samples`;
38995
+ console.log(
38996
+ `${c.accent("\u25C6")} Inspecting layout for ${c.accent(project.name)} (${sampleLabel})`
38997
+ );
38998
+ }
38999
+ try {
39000
+ const result = await runLayoutAudit(project.dir, {
39001
+ samples,
39002
+ at: at2,
39003
+ timeout,
39004
+ tolerance
39005
+ });
39006
+ const allIssues = collapseStatic ? collapseStaticLayoutIssues(result.rawIssues) : result.rawIssues;
39007
+ const limited = limitLayoutIssues(allIssues, maxIssues);
39008
+ const summary = summarizeLayoutIssues(allIssues);
39009
+ const ok = summary.errorCount === 0 && (!strict || summary.warningCount === 0);
39010
+ if (args.json) {
39011
+ console.log(
39012
+ JSON.stringify(
39013
+ withMeta({
39014
+ schemaVersion: INSPECT_SCHEMA_VERSION,
39015
+ duration: result.duration,
39016
+ samples: result.samples,
39017
+ tolerance,
39018
+ strict,
39019
+ collapseStatic,
39020
+ ...summary,
39021
+ totalIssueCount: limited.totalIssueCount,
39022
+ truncated: limited.truncated,
39023
+ ok,
39024
+ issues: limited.issues
39025
+ }),
39026
+ null,
39027
+ 2
39028
+ )
39029
+ );
39030
+ process.exit(ok ? 0 : 1);
39031
+ }
39032
+ if (result.samples.length === 0) {
39033
+ console.log();
39034
+ console.log(
39035
+ `${c.error("\u2717")} Could not determine composition duration \u2014 no layout samples run`
39036
+ );
39037
+ process.exit(1);
39038
+ }
39039
+ console.log();
39040
+ if (limited.issues.length === 0) {
39041
+ console.log(
39042
+ `${c.success("\u25C7")} 0 layout issues across ${result.samples.length} sample(s)`
39043
+ );
39044
+ return;
39045
+ }
39046
+ for (const issue of limited.issues) {
39047
+ const icon = issue.severity === "error" ? c.error("\u2717") : issue.severity === "warning" ? c.warn("\u26A0") : c.dim("\u2139");
39048
+ const formatted = formatLayoutIssue(issue).replace(/\n/g, "\n ");
39049
+ console.log(` ${icon} ${c.dim(formatted)}`);
39050
+ }
39051
+ console.log();
39052
+ const parts = [
39053
+ `${summary.errorCount} error(s)`,
39054
+ `${summary.warningCount} warning(s)`,
39055
+ `${summary.infoCount} info(s)`
39056
+ ];
39057
+ const suffix = limited.truncated ? c.dim(`, truncated at ${maxIssues} issue(s)`) : "";
39058
+ console.log(`${ok ? c.success("\u25C7") : c.error("\u25C7")} ${parts.join(", ")}${suffix}`);
39059
+ process.exit(ok ? 0 : 1);
39060
+ } catch (err) {
39061
+ const message = err instanceof Error ? err.message : String(err);
39062
+ if (args.json) {
39063
+ console.log(
39064
+ JSON.stringify(
39065
+ withMeta({
39066
+ schemaVersion: INSPECT_SCHEMA_VERSION,
39067
+ ok: false,
39068
+ error: message,
39069
+ issues: [],
39070
+ errorCount: 0,
39071
+ warningCount: 0,
39072
+ infoCount: 0,
39073
+ issueCount: 0
39074
+ }),
39075
+ null,
39076
+ 2
39077
+ )
39078
+ );
39079
+ process.exit(1);
39080
+ }
39081
+ console.error(`${c.error("\u2717")} Inspect failed: ${message}`);
39082
+ process.exit(1);
39083
+ }
39084
+ }
39085
+ });
39086
+ }
39087
+ var __filename, __dirname2, SEEK_SETTLE_MS, INSPECT_SCHEMA_VERSION, examples9, layout_default;
39088
+ var init_layout2 = __esm({
39089
+ "src/commands/layout.ts"() {
39090
+ "use strict";
39091
+ init_dist();
39092
+ init_colors();
39093
+ init_project();
39094
+ init_updateCheck();
39095
+ init_layoutAudit();
39096
+ __filename = fileURLToPath6(import.meta.url);
39097
+ __dirname2 = dirname17(__filename);
39098
+ SEEK_SETTLE_MS = 120;
39099
+ INSPECT_SCHEMA_VERSION = 1;
39100
+ examples9 = [
39101
+ ["Inspect visual layout across the current composition", "hyperframes layout"],
39102
+ ["Inspect a specific project", "hyperframes layout ./my-video"],
39103
+ ["Output agent-readable JSON", "hyperframes layout --json"],
39104
+ ["Use explicit hero-frame timestamps", "hyperframes layout --at 1.5,4.0,7.25"]
39105
+ ];
39106
+ layout_default = createInspectCommand("layout");
39107
+ }
39108
+ });
39109
+
39110
+ // src/commands/inspect.ts
39111
+ var inspect_exports = {};
39112
+ __export(inspect_exports, {
39113
+ default: () => inspect_default,
39114
+ examples: () => examples10
39115
+ });
39116
+ var examples10, inspect_default;
39117
+ var init_inspect = __esm({
39118
+ "src/commands/inspect.ts"() {
39119
+ "use strict";
39120
+ init_layout2();
39121
+ examples10 = [
39122
+ ["Inspect visual layout across the current composition", "hyperframes inspect"],
39123
+ ["Inspect a specific project", "hyperframes inspect ./my-video"],
39124
+ ["Output agent-readable JSON", "hyperframes inspect --json"],
39125
+ ["Use explicit hero-frame timestamps", "hyperframes inspect --at 1.5,4.0,7.25"],
39126
+ ["Run the compatibility alias", "hyperframes layout --json"]
39127
+ ];
39128
+ inspect_default = createInspectCommand("inspect");
39129
+ }
39130
+ });
39131
+
38554
39132
  // src/utils/dom.ts
38555
39133
  function ensureDOMParser() {
38556
39134
  if (typeof globalThis.DOMParser === "undefined") {
@@ -38568,14 +39146,14 @@ var init_dom = __esm({
38568
39146
  var info_exports = {};
38569
39147
  __export(info_exports, {
38570
39148
  default: () => info_default,
38571
- examples: () => examples9
39149
+ examples: () => examples11
38572
39150
  });
38573
- import { readFileSync as readFileSync29, readdirSync as readdirSync16, statSync as statSync16 } from "fs";
38574
- import { join as join42 } from "path";
39151
+ import { readFileSync as readFileSync30, readdirSync as readdirSync16, statSync as statSync16 } from "fs";
39152
+ import { join as join43 } from "path";
38575
39153
  function totalSize(dir) {
38576
39154
  let total = 0;
38577
39155
  for (const entry of readdirSync16(dir, { withFileTypes: true })) {
38578
- const path2 = join42(dir, entry.name);
39156
+ const path2 = join43(dir, entry.name);
38579
39157
  if (entry.isDirectory()) {
38580
39158
  total += totalSize(path2);
38581
39159
  } else {
@@ -38584,7 +39162,7 @@ function totalSize(dir) {
38584
39162
  }
38585
39163
  return total;
38586
39164
  }
38587
- var examples9, info_default;
39165
+ var examples11, info_default;
38588
39166
  var init_info = __esm({
38589
39167
  "src/commands/info.ts"() {
38590
39168
  "use strict";
@@ -38595,7 +39173,7 @@ var init_info = __esm({
38595
39173
  init_dom();
38596
39174
  init_project();
38597
39175
  init_updateCheck();
38598
- examples9 = [
39176
+ examples11 = [
38599
39177
  ["Show project metadata", "hyperframes info"],
38600
39178
  ["Output as JSON", "hyperframes info --json"]
38601
39179
  ];
@@ -38607,7 +39185,7 @@ var init_info = __esm({
38607
39185
  },
38608
39186
  async run({ args }) {
38609
39187
  const project = resolveProject(args.dir);
38610
- const html = readFileSync29(project.indexPath, "utf-8");
39188
+ const html = readFileSync30(project.indexPath, "utf-8");
38611
39189
  ensureDOMParser();
38612
39190
  const parsed = parseHtml(html);
38613
39191
  const tracks = new Set(parsed.elements.map((el) => el.zIndex));
@@ -38661,10 +39239,10 @@ var init_info = __esm({
38661
39239
  var compositions_exports = {};
38662
39240
  __export(compositions_exports, {
38663
39241
  default: () => compositions_default,
38664
- examples: () => examples10
39242
+ examples: () => examples12
38665
39243
  });
38666
- import { existsSync as existsSync41, readFileSync as readFileSync30 } from "fs";
38667
- import { resolve as resolve29, dirname as dirname17 } from "path";
39244
+ import { existsSync as existsSync42, readFileSync as readFileSync31 } from "fs";
39245
+ import { resolve as resolve30, dirname as dirname18 } from "path";
38668
39246
  function parseCompositions(html, baseDir) {
38669
39247
  const parser = new DOMParser();
38670
39248
  const doc = parser.parseFromString(html, "text/html");
@@ -38676,9 +39254,9 @@ function parseCompositions(html, baseDir) {
38676
39254
  const height = parseInt(div.getAttribute("data-height") ?? "1080", 10);
38677
39255
  const compositionSrc = div.getAttribute("data-composition-src");
38678
39256
  if (compositionSrc) {
38679
- const subPath = resolve29(baseDir, compositionSrc);
38680
- if (existsSync41(subPath)) {
38681
- const subHtml = readFileSync30(subPath, "utf-8");
39257
+ const subPath = resolve30(baseDir, compositionSrc);
39258
+ if (existsSync42(subPath)) {
39259
+ const subHtml = readFileSync31(subPath, "utf-8");
38682
39260
  const subInfo = parseSubComposition(subHtml, id, width, height);
38683
39261
  compositions.push({ ...subInfo, source: compositionSrc });
38684
39262
  return;
@@ -38751,7 +39329,7 @@ function parseSubComposition(html, fallbackId, fallbackWidth, fallbackHeight) {
38751
39329
  }
38752
39330
  return { id, duration, width, height, elementCount };
38753
39331
  }
38754
- var examples10, compositions_default;
39332
+ var examples12, compositions_default;
38755
39333
  var init_compositions = __esm({
38756
39334
  "src/commands/compositions.ts"() {
38757
39335
  "use strict";
@@ -38760,7 +39338,7 @@ var init_compositions = __esm({
38760
39338
  init_dom();
38761
39339
  init_project();
38762
39340
  init_updateCheck();
38763
- examples10 = [
39341
+ examples12 = [
38764
39342
  ["List compositions in the current project", "hyperframes compositions"],
38765
39343
  ["Output as JSON", "hyperframes compositions --json"]
38766
39344
  ];
@@ -38772,9 +39350,9 @@ var init_compositions = __esm({
38772
39350
  },
38773
39351
  async run({ args }) {
38774
39352
  const project = resolveProject(args.dir);
38775
- const html = readFileSync30(project.indexPath, "utf-8");
39353
+ const html = readFileSync31(project.indexPath, "utf-8");
38776
39354
  ensureDOMParser();
38777
- const compositions = parseCompositions(html, dirname17(project.indexPath));
39355
+ const compositions = parseCompositions(html, dirname18(project.indexPath));
38778
39356
  if (compositions.length === 0) {
38779
39357
  console.log(`${c.success("\u25C7")} ${c.accent(project.name)} \u2014 no compositions found`);
38780
39358
  return;
@@ -38808,11 +39386,11 @@ var init_compositions = __esm({
38808
39386
  var benchmark_exports = {};
38809
39387
  __export(benchmark_exports, {
38810
39388
  default: () => benchmark_default,
38811
- examples: () => examples11
39389
+ examples: () => examples13
38812
39390
  });
38813
- import { existsSync as existsSync42, statSync as statSync17 } from "fs";
38814
- import { resolve as resolve30, join as join43 } from "path";
38815
- var examples11, DEFAULT_CONFIGS, benchmark_default;
39391
+ import { existsSync as existsSync43, statSync as statSync17 } from "fs";
39392
+ import { resolve as resolve31, join as join44 } from "path";
39393
+ var examples13, DEFAULT_CONFIGS, benchmark_default;
38816
39394
  var init_benchmark = __esm({
38817
39395
  "src/commands/benchmark.ts"() {
38818
39396
  "use strict";
@@ -38823,7 +39401,7 @@ var init_benchmark = __esm({
38823
39401
  init_format();
38824
39402
  init_dist3();
38825
39403
  init_updateCheck();
38826
- examples11 = [
39404
+ examples13 = [
38827
39405
  ["Run benchmarks with default settings (3 runs)", "hyperframes benchmark"],
38828
39406
  ["Run 5 iterations per config", "hyperframes benchmark --runs 5"],
38829
39407
  ["Output results as JSON", "hyperframes benchmark --json"]
@@ -38853,7 +39431,7 @@ var init_benchmark = __esm({
38853
39431
  process.exit(1);
38854
39432
  }
38855
39433
  const jsonOutput = args.json ?? false;
38856
- const benchDir = resolve30("renders", ".benchmark");
39434
+ const benchDir = resolve31("renders", ".benchmark");
38857
39435
  let producer = null;
38858
39436
  try {
38859
39437
  producer = await loadProducer();
@@ -38886,7 +39464,7 @@ var init_benchmark = __esm({
38886
39464
  s2?.start(`Benchmarking ${config.label}...`);
38887
39465
  for (let i2 = 0; i2 < runsPerConfig; i2++) {
38888
39466
  s2?.message(`${config.label} \u2014 run ${i2 + 1}/${runsPerConfig}`);
38889
- const outputPath = join43(
39467
+ const outputPath = join44(
38890
39468
  benchDir,
38891
39469
  `${config.label.replace(/[^a-zA-Z0-9]/g, "_")}_run${i2}.mp4`
38892
39470
  );
@@ -38900,7 +39478,7 @@ var init_benchmark = __esm({
38900
39478
  await producer.executeRenderJob(job, project.dir, outputPath);
38901
39479
  const elapsedMs = Date.now() - startTime;
38902
39480
  let fileSize = null;
38903
- if (existsSync42(outputPath)) {
39481
+ if (existsSync43(outputPath)) {
38904
39482
  const stat3 = statSync17(outputPath);
38905
39483
  fileSize = stat3.size;
38906
39484
  }
@@ -38983,7 +39561,7 @@ var init_benchmark = __esm({
38983
39561
  var browser_exports = {};
38984
39562
  __export(browser_exports, {
38985
39563
  default: () => browser_default,
38986
- examples: () => examples12
39564
+ examples: () => examples14
38987
39565
  });
38988
39566
  async function runEnsure() {
38989
39567
  Wt2(c.bold("hyperframes browser ensure"));
@@ -39046,7 +39624,7 @@ function runClear() {
39046
39624
  Gt(c.dim("No cached browser to remove."));
39047
39625
  }
39048
39626
  }
39049
- var examples12, browser_default;
39627
+ var examples14, browser_default;
39050
39628
  var init_browser = __esm({
39051
39629
  "src/commands/browser.ts"() {
39052
39630
  "use strict";
@@ -39056,7 +39634,7 @@ var init_browser = __esm({
39056
39634
  init_format();
39057
39635
  init_manager2();
39058
39636
  init_events();
39059
- examples12 = [
39637
+ examples14 = [
39060
39638
  ["Find or download Chrome for rendering", "hyperframes browser ensure"],
39061
39639
  ["Print the Chrome executable path", "hyperframes browser path"],
39062
39640
  ["Remove cached Chrome download", "hyperframes browser clear"]
@@ -39114,10 +39692,10 @@ Run ${c.accent("hyperframes browser --help")} for usage.`
39114
39692
  var transcribe_exports2 = {};
39115
39693
  __export(transcribe_exports2, {
39116
39694
  default: () => transcribe_default,
39117
- examples: () => examples13
39695
+ examples: () => examples15
39118
39696
  });
39119
- import { existsSync as existsSync43, writeFileSync as writeFileSync17 } from "fs";
39120
- import { resolve as resolve31, join as join44, extname as extname8 } from "path";
39697
+ import { existsSync as existsSync44, writeFileSync as writeFileSync17 } from "fs";
39698
+ import { resolve as resolve32, join as join45, extname as extname8 } from "path";
39121
39699
  async function importTranscript(inputPath, dir, json) {
39122
39700
  const { loadTranscript: loadTranscript2, patchCaptionHtml: patchCaptionHtml2 } = await Promise.resolve().then(() => (init_normalize(), normalize_exports));
39123
39701
  const { words, format } = loadTranscript2(inputPath);
@@ -39125,7 +39703,7 @@ async function importTranscript(inputPath, dir, json) {
39125
39703
  console.error(c.error("No words found in transcript."));
39126
39704
  process.exit(1);
39127
39705
  }
39128
- const outPath = join44(dir, "transcript.json");
39706
+ const outPath = join45(dir, "transcript.json");
39129
39707
  writeFileSync17(outPath, JSON.stringify(words, null, 2));
39130
39708
  patchCaptionHtml2(dir, words);
39131
39709
  if (json) {
@@ -39192,7 +39770,7 @@ async function transcribeAudio(inputPath, dir, opts) {
39192
39770
  process.exit(1);
39193
39771
  }
39194
39772
  }
39195
- var examples13, transcribe_default;
39773
+ var examples15, transcribe_default;
39196
39774
  var init_transcribe2 = __esm({
39197
39775
  "src/commands/transcribe.ts"() {
39198
39776
  "use strict";
@@ -39200,7 +39778,7 @@ var init_transcribe2 = __esm({
39200
39778
  init_dist3();
39201
39779
  init_colors();
39202
39780
  init_manager();
39203
- examples13 = [
39781
+ examples15 = [
39204
39782
  ["Transcribe an audio file", "hyperframes transcribe audio.mp3"],
39205
39783
  ["Transcribe a video file", "hyperframes transcribe video.mp4"],
39206
39784
  ["Use a larger model for better accuracy", "hyperframes transcribe audio.mp3 --model medium.en"],
@@ -39241,12 +39819,12 @@ var init_transcribe2 = __esm({
39241
39819
  }
39242
39820
  },
39243
39821
  async run({ args }) {
39244
- const inputPath = resolve31(args.input);
39245
- if (!existsSync43(inputPath)) {
39822
+ const inputPath = resolve32(args.input);
39823
+ if (!existsSync44(inputPath)) {
39246
39824
  console.error(c.error(`File not found: ${args.input}`));
39247
39825
  process.exit(1);
39248
39826
  }
39249
- const dir = resolve31(args.dir ?? ".");
39827
+ const dir = resolve32(args.dir ?? ".");
39250
39828
  const ext = extname8(inputPath).toLowerCase();
39251
39829
  const isImport = ext === ".json" || ext === ".srt" || ext === ".vtt";
39252
39830
  if (isImport) {
@@ -39263,9 +39841,9 @@ var init_transcribe2 = __esm({
39263
39841
  });
39264
39842
 
39265
39843
  // src/tts/manager.ts
39266
- import { existsSync as existsSync44, mkdirSync as mkdirSync23 } from "fs";
39844
+ import { existsSync as existsSync45, mkdirSync as mkdirSync23 } from "fs";
39267
39845
  import { homedir as homedir8 } from "os";
39268
- import { join as join45 } from "path";
39846
+ import { join as join46 } from "path";
39269
39847
  function inferLangFromVoiceId(voiceId) {
39270
39848
  const first = voiceId.charAt(0).toLowerCase();
39271
39849
  return VOICE_PREFIX_LANG[first] ?? "en-us";
@@ -39274,8 +39852,8 @@ function isSupportedLang(value) {
39274
39852
  return SUPPORTED_LANGS.includes(value);
39275
39853
  }
39276
39854
  async function ensureModel2(model = DEFAULT_MODEL2, options) {
39277
- const modelPath = join45(MODELS_DIR2, `${model}.onnx`);
39278
- if (existsSync44(modelPath)) return modelPath;
39855
+ const modelPath = join46(MODELS_DIR2, `${model}.onnx`);
39856
+ if (existsSync45(modelPath)) return modelPath;
39279
39857
  const url = MODEL_URLS[model];
39280
39858
  if (!url) {
39281
39859
  throw new Error(
@@ -39285,18 +39863,18 @@ async function ensureModel2(model = DEFAULT_MODEL2, options) {
39285
39863
  mkdirSync23(MODELS_DIR2, { recursive: true });
39286
39864
  options?.onProgress?.(`Downloading TTS model ${model} (~311 MB)...`);
39287
39865
  await downloadFile(url, modelPath);
39288
- if (!existsSync44(modelPath)) {
39866
+ if (!existsSync45(modelPath)) {
39289
39867
  throw new Error(`Model download failed: ${model}`);
39290
39868
  }
39291
39869
  return modelPath;
39292
39870
  }
39293
39871
  async function ensureVoices(options) {
39294
- const voicesPath = join45(VOICES_DIR, "voices-v1.0.bin");
39295
- if (existsSync44(voicesPath)) return voicesPath;
39872
+ const voicesPath = join46(VOICES_DIR, "voices-v1.0.bin");
39873
+ if (existsSync45(voicesPath)) return voicesPath;
39296
39874
  mkdirSync23(VOICES_DIR, { recursive: true });
39297
39875
  options?.onProgress?.("Downloading voice data (~27 MB)...");
39298
39876
  await downloadFile(VOICES_URL, voicesPath);
39299
- if (!existsSync44(voicesPath)) {
39877
+ if (!existsSync45(voicesPath)) {
39300
39878
  throw new Error("Voice data download failed");
39301
39879
  }
39302
39880
  return voicesPath;
@@ -39306,9 +39884,9 @@ var init_manager3 = __esm({
39306
39884
  "src/tts/manager.ts"() {
39307
39885
  "use strict";
39308
39886
  init_download();
39309
- CACHE_DIR3 = join45(homedir8(), ".cache", "hyperframes", "tts");
39310
- MODELS_DIR2 = join45(CACHE_DIR3, "models");
39311
- VOICES_DIR = join45(CACHE_DIR3, "voices");
39887
+ CACHE_DIR3 = join46(homedir8(), ".cache", "hyperframes", "tts");
39888
+ MODELS_DIR2 = join46(CACHE_DIR3, "models");
39889
+ VOICES_DIR = join46(CACHE_DIR3, "voices");
39312
39890
  DEFAULT_MODEL2 = "kokoro-v1.0";
39313
39891
  MODEL_URLS = {
39314
39892
  "kokoro-v1.0": "https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0/kokoro-v1.0.onnx"
@@ -39369,8 +39947,8 @@ __export(synthesize_exports, {
39369
39947
  synthesize: () => synthesize
39370
39948
  });
39371
39949
  import { execFileSync as execFileSync6 } from "child_process";
39372
- import { existsSync as existsSync45, writeFileSync as writeFileSync18, mkdirSync as mkdirSync24, readdirSync as readdirSync17, unlinkSync as unlinkSync6 } from "fs";
39373
- import { join as join46, dirname as dirname18, basename as basename10 } from "path";
39950
+ import { existsSync as existsSync46, writeFileSync as writeFileSync18, mkdirSync as mkdirSync24, readdirSync as readdirSync17, unlinkSync as unlinkSync6 } from "fs";
39951
+ import { join as join47, dirname as dirname19, basename as basename10 } from "path";
39374
39952
  import { homedir as homedir9 } from "os";
39375
39953
  function findPython() {
39376
39954
  for (const name of ["python3", "python"]) {
@@ -39406,7 +39984,7 @@ function hasPythonPackage(python, pkg) {
39406
39984
  }
39407
39985
  }
39408
39986
  function ensureSynthScript() {
39409
- if (!existsSync45(SCRIPT_PATH)) {
39987
+ if (!existsSync46(SCRIPT_PATH)) {
39410
39988
  mkdirSync24(SCRIPT_DIR, { recursive: true });
39411
39989
  writeFileSync18(SCRIPT_PATH, SYNTH_SCRIPT);
39412
39990
  const currentName = basename10(SCRIPT_PATH);
@@ -39414,7 +39992,7 @@ function ensureSynthScript() {
39414
39992
  for (const entry of readdirSync17(SCRIPT_DIR)) {
39415
39993
  if (entry !== currentName && /^synth(-v\d+)?\.py$/.test(entry)) {
39416
39994
  try {
39417
- unlinkSync6(join46(SCRIPT_DIR, entry));
39995
+ unlinkSync6(join47(SCRIPT_DIR, entry));
39418
39996
  } catch {
39419
39997
  }
39420
39998
  }
@@ -39448,7 +40026,7 @@ async function synthesize(text, outputPath, options) {
39448
40026
  ensureVoices({ onProgress: options?.onProgress })
39449
40027
  ]);
39450
40028
  const scriptPath = ensureSynthScript();
39451
- mkdirSync24(dirname18(outputPath), { recursive: true });
40029
+ mkdirSync24(dirname19(outputPath), { recursive: true });
39452
40030
  options?.onProgress?.(`Generating speech with voice ${voice} (${lang})...`);
39453
40031
  try {
39454
40032
  const stdout2 = execFileSync6(
@@ -39460,7 +40038,7 @@ async function synthesize(text, outputPath, options) {
39460
40038
  stdio: ["pipe", "pipe", "pipe"]
39461
40039
  }
39462
40040
  );
39463
- if (!existsSync45(outputPath)) {
40041
+ if (!existsSync46(outputPath)) {
39464
40042
  throw new Error("Synthesis completed but no output file was created");
39465
40043
  }
39466
40044
  const lines = stdout2.trim().split("\n");
@@ -39473,7 +40051,7 @@ async function synthesize(text, outputPath, options) {
39473
40051
  langApplied: result.langApplied
39474
40052
  };
39475
40053
  } catch (err) {
39476
- if (err instanceof SyntaxError && existsSync45(outputPath)) {
40054
+ if (err instanceof SyntaxError && existsSync46(outputPath)) {
39477
40055
  throw new Error(
39478
40056
  "Speech was generated but metadata could not be read. Check the output file manually."
39479
40057
  );
@@ -39524,8 +40102,8 @@ print(json.dumps({
39524
40102
  "langApplied": bool(lang and supports_lang),
39525
40103
  }))
39526
40104
  `;
39527
- SCRIPT_DIR = join46(homedir9(), ".cache", "hyperframes", "tts");
39528
- SCRIPT_PATH = join46(SCRIPT_DIR, "synth-v2.py");
40105
+ SCRIPT_DIR = join47(homedir9(), ".cache", "hyperframes", "tts");
40106
+ SCRIPT_PATH = join47(SCRIPT_DIR, "synth-v2.py");
39529
40107
  }
39530
40108
  });
39531
40109
 
@@ -39533,10 +40111,10 @@ print(json.dumps({
39533
40111
  var tts_exports = {};
39534
40112
  __export(tts_exports, {
39535
40113
  default: () => tts_default,
39536
- examples: () => examples14
40114
+ examples: () => examples16
39537
40115
  });
39538
- import { existsSync as existsSync46, readFileSync as readFileSync31 } from "fs";
39539
- import { resolve as resolve32, extname as extname9 } from "path";
40116
+ import { existsSync as existsSync47, readFileSync as readFileSync32 } from "fs";
40117
+ import { resolve as resolve33, extname as extname9 } from "path";
39540
40118
  function listVoices(json) {
39541
40119
  const rows = BUNDLED_VOICES.map((v) => ({ ...v, defaultLang: inferLangFromVoiceId(v.id) }));
39542
40120
  if (json) {
@@ -39566,7 +40144,7 @@ ${c.bold("Available voices")} (Kokoro-82M)
39566
40144
  `
39567
40145
  );
39568
40146
  }
39569
- var examples14, voiceList, langList, tts_default;
40147
+ var examples16, voiceList, langList, tts_default;
39570
40148
  var init_tts = __esm({
39571
40149
  "src/commands/tts.ts"() {
39572
40150
  "use strict";
@@ -39575,7 +40153,7 @@ var init_tts = __esm({
39575
40153
  init_colors();
39576
40154
  init_format();
39577
40155
  init_manager3();
39578
- examples14 = [
40156
+ examples16 = [
39579
40157
  ["Generate speech from text", 'hyperframes tts "Welcome to HyperFrames"'],
39580
40158
  ["Choose a voice", 'hyperframes tts "Hello world" --voice am_adam'],
39581
40159
  ["Save to a specific file", 'hyperframes tts "Intro" --voice bf_emma --output narration.wav'],
@@ -39644,9 +40222,9 @@ var init_tts = __esm({
39644
40222
  process.exit(1);
39645
40223
  }
39646
40224
  let text;
39647
- const maybeFile = resolve32(args.input);
39648
- if (existsSync46(maybeFile) && extname9(maybeFile).toLowerCase() === ".txt") {
39649
- text = readFileSync31(maybeFile, "utf-8").trim();
40225
+ const maybeFile = resolve33(args.input);
40226
+ if (existsSync47(maybeFile) && extname9(maybeFile).toLowerCase() === ".txt") {
40227
+ text = readFileSync32(maybeFile, "utf-8").trim();
39650
40228
  if (!text) {
39651
40229
  console.error(c.error("File is empty."));
39652
40230
  process.exit(1);
@@ -39658,7 +40236,7 @@ var init_tts = __esm({
39658
40236
  console.error(c.error("No text provided."));
39659
40237
  process.exit(1);
39660
40238
  }
39661
- const output = resolve32(args.output ?? "speech.wav");
40239
+ const output = resolve33(args.output ?? "speech.wav");
39662
40240
  const voice = args.voice ?? DEFAULT_VOICE;
39663
40241
  const speed = args.speed ? parseFloat(args.speed) : 1;
39664
40242
  if (isNaN(speed) || speed <= 0 || speed > 3) {
@@ -39736,17 +40314,17 @@ var init_tts = __esm({
39736
40314
  var docs_exports = {};
39737
40315
  __export(docs_exports, {
39738
40316
  default: () => docs_default,
39739
- examples: () => examples15
40317
+ examples: () => examples17
39740
40318
  });
39741
- import { readFileSync as readFileSync32, existsSync as existsSync47 } from "fs";
39742
- import { resolve as resolve33, dirname as dirname19, join as join47 } from "path";
39743
- import { fileURLToPath as fileURLToPath6 } from "url";
40319
+ import { readFileSync as readFileSync33, existsSync as existsSync48 } from "fs";
40320
+ import { resolve as resolve34, dirname as dirname20, join as join48 } from "path";
40321
+ import { fileURLToPath as fileURLToPath7 } from "url";
39744
40322
  function docsDir() {
39745
- const thisFile = fileURLToPath6(import.meta.url);
39746
- const dir = dirname19(thisFile);
39747
- const devPath = resolve33(dir, "..", "docs");
39748
- const builtPath = resolve33(dir, "docs");
39749
- return existsSync47(devPath) ? devPath : builtPath;
40323
+ const thisFile = fileURLToPath7(import.meta.url);
40324
+ const dir = dirname20(thisFile);
40325
+ const devPath = resolve34(dir, "..", "docs");
40326
+ const builtPath = resolve34(dir, "docs");
40327
+ return existsSync48(devPath) ? devPath : builtPath;
39750
40328
  }
39751
40329
  function formatInlineCode(line) {
39752
40330
  return line.replace(/`([^`]+)`/g, (_match, code) => c.accent(code));
@@ -39773,13 +40351,13 @@ function renderMarkdown(content) {
39773
40351
  console.log(formatInlineCode(line));
39774
40352
  }
39775
40353
  }
39776
- var examples15, TOPICS, TOPIC_NAMES, docs_default;
40354
+ var examples17, TOPICS, TOPIC_NAMES, docs_default;
39777
40355
  var init_docs = __esm({
39778
40356
  "src/commands/docs.ts"() {
39779
40357
  "use strict";
39780
40358
  init_dist();
39781
40359
  init_colors();
39782
- examples15 = [
40360
+ examples17 = [
39783
40361
  ["List all available topics", "hyperframes docs"],
39784
40362
  ["Read about data attributes", "hyperframes docs data-attributes"],
39785
40363
  ["Read about rendering", "hyperframes docs rendering"],
@@ -39843,12 +40421,12 @@ var init_docs = __esm({
39843
40421
  }
39844
40422
  process.exit(1);
39845
40423
  }
39846
- const filePath = join47(docsDir(), entry.file);
39847
- if (!existsSync47(filePath)) {
40424
+ const filePath = join48(docsDir(), entry.file);
40425
+ if (!existsSync48(filePath)) {
39848
40426
  console.error(c.error(`Doc file not found: ${filePath}`));
39849
40427
  process.exit(1);
39850
40428
  }
39851
- const content = readFileSync32(filePath, "utf-8");
40429
+ const content = readFileSync33(filePath, "utf-8");
39852
40430
  console.log();
39853
40431
  renderMarkdown(content);
39854
40432
  }
@@ -39860,7 +40438,7 @@ var init_docs = __esm({
39860
40438
  var doctor_exports = {};
39861
40439
  __export(doctor_exports, {
39862
40440
  default: () => doctor_default,
39863
- examples: () => examples16
40441
+ examples: () => examples18
39864
40442
  });
39865
40443
  import { execSync as execSync3 } from "child_process";
39866
40444
  import { freemem as freemem4, platform as platform4 } from "os";
@@ -40002,7 +40580,7 @@ function checkEnvironment() {
40002
40580
  }
40003
40581
  return { ok: true, detail: parts.join(" \xB7 ") };
40004
40582
  }
40005
- var examples16, doctor_default;
40583
+ var examples18, doctor_default;
40006
40584
  var init_doctor = __esm({
40007
40585
  "src/commands/doctor.ts"() {
40008
40586
  "use strict";
@@ -40013,7 +40591,7 @@ var init_doctor = __esm({
40013
40591
  init_version();
40014
40592
  init_updateCheck();
40015
40593
  init_system();
40016
- examples16 = [["Check system dependencies", "hyperframes doctor"]];
40594
+ examples18 = [["Check system dependencies", "hyperframes doctor"]];
40017
40595
  doctor_default = defineCommand({
40018
40596
  meta: { name: "doctor", description: "Check system dependencies and environment" },
40019
40597
  args: {},
@@ -40068,10 +40646,10 @@ var init_doctor = __esm({
40068
40646
  var upgrade_exports = {};
40069
40647
  __export(upgrade_exports, {
40070
40648
  default: () => upgrade_default,
40071
- examples: () => examples17
40649
+ examples: () => examples19
40072
40650
  });
40073
40651
  import { execSync as execSync4 } from "child_process";
40074
- var examples17, upgrade_default;
40652
+ var examples19, upgrade_default;
40075
40653
  var init_upgrade = __esm({
40076
40654
  "src/commands/upgrade.ts"() {
40077
40655
  "use strict";
@@ -40080,7 +40658,7 @@ var init_upgrade = __esm({
40080
40658
  init_colors();
40081
40659
  init_version();
40082
40660
  init_updateCheck();
40083
- examples17 = [
40661
+ examples19 = [
40084
40662
  ["Check for updates interactively", "hyperframes upgrade"],
40085
40663
  ["Check for updates without prompting", "hyperframes upgrade --check"],
40086
40664
  ["Upgrade non-interactively", "hyperframes upgrade --yes"]
@@ -40158,7 +40736,7 @@ var init_upgrade = __esm({
40158
40736
  var telemetry_exports = {};
40159
40737
  __export(telemetry_exports, {
40160
40738
  default: () => telemetry_default,
40161
- examples: () => examples18
40739
+ examples: () => examples20
40162
40740
  });
40163
40741
  function runEnable() {
40164
40742
  const config = readConfig();
@@ -40188,14 +40766,14 @@ function runStatus() {
40188
40766
  console.log(` ${c.dim("Env var:")} ${c.accent("HYPERFRAMES_NO_TELEMETRY=1")}`);
40189
40767
  console.log();
40190
40768
  }
40191
- var examples18, telemetry_default;
40769
+ var examples20, telemetry_default;
40192
40770
  var init_telemetry = __esm({
40193
40771
  "src/commands/telemetry.ts"() {
40194
40772
  "use strict";
40195
40773
  init_dist();
40196
40774
  init_colors();
40197
40775
  init_config();
40198
- examples18 = [
40776
+ examples20 = [
40199
40777
  ["Check current telemetry status", "hyperframes telemetry status"],
40200
40778
  ["Disable telemetry", "hyperframes telemetry disable"],
40201
40779
  ["Enable telemetry", "hyperframes telemetry enable"]
@@ -40274,17 +40852,17 @@ var validate_exports = {};
40274
40852
  __export(validate_exports, {
40275
40853
  default: () => validate_default
40276
40854
  });
40277
- import { existsSync as existsSync48, readFileSync as readFileSync33 } from "fs";
40278
- import { resolve as resolve34, join as join48, dirname as dirname20 } from "path";
40279
- import { fileURLToPath as fileURLToPath7 } from "url";
40280
- async function getCompositionDuration2(page) {
40855
+ import { existsSync as existsSync49, readFileSync as readFileSync34 } from "fs";
40856
+ import { resolve as resolve35, join as join49, dirname as dirname21 } from "path";
40857
+ import { fileURLToPath as fileURLToPath8 } from "url";
40858
+ async function getCompositionDuration3(page) {
40281
40859
  return page.evaluate(() => {
40282
40860
  if (window.__hf?.duration && window.__hf.duration > 0) return window.__hf.duration;
40283
40861
  const root = document.querySelector("[data-composition-id][data-duration]");
40284
40862
  return root ? parseFloat(root.getAttribute("data-duration") ?? "0") : 0;
40285
40863
  });
40286
40864
  }
40287
- async function seekTo(page, time) {
40865
+ async function seekTo2(page, time) {
40288
40866
  await page.evaluate((t3) => {
40289
40867
  if (window.__hf && typeof window.__hf.seek === "function") {
40290
40868
  window.__hf.seek(t3);
@@ -40297,16 +40875,16 @@ async function seekTo(page, time) {
40297
40875
  }
40298
40876
  }
40299
40877
  }, time);
40300
- await new Promise((r2) => setTimeout(r2, SEEK_SETTLE_MS));
40878
+ await new Promise((r2) => setTimeout(r2, SEEK_SETTLE_MS2));
40301
40879
  }
40302
40880
  async function runContrastAudit(page) {
40303
- const duration = await getCompositionDuration2(page);
40881
+ const duration = await getCompositionDuration3(page);
40304
40882
  if (duration <= 0) return [];
40305
40883
  await page.addScriptTag({ content: contrast_audit_browser_default });
40306
40884
  const results = [];
40307
40885
  for (let i2 = 0; i2 < CONTRAST_SAMPLES; i2++) {
40308
40886
  const t3 = +((i2 + 0.5) / CONTRAST_SAMPLES * duration).toFixed(3);
40309
- await seekTo(page, t3);
40887
+ await seekTo2(page, t3);
40310
40888
  const screenshot = await page.screenshot({ encoding: "base64", type: "png" });
40311
40889
  const entries2 = await page.evaluate(
40312
40890
  (b64, time) => typeof window.__contrastAudit === "function" ? window.__contrastAudit(b64, time) : [],
@@ -40321,8 +40899,8 @@ async function validateInBrowser(projectDir, opts) {
40321
40899
  const { bundleToSingleHtml: bundleToSingleHtml2 } = await Promise.resolve().then(() => (init_compiler(), compiler_exports));
40322
40900
  const { ensureBrowser: ensureBrowser2 } = await Promise.resolve().then(() => (init_manager2(), manager_exports2));
40323
40901
  let html = await bundleToSingleHtml2(projectDir);
40324
- const runtimePath = resolve34(
40325
- __dirname2,
40902
+ const runtimePath = resolve35(
40903
+ __dirname3,
40326
40904
  "..",
40327
40905
  "..",
40328
40906
  "..",
@@ -40330,26 +40908,26 @@ async function validateInBrowser(projectDir, opts) {
40330
40908
  "dist",
40331
40909
  "hyperframe.runtime.iife.js"
40332
40910
  );
40333
- if (existsSync48(runtimePath)) {
40334
- const runtimeSource = readFileSync33(runtimePath, "utf-8");
40911
+ if (existsSync49(runtimePath)) {
40912
+ const runtimeSource = readFileSync34(runtimePath, "utf-8");
40335
40913
  html = html.replace(
40336
40914
  /<script[^>]*data-hyperframes-preview-runtime[^>]*src="[^"]*"[^>]*><\/script>/,
40337
40915
  () => `<script data-hyperframes-preview-runtime="1">${runtimeSource}</script>`
40338
40916
  );
40339
40917
  }
40340
- const { createServer } = await import("http");
40918
+ const { createServer: createServer2 } = await import("http");
40341
40919
  const { getMimeType: getMimeType2 } = await Promise.resolve().then(() => (init_studio_api(), studio_api_exports));
40342
- const server = createServer((req, res) => {
40920
+ const server = createServer2((req, res) => {
40343
40921
  const url = req.url ?? "/";
40344
40922
  if (url === "/" || url === "/index.html") {
40345
40923
  res.writeHead(200, { "Content-Type": "text/html" });
40346
40924
  res.end(html);
40347
40925
  return;
40348
40926
  }
40349
- const filePath = join48(projectDir, decodeURIComponent(url));
40350
- if (existsSync48(filePath)) {
40927
+ const filePath = join49(projectDir, decodeURIComponent(url));
40928
+ if (existsSync49(filePath)) {
40351
40929
  res.writeHead(200, { "Content-Type": getMimeType2(filePath) });
40352
- res.end(readFileSync33(filePath));
40930
+ res.end(readFileSync34(filePath));
40353
40931
  return;
40354
40932
  }
40355
40933
  res.writeHead(404);
@@ -40427,7 +41005,7 @@ function printContrastFailures(failures) {
40427
41005
  );
40428
41006
  }
40429
41007
  }
40430
- var __filename, __dirname2, CONTRAST_SAMPLES, SEEK_SETTLE_MS, validate_default;
41008
+ var __filename2, __dirname3, CONTRAST_SAMPLES, SEEK_SETTLE_MS2, validate_default;
40431
41009
  var init_validate = __esm({
40432
41010
  "src/commands/validate.ts"() {
40433
41011
  "use strict";
@@ -40436,10 +41014,10 @@ var init_validate = __esm({
40436
41014
  init_colors();
40437
41015
  init_updateCheck();
40438
41016
  init_contrast_audit_browser();
40439
- __filename = fileURLToPath7(import.meta.url);
40440
- __dirname2 = dirname20(__filename);
41017
+ __filename2 = fileURLToPath8(import.meta.url);
41018
+ __dirname3 = dirname21(__filename2);
40441
41019
  CONTRAST_SAMPLES = 5;
40442
- SEEK_SETTLE_MS = 150;
41020
+ SEEK_SETTLE_MS2 = 150;
40443
41021
  validate_default = defineCommand({
40444
41022
  meta: {
40445
41023
  name: "validate",
@@ -40537,16 +41115,16 @@ Examples:
40537
41115
  var snapshot_exports = {};
40538
41116
  __export(snapshot_exports, {
40539
41117
  default: () => snapshot_default,
40540
- examples: () => examples19
41118
+ examples: () => examples21
40541
41119
  });
40542
41120
  import { spawn as spawn11 } from "child_process";
40543
- import { existsSync as existsSync49, mkdtempSync as mkdtempSync3, readFileSync as readFileSync34, mkdirSync as mkdirSync25, rmSync as rmSync10 } from "fs";
41121
+ import { existsSync as existsSync50, mkdtempSync as mkdtempSync3, readFileSync as readFileSync35, mkdirSync as mkdirSync25, rmSync as rmSync10 } from "fs";
40544
41122
  import { tmpdir as tmpdir5 } from "os";
40545
- import { resolve as resolve35, join as join49, dirname as dirname21, relative as relative5, isAbsolute as isAbsolute6 } from "path";
40546
- import { fileURLToPath as fileURLToPath8 } from "url";
41123
+ import { resolve as resolve36, join as join50, dirname as dirname22, relative as relative6, isAbsolute as isAbsolute7 } from "path";
41124
+ import { fileURLToPath as fileURLToPath9 } from "url";
40547
41125
  async function extractVideoFrameToBuffer(videoPath, timeSeconds, useVp9AlphaDecoder = false) {
40548
- const tmp = mkdtempSync3(join49(tmpdir5(), "hf-snapshot-frame-"));
40549
- const outPath = join49(tmp, "frame.png");
41126
+ const tmp = mkdtempSync3(join50(tmpdir5(), "hf-snapshot-frame-"));
41127
+ const outPath = join50(tmp, "frame.png");
40550
41128
  try {
40551
41129
  const result = await new Promise(
40552
41130
  (resolvePromise) => {
@@ -40586,8 +41164,8 @@ async function extractVideoFrameToBuffer(videoPath, timeSeconds, useVp9AlphaDeco
40586
41164
  });
40587
41165
  }
40588
41166
  );
40589
- if (result.code !== 0 || result.timedOut || !existsSync49(outPath)) return null;
40590
- return readFileSync34(outPath);
41167
+ if (result.code !== 0 || result.timedOut || !existsSync50(outPath)) return null;
41168
+ return readFileSync35(outPath);
40591
41169
  } finally {
40592
41170
  try {
40593
41171
  rmSync10(tmp, { recursive: true, force: true });
@@ -40600,8 +41178,8 @@ async function captureSnapshots(projectDir, opts) {
40600
41178
  const { ensureBrowser: ensureBrowser2 } = await Promise.resolve().then(() => (init_manager2(), manager_exports2));
40601
41179
  const numFrames = opts.frames ?? 5;
40602
41180
  let html = await bundleToSingleHtml2(projectDir);
40603
- const runtimePath = resolve35(
40604
- __dirname3,
41181
+ const runtimePath = resolve36(
41182
+ __dirname4,
40605
41183
  "..",
40606
41184
  "..",
40607
41185
  "..",
@@ -40609,32 +41187,32 @@ async function captureSnapshots(projectDir, opts) {
40609
41187
  "dist",
40610
41188
  "hyperframe.runtime.iife.js"
40611
41189
  );
40612
- if (existsSync49(runtimePath)) {
40613
- const runtimeSource = readFileSync34(runtimePath, "utf-8");
41190
+ if (existsSync50(runtimePath)) {
41191
+ const runtimeSource = readFileSync35(runtimePath, "utf-8");
40614
41192
  html = html.replace(
40615
41193
  /<script[^>]*data-hyperframes-preview-runtime[^>]*src="[^"]*"[^>]*><\/script>/,
40616
41194
  () => `<script data-hyperframes-preview-runtime="1">${runtimeSource}</script>`
40617
41195
  );
40618
41196
  }
40619
- const { createServer } = await import("http");
41197
+ const { createServer: createServer2 } = await import("http");
40620
41198
  const { getMimeType: getMimeType2 } = await Promise.resolve().then(() => (init_studio_api(), studio_api_exports));
40621
- const server = createServer((req, res) => {
41199
+ const server = createServer2((req, res) => {
40622
41200
  const url = req.url ?? "/";
40623
41201
  if (url === "/" || url === "/index.html") {
40624
41202
  res.writeHead(200, { "Content-Type": "text/html" });
40625
41203
  res.end(html);
40626
41204
  return;
40627
41205
  }
40628
- const filePath = resolve35(projectDir, decodeURIComponent(url).replace(/^\//, ""));
40629
- const rel = relative5(projectDir, filePath);
40630
- if (rel.startsWith("..") || isAbsolute6(rel)) {
41206
+ const filePath = resolve36(projectDir, decodeURIComponent(url).replace(/^\//, ""));
41207
+ const rel = relative6(projectDir, filePath);
41208
+ if (rel.startsWith("..") || isAbsolute7(rel)) {
40631
41209
  res.writeHead(403);
40632
41210
  res.end();
40633
41211
  return;
40634
41212
  }
40635
- if (existsSync49(filePath)) {
41213
+ if (existsSync50(filePath)) {
40636
41214
  res.writeHead(200, { "Content-Type": getMimeType2(filePath) });
40637
- res.end(readFileSync34(filePath));
41215
+ res.end(readFileSync35(filePath));
40638
41216
  return;
40639
41217
  }
40640
41218
  res.writeHead(404);
@@ -40707,7 +41285,7 @@ async function captureSnapshots(projectDir, opts) {
40707
41285
  return [];
40708
41286
  }
40709
41287
  const positions = opts.at?.length ? opts.at : numFrames === 1 ? [duration / 2] : Array.from({ length: numFrames }, (_2, i2) => i2 / (numFrames - 1) * duration);
40710
- const snapshotDir = join49(projectDir, "snapshots");
41288
+ const snapshotDir = join50(projectDir, "snapshots");
40711
41289
  mkdirSync25(snapshotDir, { recursive: true });
40712
41290
  let injectVideoFramesBatch2 = null;
40713
41291
  let syncVideoFrameVisibility2 = null;
@@ -40780,9 +41358,9 @@ async function captureSnapshots(projectDir, opts) {
40780
41358
  try {
40781
41359
  const url = new URL(v.src);
40782
41360
  const decodedPath = decodeURIComponent(url.pathname).replace(/^\//, "");
40783
- const candidate = resolve35(projectDir, decodedPath);
40784
- const rel = relative5(projectDir, candidate);
40785
- if (!rel.startsWith("..") && !isAbsolute6(rel) && existsSync49(candidate)) {
41361
+ const candidate = resolve36(projectDir, decodedPath);
41362
+ const rel = relative6(projectDir, candidate);
41363
+ if (!rel.startsWith("..") && !isAbsolute7(rel) && existsSync50(candidate)) {
40786
41364
  filePath = candidate;
40787
41365
  }
40788
41366
  } catch {
@@ -40812,7 +41390,7 @@ async function captureSnapshots(projectDir, opts) {
40812
41390
  }
40813
41391
  const timeLabel = opts.at?.length ? `${time.toFixed(1)}s` : `${Math.round(time / duration * 100)}pct`;
40814
41392
  const filename = `frame-${String(i2).padStart(2, "0")}-at-${timeLabel}.png`;
40815
- const framePath = join49(snapshotDir, filename);
41393
+ const framePath = join50(snapshotDir, filename);
40816
41394
  await page.screenshot({ path: framePath, type: "png" });
40817
41395
  savedPaths.push(`snapshots/${filename}`);
40818
41396
  }
@@ -40824,17 +41402,17 @@ async function captureSnapshots(projectDir, opts) {
40824
41402
  }
40825
41403
  return savedPaths;
40826
41404
  }
40827
- var __filename2, __dirname3, FFMPEG_EXTRACT_TIMEOUT_MS, examples19, snapshot_default;
41405
+ var __filename3, __dirname4, FFMPEG_EXTRACT_TIMEOUT_MS, examples21, snapshot_default;
40828
41406
  var init_snapshot = __esm({
40829
41407
  "src/commands/snapshot.ts"() {
40830
41408
  "use strict";
40831
41409
  init_dist();
40832
41410
  init_project();
40833
41411
  init_colors();
40834
- __filename2 = fileURLToPath8(import.meta.url);
40835
- __dirname3 = dirname21(__filename2);
41412
+ __filename3 = fileURLToPath9(import.meta.url);
41413
+ __dirname4 = dirname22(__filename3);
40836
41414
  FFMPEG_EXTRACT_TIMEOUT_MS = 3e4;
40837
- examples19 = [
41415
+ examples21 = [
40838
41416
  ["Capture 5 key frames from a composition", "snapshot captures/stripe"],
40839
41417
  ["Capture 10 evenly-spaced frames", "snapshot captures/stripe --frames 10"]
40840
41418
  ];
@@ -40898,13 +41476,13 @@ ${c.error("\u2717")} Snapshot failed: ${msg}`);
40898
41476
 
40899
41477
  // src/capture/assetDownloader.ts
40900
41478
  import { writeFileSync as writeFileSync19, mkdirSync as mkdirSync26 } from "fs";
40901
- import { join as join50, extname as extname10 } from "path";
41479
+ import { join as join51, extname as extname10 } from "path";
40902
41480
  async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks) {
40903
- const assetsDir = join50(outputDir, "assets");
41481
+ const assetsDir = join51(outputDir, "assets");
40904
41482
  mkdirSync26(assetsDir, { recursive: true });
40905
41483
  const assets = [];
40906
41484
  const downloadedUrls = /* @__PURE__ */ new Set();
40907
- mkdirSync26(join50(outputDir, "assets", "svgs"), { recursive: true });
41485
+ mkdirSync26(join51(outputDir, "assets", "svgs"), { recursive: true });
40908
41486
  for (let i2 = 0; i2 < tokens.svgs.length && i2 < 30; i2++) {
40909
41487
  const svg = tokens.svgs[i2];
40910
41488
  if (!svg.outerHTML || svg.outerHTML.length < 50) continue;
@@ -40912,7 +41490,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks)
40912
41490
  const name = label2 ? slugify(label2) + ".svg" : svg.isLogo ? `logo-${i2}.svg` : `icon-${i2}.svg`;
40913
41491
  const localPath = `assets/svgs/${name}`;
40914
41492
  try {
40915
- writeFileSync19(join50(outputDir, localPath), svg.outerHTML, "utf-8");
41493
+ writeFileSync19(join51(outputDir, localPath), svg.outerHTML, "utf-8");
40916
41494
  assets.push({ url: "", localPath, type: "svg" });
40917
41495
  } catch {
40918
41496
  }
@@ -40925,7 +41503,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks)
40925
41503
  const localPath = `assets/${name}`;
40926
41504
  const buffer = await fetchBuffer(icon.href);
40927
41505
  if (buffer) {
40928
- writeFileSync19(join50(outputDir, localPath), buffer);
41506
+ writeFileSync19(join51(outputDir, localPath), buffer);
40929
41507
  assets.push({ url: icon.href, localPath, type: "favicon" });
40930
41508
  break;
40931
41509
  }
@@ -40982,7 +41560,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks)
40982
41560
  const slug = isMeaningful ? slugify(rawName) : `${prefix}-${imgIdx}`;
40983
41561
  const name = `${slug}${ext}`;
40984
41562
  const localPath = `assets/${name}`;
40985
- writeFileSync19(join50(outputDir, localPath), buffer);
41563
+ writeFileSync19(join51(outputDir, localPath), buffer);
40986
41564
  assets.push({ url, localPath, type: "image" });
40987
41565
  imgIdx++;
40988
41566
  } catch {
@@ -40995,7 +41573,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks)
40995
41573
  const localPath = `assets/og-image${ext}`;
40996
41574
  const buffer = await fetchBuffer(tokens.ogImage);
40997
41575
  if (buffer && buffer.length > 5e3) {
40998
- writeFileSync19(join50(outputDir, localPath), buffer);
41576
+ writeFileSync19(join51(outputDir, localPath), buffer);
40999
41577
  assets.push({ url: tokens.ogImage, localPath, type: "image" });
41000
41578
  }
41001
41579
  } catch {
@@ -41018,7 +41596,7 @@ function normalizeUrl(u) {
41018
41596
  }
41019
41597
  }
41020
41598
  async function downloadAndRewriteFonts(css, outputDir) {
41021
- const assetsDir = join50(outputDir, "assets", "fonts");
41599
+ const assetsDir = join51(outputDir, "assets", "fonts");
41022
41600
  mkdirSync26(assetsDir, { recursive: true });
41023
41601
  const fontUrlRegex = /url\(['"]?(https?:\/\/[^'")\s]+\.(?:woff2?|ttf|otf)[^'")\s]*?)['"]?\)/g;
41024
41602
  const fontUrls = /* @__PURE__ */ new Set();
@@ -41054,7 +41632,7 @@ async function downloadAndRewriteFonts(css, outputDir) {
41054
41632
  try {
41055
41633
  const urlObj = new URL(fontUrl);
41056
41634
  const filename = urlObj.pathname.split("/").pop() || `font-${count}.woff2`;
41057
- const localPath = join50(assetsDir, filename);
41635
+ const localPath = join51(assetsDir, filename);
41058
41636
  const relativePath = `assets/fonts/${filename}`;
41059
41637
  const buffer = await fetchBuffer(fontUrl);
41060
41638
  if (buffer) {
@@ -41851,8 +42429,8 @@ var init_animationCataloger = __esm({
41851
42429
  });
41852
42430
 
41853
42431
  // src/capture/mediaCapture.ts
41854
- import { mkdirSync as mkdirSync27, writeFileSync as writeFileSync20, readdirSync as readdirSync18, readFileSync as readFileSync35, statSync as statSync18 } from "fs";
41855
- import { join as join51 } from "path";
42432
+ import { mkdirSync as mkdirSync27, writeFileSync as writeFileSync20, readdirSync as readdirSync18, readFileSync as readFileSync36, statSync as statSync18 } from "fs";
42433
+ import { join as join52 } from "path";
41856
42434
  async function saveLottieAnimations(discoveredLotties, lottieDir) {
41857
42435
  let savedCount = 0;
41858
42436
  const savedHashes = /* @__PURE__ */ new Set();
@@ -41885,7 +42463,7 @@ async function saveLottieAnimations(discoveredLotties, lottieDir) {
41885
42463
  const hash2 = buf.toString("base64").slice(0, 100);
41886
42464
  if (savedHashes.has(hash2)) continue;
41887
42465
  savedHashes.add(hash2);
41888
- writeFileSync20(join51(lottieDir, `animation-${savedCount}.lottie`), buf);
42466
+ writeFileSync20(join52(lottieDir, `animation-${savedCount}.lottie`), buf);
41889
42467
  savedCount++;
41890
42468
  continue;
41891
42469
  }
@@ -41903,7 +42481,7 @@ async function saveLottieAnimations(discoveredLotties, lottieDir) {
41903
42481
  } catch {
41904
42482
  continue;
41905
42483
  }
41906
- writeFileSync20(join51(lottieDir, `animation-${savedCount}.json`), jsonData, "utf-8");
42484
+ writeFileSync20(join52(lottieDir, `animation-${savedCount}.json`), jsonData, "utf-8");
41907
42485
  savedCount++;
41908
42486
  }
41909
42487
  } catch {
@@ -41913,22 +42491,22 @@ async function saveLottieAnimations(discoveredLotties, lottieDir) {
41913
42491
  }
41914
42492
  async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir) {
41915
42493
  const manifest = [];
41916
- const previewDir = join51(lottieDir, "previews");
42494
+ const previewDir = join52(lottieDir, "previews");
41917
42495
  mkdirSync27(previewDir, { recursive: true });
41918
42496
  for (const file of readdirSync18(lottieDir)) {
41919
42497
  if (!file.endsWith(".json")) continue;
41920
42498
  try {
41921
- const raw = JSON.parse(readFileSync35(join51(lottieDir, file), "utf-8"));
42499
+ const raw = JSON.parse(readFileSync36(join52(lottieDir, file), "utf-8"));
41922
42500
  const fr = raw.fr || 30;
41923
42501
  const dur = ((raw.op || 0) - (raw.ip || 0)) / fr;
41924
42502
  const previewName = file.replace(".json", "-preview.png");
41925
- const fileSize = statSync18(join51(lottieDir, file)).size;
42503
+ const fileSize = statSync18(join52(lottieDir, file)).size;
41926
42504
  if (fileSize > 2e6) continue;
41927
42505
  let previewPage;
41928
42506
  try {
41929
42507
  previewPage = await chromeBrowser.newPage();
41930
42508
  await previewPage.setViewport({ width: 400, height: 400 });
41931
- const animData = JSON.parse(readFileSync35(join51(lottieDir, file), "utf-8"));
42509
+ const animData = JSON.parse(readFileSync36(join52(lottieDir, file), "utf-8"));
41932
42510
  const midFrame = Math.floor(((raw.op || 0) - (raw.ip || 0)) * 0.3);
41933
42511
  await previewPage.setContent(
41934
42512
  `<!DOCTYPE html>
@@ -41958,7 +42536,7 @@ async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir) {
41958
42536
  await previewPage.waitForFunction(() => window.__READY === true, { timeout: 5e3 }).catch(() => {
41959
42537
  });
41960
42538
  await previewPage.screenshot({
41961
- path: join51(previewDir, previewName),
42539
+ path: join52(previewDir, previewName),
41962
42540
  type: "png",
41963
42541
  omitBackground: true
41964
42542
  });
@@ -41982,7 +42560,7 @@ async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir) {
41982
42560
  }
41983
42561
  if (manifest.length > 0) {
41984
42562
  writeFileSync20(
41985
- join51(outputDir, "extracted", "lottie-manifest.json"),
42563
+ join52(outputDir, "extracted", "lottie-manifest.json"),
41986
42564
  JSON.stringify(manifest, null, 2),
41987
42565
  "utf-8"
41988
42566
  );
@@ -42044,15 +42622,15 @@ async function captureVideoManifest(page, outputDir, progress) {
42044
42622
  return true;
42045
42623
  });
42046
42624
  if (uniqueVideos.length > 0) {
42047
- const videoManifestDir = join51(outputDir, "assets", "videos");
42625
+ const videoManifestDir = join52(outputDir, "assets", "videos");
42048
42626
  mkdirSync27(videoManifestDir, { recursive: true });
42049
- const previewDir = join51(videoManifestDir, "previews");
42627
+ const previewDir = join52(videoManifestDir, "previews");
42050
42628
  mkdirSync27(previewDir, { recursive: true });
42051
42629
  const videoManifest = [];
42052
42630
  for (let vi = 0; vi < uniqueVideos.length && vi < 20; vi++) {
42053
42631
  const v = uniqueVideos[vi];
42054
42632
  const previewName = `video-${vi}-preview.png`;
42055
- const previewPath = join51(previewDir, previewName);
42633
+ const previewPath = join52(previewDir, previewName);
42056
42634
  try {
42057
42635
  await page.evaluate(`window.scrollTo(0, ${Math.max(0, v.top - 100)})`);
42058
42636
  await new Promise((r2) => setTimeout(r2, 300));
@@ -42091,7 +42669,7 @@ async function captureVideoManifest(page, outputDir, progress) {
42091
42669
  }
42092
42670
  if (videoManifest.length > 0) {
42093
42671
  writeFileSync20(
42094
- join51(outputDir, "extracted", "video-manifest.json"),
42672
+ join52(outputDir, "extracted", "video-manifest.json"),
42095
42673
  JSON.stringify(videoManifest, null, 2),
42096
42674
  "utf-8"
42097
42675
  );
@@ -42373,7 +42951,7 @@ var require_p_retry = __commonJS({
42373
42951
  return error;
42374
42952
  };
42375
42953
  var isNetworkError = (errorMessage) => networkErrorMsgs.includes(errorMessage);
42376
- var pRetry2 = (input, options) => new Promise((resolve38, reject) => {
42954
+ var pRetry2 = (input, options) => new Promise((resolve39, reject) => {
42377
42955
  options = {
42378
42956
  onFailedAttempt: () => {
42379
42957
  },
@@ -42383,7 +42961,7 @@ var require_p_retry = __commonJS({
42383
42961
  const operation = retry.operation(options);
42384
42962
  operation.attempt(async (attemptNumber) => {
42385
42963
  try {
42386
- resolve38(await input(attemptNumber));
42964
+ resolve39(await input(attemptNumber));
42387
42965
  } catch (error) {
42388
42966
  if (!(error instanceof Error)) {
42389
42967
  reject(new TypeError(`Non-error was thrown: "${error}". You should only throw errors.`));
@@ -42919,8 +43497,8 @@ var require_retry3 = __commonJS({
42919
43497
  }
42920
43498
  const delay = getNextRetryDelay(config);
42921
43499
  err.config.retryConfig.currentRetryAttempt += 1;
42922
- const backoff = config.retryBackoff ? config.retryBackoff(err, delay) : new Promise((resolve38) => {
42923
- setTimeout(resolve38, delay);
43500
+ const backoff = config.retryBackoff ? config.retryBackoff(err, delay) : new Promise((resolve39) => {
43501
+ setTimeout(resolve39, delay);
42924
43502
  });
42925
43503
  if (config.onRetryAttempt) {
42926
43504
  await config.onRetryAttempt(err);
@@ -43828,8 +44406,8 @@ var require_helpers = __commonJS({
43828
44406
  function req(url, opts = {}) {
43829
44407
  const href = typeof url === "string" ? url : url.href;
43830
44408
  const req2 = (href.startsWith("https:") ? https2 : http4).request(url, opts);
43831
- const promise = new Promise((resolve38, reject) => {
43832
- req2.once("response", resolve38).once("error", reject).end();
44409
+ const promise = new Promise((resolve39, reject) => {
44410
+ req2.once("response", resolve39).once("error", reject).end();
43833
44411
  });
43834
44412
  req2.then = promise.then.bind(promise);
43835
44413
  return req2;
@@ -44006,7 +44584,7 @@ var require_parse_proxy_response = __commonJS({
44006
44584
  var debug_1 = __importDefault(require_src2());
44007
44585
  var debug = (0, debug_1.default)("https-proxy-agent:parse-proxy-response");
44008
44586
  function parseProxyResponse(socket) {
44009
- return new Promise((resolve38, reject) => {
44587
+ return new Promise((resolve39, reject) => {
44010
44588
  let buffersLength = 0;
44011
44589
  const buffers = [];
44012
44590
  function read() {
@@ -44072,7 +44650,7 @@ var require_parse_proxy_response = __commonJS({
44072
44650
  }
44073
44651
  debug("got proxy server response: %o %o", firstLine, headers);
44074
44652
  cleanup();
44075
- resolve38({
44653
+ resolve39({
44076
44654
  connect: {
44077
44655
  statusCode,
44078
44656
  statusText,
@@ -44316,7 +44894,7 @@ var require_ponyfill_es2018 = __commonJS({
44316
44894
  return new originalPromise(executor);
44317
44895
  }
44318
44896
  function promiseResolvedWith(value) {
44319
- return newPromise((resolve38) => resolve38(value));
44897
+ return newPromise((resolve39) => resolve39(value));
44320
44898
  }
44321
44899
  function promiseRejectedWith(reason) {
44322
44900
  return originalPromiseReject(reason);
@@ -44486,8 +45064,8 @@ var require_ponyfill_es2018 = __commonJS({
44486
45064
  return new TypeError("Cannot " + name + " a stream using a released reader");
44487
45065
  }
44488
45066
  function defaultReaderClosedPromiseInitialize(reader) {
44489
- reader._closedPromise = newPromise((resolve38, reject) => {
44490
- reader._closedPromise_resolve = resolve38;
45067
+ reader._closedPromise = newPromise((resolve39, reject) => {
45068
+ reader._closedPromise_resolve = resolve39;
44491
45069
  reader._closedPromise_reject = reject;
44492
45070
  });
44493
45071
  }
@@ -44661,8 +45239,8 @@ var require_ponyfill_es2018 = __commonJS({
44661
45239
  }
44662
45240
  let resolvePromise;
44663
45241
  let rejectPromise;
44664
- const promise = newPromise((resolve38, reject) => {
44665
- resolvePromise = resolve38;
45242
+ const promise = newPromise((resolve39, reject) => {
45243
+ resolvePromise = resolve39;
44666
45244
  rejectPromise = reject;
44667
45245
  });
44668
45246
  const readRequest = {
@@ -44767,8 +45345,8 @@ var require_ponyfill_es2018 = __commonJS({
44767
45345
  const reader = this._reader;
44768
45346
  let resolvePromise;
44769
45347
  let rejectPromise;
44770
- const promise = newPromise((resolve38, reject) => {
44771
- resolvePromise = resolve38;
45348
+ const promise = newPromise((resolve39, reject) => {
45349
+ resolvePromise = resolve39;
44772
45350
  rejectPromise = reject;
44773
45351
  });
44774
45352
  const readRequest = {
@@ -45787,8 +46365,8 @@ var require_ponyfill_es2018 = __commonJS({
45787
46365
  }
45788
46366
  let resolvePromise;
45789
46367
  let rejectPromise;
45790
- const promise = newPromise((resolve38, reject) => {
45791
- resolvePromise = resolve38;
46368
+ const promise = newPromise((resolve39, reject) => {
46369
+ resolvePromise = resolve39;
45792
46370
  rejectPromise = reject;
45793
46371
  });
45794
46372
  const readIntoRequest = {
@@ -46100,10 +46678,10 @@ var require_ponyfill_es2018 = __commonJS({
46100
46678
  wasAlreadyErroring = true;
46101
46679
  reason = void 0;
46102
46680
  }
46103
- const promise = newPromise((resolve38, reject) => {
46681
+ const promise = newPromise((resolve39, reject) => {
46104
46682
  stream._pendingAbortRequest = {
46105
46683
  _promise: void 0,
46106
- _resolve: resolve38,
46684
+ _resolve: resolve39,
46107
46685
  _reject: reject,
46108
46686
  _reason: reason,
46109
46687
  _wasAlreadyErroring: wasAlreadyErroring
@@ -46120,9 +46698,9 @@ var require_ponyfill_es2018 = __commonJS({
46120
46698
  if (state === "closed" || state === "errored") {
46121
46699
  return promiseRejectedWith(new TypeError(`The stream (in ${state} state) is not in the writable state and cannot be closed`));
46122
46700
  }
46123
- const promise = newPromise((resolve38, reject) => {
46701
+ const promise = newPromise((resolve39, reject) => {
46124
46702
  const closeRequest = {
46125
- _resolve: resolve38,
46703
+ _resolve: resolve39,
46126
46704
  _reject: reject
46127
46705
  };
46128
46706
  stream._closeRequest = closeRequest;
@@ -46135,9 +46713,9 @@ var require_ponyfill_es2018 = __commonJS({
46135
46713
  return promise;
46136
46714
  }
46137
46715
  function WritableStreamAddWriteRequest(stream) {
46138
- const promise = newPromise((resolve38, reject) => {
46716
+ const promise = newPromise((resolve39, reject) => {
46139
46717
  const writeRequest = {
46140
- _resolve: resolve38,
46718
+ _resolve: resolve39,
46141
46719
  _reject: reject
46142
46720
  };
46143
46721
  stream._writeRequests.push(writeRequest);
@@ -46753,8 +47331,8 @@ var require_ponyfill_es2018 = __commonJS({
46753
47331
  return new TypeError("Cannot " + name + " a stream using a released writer");
46754
47332
  }
46755
47333
  function defaultWriterClosedPromiseInitialize(writer) {
46756
- writer._closedPromise = newPromise((resolve38, reject) => {
46757
- writer._closedPromise_resolve = resolve38;
47334
+ writer._closedPromise = newPromise((resolve39, reject) => {
47335
+ writer._closedPromise_resolve = resolve39;
46758
47336
  writer._closedPromise_reject = reject;
46759
47337
  writer._closedPromiseState = "pending";
46760
47338
  });
@@ -46790,8 +47368,8 @@ var require_ponyfill_es2018 = __commonJS({
46790
47368
  writer._closedPromiseState = "resolved";
46791
47369
  }
46792
47370
  function defaultWriterReadyPromiseInitialize(writer) {
46793
- writer._readyPromise = newPromise((resolve38, reject) => {
46794
- writer._readyPromise_resolve = resolve38;
47371
+ writer._readyPromise = newPromise((resolve39, reject) => {
47372
+ writer._readyPromise_resolve = resolve39;
46795
47373
  writer._readyPromise_reject = reject;
46796
47374
  });
46797
47375
  writer._readyPromiseState = "pending";
@@ -46878,7 +47456,7 @@ var require_ponyfill_es2018 = __commonJS({
46878
47456
  source._disturbed = true;
46879
47457
  let shuttingDown = false;
46880
47458
  let currentWrite = promiseResolvedWith(void 0);
46881
- return newPromise((resolve38, reject) => {
47459
+ return newPromise((resolve39, reject) => {
46882
47460
  let abortAlgorithm;
46883
47461
  if (signal !== void 0) {
46884
47462
  abortAlgorithm = () => {
@@ -47023,7 +47601,7 @@ var require_ponyfill_es2018 = __commonJS({
47023
47601
  if (isError) {
47024
47602
  reject(error);
47025
47603
  } else {
47026
- resolve38(void 0);
47604
+ resolve39(void 0);
47027
47605
  }
47028
47606
  return null;
47029
47607
  }
@@ -47304,8 +47882,8 @@ var require_ponyfill_es2018 = __commonJS({
47304
47882
  let branch1;
47305
47883
  let branch2;
47306
47884
  let resolveCancelPromise;
47307
- const cancelPromise = newPromise((resolve38) => {
47308
- resolveCancelPromise = resolve38;
47885
+ const cancelPromise = newPromise((resolve39) => {
47886
+ resolveCancelPromise = resolve39;
47309
47887
  });
47310
47888
  function pullAlgorithm() {
47311
47889
  if (reading) {
@@ -47396,8 +47974,8 @@ var require_ponyfill_es2018 = __commonJS({
47396
47974
  let branch1;
47397
47975
  let branch2;
47398
47976
  let resolveCancelPromise;
47399
- const cancelPromise = newPromise((resolve38) => {
47400
- resolveCancelPromise = resolve38;
47977
+ const cancelPromise = newPromise((resolve39) => {
47978
+ resolveCancelPromise = resolve39;
47401
47979
  });
47402
47980
  function forwardReaderError(thisReader) {
47403
47981
  uponRejection(thisReader._closedPromise, (r2) => {
@@ -48177,8 +48755,8 @@ var require_ponyfill_es2018 = __commonJS({
48177
48755
  const writableHighWaterMark = ExtractHighWaterMark(writableStrategy, 1);
48178
48756
  const writableSizeAlgorithm = ExtractSizeAlgorithm(writableStrategy);
48179
48757
  let startPromise_resolve;
48180
- const startPromise = newPromise((resolve38) => {
48181
- startPromise_resolve = resolve38;
48758
+ const startPromise = newPromise((resolve39) => {
48759
+ startPromise_resolve = resolve39;
48182
48760
  });
48183
48761
  InitializeTransformStream(this, startPromise, writableHighWaterMark, writableSizeAlgorithm, readableHighWaterMark, readableSizeAlgorithm);
48184
48762
  SetUpTransformStreamDefaultControllerFromTransformer(this, transformer);
@@ -48271,8 +48849,8 @@ var require_ponyfill_es2018 = __commonJS({
48271
48849
  if (stream._backpressureChangePromise !== void 0) {
48272
48850
  stream._backpressureChangePromise_resolve();
48273
48851
  }
48274
- stream._backpressureChangePromise = newPromise((resolve38) => {
48275
- stream._backpressureChangePromise_resolve = resolve38;
48852
+ stream._backpressureChangePromise = newPromise((resolve39) => {
48853
+ stream._backpressureChangePromise_resolve = resolve39;
48276
48854
  });
48277
48855
  stream._backpressure = backpressure;
48278
48856
  }
@@ -48440,8 +49018,8 @@ var require_ponyfill_es2018 = __commonJS({
48440
49018
  return controller._finishPromise;
48441
49019
  }
48442
49020
  const readable = stream._readable;
48443
- controller._finishPromise = newPromise((resolve38, reject) => {
48444
- controller._finishPromise_resolve = resolve38;
49021
+ controller._finishPromise = newPromise((resolve39, reject) => {
49022
+ controller._finishPromise_resolve = resolve39;
48445
49023
  controller._finishPromise_reject = reject;
48446
49024
  });
48447
49025
  const cancelPromise = controller._cancelAlgorithm(reason);
@@ -48467,8 +49045,8 @@ var require_ponyfill_es2018 = __commonJS({
48467
49045
  return controller._finishPromise;
48468
49046
  }
48469
49047
  const readable = stream._readable;
48470
- controller._finishPromise = newPromise((resolve38, reject) => {
48471
- controller._finishPromise_resolve = resolve38;
49048
+ controller._finishPromise = newPromise((resolve39, reject) => {
49049
+ controller._finishPromise_resolve = resolve39;
48472
49050
  controller._finishPromise_reject = reject;
48473
49051
  });
48474
49052
  const flushPromise = controller._flushAlgorithm();
@@ -48498,8 +49076,8 @@ var require_ponyfill_es2018 = __commonJS({
48498
49076
  return controller._finishPromise;
48499
49077
  }
48500
49078
  const writable = stream._writable;
48501
- controller._finishPromise = newPromise((resolve38, reject) => {
48502
- controller._finishPromise_resolve = resolve38;
49079
+ controller._finishPromise = newPromise((resolve39, reject) => {
49080
+ controller._finishPromise_resolve = resolve39;
48503
49081
  controller._finishPromise_reject = reject;
48504
49082
  });
48505
49083
  const cancelPromise = controller._cancelAlgorithm(reason);
@@ -50468,7 +51046,7 @@ import zlib from "zlib";
50468
51046
  import Stream2, { PassThrough as PassThrough2, pipeline as pump } from "stream";
50469
51047
  import { Buffer as Buffer3 } from "buffer";
50470
51048
  async function fetch3(url, options_) {
50471
- return new Promise((resolve38, reject) => {
51049
+ return new Promise((resolve39, reject) => {
50472
51050
  const request = new Request2(url, options_);
50473
51051
  const { parsedURL, options } = getNodeRequestOptions(request);
50474
51052
  if (!supportedSchemas.has(parsedURL.protocol)) {
@@ -50477,7 +51055,7 @@ async function fetch3(url, options_) {
50477
51055
  if (parsedURL.protocol === "data:") {
50478
51056
  const data = dist_default(request.url);
50479
51057
  const response2 = new Response2(data, { headers: { "Content-Type": data.typeFull } });
50480
- resolve38(response2);
51058
+ resolve39(response2);
50481
51059
  return;
50482
51060
  }
50483
51061
  const send = (parsedURL.protocol === "https:" ? https : http3).request;
@@ -50599,7 +51177,7 @@ async function fetch3(url, options_) {
50599
51177
  if (responseReferrerPolicy) {
50600
51178
  requestOptions.referrerPolicy = responseReferrerPolicy;
50601
51179
  }
50602
- resolve38(fetch3(new Request2(locationURL, requestOptions)));
51180
+ resolve39(fetch3(new Request2(locationURL, requestOptions)));
50603
51181
  finalize();
50604
51182
  return;
50605
51183
  }
@@ -50632,7 +51210,7 @@ async function fetch3(url, options_) {
50632
51210
  const codings = headers.get("Content-Encoding");
50633
51211
  if (!request.compress || request.method === "HEAD" || codings === null || response_.statusCode === 204 || response_.statusCode === 304) {
50634
51212
  response = new Response2(body, responseOptions);
50635
- resolve38(response);
51213
+ resolve39(response);
50636
51214
  return;
50637
51215
  }
50638
51216
  const zlibOptions = {
@@ -50646,7 +51224,7 @@ async function fetch3(url, options_) {
50646
51224
  }
50647
51225
  });
50648
51226
  response = new Response2(body, responseOptions);
50649
- resolve38(response);
51227
+ resolve39(response);
50650
51228
  return;
50651
51229
  }
50652
51230
  if (codings === "deflate" || codings === "x-deflate") {
@@ -50670,12 +51248,12 @@ async function fetch3(url, options_) {
50670
51248
  });
50671
51249
  }
50672
51250
  response = new Response2(body, responseOptions);
50673
- resolve38(response);
51251
+ resolve39(response);
50674
51252
  });
50675
51253
  raw.once("end", () => {
50676
51254
  if (!response) {
50677
51255
  response = new Response2(body, responseOptions);
50678
- resolve38(response);
51256
+ resolve39(response);
50679
51257
  }
50680
51258
  });
50681
51259
  return;
@@ -50687,11 +51265,11 @@ async function fetch3(url, options_) {
50687
51265
  }
50688
51266
  });
50689
51267
  response = new Response2(body, responseOptions);
50690
- resolve38(response);
51268
+ resolve39(response);
50691
51269
  return;
50692
51270
  }
50693
51271
  response = new Response2(body, responseOptions);
50694
- resolve38(response);
51272
+ resolve39(response);
50695
51273
  });
50696
51274
  writeToStream(request_, request).catch(reject);
50697
51275
  });
@@ -56773,7 +57351,7 @@ var require_jwtaccess = __commonJS({
56773
57351
  }
56774
57352
  }
56775
57353
  fromStreamAsync(inputStream) {
56776
- return new Promise((resolve38, reject) => {
57354
+ return new Promise((resolve39, reject) => {
56777
57355
  if (!inputStream) {
56778
57356
  reject(new Error("Must pass in a stream containing the service account auth settings."));
56779
57357
  }
@@ -56782,7 +57360,7 @@ var require_jwtaccess = __commonJS({
56782
57360
  try {
56783
57361
  const data = JSON.parse(s2);
56784
57362
  this.fromJSON(data);
56785
- resolve38();
57363
+ resolve39();
56786
57364
  } catch (err) {
56787
57365
  reject(err);
56788
57366
  }
@@ -57021,7 +57599,7 @@ var require_jwtclient = __commonJS({
57021
57599
  }
57022
57600
  }
57023
57601
  fromStreamAsync(inputStream) {
57024
- return new Promise((resolve38, reject) => {
57602
+ return new Promise((resolve39, reject) => {
57025
57603
  if (!inputStream) {
57026
57604
  throw new Error("Must pass in a stream containing the service account auth settings.");
57027
57605
  }
@@ -57030,7 +57608,7 @@ var require_jwtclient = __commonJS({
57030
57608
  try {
57031
57609
  const data = JSON.parse(s2);
57032
57610
  this.fromJSON(data);
57033
- resolve38();
57611
+ resolve39();
57034
57612
  } catch (e2) {
57035
57613
  reject(e2);
57036
57614
  }
@@ -57163,7 +57741,7 @@ var require_refreshclient = __commonJS({
57163
57741
  }
57164
57742
  }
57165
57743
  async fromStreamAsync(inputStream) {
57166
- return new Promise((resolve38, reject) => {
57744
+ return new Promise((resolve39, reject) => {
57167
57745
  if (!inputStream) {
57168
57746
  return reject(new Error("Must pass in a stream containing the user refresh token."));
57169
57747
  }
@@ -57172,7 +57750,7 @@ var require_refreshclient = __commonJS({
57172
57750
  try {
57173
57751
  const data = JSON.parse(s2);
57174
57752
  this.fromJSON(data);
57175
- return resolve38();
57753
+ return resolve39();
57176
57754
  } catch (err) {
57177
57755
  return reject(err);
57178
57756
  }
@@ -59005,7 +59583,7 @@ var require_pluggable_auth_handler = __commonJS({
59005
59583
  * @return A promise that resolves with the executable response.
59006
59584
  */
59007
59585
  retrieveResponseFromExecutable(envMap) {
59008
- return new Promise((resolve38, reject) => {
59586
+ return new Promise((resolve39, reject) => {
59009
59587
  const child = childProcess.spawn(this.commandComponents[0], this.commandComponents.slice(1), {
59010
59588
  env: { ...process.env, ...Object.fromEntries(envMap) }
59011
59589
  });
@@ -59027,7 +59605,7 @@ var require_pluggable_auth_handler = __commonJS({
59027
59605
  try {
59028
59606
  const responseJson = JSON.parse(output);
59029
59607
  const response = new executable_response_1.ExecutableResponse(responseJson);
59030
- return resolve38(response);
59608
+ return resolve39(response);
59031
59609
  } catch (error) {
59032
59610
  if (error instanceof executable_response_1.ExecutableResponseError) {
59033
59611
  return reject(error);
@@ -59930,7 +60508,7 @@ var require_googleauth = __commonJS({
59930
60508
  }
59931
60509
  }
59932
60510
  fromStreamAsync(inputStream, options) {
59933
- return new Promise((resolve38, reject) => {
60511
+ return new Promise((resolve39, reject) => {
59934
60512
  if (!inputStream) {
59935
60513
  throw new Error("Must pass in a stream containing the Google auth settings.");
59936
60514
  }
@@ -59940,7 +60518,7 @@ var require_googleauth = __commonJS({
59940
60518
  try {
59941
60519
  const data = JSON.parse(chunks.join(""));
59942
60520
  const r2 = this._cacheClientFromJSON(data, options);
59943
- return resolve38(r2);
60521
+ return resolve39(r2);
59944
60522
  } catch (err) {
59945
60523
  if (!this.keyFilename)
59946
60524
  throw err;
@@ -59950,7 +60528,7 @@ var require_googleauth = __commonJS({
59950
60528
  });
59951
60529
  this.cachedCredential = client;
59952
60530
  this.setGapicJWTValues(client);
59953
- return resolve38(client);
60531
+ return resolve39(client);
59954
60532
  }
59955
60533
  } catch (err) {
59956
60534
  return reject(err);
@@ -59986,17 +60564,17 @@ var require_googleauth = __commonJS({
59986
60564
  * Run the Google Cloud SDK command that prints the default project ID
59987
60565
  */
59988
60566
  async getDefaultServiceProjectId() {
59989
- return new Promise((resolve38) => {
60567
+ return new Promise((resolve39) => {
59990
60568
  (0, child_process_1.exec)("gcloud config config-helper --format json", (err, stdout2) => {
59991
60569
  if (!err && stdout2) {
59992
60570
  try {
59993
60571
  const projectId = JSON.parse(stdout2).configuration.properties.core.project;
59994
- resolve38(projectId);
60572
+ resolve39(projectId);
59995
60573
  return;
59996
60574
  } catch (e2) {
59997
60575
  }
59998
60576
  }
59999
- resolve38(null);
60577
+ resolve39(null);
60000
60578
  });
60001
60579
  });
60002
60580
  }
@@ -67768,14 +68346,14 @@ function __asyncValues(o) {
67768
68346
  }, i2);
67769
68347
  function verb(n) {
67770
68348
  i2[n] = o[n] && function(v) {
67771
- return new Promise(function(resolve38, reject) {
67772
- v = o[n](v), settle(resolve38, reject, v.done, v.value);
68349
+ return new Promise(function(resolve39, reject) {
68350
+ v = o[n](v), settle(resolve39, reject, v.done, v.value);
67773
68351
  });
67774
68352
  };
67775
68353
  }
67776
- function settle(resolve38, reject, d, v) {
68354
+ function settle(resolve39, reject, d, v) {
67777
68355
  Promise.resolve(v).then(function(v2) {
67778
- resolve38({ value: v2, done: d });
68356
+ resolve39({ value: v2, done: d });
67779
68357
  }, reject);
67780
68358
  }
67781
68359
  }
@@ -78278,8 +78856,8 @@ var init_node4 = __esm({
78278
78856
  const url = `${websocketBaseUrl}/ws/google.ai.generativelanguage.${apiVersion}.GenerativeService.BidiGenerateMusic?key=${apiKey}`;
78279
78857
  let onopenResolve = () => {
78280
78858
  };
78281
- const onopenPromise = new Promise((resolve38) => {
78282
- onopenResolve = resolve38;
78859
+ const onopenPromise = new Promise((resolve39) => {
78860
+ onopenResolve = resolve39;
78283
78861
  });
78284
78862
  const callbacks = params.callbacks;
78285
78863
  const onopenAwaitedCallback = function() {
@@ -78485,8 +79063,8 @@ var init_node4 = __esm({
78485
79063
  }
78486
79064
  let onopenResolve = () => {
78487
79065
  };
78488
- const onopenPromise = new Promise((resolve38) => {
78489
- onopenResolve = resolve38;
79066
+ const onopenPromise = new Promise((resolve39) => {
79067
+ onopenResolve = resolve39;
78490
79068
  });
78491
79069
  const callbacks = params.callbacks;
78492
79070
  const onopenAwaitedCallback = function() {
@@ -80795,7 +81373,7 @@ var init_node4 = __esm({
80795
81373
  return void 0;
80796
81374
  }
80797
81375
  };
80798
- sleep$1 = (ms) => new Promise((resolve38) => setTimeout(resolve38, ms));
81376
+ sleep$1 = (ms) => new Promise((resolve39) => setTimeout(resolve39, ms));
80799
81377
  FallbackEncoder = ({ headers, body }) => {
80800
81378
  return {
80801
81379
  bodyHeaders: {
@@ -81304,8 +81882,8 @@ ${underline2}`);
81304
81882
  };
81305
81883
  APIPromise = class _APIPromise extends Promise {
81306
81884
  constructor(client, responsePromise, parseResponse = defaultParseResponse) {
81307
- super((resolve38) => {
81308
- resolve38(null);
81885
+ super((resolve39) => {
81886
+ resolve39(null);
81309
81887
  });
81310
81888
  this.responsePromise = responsePromise;
81311
81889
  this.parseResponse = parseResponse;
@@ -82546,8 +83124,8 @@ ${underline2}`);
82546
83124
  });
82547
83125
 
82548
83126
  // src/capture/contentExtractor.ts
82549
- import { readdirSync as readdirSync19, statSync as statSync20, readFileSync as readFileSync36 } from "fs";
82550
- import { join as join52 } from "path";
83127
+ import { readdirSync as readdirSync19, statSync as statSync20, readFileSync as readFileSync37 } from "fs";
83128
+ import { join as join53 } from "path";
82551
83129
  async function detectLibraries(page, capturedShaders) {
82552
83130
  let detectedLibraries = [];
82553
83131
  try {
@@ -82667,7 +83245,7 @@ async function captionImagesWithGemini(outputDir, progress, warnings) {
82667
83245
  try {
82668
83246
  const { GoogleGenAI: GoogleGenAI2 } = await Promise.resolve().then(() => (init_node4(), node_exports));
82669
83247
  const ai = new GoogleGenAI2({ apiKey: geminiKey });
82670
- const imageFiles = readdirSync19(join52(outputDir, "assets")).filter(
83248
+ const imageFiles = readdirSync19(join53(outputDir, "assets")).filter(
82671
83249
  (f3) => /\.(png|jpg|jpeg|webp|gif)$/i.test(f3)
82672
83250
  );
82673
83251
  const model = process.env.HYPERFRAMES_GEMINI_MODEL || "gemini-3.1-flash-lite-preview";
@@ -82676,10 +83254,10 @@ async function captionImagesWithGemini(outputDir, progress, warnings) {
82676
83254
  const batch = imageFiles.slice(i2, i2 + BATCH_SIZE);
82677
83255
  const results = await Promise.allSettled(
82678
83256
  batch.map(async (file) => {
82679
- const filePath = join52(outputDir, "assets", file);
83257
+ const filePath = join53(outputDir, "assets", file);
82680
83258
  const stat3 = statSync20(filePath);
82681
83259
  if (stat3.size > 4e6) return { file, caption: "" };
82682
- const buffer = readFileSync36(filePath);
83260
+ const buffer = readFileSync37(filePath);
82683
83261
  const base64 = buffer.toString("base64");
82684
83262
  const ext = file.split(".").pop()?.toLowerCase() || "png";
82685
83263
  const mimeType = ext === "jpg" ? "image/jpeg" : `image/${ext}`;
@@ -82725,11 +83303,11 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
82725
83303
  const uncaptionedLines = [];
82726
83304
  const svgLines = [];
82727
83305
  const fontLines = [];
82728
- const assetsPath = join52(outputDir, "assets");
83306
+ const assetsPath = join53(outputDir, "assets");
82729
83307
  try {
82730
83308
  for (const file of readdirSync19(assetsPath)) {
82731
83309
  if (file === "svgs" || file === "fonts" || file === "lottie" || file === "videos") continue;
82732
- const filePath = join52(assetsPath, file);
83310
+ const filePath = join53(assetsPath, file);
82733
83311
  const stat3 = statSync20(filePath);
82734
83312
  if (!stat3.isFile()) continue;
82735
83313
  const sizeKb = Math.round(stat3.size / 1024);
@@ -82758,7 +83336,7 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
82758
83336
  } catch {
82759
83337
  }
82760
83338
  try {
82761
- const svgsPath = join52(assetsPath, "svgs");
83339
+ const svgsPath = join53(assetsPath, "svgs");
82762
83340
  for (const file of readdirSync19(svgsPath)) {
82763
83341
  if (!file.endsWith(".svg")) continue;
82764
83342
  const svgMatch = tokens.svgs.find(
@@ -82773,7 +83351,7 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
82773
83351
  } catch {
82774
83352
  }
82775
83353
  try {
82776
- const fontsPath = join52(assetsPath, "fonts");
83354
+ const fontsPath = join53(assetsPath, "fonts");
82777
83355
  for (const file of readdirSync19(fontsPath)) {
82778
83356
  fontLines.push(`fonts/${file} \u2014 font file`);
82779
83357
  }
@@ -82793,12 +83371,12 @@ __export(agentPromptGenerator_exports, {
82793
83371
  generateAgentPrompt: () => generateAgentPrompt
82794
83372
  });
82795
83373
  import { writeFileSync as writeFileSync21 } from "fs";
82796
- import { join as join53 } from "path";
83374
+ import { join as join54 } from "path";
82797
83375
  function generateAgentPrompt(outputDir, url, tokens, _animations, hasScreenshot, hasLottie, hasShaders, _catalogedAssets, detectedLibraries) {
82798
83376
  const prompt = buildPrompt(url, tokens, hasScreenshot, hasLottie, hasShaders, detectedLibraries);
82799
- writeFileSync21(join53(outputDir, "AGENTS.md"), prompt, "utf-8");
82800
- writeFileSync21(join53(outputDir, "CLAUDE.md"), prompt, "utf-8");
82801
- writeFileSync21(join53(outputDir, ".cursorrules"), prompt, "utf-8");
83377
+ writeFileSync21(join54(outputDir, "AGENTS.md"), prompt, "utf-8");
83378
+ writeFileSync21(join54(outputDir, "CLAUDE.md"), prompt, "utf-8");
83379
+ writeFileSync21(join54(outputDir, ".cursorrules"), prompt, "utf-8");
82802
83380
  }
82803
83381
  function buildPrompt(url, tokens, hasScreenshot, hasLottie, hasShaders, detectedLibraries) {
82804
83382
  const title = tokens.title || new URL(url).hostname.replace(/^www\./, "");
@@ -82865,15 +83443,15 @@ var init_agentPromptGenerator = __esm({
82865
83443
  });
82866
83444
 
82867
83445
  // src/capture/scaffolding.ts
82868
- import { existsSync as existsSync50, writeFileSync as writeFileSync22, readFileSync as readFileSync37 } from "fs";
82869
- import { join as join54, resolve as resolve36 } from "path";
83446
+ import { existsSync as existsSync51, writeFileSync as writeFileSync22, readFileSync as readFileSync38 } from "fs";
83447
+ import { join as join55, resolve as resolve37 } from "path";
82870
83448
  function loadEnvFile(startDir) {
82871
83449
  try {
82872
- let dir = resolve36(startDir);
83450
+ let dir = resolve37(startDir);
82873
83451
  for (let i2 = 0; i2 < 5; i2++) {
82874
- const envPath = resolve36(dir, ".env");
83452
+ const envPath = resolve37(dir, ".env");
82875
83453
  try {
82876
- const envContent = readFileSync37(envPath, "utf-8");
83454
+ const envContent = readFileSync38(envPath, "utf-8");
82877
83455
  for (const line of envContent.split("\n")) {
82878
83456
  const trimmed = line.trim();
82879
83457
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -82885,15 +83463,15 @@ function loadEnvFile(startDir) {
82885
83463
  }
82886
83464
  break;
82887
83465
  } catch {
82888
- dir = resolve36(dir, "..");
83466
+ dir = resolve37(dir, "..");
82889
83467
  }
82890
83468
  }
82891
83469
  } catch {
82892
83470
  }
82893
83471
  }
82894
83472
  async function generateProjectScaffold(outputDir, url, tokens, animationCatalog, hasScreenshots, hasLotties, hasShaders, catalogedAssets, progress, warnings, detectedLibraries) {
82895
- const metaPath = join54(outputDir, "meta.json");
82896
- if (!existsSync50(metaPath)) {
83473
+ const metaPath = join55(outputDir, "meta.json");
83474
+ if (!existsSync51(metaPath)) {
82897
83475
  const hostname = new URL(url).hostname.replace(/^www\./, "");
82898
83476
  writeFileSync22(
82899
83477
  metaPath,
@@ -82931,9 +83509,9 @@ __export(screenshotCapture_exports, {
82931
83509
  captureScrollScreenshots: () => captureScrollScreenshots
82932
83510
  });
82933
83511
  import { writeFileSync as writeFileSync23, mkdirSync as mkdirSync28 } from "fs";
82934
- import { join as join55 } from "path";
83512
+ import { join as join56 } from "path";
82935
83513
  async function captureScrollScreenshots(page, outputDir) {
82936
- const screenshotsDir = join55(outputDir, "screenshots");
83514
+ const screenshotsDir = join56(outputDir, "screenshots");
82937
83515
  mkdirSync28(screenshotsDir, { recursive: true });
82938
83516
  const MAX_SCREENSHOTS = 20;
82939
83517
  const filePaths = [];
@@ -82967,7 +83545,7 @@ async function captureScrollScreenshots(page, outputDir) {
82967
83545
  finalPositions[i2] / Math.max(1, scrollHeight - viewportHeight) * 100
82968
83546
  );
82969
83547
  const filename = `scroll-${String(Math.min(pct, 100)).padStart(3, "0")}.png`;
82970
- const filePath = join55(screenshotsDir, filename);
83548
+ const filePath = join56(screenshotsDir, filename);
82971
83549
  const buffer = await page.screenshot({ type: "png" });
82972
83550
  writeFileSync23(filePath, buffer);
82973
83551
  filePaths.push(`screenshots/${filename}`);
@@ -83280,8 +83858,8 @@ var capture_exports = {};
83280
83858
  __export(capture_exports, {
83281
83859
  captureWebsite: () => captureWebsite
83282
83860
  });
83283
- import { mkdirSync as mkdirSync29, writeFileSync as writeFileSync24, existsSync as existsSync51 } from "fs";
83284
- import { join as join56 } from "path";
83861
+ import { mkdirSync as mkdirSync29, writeFileSync as writeFileSync24, existsSync as existsSync52 } from "fs";
83862
+ import { join as join57 } from "path";
83285
83863
  async function captureWebsite(opts, onProgress) {
83286
83864
  const {
83287
83865
  url,
@@ -83298,9 +83876,9 @@ async function captureWebsite(opts, onProgress) {
83298
83876
  onProgress?.(stage, detail);
83299
83877
  };
83300
83878
  loadEnvFile(outputDir);
83301
- mkdirSync29(join56(outputDir, "extracted"), { recursive: true });
83302
- mkdirSync29(join56(outputDir, "screenshots"), { recursive: true });
83303
- mkdirSync29(join56(outputDir, "assets"), { recursive: true });
83879
+ mkdirSync29(join57(outputDir, "extracted"), { recursive: true });
83880
+ mkdirSync29(join57(outputDir, "screenshots"), { recursive: true });
83881
+ mkdirSync29(join57(outputDir, "assets"), { recursive: true });
83304
83882
  progress("browser", "Launching headless Chrome...");
83305
83883
  const { ensureBrowser: ensureBrowser2 } = await Promise.resolve().then(() => (init_manager2(), manager_exports2));
83306
83884
  const browser = await ensureBrowser2();
@@ -83456,7 +84034,7 @@ async function captureWebsite(opts, onProgress) {
83456
84034
  } catch {
83457
84035
  }
83458
84036
  if (discoveredLotties.length > 0) {
83459
- const lottieDir = join56(outputDir, "assets", "lottie");
84037
+ const lottieDir = join57(outputDir, "assets", "lottie");
83460
84038
  mkdirSync29(lottieDir, { recursive: true });
83461
84039
  const savedCount = await saveLottieAnimations(discoveredLotties, lottieDir);
83462
84040
  if (savedCount > 0) {
@@ -83476,7 +84054,7 @@ async function captureWebsite(opts, onProgress) {
83476
84054
  });
83477
84055
  capturedShaders = unique;
83478
84056
  writeFileSync24(
83479
- join56(outputDir, "extracted", "shaders.json"),
84057
+ join57(outputDir, "extracted", "shaders.json"),
83480
84058
  JSON.stringify(unique, null, 2),
83481
84059
  "utf-8"
83482
84060
  );
@@ -83487,7 +84065,7 @@ async function captureWebsite(opts, onProgress) {
83487
84065
  progress("tokens", "Extracting design tokens...");
83488
84066
  const tokens = await extractTokens(page1);
83489
84067
  writeFileSync24(
83490
- join56(outputDir, "extracted", "tokens.json"),
84068
+ join57(outputDir, "extracted", "tokens.json"),
83491
84069
  JSON.stringify(tokens, null, 2),
83492
84070
  "utf-8"
83493
84071
  );
@@ -83561,7 +84139,7 @@ async function captureWebsite(opts, onProgress) {
83561
84139
  representativeAnimations: representativeAnims
83562
84140
  };
83563
84141
  writeFileSync24(
83564
- join56(outputDir, "extracted", "animations.json"),
84142
+ join57(outputDir, "extracted", "animations.json"),
83565
84143
  JSON.stringify(leanCatalog, null, 2),
83566
84144
  "utf-8"
83567
84145
  );
@@ -83572,18 +84150,18 @@ async function captureWebsite(opts, onProgress) {
83572
84150
  assets = await downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks);
83573
84151
  }
83574
84152
  if (visibleTextContent) {
83575
- writeFileSync24(join56(outputDir, "extracted", "visible-text.txt"), visibleTextContent, "utf-8");
84153
+ writeFileSync24(join57(outputDir, "extracted", "visible-text.txt"), visibleTextContent, "utf-8");
83576
84154
  }
83577
84155
  if (catalogedAssets.length > 0) {
83578
84156
  writeFileSync24(
83579
- join56(outputDir, "extracted", "assets-catalog.json"),
84157
+ join57(outputDir, "extracted", "assets-catalog.json"),
83580
84158
  JSON.stringify(catalogedAssets, null, 2),
83581
84159
  "utf-8"
83582
84160
  );
83583
84161
  }
83584
84162
  if (detectedLibraries.length > 0) {
83585
84163
  writeFileSync24(
83586
- join56(outputDir, "extracted", "detected-libraries.json"),
84164
+ join57(outputDir, "extracted", "detected-libraries.json"),
83587
84165
  JSON.stringify(detectedLibraries, null, 2),
83588
84166
  "utf-8"
83589
84167
  );
@@ -83594,7 +84172,7 @@ async function captureWebsite(opts, onProgress) {
83594
84172
  const lines = generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCaptions);
83595
84173
  if (lines.length > 0) {
83596
84174
  writeFileSync24(
83597
- join56(outputDir, "extracted", "asset-descriptions.md"),
84175
+ join57(outputDir, "extracted", "asset-descriptions.md"),
83598
84176
  "# Asset Descriptions\n\nOne line per file. Read this instead of opening every image individually.\n\n" + lines.map((l) => "- " + l).join("\n") + "\n",
83599
84177
  "utf-8"
83600
84178
  );
@@ -83610,7 +84188,7 @@ async function captureWebsite(opts, onProgress) {
83610
84188
  animationCatalog,
83611
84189
  screenshots.length > 0,
83612
84190
  discoveredLotties.length > 0,
83613
- existsSync51(join56(outputDir, "extracted", "shaders.json")),
84191
+ existsSync52(join57(outputDir, "extracted", "shaders.json")),
83614
84192
  catalogedAssets,
83615
84193
  progress,
83616
84194
  warnings,
@@ -83650,15 +84228,15 @@ var init_capture = __esm({
83650
84228
  var capture_exports2 = {};
83651
84229
  __export(capture_exports2, {
83652
84230
  default: () => capture_default,
83653
- examples: () => examples20
84231
+ examples: () => examples22
83654
84232
  });
83655
- import { resolve as resolve37 } from "path";
83656
- var examples20, capture_default;
84233
+ import { resolve as resolve38 } from "path";
84234
+ var examples22, capture_default;
83657
84235
  var init_capture2 = __esm({
83658
84236
  "src/commands/capture.ts"() {
83659
84237
  "use strict";
83660
84238
  init_dist();
83661
- examples20 = [
84239
+ examples22 = [
83662
84240
  ["Capture a website", "hyperframes capture https://stripe.com"],
83663
84241
  ["Capture to a specific directory", "hyperframes capture https://linear.app -o linear-video"],
83664
84242
  ["JSON output for AI agents", "hyperframes capture https://example.com --json"]
@@ -83711,7 +84289,7 @@ var init_capture2 = __esm({
83711
84289
  const hostname = new URL(url).hostname.replace(/^www\./, "");
83712
84290
  outputName = `captures/${hostname.replace(/\./g, "-")}`;
83713
84291
  }
83714
- const outputDir = resolve37(outputName);
84292
+ const outputDir = resolve38(outputName);
83715
84293
  const isJson = args.json;
83716
84294
  if (!isJson) {
83717
84295
  const { c: c2 } = await Promise.resolve().then(() => (init_colors(), colors_exports));
@@ -83959,7 +84537,7 @@ __export(autoUpdate_exports, {
83959
84537
  import { spawn as spawn12 } from "child_process";
83960
84538
  import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync30, openSync } from "fs";
83961
84539
  import { homedir as homedir10 } from "os";
83962
- import { join as join57 } from "path";
84540
+ import { join as join58 } from "path";
83963
84541
  import { compareVersions as compareVersions2 } from "compare-versions";
83964
84542
  function isAutoInstallDisabled() {
83965
84543
  if (isDevMode()) return true;
@@ -83982,7 +84560,7 @@ function log(line) {
83982
84560
  }
83983
84561
  function launchDetachedInstall(installCommand, version) {
83984
84562
  mkdirSync30(CONFIG_DIR2, { recursive: true, mode: 448 });
83985
- const configFile = join57(CONFIG_DIR2, "config.json");
84563
+ const configFile = join58(CONFIG_DIR2, "config.json");
83986
84564
  const nodeScript = `
83987
84565
  const { exec } = require("node:child_process");
83988
84566
  const { readFileSync, renameSync, writeFileSync } = require("node:fs");
@@ -84101,18 +84679,31 @@ var init_autoUpdate = __esm({
84101
84679
  init_config();
84102
84680
  init_env();
84103
84681
  init_installerDetection();
84104
- CONFIG_DIR2 = join57(homedir10(), ".hyperframes");
84105
- LOG_FILE = join57(CONFIG_DIR2, "auto-update.log");
84682
+ CONFIG_DIR2 = join58(homedir10(), ".hyperframes");
84683
+ LOG_FILE = join58(CONFIG_DIR2, "auto-update.log");
84106
84684
  PENDING_TIMEOUT_MS = 10 * 60 * 1e3;
84107
84685
  }
84108
84686
  });
84109
84687
 
84688
+ // src/commands/layout-audit.browser.js
84689
+ var layout_audit_browser_exports = {};
84690
+ __export(layout_audit_browser_exports, {
84691
+ default: () => layout_audit_browser_default
84692
+ });
84693
+ var layout_audit_browser_default;
84694
+ var init_layout_audit_browser = __esm({
84695
+ "src/commands/layout-audit.browser.js"() {
84696
+ layout_audit_browser_default = '(function () {\n const IGNORE_TAGS = new Set(["SCRIPT", "STYLE", "TEMPLATE", "NOSCRIPT", "META", "LINK"]);\n\n function toRect(rect) {\n return {\n left: round(rect.left),\n top: round(rect.top),\n right: round(rect.right),\n bottom: round(rect.bottom),\n width: round(rect.width),\n height: round(rect.height),\n };\n }\n\n function rectFromOrigin(left, top, width, height) {\n return {\n left: round(left),\n top: round(top),\n right: round(left + width),\n bottom: round(top + height),\n width: round(width),\n height: round(height),\n };\n }\n\n function round(value) {\n return Math.round(value * 100) / 100;\n }\n\n function overflowFor(subject, container, tolerance) {\n const overflow = {};\n if (subject.left < container.left - tolerance)\n overflow.left = round(container.left - subject.left);\n if (subject.right > container.right + tolerance)\n overflow.right = round(subject.right - container.right);\n if (subject.top < container.top - tolerance) overflow.top = round(container.top - subject.top);\n if (subject.bottom > container.bottom + tolerance)\n overflow.bottom = round(subject.bottom - container.bottom);\n return Object.keys(overflow).length > 0 ? overflow : null;\n }\n\n function escapeCss(value) {\n if (window.CSS && typeof window.CSS.escape === "function") return window.CSS.escape(value);\n return value.replace(/[^a-zA-Z0-9_-]/g, "\\\\$&");\n }\n\n function escapeAttr(value) {\n return value.replace(/\\\\/g, "\\\\\\\\").replace(/"/g, \'\\\\"\');\n }\n\n function selectorFor(element) {\n if (element.id) return `#${escapeCss(element.id)}`;\n const dataName =\n element.getAttribute("data-layout-name") ||\n element.getAttribute("data-composition-id") ||\n element.getAttribute("data-start");\n if (dataName) {\n const attr = element.hasAttribute("data-layout-name")\n ? "data-layout-name"\n : element.hasAttribute("data-composition-id")\n ? "data-composition-id"\n : "data-start";\n const attrSelector = `[${attr}="${escapeAttr(dataName)}"]`;\n if (document.querySelectorAll(attrSelector).length === 1) return attrSelector;\n return `${element.tagName.toLowerCase()}${attrSelector}`;\n }\n const classes = Array.from(element.classList).slice(0, 2);\n if (classes.length > 0) {\n return `${element.tagName.toLowerCase()}.${classes.map(escapeCss).join(".")}`;\n }\n const parent = element.parentElement;\n if (!parent) return element.tagName.toLowerCase();\n const siblings = Array.from(parent.children).filter(\n (child) => child.tagName === element.tagName,\n );\n const index = siblings.indexOf(element) + 1;\n return `${selectorFor(parent)} > ${element.tagName.toLowerCase()}:nth-of-type(${index})`;\n }\n\n function hasIgnoreFlag(element) {\n return !!element.closest("[data-layout-ignore], [data-layout-check=\'ignore\']");\n }\n\n function hasAllowOverflowFlag(element) {\n return !!element.closest("[data-layout-allow-overflow]");\n }\n\n function opacityChain(element) {\n let opacity = 1;\n for (let current = element; current; current = current.parentElement) {\n const parsed = Number.parseFloat(getComputedStyle(current).opacity || "1");\n if (Number.isFinite(parsed)) opacity *= parsed;\n }\n return opacity;\n }\n\n function isVisibleElement(element) {\n if (IGNORE_TAGS.has(element.tagName)) return false;\n if (hasIgnoreFlag(element)) return false;\n const style = getComputedStyle(element);\n if (\n style.display === "none" ||\n style.visibility === "hidden" ||\n style.visibility === "collapse"\n ) {\n return false;\n }\n if (opacityChain(element) < 0.2) return false;\n const rect = element.getBoundingClientRect();\n return rect.width > 0.5 && rect.height > 0.5;\n }\n\n function textContentFor(element) {\n return (element.innerText || element.textContent || "").replace(/\\s+/g, " ").trim();\n }\n\n function hasOwnTextCandidate(element) {\n const text = textContentFor(element);\n if (!text) return false;\n for (const child of Array.from(element.children)) {\n if (isVisibleElement(child) && textContentFor(child)) return false;\n }\n return true;\n }\n\n function textRectFor(element) {\n const range = document.createRange();\n range.selectNodeContents(element);\n const rects = Array.from(range.getClientRects()).filter(\n (rect) => rect.width > 0.5 && rect.height > 0.5,\n );\n range.detach();\n if (rects.length === 0) return null;\n\n const union = rects.reduce(\n (acc, rect) => ({\n left: Math.min(acc.left, rect.left),\n top: Math.min(acc.top, rect.top),\n right: Math.max(acc.right, rect.right),\n bottom: Math.max(acc.bottom, rect.bottom),\n }),\n {\n left: Number.POSITIVE_INFINITY,\n top: Number.POSITIVE_INFINITY,\n right: Number.NEGATIVE_INFINITY,\n bottom: Number.NEGATIVE_INFINITY,\n },\n );\n\n return toRect({\n ...union,\n width: union.right - union.left,\n height: union.bottom - union.top,\n });\n }\n\n function parsePx(value) {\n const parsed = Number.parseFloat(value);\n return Number.isFinite(parsed) ? parsed : 0;\n }\n\n function hasMeaningfulBoxStyle(style) {\n return (\n parsePx(style.paddingTop) +\n parsePx(style.paddingRight) +\n parsePx(style.paddingBottom) +\n parsePx(style.paddingLeft) +\n parsePx(style.borderTopWidth) +\n parsePx(style.borderRightWidth) +\n parsePx(style.borderBottomWidth) +\n parsePx(style.borderLeftWidth) +\n parsePx(style.borderTopLeftRadius) +\n parsePx(style.borderTopRightRadius) +\n parsePx(style.borderBottomRightRadius) +\n parsePx(style.borderBottomLeftRadius) >\n 0\n );\n }\n\n function hasPaint(style) {\n const backgroundColor = style.backgroundColor || "";\n const hasBackground =\n backgroundColor !== "" &&\n backgroundColor !== "transparent" &&\n !backgroundColor.endsWith(", 0)") &&\n backgroundColor !== "rgba(0, 0, 0, 0)";\n const hasImage = style.backgroundImage && style.backgroundImage !== "none";\n const hasBorder =\n parsePx(style.borderTopWidth) +\n parsePx(style.borderRightWidth) +\n parsePx(style.borderBottomWidth) +\n parsePx(style.borderLeftWidth) >\n 0;\n const hasRadius =\n parsePx(style.borderTopLeftRadius) +\n parsePx(style.borderTopRightRadius) +\n parsePx(style.borderBottomRightRadius) +\n parsePx(style.borderBottomLeftRadius) >\n 0;\n return hasBackground || hasImage || hasBorder || hasRadius;\n }\n\n function clipsOverflow(style) {\n return [style.overflowX, style.overflowY, style.overflow].some(\n (value) => value && value !== "visible" && value !== "clip visible",\n );\n }\n\n function rootRectFor(root) {\n const measured = toRect(root.getBoundingClientRect());\n const authoredWidth = Number.parseFloat(root.getAttribute("data-width") || "");\n const authoredHeight = Number.parseFloat(root.getAttribute("data-height") || "");\n const hasAuthoredSize =\n Number.isFinite(authoredWidth) &&\n authoredWidth > 0 &&\n Number.isFinite(authoredHeight) &&\n authoredHeight > 0;\n\n if (!hasAuthoredSize) return measured;\n if (measured.width > 0.5 && measured.height > 0.5) return measured;\n return rectFromOrigin(measured.left, measured.top, authoredWidth, authoredHeight);\n }\n\n function isConstraintCandidate(element, root, rootRect) {\n if (element === root) return true;\n const style = getComputedStyle(element);\n if (clipsOverflow(style)) return true;\n if (element.hasAttribute("data-layout-boundary")) return true;\n if (!hasPaint(style)) return false;\n if (!hasMeaningfulBoxStyle(style)) return false;\n const rect = element.getBoundingClientRect();\n const rootArea = rootRect.width * rootRect.height;\n const area = rect.width * rect.height;\n return area > 0 && area < rootArea * 0.95;\n }\n\n function nearestConstraint(element, root, rootRect) {\n for (\n let current = element;\n current && current !== document.body;\n current = current.parentElement\n ) {\n if (!isVisibleElement(current)) continue;\n if (isConstraintCandidate(current, root, rootRect)) return current;\n if (current === root) return current;\n }\n return root;\n }\n\n function formatPx(value) {\n return `${Math.round(value)}px`;\n }\n\n function maxOverflow(overflow) {\n return Math.max(...Object.values(overflow).filter((value) => typeof value === "number"));\n }\n\n function textOverflowFixHint(textRect, containerRect, overflow, fontSize, targetName) {\n const horizontalOverflow = (overflow.left || 0) + (overflow.right || 0);\n const verticalOverflow = (overflow.top || 0) + (overflow.bottom || 0);\n const neededWidth = containerRect.width + horizontalOverflow;\n const neededHeight = containerRect.height + verticalOverflow;\n const widthRatio = containerRect.width > 0 ? containerRect.width / textRect.width : 0;\n const heightRatio = containerRect.height > 0 ? containerRect.height / textRect.height : 0;\n const limitingRatio = Math.min(\n widthRatio > 0 ? widthRatio : Number.POSITIVE_INFINITY,\n heightRatio > 0 ? heightRatio : Number.POSITIVE_INFINITY,\n );\n const shrinkPercent =\n Number.isFinite(limitingRatio) && limitingRatio < 1\n ? Math.ceil((1 - limitingRatio) * 100)\n : 0;\n const targetFont =\n shrinkPercent > 0 && Number.isFinite(fontSize) && fontSize > 0\n ? ` or shrink font-size from ${formatPx(fontSize)} to ~${formatPx(fontSize * limitingRatio)}`\n : "";\n const sizeTarget =\n horizontalOverflow > 0 && verticalOverflow > 0\n ? `resize ${targetName} to at least ~${formatPx(neededWidth)} x ${formatPx(neededHeight)}`\n : horizontalOverflow > 0\n ? `widen ${targetName} to at least ~${formatPx(neededWidth)}`\n : `increase ${targetName} height to at least ~${formatPx(neededHeight)}`;\n\n return `Text is ${formatPx(textRect.width)} x ${formatPx(textRect.height)} inside ${formatPx(containerRect.width)} x ${formatPx(containerRect.height)} and overflows by up to ${formatPx(maxOverflow(overflow))}; ${sizeTarget}${targetFont}, or allow wrapping with max-width/fitTextFontSize.`;\n }\n\n function clippedTextIssue(element, time, tolerance) {\n const style = getComputedStyle(element);\n if (!clipsOverflow(style)) return null;\n const overflowX = element.scrollWidth - element.clientWidth;\n const overflowY = element.scrollHeight - element.clientHeight;\n if (overflowX <= tolerance && overflowY <= tolerance) return null;\n const overflow = {};\n if (overflowX > tolerance) overflow.right = round(overflowX);\n if (overflowY > tolerance) overflow.bottom = round(overflowY);\n const selector = selectorFor(element);\n const text = textContentFor(element);\n const rect = toRect(element.getBoundingClientRect());\n const fontSize = parsePx(style.fontSize);\n return {\n code: "clipped_text",\n severity: "error",\n time,\n selector,\n text,\n message: "Text content is clipped by its own box.",\n rect,\n overflow,\n fixHint: textOverflowFixHint(rect, rect, overflow, fontSize, "the text box"),\n };\n }\n\n function textOverflowIssues(element, root, rootRect, time, tolerance) {\n const textRect = textRectFor(element);\n if (!textRect) return [];\n const text = textContentFor(element);\n const selector = selectorFor(element);\n const issues = [];\n\n const container = nearestConstraint(element, root, rootRect);\n const containerRect = container === root ? rootRect : toRect(container.getBoundingClientRect());\n const containerOverflow = overflowFor(textRect, containerRect, tolerance);\n if (containerOverflow && !hasAllowOverflowFlag(element)) {\n const style = getComputedStyle(element);\n issues.push({\n code: "text_box_overflow",\n severity: "error",\n time,\n selector,\n containerSelector: selectorFor(container),\n text,\n message: "Text extends outside its nearest visual/container box.",\n rect: textRect,\n containerRect,\n overflow: containerOverflow,\n fixHint: textOverflowFixHint(\n textRect,\n containerRect,\n containerOverflow,\n parsePx(style.fontSize),\n "the container",\n ),\n });\n }\n\n const canvasOverflow = overflowFor(textRect, rootRect, tolerance);\n if (canvasOverflow && !hasAllowOverflowFlag(element)) {\n issues.push({\n code: "canvas_overflow",\n severity: "info",\n time,\n selector,\n containerSelector: selectorFor(root),\n text,\n message: "Text extends outside the composition canvas.",\n rect: textRect,\n containerRect: rootRect,\n overflow: canvasOverflow,\n fixHint:\n "Move the text inward, reduce its size, or mark intentional off-canvas animation with data-layout-allow-overflow.",\n });\n }\n\n return issues;\n }\n\n function containerOverflowIssues(root, time, tolerance) {\n const issues = [];\n const containers = Array.from(root.querySelectorAll("*")).filter((element) => {\n if (!isVisibleElement(element) || hasAllowOverflowFlag(element)) return false;\n const style = getComputedStyle(element);\n return clipsOverflow(style) || element.hasAttribute("data-layout-boundary");\n });\n\n for (const container of containers) {\n const containerRect = toRect(container.getBoundingClientRect());\n for (const child of Array.from(container.children)) {\n if (!isVisibleElement(child) || hasAllowOverflowFlag(child)) continue;\n const childRect = toRect(child.getBoundingClientRect());\n const overflow = overflowFor(childRect, containerRect, tolerance);\n if (!overflow) continue;\n issues.push({\n code: "container_overflow",\n severity: "warning",\n time,\n selector: selectorFor(child),\n containerSelector: selectorFor(container),\n message: "Element extends outside a clipping layout container.",\n rect: childRect,\n containerRect,\n overflow,\n fixHint:\n "Resize/reposition the child or container, or mark intentional overflow with data-layout-allow-overflow.",\n });\n }\n }\n\n return issues;\n }\n\n window.__hyperframesLayoutAudit = function auditLayout(options) {\n const time = options && typeof options.time === "number" ? options.time : 0;\n const tolerance =\n options && typeof options.tolerance === "number" ? Math.max(0, options.tolerance) : 2;\n const root =\n document.querySelector("[data-composition-id][data-width][data-height]") ||\n document.querySelector("[data-composition-id]") ||\n document.body;\n const rootRect = rootRectFor(root);\n const elements = Array.from(root.querySelectorAll("*")).filter(isVisibleElement);\n const issues = [];\n\n for (const element of elements) {\n if (!hasOwnTextCandidate(element)) continue;\n const clipped = clippedTextIssue(element, time, tolerance);\n if (clipped) issues.push(clipped);\n issues.push(...textOverflowIssues(element, root, rootRect, time, tolerance));\n }\n\n issues.push(...containerOverflowIssues(root, time, tolerance));\n return issues;\n };\n})();\n';
84697
+ }
84698
+ });
84699
+
84110
84700
  // import("./commands/**/*.js") in src/help.ts
84111
84701
  var globImport_commands_js;
84112
84702
  var init_ = __esm({
84113
84703
  'import("./commands/**/*.js") in src/help.ts'() {
84114
84704
  globImport_commands_js = __glob({
84115
- "./commands/contrast-audit.browser.js": () => Promise.resolve().then(() => (init_contrast_audit_browser(), contrast_audit_browser_exports))
84705
+ "./commands/contrast-audit.browser.js": () => Promise.resolve().then(() => (init_contrast_audit_browser(), contrast_audit_browser_exports)),
84706
+ "./commands/layout-audit.browser.js": () => Promise.resolve().then(() => (init_layout_audit_browser(), layout_audit_browser_exports))
84116
84707
  });
84117
84708
  }
84118
84709
  });
@@ -84155,10 +84746,10 @@ function renderRootHelp() {
84155
84746
  lines.push(`Run ${c.cyan("hyperframes <command> --help")} for more information about a command.`);
84156
84747
  return lines.join("\n");
84157
84748
  }
84158
- function formatExamples(examples21) {
84749
+ function formatExamples(examples23) {
84159
84750
  const lines = [];
84160
84751
  lines.push(c.bold("Examples:"));
84161
- for (const [comment, command2] of examples21) {
84752
+ for (const [comment, command2] of examples23) {
84162
84753
  lines.push(` ${c.gray(`# ${comment}`)}`);
84163
84754
  lines.push(` ${command2}`);
84164
84755
  lines.push("");
@@ -84175,9 +84766,9 @@ async function showUsage2(cmd, parent) {
84175
84766
  console.log(usage + "\n");
84176
84767
  const name = meta?.name;
84177
84768
  if (name) {
84178
- const examples21 = STATIC_EXAMPLES[name] ?? await loadExamples(name);
84179
- if (examples21) {
84180
- console.log(formatExamples(examples21) + "\n");
84769
+ const examples23 = STATIC_EXAMPLES[name] ?? await loadExamples(name);
84770
+ if (examples23) {
84771
+ console.log(formatExamples(examples23) + "\n");
84181
84772
  }
84182
84773
  }
84183
84774
  }
@@ -84206,6 +84797,7 @@ var init_help = __esm({
84206
84797
  title: "Project",
84207
84798
  commands: [
84208
84799
  ["lint", "Validate a composition for common mistakes"],
84800
+ ["inspect", "Inspect rendered visual layout across the timeline"],
84209
84801
  ["snapshot", "Capture key frames as PNG screenshots for visual verification"],
84210
84802
  ["info", "Print project metadata"],
84211
84803
  ["compositions", "List all compositions in a project"],
@@ -84247,6 +84839,7 @@ var init_help = __esm({
84247
84839
  ["Render to MP4", "hyperframes render -o out.mp4"],
84248
84840
  ["Transparent WebM overlay", "hyperframes render --format webm -o out.webm"],
84249
84841
  ["Validate your composition", "hyperframes lint"],
84842
+ ["Inspect visual layout", "hyperframes inspect"],
84250
84843
  ["Check system dependencies", "hyperframes doctor"]
84251
84844
  ];
84252
84845
  STATIC_EXAMPLES = {
@@ -84272,6 +84865,8 @@ var subCommands = {
84272
84865
  publish: () => Promise.resolve().then(() => (init_publish(), publish_exports)).then((m2) => m2.default),
84273
84866
  render: () => Promise.resolve().then(() => (init_render2(), render_exports)).then((m2) => m2.default),
84274
84867
  lint: () => Promise.resolve().then(() => (init_lint3(), lint_exports2)).then((m2) => m2.default),
84868
+ inspect: () => Promise.resolve().then(() => (init_inspect(), inspect_exports)).then((m2) => m2.default),
84869
+ layout: () => Promise.resolve().then(() => (init_layout2(), layout_exports)).then((m2) => m2.default),
84275
84870
  info: () => Promise.resolve().then(() => (init_info(), info_exports)).then((m2) => m2.default),
84276
84871
  compositions: () => Promise.resolve().then(() => (init_compositions(), compositions_exports)).then((m2) => m2.default),
84277
84872
  benchmark: () => Promise.resolve().then(() => (init_benchmark(), benchmark_exports)).then((m2) => m2.default),