hyperframes 0.4.38 → 0.5.0-alpha.10

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.38" : "0.0.0-dev";
57
+ VERSION = true ? "0.5.0-alpha.10" : "0.0.0-dev";
58
58
  }
59
59
  });
60
60
 
@@ -5486,7 +5486,7 @@ var init_captions = __esm({
5486
5486
  const hasHardKill = /\.set\s*\([^,]+,\s*\{[^}]*(?:visibility\s*:\s*["']hidden["']|opacity\s*:\s*0)/.test(
5487
5487
  content
5488
5488
  );
5489
- const hasCaptionLoop = /forEach|\.forEach\s*\(/.test(content) && /createElement|caption|group|cg-/.test(content);
5489
+ const hasCaptionLoop = /forEach|\.forEach\s*\(/.test(content) && /karaoke|caption[-_]?(?:group|word|line|block)|cg-/.test(content);
5490
5490
  if (hasCaptionLoop && hasExitTween && !hasHardKill) {
5491
5491
  findings.push({
5492
5492
  code: "caption_exit_missing_hard_kill",
@@ -6182,6 +6182,18 @@ function rewriteAssetPaths(elements, compSrcPath, getAttr2, setAttr) {
6182
6182
  }
6183
6183
  }
6184
6184
  }
6185
+ function rewriteInlineStyleAssetUrls(elements, compSrcPath, getStyle, setStyle) {
6186
+ const compDir = dirname(compSrcPath);
6187
+ if (!compDir || compDir === ".") return;
6188
+ for (const el of elements) {
6189
+ const style = getStyle(el);
6190
+ if (!style) continue;
6191
+ const rewritten = rewriteCssAssetUrls(style, compSrcPath);
6192
+ if (rewritten !== style) {
6193
+ setStyle(el, rewritten);
6194
+ }
6195
+ }
6196
+ }
6185
6197
  function rewriteCssAssetUrls(cssText, compSrcPath) {
6186
6198
  if (!cssText) return cssText;
6187
6199
  return cssText.replace(CSS_URL_RE, (full, quote, rawUrl) => {
@@ -11636,7 +11648,7 @@ function registerProjectRoutes(api, adapter2) {
11636
11648
  const project = await adapter2.resolveProject(c2.req.param("id"));
11637
11649
  if (!project) return c2.json({ error: "not found" }, 404);
11638
11650
  const files = walkDir(project.dir);
11639
- return c2.json({ id: project.id, files });
11651
+ return c2.json({ id: project.id, dir: project.dir, title: project.title, files });
11640
11652
  });
11641
11653
  }
11642
11654
  var init_projects = __esm({
@@ -24751,6 +24763,14 @@ function buildSubCompositionHtml(projectDir, compPath, runtimeUrl, baseHref) {
24751
24763
  el.setAttribute(attr, value);
24752
24764
  }
24753
24765
  );
24766
+ rewriteInlineStyleAssetUrls(
24767
+ contentDoc.querySelectorAll("[style]"),
24768
+ compPath,
24769
+ (el) => el.getAttribute("style"),
24770
+ (el, value) => {
24771
+ el.setAttribute("style", value);
24772
+ }
24773
+ );
24754
24774
  for (const styleEl of contentDoc.querySelectorAll("style")) {
24755
24775
  styleEl.textContent = rewriteCssAssetUrls(styleEl.textContent || "", compPath);
24756
24776
  }
@@ -25145,7 +25165,7 @@ var init_render = __esm({
25145
25165
  });
25146
25166
 
25147
25167
  // ../core/src/studio-api/routes/thumbnail.ts
25148
- import { existsSync as existsSync14, readFileSync as readFileSync15, writeFileSync as writeFileSync9, mkdirSync as mkdirSync8 } from "fs";
25168
+ import { existsSync as existsSync14, readFileSync as readFileSync15, writeFileSync as writeFileSync9, mkdirSync as mkdirSync8, statSync as statSync4 } from "fs";
25149
25169
  import { join as join17 } from "path";
25150
25170
  function registerThumbnailRoutes(api, adapter2) {
25151
25171
  api.get("/projects/:id/thumbnail/*", async (c2) => {
@@ -25163,13 +25183,16 @@ function registerThumbnailRoutes(api, adapter2) {
25163
25183
  const vpWidth = parseInt(url.searchParams.get("w") || "0") || 0;
25164
25184
  const vpHeight = parseInt(url.searchParams.get("h") || "0") || 0;
25165
25185
  const selector = url.searchParams.get("selector") || void 0;
25166
- const format = url.searchParams.get("format") === "png" ? "png" : "jpeg";
25167
- const contentType = format === "png" ? "image/png" : "image/jpeg";
25186
+ const rawSelectorIndex = Number.parseInt(url.searchParams.get("selectorIndex") || "0", 10);
25187
+ const selectorIndex = Number.isFinite(rawSelectorIndex) && rawSelectorIndex > 0 ? rawSelectorIndex : void 0;
25188
+ const urlVersion = url.searchParams.get("v") || "";
25168
25189
  let compW = vpWidth || 1920;
25169
25190
  let compH = vpHeight || 1080;
25191
+ let sourceMtime = 0;
25170
25192
  if (!vpWidth) {
25171
25193
  const htmlFile = join17(project.dir, compPath);
25172
25194
  if (existsSync14(htmlFile)) {
25195
+ sourceMtime = Math.round(statSync4(htmlFile).mtimeMs);
25173
25196
  const html = readFileSync15(htmlFile, "utf-8");
25174
25197
  const wMatch = html.match(/data-width=["'](\d+)["']/);
25175
25198
  const hMatch = html.match(/data-height=["'](\d+)["']/);
@@ -25179,12 +25202,13 @@ function registerThumbnailRoutes(api, adapter2) {
25179
25202
  }
25180
25203
  const previewUrl = compPath === "index.html" ? `http://${c2.req.header("host")}/api/projects/${project.id}/preview` : `http://${c2.req.header("host")}/api/projects/${project.id}/preview/comp/${compPath}`;
25181
25204
  const cacheDir = join17(project.dir, ".thumbnails");
25182
- const selectorKey = selector ? `_${selector.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 80)}` : "";
25183
- const cacheKey = `${THUMBNAIL_CACHE_VERSION}_${format}_${compPath.replace(/\//g, "_")}_${seekTime.toFixed(2)}${selectorKey}.${format === "png" ? "png" : "jpg"}`;
25205
+ const selectorKey = selector ? `_${selector.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 80)}_${selectorIndex ?? 0}` : "";
25206
+ const urlVersionKey = urlVersion ? `_${urlVersion.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32)}` : "";
25207
+ const cacheKey = `${THUMBNAIL_CACHE_VERSION}${urlVersionKey}_${compPath.replace(/\//g, "_")}_${compW}x${compH}_${sourceMtime}_${seekTime.toFixed(2)}${selectorKey}.jpg`;
25184
25208
  const cachePath2 = join17(cacheDir, cacheKey);
25185
25209
  if (existsSync14(cachePath2)) {
25186
25210
  return new Response(new Uint8Array(readFileSync15(cachePath2)), {
25187
- headers: { "Content-Type": contentType, "Cache-Control": "public, max-age=60" }
25211
+ headers: { "Content-Type": "image/jpeg", "Cache-Control": "public, max-age=60" }
25188
25212
  });
25189
25213
  }
25190
25214
  try {
@@ -25196,7 +25220,7 @@ function registerThumbnailRoutes(api, adapter2) {
25196
25220
  height: compH,
25197
25221
  previewUrl,
25198
25222
  selector,
25199
- format
25223
+ selectorIndex
25200
25224
  });
25201
25225
  if (!buffer) {
25202
25226
  return c2.json({ error: "Thumbnail generation returned null" }, 500);
@@ -25204,7 +25228,7 @@ function registerThumbnailRoutes(api, adapter2) {
25204
25228
  if (!existsSync14(cacheDir)) mkdirSync8(cacheDir, { recursive: true });
25205
25229
  writeFileSync9(cachePath2, buffer);
25206
25230
  return new Response(new Uint8Array(buffer), {
25207
- headers: { "Content-Type": contentType, "Cache-Control": "public, max-age=60" }
25231
+ headers: { "Content-Type": "image/jpeg", "Cache-Control": "public, max-age=60" }
25208
25232
  });
25209
25233
  } catch (err) {
25210
25234
  const msg = err instanceof Error ? err.message : String(err);
@@ -25216,7 +25240,7 @@ var THUMBNAIL_CACHE_VERSION;
25216
25240
  var init_thumbnail = __esm({
25217
25241
  "../core/src/studio-api/routes/thumbnail.ts"() {
25218
25242
  "use strict";
25219
- THUMBNAIL_CACHE_VERSION = "v2";
25243
+ THUMBNAIL_CACHE_VERSION = "v3";
25220
25244
  }
25221
25245
  });
25222
25246
 
@@ -25264,6 +25288,218 @@ var init_waveform2 = __esm({
25264
25288
  }
25265
25289
  });
25266
25290
 
25291
+ // ../core/src/studio-api/routes/fonts.ts
25292
+ import { execFileSync as execFileSync4 } from "child_process";
25293
+ import { existsSync as existsSync16, readdirSync as readdirSync6, statSync as statSync5 } from "fs";
25294
+ import { homedir as homedir4, platform as platform3 } from "os";
25295
+ import { join as join19 } from "path";
25296
+ function isRecord(value) {
25297
+ return typeof value === "object" && value !== null;
25298
+ }
25299
+ function fontDirectories() {
25300
+ const home = homedir4();
25301
+ if (platform3() === "darwin") {
25302
+ return [
25303
+ join19(home, "Library", "Fonts"),
25304
+ "/Library/Fonts",
25305
+ "/System/Library/Fonts",
25306
+ "/System/Library/Fonts/Supplemental"
25307
+ ];
25308
+ }
25309
+ if (platform3() === "win32") {
25310
+ return [join19(process.env.WINDIR || "C:\\Windows", "Fonts")];
25311
+ }
25312
+ return [
25313
+ join19(home, ".fonts"),
25314
+ join19(home, ".local", "share", "fonts"),
25315
+ "/usr/local/share/fonts",
25316
+ "/usr/share/fonts"
25317
+ ];
25318
+ }
25319
+ function toFamilyName(fileName) {
25320
+ const withoutExt = fileName.replace(FONT_EXT_RE, "");
25321
+ if (!withoutExt || withoutExt.startsWith(".")) return null;
25322
+ const spaced = withoutExt.replace(/[_-]+/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/\s+/g, " ").trim();
25323
+ const words = spaced.split(" ").filter(Boolean);
25324
+ while (words.length > 1 && STYLE_SUFFIXES.has((words.at(-1) ?? "").toLowerCase())) {
25325
+ words.pop();
25326
+ }
25327
+ const family = words.join(" ").trim();
25328
+ return family.length >= 2 ? family : null;
25329
+ }
25330
+ function collectMacSystemProfilerFonts() {
25331
+ if (platform3() !== "darwin") return [];
25332
+ let parsed;
25333
+ try {
25334
+ const raw = execFileSync4("system_profiler", ["SPFontsDataType", "-json"], {
25335
+ encoding: "utf8",
25336
+ maxBuffer: 12 * 1024 * 1024,
25337
+ timeout: 5e3
25338
+ });
25339
+ parsed = JSON.parse(raw);
25340
+ } catch {
25341
+ return [];
25342
+ }
25343
+ if (!isRecord(parsed) || !Array.isArray(parsed.SPFontsDataType)) return [];
25344
+ const fonts = [];
25345
+ for (const fontEntry of parsed.SPFontsDataType) {
25346
+ if (!isRecord(fontEntry)) continue;
25347
+ const typefaces = fontEntry.typefaces;
25348
+ if (!Array.isArray(typefaces)) continue;
25349
+ for (const typeface of typefaces) {
25350
+ if (!isRecord(typeface)) continue;
25351
+ const family = typeface.family;
25352
+ const fullName = typeface.fullname;
25353
+ const name = typeface._name;
25354
+ if (typeof family === "string" && family.trim()) {
25355
+ fonts.push(family.trim());
25356
+ } else if (typeof fullName === "string" && fullName.trim()) {
25357
+ fonts.push(fullName.trim());
25358
+ } else if (typeof name === "string" && name.trim()) {
25359
+ fonts.push(name.trim());
25360
+ }
25361
+ }
25362
+ }
25363
+ return fonts;
25364
+ }
25365
+ function collectFontsFromDir(dir, depth = 0) {
25366
+ if (!existsSync16(dir) || depth > 2) return [];
25367
+ const fonts = [];
25368
+ for (const entry of readdirSync6(dir, { withFileTypes: true })) {
25369
+ const fullPath = join19(dir, entry.name);
25370
+ if (entry.isDirectory()) {
25371
+ fonts.push(...collectFontsFromDir(fullPath, depth + 1));
25372
+ continue;
25373
+ }
25374
+ if (!entry.isFile() || !FONT_EXT_RE.test(entry.name)) continue;
25375
+ try {
25376
+ if (!statSync5(fullPath).isFile()) continue;
25377
+ } catch {
25378
+ continue;
25379
+ }
25380
+ const family = toFamilyName(entry.name);
25381
+ if (family) fonts.push(family);
25382
+ }
25383
+ return fonts;
25384
+ }
25385
+ function listInstalledFontFamilies() {
25386
+ if (cachedFonts) return cachedFonts;
25387
+ const families = /* @__PURE__ */ new Set();
25388
+ for (const family of collectMacSystemProfilerFonts()) {
25389
+ families.add(family);
25390
+ if (families.size >= MAX_FONT_RESULTS) break;
25391
+ }
25392
+ for (const dir of fontDirectories()) {
25393
+ for (const family of collectFontsFromDir(dir)) {
25394
+ families.add(family);
25395
+ if (families.size >= MAX_FONT_RESULTS) break;
25396
+ }
25397
+ if (families.size >= MAX_FONT_RESULTS) break;
25398
+ }
25399
+ cachedFonts = Array.from(families).sort((a, b) => a.localeCompare(b));
25400
+ return cachedFonts;
25401
+ }
25402
+ function parseGoogleFontMetadata(value) {
25403
+ if (!isRecord(value) || !Array.isArray(value.familyMetadataList)) return [];
25404
+ const families = [];
25405
+ for (const entry of value.familyMetadataList) {
25406
+ if (!isRecord(entry) || typeof entry.family !== "string") continue;
25407
+ families.push(entry.family);
25408
+ }
25409
+ return families;
25410
+ }
25411
+ function stripGoogleJsonGuard(raw) {
25412
+ const prefix = ")]}'";
25413
+ if (!raw.startsWith(prefix)) return raw;
25414
+ let index = prefix.length;
25415
+ while (index < raw.length && (raw[index] === " " || raw[index] === "\n" || raw[index] === "\r" || raw[index] === " " || raw[index] === "\f")) {
25416
+ index += 1;
25417
+ }
25418
+ return raw.slice(index);
25419
+ }
25420
+ async function listGoogleFontFamilies() {
25421
+ if (cachedGoogleFonts) return cachedGoogleFonts;
25422
+ const controller = new AbortController();
25423
+ const timer = setTimeout(() => controller.abort(), GOOGLE_FONTS_FETCH_TIMEOUT_MS);
25424
+ try {
25425
+ const response = await fetch(GOOGLE_FONTS_METADATA_URL, { signal: controller.signal });
25426
+ if (!response.ok) {
25427
+ cachedGoogleFonts = GOOGLE_FONT_FALLBACKS;
25428
+ return cachedGoogleFonts;
25429
+ }
25430
+ const raw = await response.text();
25431
+ const jsonText = stripGoogleJsonGuard(raw);
25432
+ const families = parseGoogleFontMetadata(JSON.parse(jsonText));
25433
+ cachedGoogleFonts = families.length > 0 ? families : GOOGLE_FONT_FALLBACKS;
25434
+ } catch {
25435
+ cachedGoogleFonts = GOOGLE_FONT_FALLBACKS;
25436
+ } finally {
25437
+ clearTimeout(timer);
25438
+ }
25439
+ return cachedGoogleFonts;
25440
+ }
25441
+ function registerFontRoutes(api) {
25442
+ api.get("/fonts", (c2) => c2.json({ fonts: listInstalledFontFamilies() }));
25443
+ api.get("/fonts/google", async (c2) => c2.json({ fonts: await listGoogleFontFamilies() }));
25444
+ }
25445
+ var FONT_EXT_RE, MAX_FONT_RESULTS, GOOGLE_FONTS_METADATA_URL, GOOGLE_FONTS_FETCH_TIMEOUT_MS, cachedFonts, cachedGoogleFonts, STYLE_SUFFIXES, GOOGLE_FONT_FALLBACKS;
25446
+ var init_fonts = __esm({
25447
+ "../core/src/studio-api/routes/fonts.ts"() {
25448
+ "use strict";
25449
+ FONT_EXT_RE = /\.(otf|ttf|ttc|woff2?)$/i;
25450
+ MAX_FONT_RESULTS = 2e3;
25451
+ GOOGLE_FONTS_METADATA_URL = "https://fonts.google.com/metadata/fonts";
25452
+ GOOGLE_FONTS_FETCH_TIMEOUT_MS = 3e3;
25453
+ cachedFonts = null;
25454
+ cachedGoogleFonts = null;
25455
+ STYLE_SUFFIXES = /* @__PURE__ */ new Set([
25456
+ "black",
25457
+ "bold",
25458
+ "book",
25459
+ "condensed",
25460
+ "demi",
25461
+ "demibold",
25462
+ "display",
25463
+ "extra",
25464
+ "extrabold",
25465
+ "hairline",
25466
+ "heavy",
25467
+ "italic",
25468
+ "light",
25469
+ "medium",
25470
+ "normal",
25471
+ "regular",
25472
+ "roman",
25473
+ "semibold",
25474
+ "thin",
25475
+ "ultra",
25476
+ "ultralight"
25477
+ ]);
25478
+ GOOGLE_FONT_FALLBACKS = [
25479
+ "Inter",
25480
+ "Roboto",
25481
+ "Open Sans",
25482
+ "Montserrat",
25483
+ "Poppins",
25484
+ "Lato",
25485
+ "Oswald",
25486
+ "Raleway",
25487
+ "Nunito",
25488
+ "Playfair Display",
25489
+ "Merriweather",
25490
+ "Source Sans 3",
25491
+ "Source Serif 4",
25492
+ "Source Code Pro",
25493
+ "DM Sans",
25494
+ "Space Grotesk",
25495
+ "Space Mono",
25496
+ "Bebas Neue",
25497
+ "Outfit",
25498
+ "JetBrains Mono"
25499
+ ];
25500
+ }
25501
+ });
25502
+
25267
25503
  // ../core/src/studio-api/createStudioApi.ts
25268
25504
  import { Hono } from "hono";
25269
25505
  function createStudioApi(adapter2) {
@@ -25275,6 +25511,7 @@ function createStudioApi(adapter2) {
25275
25511
  registerRenderRoutes(api, adapter2);
25276
25512
  registerThumbnailRoutes(api, adapter2);
25277
25513
  registerWaveformRoutes(api, adapter2);
25514
+ registerFontRoutes(api);
25278
25515
  return api;
25279
25516
  }
25280
25517
  var init_createStudioApi = __esm({
@@ -25287,6 +25524,7 @@ var init_createStudioApi = __esm({
25287
25524
  init_render();
25288
25525
  init_thumbnail();
25289
25526
  init_waveform2();
25527
+ init_fonts();
25290
25528
  }
25291
25529
  });
25292
25530
 
@@ -25321,9 +25559,9 @@ __export(manager_exports2, {
25321
25559
  setBrowserPath: () => setBrowserPath
25322
25560
  });
25323
25561
  import { execSync } from "child_process";
25324
- import { existsSync as existsSync16, rmSync as rmSync4 } from "fs";
25325
- import { homedir as homedir4 } from "os";
25326
- import { join as join19 } from "path";
25562
+ import { existsSync as existsSync17, rmSync as rmSync4 } from "fs";
25563
+ import { homedir as homedir5 } from "os";
25564
+ import { join as join20 } from "path";
25327
25565
  import { Browser, detectBrowserPlatform, getInstalledBrowsers, install } from "@puppeteer/browsers";
25328
25566
  function setBrowserPath(path2) {
25329
25567
  _browserPathOverride = path2;
@@ -25343,17 +25581,17 @@ function whichBinary2(name) {
25343
25581
  }
25344
25582
  }
25345
25583
  function findFromEnv2() {
25346
- if (_browserPathOverride && existsSync16(_browserPathOverride)) {
25584
+ if (_browserPathOverride && existsSync17(_browserPathOverride)) {
25347
25585
  return { executablePath: _browserPathOverride, source: "env" };
25348
25586
  }
25349
25587
  const envPath = process.env["HYPERFRAMES_BROWSER_PATH"];
25350
- if (envPath && existsSync16(envPath)) {
25588
+ if (envPath && existsSync17(envPath)) {
25351
25589
  return { executablePath: envPath, source: "env" };
25352
25590
  }
25353
25591
  return void 0;
25354
25592
  }
25355
25593
  async function findFromCache() {
25356
- if (!existsSync16(CACHE_DIR2)) {
25594
+ if (!existsSync17(CACHE_DIR2)) {
25357
25595
  return void 0;
25358
25596
  }
25359
25597
  const installed = await getInstalledBrowsers({ cacheDir: CACHE_DIR2 });
@@ -25365,7 +25603,7 @@ async function findFromCache() {
25365
25603
  }
25366
25604
  function findFromSystem2() {
25367
25605
  for (const p of SYSTEM_CHROME_PATHS) {
25368
- if (existsSync16(p)) {
25606
+ if (existsSync17(p)) {
25369
25607
  return { executablePath: p, source: "system" };
25370
25608
  }
25371
25609
  }
@@ -25385,21 +25623,21 @@ async function findBrowser() {
25385
25623
  async function ensureBrowser(options) {
25386
25624
  const existing = await findBrowser();
25387
25625
  if (existing) return existing;
25388
- const platform5 = detectBrowserPlatform();
25389
- if (!platform5) {
25626
+ const platform6 = detectBrowserPlatform();
25627
+ if (!platform6) {
25390
25628
  throw new Error(`Unsupported platform: ${process.platform} ${process.arch}`);
25391
25629
  }
25392
25630
  const installed = await install({
25393
25631
  cacheDir: CACHE_DIR2,
25394
25632
  browser: Browser.CHROMEHEADLESSSHELL,
25395
25633
  buildId: CHROME_VERSION,
25396
- platform: platform5,
25634
+ platform: platform6,
25397
25635
  downloadProgressCallback: options?.onProgress
25398
25636
  });
25399
25637
  return { executablePath: installed.executablePath, source: "download" };
25400
25638
  }
25401
25639
  function clearBrowser() {
25402
- if (!existsSync16(CACHE_DIR2)) {
25640
+ if (!existsSync17(CACHE_DIR2)) {
25403
25641
  return false;
25404
25642
  }
25405
25643
  rmSync4(CACHE_DIR2, { recursive: true, force: true });
@@ -25410,7 +25648,7 @@ var init_manager2 = __esm({
25410
25648
  "src/browser/manager.ts"() {
25411
25649
  "use strict";
25412
25650
  CHROME_VERSION = "131.0.6778.85";
25413
- CACHE_DIR2 = join19(homedir4(), ".cache", "hyperframes", "chrome");
25651
+ CACHE_DIR2 = join20(homedir5(), ".cache", "hyperframes", "chrome");
25414
25652
  SYSTEM_CHROME_PATHS = process.platform === "darwin" ? ["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"] : [
25415
25653
  "/usr/bin/google-chrome",
25416
25654
  "/usr/bin/google-chrome-stable",
@@ -25537,9 +25775,9 @@ var init_config2 = __esm({
25537
25775
  });
25538
25776
 
25539
25777
  // ../engine/src/services/browserManager.ts
25540
- import { existsSync as existsSync17, readdirSync as readdirSync6 } from "fs";
25541
- import { join as join20 } from "path";
25542
- import { homedir as homedir5 } from "os";
25778
+ import { existsSync as existsSync18, readdirSync as readdirSync7 } from "fs";
25779
+ import { join as join21 } from "path";
25780
+ import { homedir as homedir6 } from "os";
25543
25781
  async function getPuppeteer() {
25544
25782
  if (_puppeteer) return _puppeteer;
25545
25783
  try {
@@ -25559,19 +25797,19 @@ function resolveHeadlessShellPath(config) {
25559
25797
  if (process.env.PRODUCER_HEADLESS_SHELL_PATH) {
25560
25798
  return process.env.PRODUCER_HEADLESS_SHELL_PATH;
25561
25799
  }
25562
- const baseDir = join20(homedir5(), ".cache", "puppeteer", "chrome-headless-shell");
25563
- if (!existsSync17(baseDir)) return void 0;
25800
+ const baseDir = join21(homedir6(), ".cache", "puppeteer", "chrome-headless-shell");
25801
+ if (!existsSync18(baseDir)) return void 0;
25564
25802
  try {
25565
- const versions = readdirSync6(baseDir).sort().reverse();
25803
+ const versions = readdirSync7(baseDir).sort().reverse();
25566
25804
  for (const version of versions) {
25567
25805
  const candidates = [
25568
- join20(baseDir, version, "chrome-headless-shell-linux64", "chrome-headless-shell"),
25569
- join20(baseDir, version, "chrome-headless-shell-mac-arm64", "chrome-headless-shell"),
25570
- join20(baseDir, version, "chrome-headless-shell-mac-x64", "chrome-headless-shell"),
25571
- join20(baseDir, version, "chrome-headless-shell-win64", "chrome-headless-shell.exe")
25806
+ join21(baseDir, version, "chrome-headless-shell-linux64", "chrome-headless-shell"),
25807
+ join21(baseDir, version, "chrome-headless-shell-mac-arm64", "chrome-headless-shell"),
25808
+ join21(baseDir, version, "chrome-headless-shell-mac-x64", "chrome-headless-shell"),
25809
+ join21(baseDir, version, "chrome-headless-shell-win64", "chrome-headless-shell.exe")
25572
25810
  ];
25573
25811
  for (const binary of candidates) {
25574
- if (existsSync17(binary)) return binary;
25812
+ if (existsSync18(binary)) return binary;
25575
25813
  }
25576
25814
  }
25577
25815
  } catch {
@@ -26034,10 +26272,10 @@ var init_screenshotService = __esm({
26034
26272
  });
26035
26273
 
26036
26274
  // ../engine/src/services/frameCapture.ts
26037
- import { existsSync as existsSync18, mkdirSync as mkdirSync10, writeFileSync as writeFileSync11 } from "fs";
26038
- import { join as join21 } from "path";
26275
+ import { existsSync as existsSync19, mkdirSync as mkdirSync10, writeFileSync as writeFileSync11 } from "fs";
26276
+ import { join as join22 } from "path";
26039
26277
  async function createCaptureSession(serverUrl, outputDir, options, onBeforeCapture = null, config) {
26040
- if (!existsSync18(outputDir)) mkdirSync10(outputDir, { recursive: true });
26278
+ if (!existsSync19(outputDir)) mkdirSync10(outputDir, { recursive: true });
26041
26279
  const headlessShell = resolveHeadlessShellPath(config);
26042
26280
  const isLinux = process.platform === "linux";
26043
26281
  const forceScreenshot = config?.forceScreenshot ?? DEFAULT_CONFIG2.forceScreenshot;
@@ -26241,9 +26479,9 @@ async function initializeSession(session) {
26241
26479
  }
26242
26480
  async function captureFrameErrorDiagnostics(session, frameIndex, time, error) {
26243
26481
  try {
26244
- const diagnosticsDir = join21(session.outputDir, "diagnostics");
26245
- if (!existsSync18(diagnosticsDir)) mkdirSync10(diagnosticsDir, { recursive: true });
26246
- const base = join21(diagnosticsDir, `frame-error-${frameIndex}`);
26482
+ const diagnosticsDir = join22(session.outputDir, "diagnostics");
26483
+ if (!existsSync19(diagnosticsDir)) mkdirSync10(diagnosticsDir, { recursive: true });
26484
+ const base = join22(diagnosticsDir, `frame-error-${frameIndex}`);
26247
26485
  await session.page.screenshot({ path: `${base}.png`, type: "png", fullPage: true });
26248
26486
  const html = await session.page.content();
26249
26487
  writeFileSync11(`${base}.html`, html, "utf-8");
@@ -26341,7 +26579,7 @@ async function captureFrame(session, frameIndex, time) {
26341
26579
  );
26342
26580
  const ext = options.format === "png" ? "png" : "jpg";
26343
26581
  const frameName = `frame_${String(frameIndex).padStart(6, "0")}.${ext}`;
26344
- const framePath = join21(outputDir, frameName);
26582
+ const framePath = join22(outputDir, frameName);
26345
26583
  writeFileSync11(framePath, buffer);
26346
26584
  return { frameIndex, time: quantizedTime, path: framePath, captureTimeMs };
26347
26585
  }
@@ -26362,7 +26600,7 @@ async function closeCaptureSession(session) {
26362
26600
  session.isInitialized = false;
26363
26601
  }
26364
26602
  function prepareCaptureSessionForReuse(session, outputDir, onBeforeCapture) {
26365
- if (!existsSync18(outputDir)) {
26603
+ if (!existsSync19(outputDir)) {
26366
26604
  mkdirSync10(outputDir, { recursive: true });
26367
26605
  }
26368
26606
  session.outputDir = outputDir;
@@ -26600,8 +26838,8 @@ var init_runFfmpeg = __esm({
26600
26838
 
26601
26839
  // ../engine/src/services/chunkEncoder.ts
26602
26840
  import { spawn as spawn5 } from "child_process";
26603
- import { copyFileSync, existsSync as existsSync19, mkdirSync as mkdirSync11, readdirSync as readdirSync7, statSync as statSync4, writeFileSync as writeFileSync12 } from "fs";
26604
- import { join as join22, dirname as dirname7 } from "path";
26841
+ import { copyFileSync, existsSync as existsSync20, mkdirSync as mkdirSync11, readdirSync as readdirSync8, statSync as statSync6, writeFileSync as writeFileSync12 } from "fs";
26842
+ import { join as join23, dirname as dirname7 } from "path";
26605
26843
  function getEncoderPreset(quality, format = "mp4", hdr) {
26606
26844
  const base = ENCODER_PRESETS[quality];
26607
26845
  if (format === "webm") {
@@ -26752,8 +26990,8 @@ function buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder = null) {
26752
26990
  async function encodeFramesFromDir(framesDir, framePattern, outputPath, options, signal, config) {
26753
26991
  const startTime = Date.now();
26754
26992
  const outputDir = dirname7(outputPath);
26755
- if (!existsSync19(outputDir)) mkdirSync11(outputDir, { recursive: true });
26756
- const files = readdirSync7(framesDir).filter((f3) => f3.match(/\.(jpg|jpeg|png)$/i));
26993
+ if (!existsSync20(outputDir)) mkdirSync11(outputDir, { recursive: true });
26994
+ const files = readdirSync8(framesDir).filter((f3) => f3.match(/\.(jpg|jpeg|png)$/i));
26757
26995
  const frameCount = files.length;
26758
26996
  if (frameCount === 0) {
26759
26997
  return {
@@ -26769,7 +27007,7 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
26769
27007
  if (options.useGpu) {
26770
27008
  gpuEncoder = await getCachedGpuEncoder();
26771
27009
  }
26772
- const inputPath = join22(framesDir, framePattern);
27010
+ const inputPath = join23(framesDir, framePattern);
26773
27011
  const inputArgs = ["-framerate", String(options.fps), "-i", inputPath];
26774
27012
  const args = buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder);
26775
27013
  return new Promise((resolve39) => {
@@ -26818,7 +27056,7 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
26818
27056
  });
26819
27057
  return;
26820
27058
  }
26821
- const fileSize = existsSync19(outputPath) ? statSync4(outputPath).size : 0;
27059
+ const fileSize = existsSync20(outputPath) ? statSync6(outputPath).size : 0;
26822
27060
  resolve39({ success: true, outputPath, durationMs, framesEncoded: frameCount, fileSize });
26823
27061
  });
26824
27062
  ffmpeg.on("error", (err) => {
@@ -26837,7 +27075,7 @@ async function encodeFramesFromDir(framesDir, framePattern, outputPath, options,
26837
27075
  }
26838
27076
  async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, options, chunkSizeFrames, signal) {
26839
27077
  const start = Date.now();
26840
- const files = readdirSync7(framesDir).filter((f3) => f3.match(/\.(jpg|jpeg|png)$/i)).sort();
27078
+ const files = readdirSync8(framesDir).filter((f3) => f3.match(/\.(jpg|jpeg|png)$/i)).sort();
26841
27079
  if (files.length === 0) {
26842
27080
  return {
26843
27081
  success: false,
@@ -26850,8 +27088,8 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
26850
27088
  }
26851
27089
  const chunkSize = Math.max(30, Math.floor(chunkSizeFrames));
26852
27090
  const chunkCount = Math.ceil(files.length / chunkSize);
26853
- const chunkDir = join22(dirname7(outputPath), "chunk-encode");
26854
- if (!existsSync19(chunkDir)) mkdirSync11(chunkDir, { recursive: true });
27091
+ const chunkDir = join23(dirname7(outputPath), "chunk-encode");
27092
+ if (!existsSync20(chunkDir)) mkdirSync11(chunkDir, { recursive: true });
26855
27093
  const chunkPaths = [];
26856
27094
  for (let i2 = 0; i2 < chunkCount; i2++) {
26857
27095
  if (signal?.aborted) {
@@ -26867,8 +27105,8 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
26867
27105
  const startNumber = i2 * chunkSize;
26868
27106
  const framesInChunk = Math.min(chunkSize, files.length - startNumber);
26869
27107
  const ext = outputPath.endsWith(".webm") ? ".webm" : outputPath.endsWith(".mov") ? ".mov" : ".mp4";
26870
- const chunkPath = join22(chunkDir, `chunk_${String(i2).padStart(4, "0")}${ext}`);
26871
- const inputPath = join22(framesDir, framePattern);
27108
+ const chunkPath = join23(chunkDir, `chunk_${String(i2).padStart(4, "0")}${ext}`);
27109
+ const inputPath = join23(framesDir, framePattern);
26872
27110
  const inputArgs = [
26873
27111
  "-framerate",
26874
27112
  String(options.fps),
@@ -26908,7 +27146,7 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
26908
27146
  }
26909
27147
  chunkPaths.push(chunkPath);
26910
27148
  }
26911
- const concatListPath = join22(chunkDir, "concat-list.txt");
27149
+ const concatListPath = join23(chunkDir, "concat-list.txt");
26912
27150
  const concatInput = chunkPaths.map((path2) => `file '${path2.replace(/'/g, "'\\''")}'`).join("\n");
26913
27151
  writeFileSync12(concatListPath, concatInput, "utf-8");
26914
27152
  const concatArgs = [
@@ -26947,7 +27185,7 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
26947
27185
  error: concatResult.error
26948
27186
  };
26949
27187
  }
26950
- const fileSize = existsSync19(outputPath) ? statSync4(outputPath).size : 0;
27188
+ const fileSize = existsSync20(outputPath) ? statSync6(outputPath).size : 0;
26951
27189
  return {
26952
27190
  success: true,
26953
27191
  outputPath,
@@ -26958,7 +27196,7 @@ async function encodeFramesChunkedConcat(framesDir, framePattern, outputPath, op
26958
27196
  }
26959
27197
  async function muxVideoWithAudio(videoPath, audioPath, outputPath, signal, config) {
26960
27198
  const outputDir = dirname7(outputPath);
26961
- if (!existsSync19(outputDir)) mkdirSync11(outputDir, { recursive: true });
27199
+ if (!existsSync20(outputDir)) mkdirSync11(outputDir, { recursive: true });
26962
27200
  const isWebm = outputPath.endsWith(".webm");
26963
27201
  const isMov = outputPath.endsWith(".mov");
26964
27202
  const args = ["-i", videoPath, "-i", audioPath, "-c:v", "copy"];
@@ -27029,7 +27267,7 @@ var init_chunkEncoder = __esm({
27029
27267
 
27030
27268
  // ../engine/src/services/streamingEncoder.ts
27031
27269
  import { spawn as spawn6 } from "child_process";
27032
- import { existsSync as existsSync20, mkdirSync as mkdirSync12, statSync as statSync5 } from "fs";
27270
+ import { existsSync as existsSync21, mkdirSync as mkdirSync12, statSync as statSync7 } from "fs";
27033
27271
  import { dirname as dirname8 } from "path";
27034
27272
  function createFrameReorderBuffer(startFrame, endFrame) {
27035
27273
  let cursor = startFrame;
@@ -27212,7 +27450,7 @@ function buildStreamingArgs(options, outputPath, gpuEncoder = null) {
27212
27450
  }
27213
27451
  async function spawnStreamingEncoder(outputPath, options, signal, config) {
27214
27452
  const outputDir = dirname8(outputPath);
27215
- if (!existsSync20(outputDir)) mkdirSync12(outputDir, { recursive: true });
27453
+ if (!existsSync21(outputDir)) mkdirSync12(outputDir, { recursive: true });
27216
27454
  let gpuEncoder = null;
27217
27455
  if (options.useGpu) {
27218
27456
  gpuEncoder = await getCachedGpuEncoder();
@@ -27294,7 +27532,7 @@ Process error: ${err.message}`;
27294
27532
  error: formatFfmpegError(exitCode, stderr)
27295
27533
  };
27296
27534
  }
27297
- const fileSize = existsSync20(outputPath) ? statSync5(outputPath).size : 0;
27535
+ const fileSize = existsSync21(outputPath) ? statSync7(outputPath).size : 0;
27298
27536
  return { success: true, durationMs, fileSize };
27299
27537
  },
27300
27538
  getExitStatus: () => exitStatus
@@ -27586,9 +27824,9 @@ var init_ffprobe = __esm({
27586
27824
  });
27587
27825
 
27588
27826
  // ../engine/src/utils/urlDownloader.ts
27589
- import { createWriteStream as createWriteStream2, existsSync as existsSync21, mkdirSync as mkdirSync13 } from "fs";
27827
+ import { createWriteStream as createWriteStream2, existsSync as existsSync22, mkdirSync as mkdirSync13 } from "fs";
27590
27828
  import { createHash } from "crypto";
27591
- import { join as join23, extname as extname5 } from "path";
27829
+ import { join as join24, extname as extname5 } from "path";
27592
27830
  import { Readable } from "stream";
27593
27831
  import { finished } from "stream/promises";
27594
27832
  function getFilenameFromUrl(url) {
@@ -27599,19 +27837,19 @@ function getFilenameFromUrl(url) {
27599
27837
  }
27600
27838
  async function downloadToTemp(url, destDir, timeoutMs = 3e5) {
27601
27839
  const cachedPath = downloadPathCache.get(url);
27602
- if (cachedPath && existsSync21(cachedPath)) {
27840
+ if (cachedPath && existsSync22(cachedPath)) {
27603
27841
  return cachedPath;
27604
27842
  }
27605
27843
  const inFlight = inFlightDownloads.get(url);
27606
27844
  if (inFlight) {
27607
27845
  return inFlight;
27608
27846
  }
27609
- if (!existsSync21(destDir)) {
27847
+ if (!existsSync22(destDir)) {
27610
27848
  mkdirSync13(destDir, { recursive: true });
27611
27849
  }
27612
27850
  const filename = getFilenameFromUrl(url);
27613
- const localPath = join23(destDir, filename);
27614
- if (existsSync21(localPath)) {
27851
+ const localPath = join24(destDir, filename);
27852
+ if (existsSync22(localPath)) {
27615
27853
  downloadPathCache.set(url, localPath);
27616
27854
  return localPath;
27617
27855
  }
@@ -27703,12 +27941,12 @@ var init_htmlTemplate = __esm({
27703
27941
 
27704
27942
  // ../engine/src/services/extractionCache.ts
27705
27943
  import { createHash as createHash2 } from "crypto";
27706
- import { mkdirSync as mkdirSync14, readdirSync as readdirSync8, statSync as statSync6, writeFileSync as writeFileSync13 } from "fs";
27707
- import { existsSync as existsSync22 } from "fs";
27708
- import { join as join24 } from "path";
27944
+ import { mkdirSync as mkdirSync14, readdirSync as readdirSync9, statSync as statSync8, writeFileSync as writeFileSync13 } from "fs";
27945
+ import { existsSync as existsSync23 } from "fs";
27946
+ import { join as join25 } from "path";
27709
27947
  function readKeyStat(videoPath) {
27710
27948
  try {
27711
- const stat3 = statSync6(videoPath);
27949
+ const stat3 = statSync8(videoPath);
27712
27950
  return { mtimeMs: Math.floor(stat3.mtimeMs), size: stat3.size };
27713
27951
  } catch {
27714
27952
  return null;
@@ -27734,23 +27972,23 @@ function cacheEntryDirName(keyHash) {
27734
27972
  }
27735
27973
  function lookupCacheEntry(rootDir, input) {
27736
27974
  const keyHash = computeCacheKey(input);
27737
- const dir = join24(rootDir, cacheEntryDirName(keyHash));
27738
- const complete = existsSync22(join24(dir, COMPLETE_SENTINEL));
27975
+ const dir = join25(rootDir, cacheEntryDirName(keyHash));
27976
+ const complete = existsSync23(join25(dir, COMPLETE_SENTINEL));
27739
27977
  return { entry: { dir, keyHash }, hit: complete };
27740
27978
  }
27741
27979
  function ensureCacheEntryDir(entry) {
27742
27980
  mkdirSync14(entry.dir, { recursive: true });
27743
27981
  }
27744
27982
  function markCacheEntryComplete(entry) {
27745
- writeFileSync13(join24(entry.dir, COMPLETE_SENTINEL), "", "utf-8");
27983
+ writeFileSync13(join25(entry.dir, COMPLETE_SENTINEL), "", "utf-8");
27746
27984
  }
27747
27985
  function rehydrateCacheEntry(entry, options) {
27748
27986
  const framePattern = `${FRAME_FILENAME_PREFIX}%05d.${options.format}`;
27749
27987
  const framePaths = /* @__PURE__ */ new Map();
27750
27988
  const suffix = `.${options.format}`;
27751
- const files = readdirSync8(entry.dir).filter((f3) => f3.startsWith(FRAME_FILENAME_PREFIX) && f3.endsWith(suffix)).sort();
27989
+ const files = readdirSync9(entry.dir).filter((f3) => f3.startsWith(FRAME_FILENAME_PREFIX) && f3.endsWith(suffix)).sort();
27752
27990
  files.forEach((file, idx) => {
27753
- framePaths.set(idx, join24(entry.dir, file));
27991
+ framePaths.set(idx, join25(entry.dir, file));
27754
27992
  });
27755
27993
  return {
27756
27994
  videoId: options.videoId,
@@ -27776,8 +28014,8 @@ var init_extractionCache = __esm({
27776
28014
 
27777
28015
  // ../engine/src/services/videoFrameExtractor.ts
27778
28016
  import { spawn as spawn8 } from "child_process";
27779
- import { existsSync as existsSync23, mkdirSync as mkdirSync15, readdirSync as readdirSync9, rmSync as rmSync5 } from "fs";
27780
- import { isAbsolute as isAbsolute2, join as join25 } from "path";
28017
+ import { existsSync as existsSync24, mkdirSync as mkdirSync15, readdirSync as readdirSync10, rmSync as rmSync5 } from "fs";
28018
+ import { isAbsolute as isAbsolute2, join as join26 } from "path";
27781
28019
  function parseVideoElements(html) {
27782
28020
  const videos = [];
27783
28021
  const { document: document2 } = parseHTML(unwrapTemplate(html));
@@ -27847,12 +28085,12 @@ function parseImageElements(html) {
27847
28085
  async function extractVideoFramesRange(videoPath, videoId, startTime, duration, options, signal, config, outputDirOverride) {
27848
28086
  const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG2.ffmpegProcessTimeout;
27849
28087
  const { fps, outputDir, quality = 95 } = options;
27850
- const videoOutputDir = outputDirOverride ?? join25(outputDir, videoId);
27851
- if (!existsSync23(videoOutputDir)) mkdirSync15(videoOutputDir, { recursive: true });
28088
+ const videoOutputDir = outputDirOverride ?? join26(outputDir, videoId);
28089
+ if (!existsSync24(videoOutputDir)) mkdirSync15(videoOutputDir, { recursive: true });
27852
28090
  const metadata = await extractMediaMetadata(videoPath);
27853
28091
  const format = resolveFrameFormat(metadata, options.format);
27854
28092
  const framePattern = `${FRAME_FILENAME_PREFIX}%05d.${format}`;
27855
- const outputPattern = join25(videoOutputDir, framePattern);
28093
+ const outputPattern = join26(videoOutputDir, framePattern);
27856
28094
  const isHdr = isHdrColorSpace(metadata.colorSpace);
27857
28095
  const isMacOS = process.platform === "darwin";
27858
28096
  const args = [];
@@ -27903,9 +28141,9 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
27903
28141
  return;
27904
28142
  }
27905
28143
  const framePaths = /* @__PURE__ */ new Map();
27906
- const files = readdirSync9(videoOutputDir).filter((f3) => f3.startsWith(FRAME_FILENAME_PREFIX) && f3.endsWith(`.${format}`)).sort();
28144
+ const files = readdirSync10(videoOutputDir).filter((f3) => f3.startsWith(FRAME_FILENAME_PREFIX) && f3.endsWith(`.${format}`)).sort();
27907
28145
  files.forEach((file, index) => {
27908
- framePaths.set(index, join25(videoOutputDir, file));
28146
+ framePaths.set(index, join26(videoOutputDir, file));
27909
28147
  });
27910
28148
  resolve39({
27911
28149
  videoId,
@@ -28032,15 +28270,15 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
28032
28270
  try {
28033
28271
  let videoPath = video.src;
28034
28272
  if (!isAbsolute2(videoPath) && !isHttpUrl(videoPath)) {
28035
- const fromCompiled = compiledDir ? join25(compiledDir, videoPath) : null;
28036
- videoPath = fromCompiled && existsSync23(fromCompiled) ? fromCompiled : join25(baseDir, videoPath);
28273
+ const fromCompiled = compiledDir ? join26(compiledDir, videoPath) : null;
28274
+ videoPath = fromCompiled && existsSync24(fromCompiled) ? fromCompiled : join26(baseDir, videoPath);
28037
28275
  }
28038
28276
  if (isHttpUrl(videoPath)) {
28039
- const downloadDir = join25(options.outputDir, "_downloads");
28277
+ const downloadDir = join26(options.outputDir, "_downloads");
28040
28278
  mkdirSync15(downloadDir, { recursive: true });
28041
28279
  videoPath = await downloadToTemp(videoPath, downloadDir);
28042
28280
  }
28043
- if (!existsSync23(videoPath)) {
28281
+ if (!existsSync24(videoPath)) {
28044
28282
  errors.push({ videoId: video.id, error: `Video file not found: ${videoPath}` });
28045
28283
  continue;
28046
28284
  }
@@ -28073,7 +28311,7 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
28073
28311
  const hdrSkippedIndices = /* @__PURE__ */ new Set();
28074
28312
  if (hdrInfo.hasHdr && hdrInfo.dominantTransfer) {
28075
28313
  const targetTransfer = hdrInfo.dominantTransfer;
28076
- const convertDir = join25(options.outputDir, "_hdr_normalized");
28314
+ const convertDir = join26(options.outputDir, "_hdr_normalized");
28077
28315
  mkdirSync15(convertDir, { recursive: true });
28078
28316
  for (let i2 = 0; i2 < resolvedVideos.length; i2++) {
28079
28317
  if (signal?.aborted) break;
@@ -28095,7 +28333,7 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
28095
28333
  const sourceRemaining = metadata.durationSeconds - entry.video.mediaStart;
28096
28334
  segDuration = sourceRemaining > 0 ? sourceRemaining : metadata.durationSeconds;
28097
28335
  }
28098
- const convertedPath = join25(convertDir, `${entry.video.id}_hdr.mp4`);
28336
+ const convertedPath = join26(convertDir, `${entry.video.id}_hdr.mp4`);
28099
28337
  try {
28100
28338
  await convertSdrToHdr(
28101
28339
  entry.videoPath,
@@ -28130,7 +28368,7 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
28130
28368
  }
28131
28369
  }
28132
28370
  const vfrPreflightStart = Date.now();
28133
- const vfrNormDir = join25(options.outputDir, "_vfr_normalized");
28371
+ const vfrNormDir = join26(options.outputDir, "_vfr_normalized");
28134
28372
  for (let i2 = 0; i2 < resolvedVideos.length; i2++) {
28135
28373
  if (signal?.aborted) break;
28136
28374
  const entry = resolvedVideos[i2];
@@ -28145,7 +28383,7 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
28145
28383
  segDuration = sourceRemaining > 0 ? sourceRemaining : metadata.durationSeconds;
28146
28384
  }
28147
28385
  mkdirSync15(vfrNormDir, { recursive: true });
28148
- const normalizedPath = join25(vfrNormDir, `${entry.video.id}_cfr.mp4`);
28386
+ const normalizedPath = join26(vfrNormDir, `${entry.video.id}_cfr.mp4`);
28149
28387
  try {
28150
28388
  await convertVfrToCfr(
28151
28389
  entry.videoPath,
@@ -28401,7 +28639,7 @@ var init_videoFrameExtractor = __esm({
28401
28639
  cleanup() {
28402
28640
  for (const video of this.videos.values()) {
28403
28641
  if (video.extracted.ownedByLookup) continue;
28404
- if (existsSync23(video.extracted.outputDir)) {
28642
+ if (existsSync24(video.extracted.outputDir)) {
28405
28643
  rmSync5(video.extracted.outputDir, { recursive: true, force: true });
28406
28644
  }
28407
28645
  }
@@ -28737,8 +28975,8 @@ var init_videoFrameInjector = __esm({
28737
28975
  });
28738
28976
 
28739
28977
  // ../engine/src/services/audioMixer.ts
28740
- import { existsSync as existsSync24, mkdirSync as mkdirSync16, rmSync as rmSync6 } from "fs";
28741
- import { isAbsolute as isAbsolute3, join as join26, dirname as dirname9 } from "path";
28978
+ import { existsSync as existsSync25, mkdirSync as mkdirSync16, rmSync as rmSync6 } from "fs";
28979
+ import { isAbsolute as isAbsolute3, join as join27, dirname as dirname9 } from "path";
28742
28980
  function parseAudioElements(html) {
28743
28981
  const elements = [];
28744
28982
  const { document: document2 } = parseHTML(unwrapTemplate(html));
@@ -28789,7 +29027,7 @@ function parseAudioElements(html) {
28789
29027
  async function extractAudioFromVideo(videoPath, outputPath, options, signal, config) {
28790
29028
  const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG2.ffmpegProcessTimeout;
28791
29029
  const outputDir = dirname9(outputPath);
28792
- if (!existsSync24(outputDir)) mkdirSync16(outputDir, { recursive: true });
29030
+ if (!existsSync25(outputDir)) mkdirSync16(outputDir, { recursive: true });
28793
29031
  const args = ["-i", videoPath];
28794
29032
  if (options?.startTime !== void 0) args.push("-ss", String(options.startTime));
28795
29033
  if (options?.duration !== void 0) args.push("-t", String(options.duration));
@@ -28816,7 +29054,7 @@ async function extractAudioFromVideo(videoPath, outputPath, options, signal, con
28816
29054
  async function prepareAudioTrack(srcPath, outputPath, mediaStart, duration, signal, config) {
28817
29055
  const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG2.ffmpegProcessTimeout;
28818
29056
  const outputDir = dirname9(outputPath);
28819
- if (!existsSync24(outputDir)) mkdirSync16(outputDir, { recursive: true });
29057
+ if (!existsSync25(outputDir)) mkdirSync16(outputDir, { recursive: true });
28820
29058
  const args = [
28821
29059
  "-ss",
28822
29060
  String(mediaStart),
@@ -28852,7 +29090,7 @@ async function prepareAudioTrack(srcPath, outputPath, mediaStart, duration, sign
28852
29090
  async function generateSilence(outputPath, duration, signal, config) {
28853
29091
  const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG2.ffmpegProcessTimeout;
28854
29092
  const outputDir = dirname9(outputPath);
28855
- if (!existsSync24(outputDir)) mkdirSync16(outputDir, { recursive: true });
29093
+ if (!existsSync25(outputDir)) mkdirSync16(outputDir, { recursive: true });
28856
29094
  const args = [
28857
29095
  "-f",
28858
29096
  "lavfi",
@@ -28895,7 +29133,7 @@ async function mixAudioTracks(tracks, outputPath, totalDuration, signal, config)
28895
29133
  };
28896
29134
  }
28897
29135
  const outputDir = dirname9(outputPath);
28898
- if (!existsSync24(outputDir)) mkdirSync16(outputDir, { recursive: true });
29136
+ if (!existsSync25(outputDir)) mkdirSync16(outputDir, { recursive: true });
28899
29137
  const inputs = [];
28900
29138
  const filterParts = [];
28901
29139
  tracks.forEach((track, i2) => {
@@ -28956,7 +29194,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
28956
29194
  const startMs = Date.now();
28957
29195
  const tracks = [];
28958
29196
  const errors = [];
28959
- if (!existsSync24(workDir)) mkdirSync16(workDir, { recursive: true });
29197
+ if (!existsSync25(workDir)) mkdirSync16(workDir, { recursive: true });
28960
29198
  await Promise.all(
28961
29199
  elements.map(async (element) => {
28962
29200
  if (signal?.aborted) {
@@ -28966,8 +29204,8 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
28966
29204
  try {
28967
29205
  let srcPath = element.src;
28968
29206
  if (!isAbsolute3(srcPath) && !isHttpUrl(srcPath)) {
28969
- const fromCompiled = compiledDir ? join26(compiledDir, srcPath) : null;
28970
- srcPath = fromCompiled && existsSync24(fromCompiled) ? fromCompiled : join26(baseDir, srcPath);
29207
+ const fromCompiled = compiledDir ? join27(compiledDir, srcPath) : null;
29208
+ srcPath = fromCompiled && existsSync25(fromCompiled) ? fromCompiled : join27(baseDir, srcPath);
28971
29209
  }
28972
29210
  if (isHttpUrl(srcPath)) {
28973
29211
  try {
@@ -28979,7 +29217,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
28979
29217
  return;
28980
29218
  }
28981
29219
  }
28982
- if (!existsSync24(srcPath)) {
29220
+ if (!existsSync25(srcPath)) {
28983
29221
  errors.push(`Source not found: ${element.id} (${element.src})`);
28984
29222
  return;
28985
29223
  }
@@ -28990,7 +29228,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
28990
29228
  }
28991
29229
  let audioSrcPath = srcPath;
28992
29230
  if (element.type === "video") {
28993
- const extractedPath = join26(workDir, `${element.id}-extracted.wav`);
29231
+ const extractedPath = join27(workDir, `${element.id}-extracted.wav`);
28994
29232
  const extractResult = await extractAudioFromVideo(
28995
29233
  srcPath,
28996
29234
  extractedPath,
@@ -29007,7 +29245,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
29007
29245
  }
29008
29246
  audioSrcPath = extractedPath;
29009
29247
  } else {
29010
- const trimmedPath = join26(workDir, `${element.id}-trimmed.wav`);
29248
+ const trimmedPath = join27(workDir, `${element.id}-trimmed.wav`);
29011
29249
  const prepResult = await prepareAudioTrack(
29012
29250
  srcPath,
29013
29251
  trimmedPath,
@@ -29061,9 +29299,9 @@ var init_audioMixer = __esm({
29061
29299
 
29062
29300
  // ../engine/src/services/parallelCoordinator.ts
29063
29301
  import { cpus as cpus2, freemem, totalmem as totalmem2 } from "os";
29064
- import { existsSync as existsSync25, mkdirSync as mkdirSync17, readdirSync as readdirSync10 } from "fs";
29302
+ import { existsSync as existsSync26, mkdirSync as mkdirSync17, readdirSync as readdirSync11 } from "fs";
29065
29303
  import { copyFile, rename } from "fs/promises";
29066
- import { join as join27 } from "path";
29304
+ import { join as join28 } from "path";
29067
29305
  function calculateOptimalWorkers(totalFrames, requested, config) {
29068
29306
  const effectiveMaxWorkers = (() => {
29069
29307
  const concurrency = config?.concurrency ?? DEFAULT_CONFIG2.concurrency;
@@ -29113,7 +29351,7 @@ function distributeFrames(totalFrames, workerCount, workDir) {
29113
29351
  workerId: i2,
29114
29352
  startFrame,
29115
29353
  endFrame,
29116
- outputDir: join27(workDir, `worker-${i2}`)
29354
+ outputDir: join28(workDir, `worker-${i2}`)
29117
29355
  });
29118
29356
  }
29119
29357
  return tasks;
@@ -29121,7 +29359,7 @@ function distributeFrames(totalFrames, workerCount, workDir) {
29121
29359
  async function executeWorkerTask(task, serverUrl, captureOptions, createBeforeCaptureHook, signal, onFrameCaptured, onFrameBuffer, config) {
29122
29360
  const startTime = Date.now();
29123
29361
  let framesCaptured = 0;
29124
- if (!existsSync25(task.outputDir)) mkdirSync17(task.outputDir, { recursive: true });
29362
+ if (!existsSync26(task.outputDir)) mkdirSync17(task.outputDir, { recursive: true });
29125
29363
  let session = null;
29126
29364
  let perf;
29127
29365
  try {
@@ -29211,17 +29449,17 @@ async function executeParallelCapture(serverUrl, workDir, tasks, captureOptions,
29211
29449
  return results;
29212
29450
  }
29213
29451
  async function mergeWorkerFrames(workDir, tasks, outputDir) {
29214
- if (!existsSync25(outputDir)) mkdirSync17(outputDir, { recursive: true });
29452
+ if (!existsSync26(outputDir)) mkdirSync17(outputDir, { recursive: true });
29215
29453
  let totalFrames = 0;
29216
29454
  const sortedTasks = [...tasks].sort((a, b) => a.startFrame - b.startFrame);
29217
29455
  for (const task of sortedTasks) {
29218
- if (!existsSync25(task.outputDir)) {
29456
+ if (!existsSync26(task.outputDir)) {
29219
29457
  continue;
29220
29458
  }
29221
- const files = readdirSync10(task.outputDir).filter((f3) => f3.startsWith("frame_") && (f3.endsWith(".jpg") || f3.endsWith(".png"))).sort();
29459
+ const files = readdirSync11(task.outputDir).filter((f3) => f3.startsWith("frame_") && (f3.endsWith(".jpg") || f3.endsWith(".png"))).sort();
29222
29460
  const copyTasks = files.map(async (file) => {
29223
- const sourcePath = join27(task.outputDir, file);
29224
- const targetPath = join27(outputDir, file);
29461
+ const sourcePath = join28(task.outputDir, file);
29462
+ const targetPath = join28(outputDir, file);
29225
29463
  try {
29226
29464
  await rename(sourcePath, targetPath);
29227
29465
  } catch {
@@ -29258,8 +29496,8 @@ var init_parallelCoordinator = __esm({
29258
29496
  // ../engine/src/services/fileServer.ts
29259
29497
  import { Hono as Hono2 } from "hono";
29260
29498
  import { serve } from "@hono/node-server";
29261
- import { readFileSync as readFileSync18, existsSync as existsSync26, statSync as statSync7 } from "fs";
29262
- import { join as join28, extname as extname6 } from "path";
29499
+ import { readFileSync as readFileSync18, existsSync as existsSync27, statSync as statSync9 } from "fs";
29500
+ import { join as join29, extname as extname6 } from "path";
29263
29501
  function stripEmbeddedRuntimeScripts(html) {
29264
29502
  if (!html) return html;
29265
29503
  const scriptRe = /<script\b[^>]*>[\s\S]*?<\/script>/gi;
@@ -29328,12 +29566,12 @@ function createFileServer(options) {
29328
29566
  let requestPath = c2.req.path;
29329
29567
  if (requestPath === "/") requestPath = "/index.html";
29330
29568
  const relativePath = requestPath.replace(/^\//, "");
29331
- const compiledPath = compiledDir ? join28(compiledDir, relativePath) : null;
29569
+ const compiledPath = compiledDir ? join29(compiledDir, relativePath) : null;
29332
29570
  const hasCompiledFile = Boolean(
29333
- compiledPath && existsSync26(compiledPath) && statSync7(compiledPath).isFile()
29571
+ compiledPath && existsSync27(compiledPath) && statSync9(compiledPath).isFile()
29334
29572
  );
29335
- const filePath = hasCompiledFile ? compiledPath : join28(projectDir, relativePath);
29336
- if (!existsSync26(filePath) || !statSync7(filePath).isFile()) {
29573
+ const filePath = hasCompiledFile ? compiledPath : join29(projectDir, relativePath);
29574
+ if (!existsSync27(filePath) || !statSync9(filePath).isFile()) {
29337
29575
  return c2.text("Not found", 404);
29338
29576
  }
29339
29577
  const ext = extname6(filePath).toLowerCase();
@@ -30638,9 +30876,9 @@ var init_shaderTransitions = __esm({
30638
30876
  });
30639
30877
 
30640
30878
  // ../engine/src/services/hdrCapture.ts
30641
- import { existsSync as existsSync27, readdirSync as readdirSync11 } from "fs";
30642
- import { join as join29 } from "path";
30643
- import { homedir as homedir6 } from "os";
30879
+ import { existsSync as existsSync28, readdirSync as readdirSync12 } from "fs";
30880
+ import { join as join30 } from "path";
30881
+ import { homedir as homedir7 } from "os";
30644
30882
  function linearToPQ(L2) {
30645
30883
  const Lp = Math.max(0, L2 * SDR_NITS / PQ_MAX_NITS);
30646
30884
  const Lm1 = Math.pow(Lp, PQ_M12);
@@ -30755,12 +30993,12 @@ function float16ToPqRgb(rawBuffer, bytesPerRow, width, height) {
30755
30993
  return output;
30756
30994
  }
30757
30995
  function resolveHeadedChromePath() {
30758
- const baseDir = join29(homedir6(), ".cache", "puppeteer", "chrome");
30759
- if (!existsSync27(baseDir)) return void 0;
30760
- const versions = readdirSync11(baseDir).sort().reverse();
30996
+ const baseDir = join30(homedir7(), ".cache", "puppeteer", "chrome");
30997
+ if (!existsSync28(baseDir)) return void 0;
30998
+ const versions = readdirSync12(baseDir).sort().reverse();
30761
30999
  for (const version of versions) {
30762
31000
  const candidates = [
30763
- join29(
31001
+ join30(
30764
31002
  baseDir,
30765
31003
  version,
30766
31004
  "chrome-mac-arm64",
@@ -30769,7 +31007,7 @@ function resolveHeadedChromePath() {
30769
31007
  "MacOS",
30770
31008
  "Google Chrome for Testing"
30771
31009
  ),
30772
- join29(
31010
+ join30(
30773
31011
  baseDir,
30774
31012
  version,
30775
31013
  "chrome-mac-x64",
@@ -30778,11 +31016,11 @@ function resolveHeadedChromePath() {
30778
31016
  "MacOS",
30779
31017
  "Google Chrome for Testing"
30780
31018
  ),
30781
- join29(baseDir, version, "chrome-linux64", "chrome"),
30782
- join29(baseDir, version, "chrome-win64", "chrome.exe")
31019
+ join30(baseDir, version, "chrome-linux64", "chrome"),
31020
+ join30(baseDir, version, "chrome-win64", "chrome.exe")
30783
31021
  ];
30784
31022
  for (const binary of candidates) {
30785
- if (existsSync27(binary)) return binary;
31023
+ if (existsSync28(binary)) return binary;
30786
31024
  }
30787
31025
  }
30788
31026
  return void 0;
@@ -31309,8 +31547,8 @@ var init_staticGuard = __esm({
31309
31547
  });
31310
31548
 
31311
31549
  // ../core/src/compiler/htmlBundler.ts
31312
- import { readFileSync as readFileSync19, existsSync as existsSync28 } from "fs";
31313
- import { join as join30, resolve as resolve12, isAbsolute as isAbsolute4, sep as sep2 } from "path";
31550
+ import { readFileSync as readFileSync19, existsSync as existsSync29 } from "fs";
31551
+ import { join as join31, resolve as resolve12, isAbsolute as isAbsolute4, sep as sep2 } from "path";
31314
31552
  import { transformSync } from "esbuild";
31315
31553
  function parseHTMLContent2(html) {
31316
31554
  const trimmed = html.trimStart().toLowerCase();
@@ -31378,7 +31616,7 @@ function isRelativeUrl(url) {
31378
31616
  return !url.startsWith("http://") && !url.startsWith("https://") && !url.startsWith("//") && !url.startsWith("data:") && !isAbsolute4(url);
31379
31617
  }
31380
31618
  function safeReadFile(filePath) {
31381
- if (!existsSync28(filePath)) return null;
31619
+ if (!existsSync29(filePath)) return null;
31382
31620
  try {
31383
31621
  return readFileSync19(filePath, "utf-8");
31384
31622
  } catch {
@@ -31386,7 +31624,7 @@ function safeReadFile(filePath) {
31386
31624
  }
31387
31625
  }
31388
31626
  function safeReadFileBuffer(filePath) {
31389
- if (!existsSync28(filePath)) return null;
31627
+ if (!existsSync29(filePath)) return null;
31390
31628
  try {
31391
31629
  return readFileSync19(filePath);
31392
31630
  } catch {
@@ -31585,8 +31823,8 @@ function stripJsCommentsParserSafe(source) {
31585
31823
  }
31586
31824
  }
31587
31825
  async function bundleToSingleHtml(projectDir, options) {
31588
- const indexPath = join30(projectDir, "index.html");
31589
- if (!existsSync28(indexPath)) throw new Error("index.html not found in project directory");
31826
+ const indexPath = join31(projectDir, "index.html");
31827
+ if (!existsSync29(indexPath)) throw new Error("index.html not found in project directory");
31590
31828
  const rawHtml = readFileSync19(indexPath, "utf-8");
31591
31829
  const compiled = await compileHtml(rawHtml, projectDir, options?.probeMediaDuration);
31592
31830
  const staticGuard = validateHyperframeHtmlContract(compiled);
@@ -31741,6 +31979,15 @@ async function bundleToSingleHtml(projectDir, options) {
31741
31979
  el.setAttribute(attr, val);
31742
31980
  }
31743
31981
  );
31982
+ const styledEls = innerRoot ? innerRoot.querySelectorAll("[style]") : contentDoc.querySelectorAll("[style]");
31983
+ rewriteInlineStyleAssetUrls(
31984
+ styledEls,
31985
+ src,
31986
+ (el) => el.getAttribute("style"),
31987
+ (el, val) => {
31988
+ el.setAttribute("style", val);
31989
+ }
31990
+ );
31744
31991
  if (innerRoot) {
31745
31992
  const innerW = innerRoot.getAttribute("data-width");
31746
31993
  const innerH = innerRoot.getAttribute("data-height");
@@ -31904,7 +32151,7 @@ var init_compiler = __esm({
31904
32151
 
31905
32152
  // ../producer/src/services/hyperframeRuntimeLoader.ts
31906
32153
  import { createHash as createHash3 } from "crypto";
31907
- import { existsSync as existsSync29, readFileSync as readFileSync20 } from "fs";
32154
+ import { existsSync as existsSync30, readFileSync as readFileSync20 } from "fs";
31908
32155
  import { dirname as dirname10, resolve as resolve13 } from "path";
31909
32156
  import { fileURLToPath as fileURLToPath2 } from "url";
31910
32157
  function resolveHyperframeManifestPath() {
@@ -31917,7 +32164,7 @@ function resolveHyperframeManifestPath() {
31917
32164
  MODULE_RELATIVE_MANIFEST_PATH
31918
32165
  ];
31919
32166
  for (const candidate of candidates) {
31920
- if (existsSync29(candidate)) {
32167
+ if (existsSync30(candidate)) {
31921
32168
  return candidate;
31922
32169
  }
31923
32170
  }
@@ -31928,7 +32175,7 @@ function getVerifiedHyperframeRuntimeSource() {
31928
32175
  }
31929
32176
  function resolveVerifiedHyperframeRuntime() {
31930
32177
  const manifestPath = resolveHyperframeManifestPath();
31931
- if (!existsSync29(manifestPath)) {
32178
+ if (!existsSync30(manifestPath)) {
31932
32179
  throw new Error(
31933
32180
  `[HyperframeRuntimeLoader] Missing manifest at ${manifestPath}. Build core runtime artifacts before rendering.`
31934
32181
  );
@@ -31942,7 +32189,7 @@ function resolveVerifiedHyperframeRuntime() {
31942
32189
  );
31943
32190
  }
31944
32191
  const runtimePath = resolve13(dirname10(manifestPath), runtimeFileName);
31945
- if (!existsSync29(runtimePath)) {
32192
+ if (!existsSync30(runtimePath)) {
31946
32193
  throw new Error(`[HyperframeRuntimeLoader] Missing runtime artifact at ${runtimePath}.`);
31947
32194
  }
31948
32195
  const runtimeSource = readFileSync20(runtimePath, "utf8");
@@ -31984,16 +32231,16 @@ var init_hyperframeRuntimeLoader = __esm({
31984
32231
  // ../producer/src/services/fileServer.ts
31985
32232
  import { Hono as Hono3 } from "hono";
31986
32233
  import { serve as serve2 } from "@hono/node-server";
31987
- import { readFileSync as readFileSync21, existsSync as existsSync30, realpathSync, statSync as statSync8 } from "fs";
31988
- import { join as join31, extname as extname7, resolve as resolve14, sep as sep3 } from "path";
32234
+ import { readFileSync as readFileSync21, existsSync as existsSync31, realpathSync, statSync as statSync10 } from "fs";
32235
+ import { join as join32, extname as extname7, resolve as resolve14, sep as sep3 } from "path";
31989
32236
  function isPathInside(child, parent, options = {}) {
31990
32237
  const { resolveSymlinks = false, pathModule } = options;
31991
32238
  const resolveFn = pathModule?.resolve ?? resolve14;
31992
32239
  const separator = pathModule?.sep ?? sep3;
31993
32240
  const resolvedChild = resolveFn(child);
31994
32241
  const resolvedParent = resolveFn(parent);
31995
- const normalizedChild = resolveSymlinks && existsSync30(resolvedChild) ? realpathSync.native(resolvedChild) : resolvedChild;
31996
- const normalizedParent = resolveSymlinks && existsSync30(resolvedParent) ? realpathSync.native(resolvedParent) : resolvedParent;
32242
+ const normalizedChild = resolveSymlinks && existsSync31(resolvedChild) ? realpathSync.native(resolvedChild) : resolvedChild;
32243
+ const normalizedParent = resolveSymlinks && existsSync31(resolvedParent) ? realpathSync.native(resolvedParent) : resolvedParent;
31997
32244
  if (normalizedChild === normalizedParent) return true;
31998
32245
  const parentWithSep = normalizedParent.endsWith(separator) ? normalizedParent : normalizedParent + separator;
31999
32246
  return normalizedChild.startsWith(parentWithSep);
@@ -32082,14 +32329,14 @@ function createFileServer2(options) {
32082
32329
  const relativePath = requestPath.replace(/^\//, "");
32083
32330
  let filePath = null;
32084
32331
  if (compiledDir) {
32085
- const candidate = join31(compiledDir, relativePath);
32086
- if (existsSync30(candidate) && isPathInside(candidate, compiledDir) && statSync8(candidate).isFile()) {
32332
+ const candidate = join32(compiledDir, relativePath);
32333
+ if (existsSync31(candidate) && isPathInside(candidate, compiledDir) && statSync10(candidate).isFile()) {
32087
32334
  filePath = candidate;
32088
32335
  }
32089
32336
  }
32090
32337
  if (!filePath) {
32091
- const candidate = join31(projectDir, relativePath);
32092
- if (existsSync30(candidate) && isPathInside(candidate, projectDir) && statSync8(candidate).isFile()) {
32338
+ const candidate = join32(projectDir, relativePath);
32339
+ if (existsSync31(candidate) && isPathInside(candidate, projectDir) && statSync10(candidate).isFile()) {
32093
32340
  filePath = candidate;
32094
32341
  }
32095
32342
  }
@@ -32463,7 +32710,7 @@ var init_ffprobe2 = __esm({
32463
32710
  });
32464
32711
 
32465
32712
  // ../producer/src/utils/paths.ts
32466
- import { resolve as resolve15, basename as basename2, join as join32, relative as relative2, isAbsolute as isAbsolute5 } from "path";
32713
+ import { resolve as resolve15, basename as basename2, join as join33, relative as relative2, isAbsolute as isAbsolute5 } from "path";
32467
32714
  function isPathInside2(childPath, parentPath) {
32468
32715
  const absChild = resolve15(childPath);
32469
32716
  const absParent = resolve15(parentPath);
@@ -32484,7 +32731,7 @@ function toExternalAssetKey(absPath) {
32484
32731
  function resolveRenderPaths(projectDir, outputPath, rendersDir = DEFAULT_RENDERS_DIR) {
32485
32732
  const absoluteProjectDir = resolve15(projectDir);
32486
32733
  const projectName = basename2(absoluteProjectDir);
32487
- const resolvedOutputPath = outputPath ?? join32(rendersDir, `${projectName}.mp4`);
32734
+ const resolvedOutputPath = outputPath ?? join33(rendersDir, `${projectName}.mp4`);
32488
32735
  const absoluteOutputPath = resolve15(resolvedOutputPath);
32489
32736
  return { absoluteProjectDir, absoluteOutputPath };
32490
32737
  }
@@ -32557,9 +32804,9 @@ var init_fontData_generated = __esm({
32557
32804
  });
32558
32805
 
32559
32806
  // ../producer/src/services/deterministicFonts.ts
32560
- import { existsSync as existsSync31, mkdirSync as mkdirSync18, readFileSync as readFileSync22, writeFileSync as writeFileSync14 } from "fs";
32561
- import { homedir as homedir7 } from "os";
32562
- import { join as join33 } from "path";
32807
+ import { existsSync as existsSync32, mkdirSync as mkdirSync18, readFileSync as readFileSync22, writeFileSync as writeFileSync14 } from "fs";
32808
+ import { homedir as homedir8 } from "os";
32809
+ import { join as join34 } from "path";
32563
32810
  function normalizeFamilyName(family) {
32564
32811
  return family.trim().replace(/^['"]|['"]$/g, "").trim().toLowerCase();
32565
32812
  }
@@ -32670,14 +32917,14 @@ function fontSlug(familyName) {
32670
32917
  return familyName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
32671
32918
  }
32672
32919
  function fontCacheDir(slug) {
32673
- const dir = join33(GOOGLE_FONTS_CACHE_DIR, slug);
32674
- if (!existsSync31(dir)) {
32920
+ const dir = join34(GOOGLE_FONTS_CACHE_DIR, slug);
32921
+ if (!existsSync32(dir)) {
32675
32922
  mkdirSync18(dir, { recursive: true });
32676
32923
  }
32677
32924
  return dir;
32678
32925
  }
32679
32926
  function cachedWoff2Path(slug, weight, style) {
32680
- return join33(fontCacheDir(slug), `${weight}-${style}.woff2`);
32927
+ return join34(fontCacheDir(slug), `${weight}-${style}.woff2`);
32681
32928
  }
32682
32929
  async function fetchGoogleFont(familyName) {
32683
32930
  const slug = fontSlug(familyName);
@@ -32703,7 +32950,7 @@ async function fetchGoogleFont(familyName) {
32703
32950
  const woff2Url = match[3] || "";
32704
32951
  if (!woff2Url) continue;
32705
32952
  const cachePath2 = cachedWoff2Path(slug, weight, style);
32706
- if (!existsSync31(cachePath2)) {
32953
+ if (!existsSync32(cachePath2)) {
32707
32954
  try {
32708
32955
  const fontRes = await fetch(woff2Url);
32709
32956
  if (!fontRes.ok) continue;
@@ -32889,14 +33136,14 @@ var init_deterministicFonts = __esm({
32889
33136
  poppins: "poppins",
32890
33137
  "segoe ui": "roboto"
32891
33138
  };
32892
- GOOGLE_FONTS_CACHE_DIR = join33(homedir7(), ".cache", "hyperframes", "fonts");
33139
+ GOOGLE_FONTS_CACHE_DIR = join34(homedir8(), ".cache", "hyperframes", "fonts");
32893
33140
  WOFF2_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";
32894
33141
  }
32895
33142
  });
32896
33143
 
32897
33144
  // ../producer/src/services/htmlCompiler.ts
32898
- import { readFileSync as readFileSync23, existsSync as existsSync32, mkdirSync as mkdirSync19 } from "fs";
32899
- import { join as join34, dirname as dirname11, resolve as resolve16 } from "path";
33145
+ import { readFileSync as readFileSync23, existsSync as existsSync33, mkdirSync as mkdirSync19 } from "fs";
33146
+ import { join as join35, dirname as dirname11, resolve as resolve16 } from "path";
32900
33147
  function dedupeElementsById(elements) {
32901
33148
  const deduped = /* @__PURE__ */ new Map();
32902
33149
  for (const element of elements) {
@@ -32957,16 +33204,16 @@ function detectShaderTransitionUsage(html) {
32957
33204
  async function resolveMediaDuration(src, mediaStart, baseDir, downloadDir, tagName19) {
32958
33205
  let filePath = src;
32959
33206
  if (isHttpUrl(src)) {
32960
- if (!existsSync32(downloadDir)) mkdirSync19(downloadDir, { recursive: true });
33207
+ if (!existsSync33(downloadDir)) mkdirSync19(downloadDir, { recursive: true });
32961
33208
  try {
32962
33209
  filePath = await downloadToTemp(src, downloadDir);
32963
33210
  } catch {
32964
33211
  return { duration: 0, resolvedPath: src };
32965
33212
  }
32966
33213
  } else if (!filePath.startsWith("/")) {
32967
- filePath = join34(baseDir, filePath);
33214
+ filePath = join35(baseDir, filePath);
32968
33215
  }
32969
- if (!existsSync32(filePath)) {
33216
+ if (!existsSync33(filePath)) {
32970
33217
  return { duration: 0, resolvedPath: filePath };
32971
33218
  }
32972
33219
  let metadata;
@@ -33044,7 +33291,7 @@ async function parseSubCompositions(html, projectDir, downloadDir, parentOffset
33044
33291
  if (visited.has(filePath)) {
33045
33292
  continue;
33046
33293
  }
33047
- if (!existsSync32(filePath)) {
33294
+ if (!existsSync33(filePath)) {
33048
33295
  continue;
33049
33296
  }
33050
33297
  const rawSubHtml = readFileSync23(filePath, "utf-8");
@@ -33233,7 +33480,7 @@ function inlineSubCompositions(html, subCompositions, projectDir) {
33233
33480
  let compHtml = subCompositions.get(srcPath) || null;
33234
33481
  if (!compHtml) {
33235
33482
  const filePath = resolve16(projectDir, srcPath);
33236
- if (existsSync32(filePath)) {
33483
+ if (existsSync33(filePath)) {
33237
33484
  compHtml = readFileSync23(filePath, "utf-8");
33238
33485
  }
33239
33486
  }
@@ -33435,7 +33682,7 @@ function collectExternalAssets(html, projectDir) {
33435
33682
  if (isPathInside2(absPath, absProjectDir)) {
33436
33683
  return null;
33437
33684
  }
33438
- if (!existsSync32(absPath)) return null;
33685
+ if (!existsSync33(absPath)) return null;
33439
33686
  const safeKey = toExternalAssetKey(absPath);
33440
33687
  externalAssets.set(safeKey, absPath);
33441
33688
  return safeKey;
@@ -33786,20 +34033,20 @@ var init_hdrImageTransferCache = __esm({
33786
34033
 
33787
34034
  // ../producer/src/services/renderOrchestrator.ts
33788
34035
  import {
33789
- existsSync as existsSync33,
34036
+ existsSync as existsSync34,
33790
34037
  mkdirSync as mkdirSync20,
33791
34038
  rmSync as rmSync7,
33792
34039
  readFileSync as readFileSync24,
33793
34040
  openSync,
33794
34041
  readSync,
33795
34042
  closeSync,
33796
- readdirSync as readdirSync12,
33797
- statSync as statSync9,
34043
+ readdirSync as readdirSync13,
34044
+ statSync as statSync11,
33798
34045
  writeFileSync as writeFileSync15,
33799
34046
  copyFileSync as copyFileSync2,
33800
34047
  appendFileSync
33801
34048
  } from "fs";
33802
- import { join as join35, dirname as dirname12, resolve as resolve17 } from "path";
34049
+ import { join as join36, dirname as dirname12, resolve as resolve17 } from "path";
33803
34050
  import { randomUUID as randomUUID2 } from "crypto";
33804
34051
  import { freemem as freemem2 } from "os";
33805
34052
  import { fileURLToPath as fileURLToPath3 } from "url";
@@ -33820,14 +34067,14 @@ function sampleDirectoryBytes(dir) {
33820
34067
  if (!current) continue;
33821
34068
  let entries2 = [];
33822
34069
  try {
33823
- entries2 = readdirSync12(current);
34070
+ entries2 = readdirSync13(current);
33824
34071
  } catch {
33825
34072
  continue;
33826
34073
  }
33827
34074
  for (const name of entries2) {
33828
- const full = join35(current, name);
34075
+ const full = join36(current, name);
33829
34076
  try {
33830
- const st2 = statSync9(full);
34077
+ const st2 = statSync11(full);
33831
34078
  if (st2.isDirectory()) {
33832
34079
  stack.push(full);
33833
34080
  } else if (st2.isFile()) {
@@ -33988,16 +34235,16 @@ function installDebugLogger(logPath, log2 = defaultLogger) {
33988
34235
  };
33989
34236
  }
33990
34237
  function writeCompiledArtifacts(compiled, workDir, includeSummary) {
33991
- const compileDir = join35(workDir, "compiled");
34238
+ const compileDir = join36(workDir, "compiled");
33992
34239
  mkdirSync20(compileDir, { recursive: true });
33993
- writeFileSync15(join35(compileDir, "index.html"), compiled.html, "utf-8");
34240
+ writeFileSync15(join36(compileDir, "index.html"), compiled.html, "utf-8");
33994
34241
  for (const [srcPath, html] of compiled.subCompositions) {
33995
- const outPath = join35(compileDir, srcPath);
34242
+ const outPath = join36(compileDir, srcPath);
33996
34243
  mkdirSync20(dirname12(outPath), { recursive: true });
33997
34244
  writeFileSync15(outPath, html, "utf-8");
33998
34245
  }
33999
34246
  for (const [relativePath, absolutePath] of compiled.externalAssets) {
34000
- const outPath = resolve17(join35(compileDir, relativePath));
34247
+ const outPath = resolve17(join36(compileDir, relativePath));
34001
34248
  if (!isPathInside2(outPath, compileDir)) {
34002
34249
  console.warn(`[Render] Skipping external asset with unsafe path: ${relativePath}`);
34003
34250
  continue;
@@ -34028,7 +34275,7 @@ function writeCompiledArtifacts(compiled, workDir, includeSummary) {
34028
34275
  renderModeHints: compiled.renderModeHints,
34029
34276
  hasShaderTransitions: compiled.hasShaderTransitions
34030
34277
  };
34031
- writeFileSync15(join35(compileDir, "summary.json"), JSON.stringify(summary, null, 2), "utf-8");
34278
+ writeFileSync15(join36(compileDir, "summary.json"), JSON.stringify(summary, null, 2), "utf-8");
34032
34279
  }
34033
34280
  }
34034
34281
  function applyRenderModeHints(cfg, compiled, log2 = defaultLogger) {
@@ -34154,8 +34401,8 @@ function findMissingFrameRanges(totalFrames, framesDir, frameExt) {
34154
34401
  const ranges = [];
34155
34402
  let rangeStart = null;
34156
34403
  for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
34157
- const framePath = join35(framesDir, `frame_${String(frameIndex).padStart(6, "0")}.${frameExt}`);
34158
- const missing = !existsSync33(framePath);
34404
+ const framePath = join36(framesDir, `frame_${String(frameIndex).padStart(6, "0")}.${frameExt}`);
34405
+ const missing = !existsSync34(framePath);
34159
34406
  if (missing && rangeStart === null) {
34160
34407
  rangeStart = frameIndex;
34161
34408
  } else if (!missing && rangeStart !== null) {
@@ -34177,7 +34424,7 @@ function buildMissingFrameRetryBatches(ranges, maxWorkers, workDir, attempt) {
34177
34424
  workerId,
34178
34425
  startFrame: range.startFrame,
34179
34426
  endFrame: range.endFrame,
34180
- outputDir: join35(workDir, `retry-${attempt}-batch-${batchIndex}-worker-${workerId}`)
34427
+ outputDir: join36(workDir, `retry-${attempt}-batch-${batchIndex}-worker-${workerId}`)
34181
34428
  }));
34182
34429
  batches.push(batch);
34183
34430
  }
@@ -34201,8 +34448,8 @@ function shouldFallbackToScreenshotAfterCalibrationError(error) {
34201
34448
  function countCapturedFrames(totalFrames, framesDir, frameExt) {
34202
34449
  let captured = 0;
34203
34450
  for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
34204
- const framePath = join35(framesDir, `frame_${String(frameIndex).padStart(6, "0")}.${frameExt}`);
34205
- if (existsSync33(framePath)) captured++;
34451
+ const framePath = join36(framesDir, `frame_${String(frameIndex).padStart(6, "0")}.${frameExt}`);
34452
+ if (existsSync34(framePath)) captured++;
34206
34453
  }
34207
34454
  return captured;
34208
34455
  }
@@ -34239,7 +34486,7 @@ async function executeDiskCaptureWithAdaptiveRetry(options) {
34239
34486
  frameCount,
34240
34487
  reason: attempt === 0 ? "initial" : "retry"
34241
34488
  });
34242
- const attemptWorkDir = join35(options.workDir, `capture-attempt-${attempt}`);
34489
+ const attemptWorkDir = join36(options.workDir, `capture-attempt-${attempt}`);
34243
34490
  const batches = missingRanges ? buildMissingFrameRetryBatches(missingRanges, currentWorkers, attemptWorkDir, attempt) : [distributeFrames(options.totalFrames, currentWorkers, attemptWorkDir)];
34244
34491
  try {
34245
34492
  for (const tasks of batches) {
@@ -34656,7 +34903,7 @@ async function compositeHdrFrame(ctx, canvas, time, fullStacking, elementFilter,
34656
34903
  if (shouldLog && debugDumpDir) {
34657
34904
  const after2 = countNonZeroRgb48(canvas);
34658
34905
  const dumpName = `frame_${String(debugFrameIndex).padStart(4, "0")}_layer_${String(layerIdx).padStart(2, "0")}_dom.png`;
34659
- const dumpPath = join35(debugDumpDir, dumpName);
34906
+ const dumpPath = join36(debugDumpDir, dumpName);
34660
34907
  writeFileSync15(dumpPath, domPng);
34661
34908
  log2.info("[diag] dom layer blit", {
34662
34909
  frame: debugFrameIndex,
@@ -34730,8 +34977,8 @@ function extractStandaloneEntryFromIndex(indexHtml, entryFile) {
34730
34977
  async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSignal) {
34731
34978
  const moduleDir = dirname12(fileURLToPath3(import.meta.url));
34732
34979
  const producerRoot = process.env.PRODUCER_RENDERS_DIR ? resolve17(process.env.PRODUCER_RENDERS_DIR, "..") : resolve17(moduleDir, "../..");
34733
- const debugDir = join35(producerRoot, ".debug");
34734
- const workDir = job.config.debug ? join35(debugDir, job.id) : join35(dirname12(outputPath), `work-${job.id}`);
34980
+ const debugDir = join36(producerRoot, ".debug");
34981
+ const workDir = job.config.debug ? join36(debugDir, job.id) : join36(dirname12(outputPath), `work-${job.id}`);
34735
34982
  const pipelineStart = Date.now();
34736
34983
  const log2 = job.config.logger ?? defaultLogger;
34737
34984
  let fileServer = null;
@@ -34744,7 +34991,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
34744
34991
  imageDecodeFailures: 0
34745
34992
  };
34746
34993
  let hdrPerf;
34747
- const perfOutputPath = join35(workDir, "perf-summary.json");
34994
+ const perfOutputPath = join36(workDir, "perf-summary.json");
34748
34995
  const cfg = { ...job.config.producerConfig ?? resolveConfig() };
34749
34996
  const outputFormat = job.config.format ?? "mp4";
34750
34997
  const isWebm = outputFormat === "webm";
@@ -34778,22 +35025,22 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
34778
35025
  };
34779
35026
  job.startedAt = /* @__PURE__ */ new Date();
34780
35027
  assertNotAborted();
34781
- if (!existsSync33(workDir)) mkdirSync20(workDir, { recursive: true });
35028
+ if (!existsSync34(workDir)) mkdirSync20(workDir, { recursive: true });
34782
35029
  if (job.config.debug) {
34783
- const logPath = join35(workDir, "render.log");
35030
+ const logPath = join36(workDir, "render.log");
34784
35031
  restoreLogger = installDebugLogger(logPath, log2);
34785
35032
  }
34786
35033
  const entryFile = job.config.entryFile || "index.html";
34787
- let htmlPath = join35(projectDir, entryFile);
34788
- if (!existsSync33(htmlPath)) {
35034
+ let htmlPath = join36(projectDir, entryFile);
35035
+ if (!existsSync34(htmlPath)) {
34789
35036
  throw new Error(`Entry file not found: ${htmlPath}`);
34790
35037
  }
34791
35038
  assertNotAborted();
34792
35039
  const rawEntry = readFileSync24(htmlPath, "utf-8");
34793
35040
  if (entryFile !== "index.html" && rawEntry.trimStart().startsWith("<template")) {
34794
- const wrapperPath = join35(workDir, "standalone-entry.html");
34795
- const projectIndexPath = join35(projectDir, "index.html");
34796
- if (!existsSync33(projectIndexPath)) {
35041
+ const wrapperPath = join36(workDir, "standalone-entry.html");
35042
+ const projectIndexPath = join36(projectDir, "index.html");
35043
+ if (!existsSync34(projectIndexPath)) {
34797
35044
  throw new Error(
34798
35045
  `Template entry file "${entryFile}" requires a project index.html to extract its render shell.`
34799
35046
  );
@@ -34816,7 +35063,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
34816
35063
  const stage1Start = Date.now();
34817
35064
  updateJobStatus(job, "preprocessing", "Compiling composition", 5, onProgress);
34818
35065
  const compileStart = Date.now();
34819
- let compiled = await compileForRender(projectDir, htmlPath, join35(workDir, "downloads"));
35066
+ let compiled = await compileForRender(projectDir, htmlPath, join36(workDir, "downloads"));
34820
35067
  assertNotAborted();
34821
35068
  perfStages.compileOnlyMs = Date.now() - compileStart;
34822
35069
  applyRenderModeHints(cfg, compiled, log2);
@@ -34848,7 +35095,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
34848
35095
  reasons.push(`${compiled.unresolvedCompositions.length} unresolved composition(s)`);
34849
35096
  fileServer = await createFileServer2({
34850
35097
  projectDir,
34851
- compiledDir: join35(workDir, "compiled"),
35098
+ compiledDir: join36(workDir, "compiled"),
34852
35099
  port: 0,
34853
35100
  preHeadScripts: [VIRTUAL_TIME_SHIM]
34854
35101
  });
@@ -34862,7 +35109,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
34862
35109
  };
34863
35110
  probeSession = await createCaptureSession(
34864
35111
  fileServer.url,
34865
- join35(workDir, "probe"),
35112
+ join36(workDir, "probe"),
34866
35113
  captureOpts,
34867
35114
  null,
34868
35115
  cfg
@@ -34894,7 +35141,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
34894
35141
  compiled,
34895
35142
  resolutions,
34896
35143
  projectDir,
34897
- join35(workDir, "downloads")
35144
+ join36(workDir, "downloads")
34898
35145
  );
34899
35146
  assertNotAborted();
34900
35147
  composition.videos = compiled.videos;
@@ -35048,7 +35295,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
35048
35295
  const stage2Start = Date.now();
35049
35296
  updateJobStatus(job, "preprocessing", "Extracting video frames", 10, onProgress);
35050
35297
  let frameLookup = null;
35051
- const compiledDir = join35(workDir, "compiled");
35298
+ const compiledDir = join36(workDir, "compiled");
35052
35299
  let extractionResult = null;
35053
35300
  const nativeHdrVideoIds = /* @__PURE__ */ new Set();
35054
35301
  const videoTransfers = /* @__PURE__ */ new Map();
@@ -35057,10 +35304,10 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
35057
35304
  composition.videos.map(async (v) => {
35058
35305
  let videoPath = v.src;
35059
35306
  if (!videoPath.startsWith("/")) {
35060
- const fromCompiled = existsSync33(join35(compiledDir, videoPath)) ? join35(compiledDir, videoPath) : join35(projectDir, videoPath);
35307
+ const fromCompiled = existsSync34(join36(compiledDir, videoPath)) ? join36(compiledDir, videoPath) : join36(projectDir, videoPath);
35061
35308
  videoPath = fromCompiled;
35062
35309
  }
35063
- if (!existsSync33(videoPath)) return;
35310
+ if (!existsSync34(videoPath)) return;
35064
35311
  const meta = await extractMediaMetadata(videoPath);
35065
35312
  if (isHdrColorSpace(meta.colorSpace)) {
35066
35313
  nativeHdrVideoIds.add(v.id);
@@ -35078,10 +35325,10 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
35078
35325
  composition.images.map(async (img) => {
35079
35326
  let imgPath = img.src;
35080
35327
  if (!imgPath.startsWith("/")) {
35081
- const fromCompiled = existsSync33(join35(compiledDir, imgPath)) ? join35(compiledDir, imgPath) : join35(projectDir, imgPath);
35328
+ const fromCompiled = existsSync34(join36(compiledDir, imgPath)) ? join36(compiledDir, imgPath) : join36(projectDir, imgPath);
35082
35329
  imgPath = fromCompiled;
35083
35330
  }
35084
- if (!existsSync33(imgPath)) return null;
35331
+ if (!existsSync34(imgPath)) return null;
35085
35332
  const meta = await extractMediaMetadata(imgPath);
35086
35333
  if (isHdrColorSpace(meta.colorSpace)) {
35087
35334
  nativeHdrImageIds.add(img.id);
@@ -35097,7 +35344,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
35097
35344
  extractionResult = await extractAllVideoFrames(
35098
35345
  composition.videos,
35099
35346
  projectDir,
35100
- { fps: job.config.fps, outputDir: join35(workDir, "video-frames") },
35347
+ { fps: job.config.fps, outputDir: join36(workDir, "video-frames") },
35101
35348
  abortSignal,
35102
35349
  { extractCacheDir: cfg.extractCacheDir },
35103
35350
  compiledDir
@@ -35180,13 +35427,13 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
35180
35427
  }
35181
35428
  const stage3Start = Date.now();
35182
35429
  updateJobStatus(job, "preprocessing", "Processing audio tracks", 20, onProgress);
35183
- const audioOutputPath = join35(workDir, "audio.aac");
35430
+ const audioOutputPath = join36(workDir, "audio.aac");
35184
35431
  let hasAudio = false;
35185
35432
  if (composition.audios.length > 0) {
35186
35433
  const audioResult = await processCompositionAudio(
35187
35434
  composition.audios,
35188
35435
  projectDir,
35189
- join35(workDir, "audio-work"),
35436
+ join36(workDir, "audio-work"),
35190
35437
  audioOutputPath,
35191
35438
  job.duration,
35192
35439
  abortSignal,
@@ -35204,14 +35451,14 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
35204
35451
  if (!fileServer) {
35205
35452
  fileServer = await createFileServer2({
35206
35453
  projectDir,
35207
- compiledDir: join35(workDir, "compiled"),
35454
+ compiledDir: join36(workDir, "compiled"),
35208
35455
  port: 0,
35209
35456
  preHeadScripts: [VIRTUAL_TIME_SHIM]
35210
35457
  });
35211
35458
  assertNotAborted();
35212
35459
  }
35213
- const framesDir = join35(workDir, "captured-frames");
35214
- if (!existsSync33(framesDir)) mkdirSync20(framesDir, { recursive: true });
35460
+ const framesDir = join36(workDir, "captured-frames");
35461
+ if (!existsSync34(framesDir)) mkdirSync20(framesDir, { recursive: true });
35215
35462
  const captureOptions = {
35216
35463
  width,
35217
35464
  height,
@@ -35226,7 +35473,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
35226
35473
  let captureCalibration;
35227
35474
  let switchedToScreenshotAfterCalibration = false;
35228
35475
  if (job.config.workers === void 0 && totalFrames >= 60) {
35229
- const calibrationDir = join35(workDir, "capture-calibration");
35476
+ const calibrationDir = join36(workDir, "capture-calibration");
35230
35477
  const calibrationCfg = createCaptureCalibrationConfig(cfg);
35231
35478
  const videoInjector = createVideoFrameInjector(frameLookup);
35232
35479
  let calibrationSession = null;
@@ -35325,7 +35572,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
35325
35572
  "png-sequence": ""
35326
35573
  };
35327
35574
  const videoExt = FORMAT_EXT2[outputFormat] ?? ".mp4";
35328
- const videoOnlyPath = join35(workDir, `video-only${videoExt}`);
35575
+ const videoOnlyPath = join36(workDir, `video-only${videoExt}`);
35329
35576
  const nativeHdrIds = /* @__PURE__ */ new Set([...nativeHdrVideoIds, ...nativeHdrImageIds]);
35330
35577
  const hasHdrContent = effectiveHdr && nativeHdrIds.size > 0;
35331
35578
  const encoderHdr = hasHdrContent ? effectiveHdr : void 0;
@@ -35349,8 +35596,8 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
35349
35596
  if (!hdrVideoIds.includes(v.id)) continue;
35350
35597
  let srcPath = v.src;
35351
35598
  if (!srcPath.startsWith("/")) {
35352
- const fromCompiled = join35(compiledDir, srcPath);
35353
- srcPath = existsSync33(fromCompiled) ? fromCompiled : join35(projectDir, srcPath);
35599
+ const fromCompiled = join36(compiledDir, srcPath);
35600
+ srcPath = existsSync34(fromCompiled) ? fromCompiled : join36(projectDir, srcPath);
35354
35601
  }
35355
35602
  hdrVideoSrcPaths.set(v.id, srcPath);
35356
35603
  }
@@ -35480,11 +35727,11 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
35480
35727
  for (const [videoId, srcPath] of hdrVideoSrcPaths) {
35481
35728
  const video = composition.videos.find((v) => v.id === videoId);
35482
35729
  if (!video) continue;
35483
- const frameDir = join35(framesDir, `hdr_${videoId}`);
35730
+ const frameDir = join36(framesDir, `hdr_${videoId}`);
35484
35731
  mkdirSync20(frameDir, { recursive: true });
35485
35732
  const duration = video.end - video.start;
35486
35733
  const dims = hdrExtractionDims.get(videoId) ?? { width, height };
35487
- const rawPath = join35(frameDir, "frames.rgb48le");
35734
+ const rawPath = join36(frameDir, "frames.rgb48le");
35488
35735
  const ffmpegArgs = [
35489
35736
  "-ss",
35490
35737
  String(video.mediaStart),
@@ -35516,7 +35763,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
35516
35763
  );
35517
35764
  }
35518
35765
  const frameSize = dims.width * dims.height * 6;
35519
- const frameCount = Math.floor(statSync9(rawPath).size / frameSize);
35766
+ const frameCount = Math.floor(statSync11(rawPath).size / frameSize);
35520
35767
  if (frameCount < 1) {
35521
35768
  hdrDiagnostics.videoExtractionFailures += 1;
35522
35769
  throw new Error(
@@ -35586,8 +35833,8 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
35586
35833
  }
35587
35834
  }
35588
35835
  const debugDumpEnabled = process.env.KEEP_TEMP === "1";
35589
- const debugDumpDir = debugDumpEnabled ? join35(framesDir, "debug-composite") : null;
35590
- if (debugDumpDir && !existsSync33(debugDumpDir)) {
35836
+ const debugDumpDir = debugDumpEnabled ? join36(framesDir, "debug-composite") : null;
35837
+ if (debugDumpDir && !existsSync34(debugDumpDir)) {
35591
35838
  mkdirSync20(debugDumpDir, { recursive: true });
35592
35839
  }
35593
35840
  if (!effectiveHdr) {
@@ -35771,7 +36018,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
35771
36018
  );
35772
36019
  addHdrTiming(hdrPerf, "normalCompositeMs", timingStart);
35773
36020
  if (debugDumpEnabled && debugDumpDir && i2 % 30 === 0) {
35774
- const previewPath = join35(
36021
+ const previewPath = join36(
35775
36022
  debugDumpDir,
35776
36023
  `frame_${String(i2).padStart(4, "0")}_final_rgb48le.bin`
35777
36024
  );
@@ -36049,19 +36296,19 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
36049
36296
  if (isPngSequence) {
36050
36297
  const stage5Start = Date.now();
36051
36298
  updateJobStatus(job, "encoding", "Writing PNG sequence", 75, onProgress);
36052
- if (!existsSync33(outputPath)) mkdirSync20(outputPath, { recursive: true });
36053
- const captured = readdirSync12(framesDir).filter((name) => name.endsWith(".png")).sort();
36299
+ if (!existsSync34(outputPath)) mkdirSync20(outputPath, { recursive: true });
36300
+ const captured = readdirSync13(framesDir).filter((name) => name.endsWith(".png")).sort();
36054
36301
  if (captured.length === 0) {
36055
36302
  throw new Error(
36056
36303
  `[Render] png-sequence output requested but no PNGs were captured to ${framesDir}`
36057
36304
  );
36058
36305
  }
36059
36306
  captured.forEach((name, i2) => {
36060
- const dst = join35(outputPath, `frame_${String(i2 + 1).padStart(6, "0")}.png`);
36061
- copyFileSync2(join35(framesDir, name), dst);
36307
+ const dst = join36(outputPath, `frame_${String(i2 + 1).padStart(6, "0")}.png`);
36308
+ copyFileSync2(join36(framesDir, name), dst);
36062
36309
  });
36063
- if (hasAudio && existsSync33(audioOutputPath)) {
36064
- copyFileSync2(audioOutputPath, join35(outputPath, "audio.aac"));
36310
+ if (hasAudio && existsSync34(audioOutputPath)) {
36311
+ copyFileSync2(audioOutputPath, join36(outputPath, "audio.aac"));
36065
36312
  log2.info(
36066
36313
  `[Render] png-sequence: audio.aac sidecar written to ${outputPath}/audio.aac`
36067
36314
  );
@@ -36153,7 +36400,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
36153
36400
  updateJobStatus(job, "complete", "Render complete", 100, onProgress);
36154
36401
  const totalElapsed = Date.now() - pipelineStart;
36155
36402
  sampleMemory();
36156
- const tmpPeakBytes = existsSync33(workDir) ? sampleDirectoryBytes(workDir) : 0;
36403
+ const tmpPeakBytes = existsSync34(workDir) ? sampleDirectoryBytes(workDir) : 0;
36157
36404
  const perfSummary = {
36158
36405
  renderId: job.id,
36159
36406
  totalElapsedMs: totalElapsed,
@@ -36195,8 +36442,8 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
36195
36442
  }
36196
36443
  }
36197
36444
  if (job.config.debug) {
36198
- if (!isPngSequence && existsSync33(outputPath)) {
36199
- const debugOutput = join35(workDir, `output${videoExt}`);
36445
+ if (!isPngSequence && existsSync34(outputPath)) {
36446
+ const debugOutput = join36(workDir, `output${videoExt}`);
36200
36447
  copyFileSync2(outputPath, debugOutput);
36201
36448
  }
36202
36449
  } else if (process.env.KEEP_TEMP === "1") {
@@ -36282,7 +36529,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
36282
36529
  await safeCleanup(
36283
36530
  "remove workDir (error)",
36284
36531
  () => {
36285
- if (existsSync33(workDir)) rmSync7(workDir, { recursive: true, force: true });
36532
+ if (existsSync34(workDir)) rmSync7(workDir, { recursive: true, force: true });
36286
36533
  },
36287
36534
  log2
36288
36535
  );
@@ -36344,8 +36591,8 @@ var init_config3 = __esm({
36344
36591
  });
36345
36592
 
36346
36593
  // ../producer/src/services/hyperframeLint.ts
36347
- import { existsSync as existsSync34, readFileSync as readFileSync25, statSync as statSync10 } from "fs";
36348
- import { resolve as resolve18, join as join36 } from "path";
36594
+ import { existsSync as existsSync35, readFileSync as readFileSync25, statSync as statSync12 } from "fs";
36595
+ import { resolve as resolve18, join as join37 } from "path";
36349
36596
  function isStringRecord(value) {
36350
36597
  if (!value || typeof value !== "object" || Array.isArray(value)) {
36351
36598
  return false;
@@ -36373,7 +36620,7 @@ function pickEntryFile(files, preferredEntryFile) {
36373
36620
  }
36374
36621
  function readProjectEntryFile(projectDir, preferredEntryFile) {
36375
36622
  const absProjectDir = resolve18(projectDir);
36376
- if (!existsSync34(absProjectDir) || !statSync10(absProjectDir).isDirectory()) {
36623
+ if (!existsSync35(absProjectDir) || !statSync12(absProjectDir).isDirectory()) {
36377
36624
  return { error: `Project directory not found: ${absProjectDir}` };
36378
36625
  }
36379
36626
  const entryCandidates = [preferredEntryFile, "index.html", "src/index.html"].filter(
@@ -36384,7 +36631,7 @@ function readProjectEntryFile(projectDir, preferredEntryFile) {
36384
36631
  if (!absoluteEntryPath.startsWith(absProjectDir)) {
36385
36632
  return { error: `Entry file must stay inside project directory: ${entryFile}` };
36386
36633
  }
36387
- if (existsSync34(absoluteEntryPath) && statSync10(absoluteEntryPath).isFile()) {
36634
+ if (existsSync35(absoluteEntryPath) && statSync12(absoluteEntryPath).isFile()) {
36388
36635
  return {
36389
36636
  entryFile,
36390
36637
  html: readFileSync25(absoluteEntryPath, "utf-8"),
@@ -36393,7 +36640,7 @@ function readProjectEntryFile(projectDir, preferredEntryFile) {
36393
36640
  }
36394
36641
  }
36395
36642
  return {
36396
- error: `No HTML entry file found in project directory: ${join36(absProjectDir, preferredEntryFile || "index.html")}`
36643
+ error: `No HTML entry file found in project directory: ${join37(absProjectDir, preferredEntryFile || "index.html")}`
36397
36644
  };
36398
36645
  }
36399
36646
  function prepareHyperframeLintBody(body) {
@@ -36481,15 +36728,15 @@ var init_semaphore = __esm({
36481
36728
 
36482
36729
  // ../producer/src/server.ts
36483
36730
  import {
36484
- existsSync as existsSync35,
36731
+ existsSync as existsSync36,
36485
36732
  mkdirSync as mkdirSync21,
36486
- statSync as statSync11,
36733
+ statSync as statSync13,
36487
36734
  mkdtempSync as mkdtempSync2,
36488
36735
  writeFileSync as writeFileSync16,
36489
36736
  rmSync as rmSync8,
36490
36737
  createReadStream
36491
36738
  } from "fs";
36492
- import { resolve as resolve19, dirname as dirname13, join as join37 } from "path";
36739
+ import { resolve as resolve19, dirname as dirname13, join as join38 } from "path";
36493
36740
  import { tmpdir as tmpdir3 } from "os";
36494
36741
  import { parseArgs as parseArgs2 } from "util";
36495
36742
  import crypto2 from "crypto";
@@ -36512,11 +36759,11 @@ async function prepareRenderBody(body) {
36512
36759
  const projectDir = typeof body.projectDir === "string" ? body.projectDir : void 0;
36513
36760
  if (projectDir) {
36514
36761
  const absProjectDir = resolve19(projectDir);
36515
- if (!existsSync35(absProjectDir) || !statSync11(absProjectDir).isDirectory()) {
36762
+ if (!existsSync36(absProjectDir) || !statSync13(absProjectDir).isDirectory()) {
36516
36763
  return { error: `Project directory not found: ${absProjectDir}` };
36517
36764
  }
36518
36765
  const entry = options.entryFile || "index.html";
36519
- if (!existsSync35(resolve19(absProjectDir, entry))) {
36766
+ if (!existsSync36(resolve19(absProjectDir, entry))) {
36520
36767
  return { error: `Entry file "${entry}" not found in project directory: ${absProjectDir}` };
36521
36768
  }
36522
36769
  return { prepared: { input: { projectDir: absProjectDir, ...options } } };
@@ -36541,8 +36788,8 @@ async function prepareRenderBody(body) {
36541
36788
  }
36542
36789
  }
36543
36790
  const tempRoot = process.env.PRODUCER_TMP_PROJECT_DIR || tmpdir3();
36544
- const tempProjectDir = mkdtempSync2(join37(tempRoot, "producer-project-"));
36545
- writeFileSync16(join37(tempProjectDir, "index.html"), htmlContent, "utf-8");
36791
+ const tempProjectDir = mkdtempSync2(join38(tempRoot, "producer-project-"));
36792
+ writeFileSync16(join38(tempProjectDir, "index.html"), htmlContent, "utf-8");
36546
36793
  return {
36547
36794
  prepared: {
36548
36795
  input: {
@@ -36665,7 +36912,7 @@ function createRenderHandlers(options = {}) {
36665
36912
  log2
36666
36913
  );
36667
36914
  const outputDir = dirname13(absoluteOutputPath);
36668
- if (!existsSync35(outputDir)) mkdirSync21(outputDir, { recursive: true });
36915
+ if (!existsSync36(outputDir)) mkdirSync21(outputDir, { recursive: true });
36669
36916
  const release2 = await renderSemaphore.acquire();
36670
36917
  log2.info("render started", {
36671
36918
  requestId,
@@ -36692,7 +36939,7 @@ function createRenderHandlers(options = {}) {
36692
36939
  log2.info(`render progress ${pct}%`, { requestId, stage: j2.currentStage, message });
36693
36940
  }
36694
36941
  });
36695
- const fileSize = existsSync35(absoluteOutputPath) ? statSync11(absoluteOutputPath).size : 0;
36942
+ const fileSize = existsSync36(absoluteOutputPath) ? statSync13(absoluteOutputPath).size : 0;
36696
36943
  const durationMs = Date.now() - t0;
36697
36944
  const outputToken = store.register(absoluteOutputPath);
36698
36945
  const outputUrl = `${outputUrlPrefix}/${outputToken}`;
@@ -36776,7 +37023,7 @@ function createRenderHandlers(options = {}) {
36776
37023
  log2
36777
37024
  );
36778
37025
  const outputDir = dirname13(absoluteOutputPath);
36779
- if (!existsSync35(outputDir)) mkdirSync21(outputDir, { recursive: true });
37026
+ if (!existsSync36(outputDir)) mkdirSync21(outputDir, { recursive: true });
36780
37027
  log2.info("render-stream started", { requestId, projectDir: input.projectDir });
36781
37028
  const job = createRenderJob({
36782
37029
  fps: input.fps,
@@ -36821,7 +37068,7 @@ function createRenderHandlers(options = {}) {
36821
37068
  },
36822
37069
  abortController.signal
36823
37070
  );
36824
- const fileSize = existsSync35(absoluteOutputPath) ? statSync11(absoluteOutputPath).size : 0;
37071
+ const fileSize = existsSync36(absoluteOutputPath) ? statSync13(absoluteOutputPath).size : 0;
36825
37072
  const outputToken = store.register(absoluteOutputPath);
36826
37073
  const outputUrl = `${outputUrlPrefix}/${outputToken}`;
36827
37074
  log2.info("render-stream completed", { requestId, fileSize, perf: job.perfSummary ?? null });
@@ -36880,11 +37127,11 @@ function createRenderHandlers(options = {}) {
36880
37127
  if (!artifact) {
36881
37128
  return c2.json({ success: false, error: "Output artifact not found or expired" }, 404);
36882
37129
  }
36883
- if (!existsSync35(artifact.path)) {
37130
+ if (!existsSync36(artifact.path)) {
36884
37131
  store.delete(token);
36885
37132
  return c2.json({ success: false, error: "Output artifact file missing" }, 404);
36886
37133
  }
36887
- const stats = statSync11(artifact.path);
37134
+ const stats = statSync13(artifact.path);
36888
37135
  return new Response(createReadStream(artifact.path), {
36889
37136
  headers: {
36890
37137
  "content-type": "video/mp4",
@@ -37018,20 +37265,20 @@ __export(studioServer_exports, {
37018
37265
  });
37019
37266
  import { Hono as Hono5 } from "hono";
37020
37267
  import { streamSSE as streamSSE3 } from "hono/streaming";
37021
- import { existsSync as existsSync36, readFileSync as readFileSync26, writeFileSync as writeFileSync17, statSync as statSync12 } from "fs";
37022
- import { resolve as resolve20, join as join38, basename as basename3 } from "path";
37268
+ import { existsSync as existsSync37, readFileSync as readFileSync26, writeFileSync as writeFileSync17, statSync as statSync14 } from "fs";
37269
+ import { resolve as resolve20, join as join39, basename as basename3 } from "path";
37023
37270
  function resolveDistDir() {
37024
37271
  return resolveStudioBundle().dir;
37025
37272
  }
37026
37273
  function resolveStudioBundle() {
37027
37274
  const builtPath = resolve20(__dirname, "studio");
37028
37275
  const builtIndex = resolve20(builtPath, "index.html");
37029
- if (existsSync36(builtIndex)) {
37276
+ if (existsSync37(builtIndex)) {
37030
37277
  return { dir: builtPath, indexPath: builtIndex, available: true, checkedPaths: [builtIndex] };
37031
37278
  }
37032
37279
  const devPath = resolve20(__dirname, "..", "..", "..", "studio", "dist");
37033
37280
  const devIndex = resolve20(devPath, "index.html");
37034
- if (existsSync36(devIndex)) {
37281
+ if (existsSync37(devIndex)) {
37035
37282
  return {
37036
37283
  dir: devPath,
37037
37284
  indexPath: devIndex,
@@ -37048,9 +37295,9 @@ function resolveStudioBundle() {
37048
37295
  }
37049
37296
  function resolveRuntimePath() {
37050
37297
  const builtPath = resolve20(__dirname, "hyperframe-runtime.js");
37051
- if (existsSync36(builtPath)) return builtPath;
37298
+ if (existsSync37(builtPath)) return builtPath;
37052
37299
  const iifePath = resolve20(__dirname, "hyperframe.runtime.iife.js");
37053
- if (existsSync36(iifePath)) return iifePath;
37300
+ if (existsSync37(iifePath)) return iifePath;
37054
37301
  const devPath = resolve20(
37055
37302
  __dirname,
37056
37303
  "..",
@@ -37060,7 +37307,7 @@ function resolveRuntimePath() {
37060
37307
  "dist",
37061
37308
  "hyperframe.runtime.iife.js"
37062
37309
  );
37063
- if (existsSync36(devPath)) return devPath;
37310
+ if (existsSync37(devPath)) return devPath;
37064
37311
  return builtPath;
37065
37312
  }
37066
37313
  async function getThumbnailBrowser() {
@@ -37122,7 +37369,7 @@ function createStudioServer(options) {
37122
37369
  return lintHyperframeHtml2(html, opts);
37123
37370
  },
37124
37371
  runtimeUrl: "/api/runtime.js",
37125
- rendersDir: () => join38(projectDir, "renders"),
37372
+ rendersDir: () => join39(projectDir, "renders"),
37126
37373
  startRender(opts) {
37127
37374
  const state = {
37128
37375
  id: opts.jobId,
@@ -37194,34 +37441,40 @@ function createStudioServer(options) {
37194
37441
  await new Promise((r2) => setTimeout(r2, 200));
37195
37442
  let clip;
37196
37443
  if (opts.selector) {
37197
- clip = await page.evaluate((selector) => {
37198
- const el = document.querySelector(selector);
37199
- if (!(el instanceof HTMLElement)) return void 0;
37200
- const rect = el.getBoundingClientRect();
37201
- if (rect.width < 4 || rect.height < 4) return void 0;
37202
- const pad = 8;
37203
- const x4 = Math.max(0, rect.left - pad);
37204
- const y2 = Math.max(0, rect.top - pad);
37205
- const maxWidth = window.innerWidth - x4;
37206
- const maxHeight = window.innerHeight - y2;
37207
- return {
37208
- x: x4,
37209
- y: y2,
37210
- width: Math.max(1, Math.min(rect.width + pad * 2, maxWidth)),
37211
- height: Math.max(1, Math.min(rect.height + pad * 2, maxHeight))
37212
- };
37213
- }, opts.selector);
37444
+ clip = await page.evaluate(
37445
+ (selector, selectorIndex) => {
37446
+ const matches2 = Array.from(document.querySelectorAll(selector)).filter(
37447
+ (el2) => el2 instanceof HTMLElement
37448
+ );
37449
+ const safeIndex = Math.max(
37450
+ 0,
37451
+ Math.min(matches2.length - 1, Math.floor(selectorIndex ?? 0))
37452
+ );
37453
+ const el = matches2[safeIndex] ?? null;
37454
+ if (!(el instanceof HTMLElement)) return void 0;
37455
+ const rect = el.getBoundingClientRect();
37456
+ if (rect.width < 4 || rect.height < 4) return void 0;
37457
+ const pad = 8;
37458
+ const x4 = Math.max(0, rect.left - pad);
37459
+ const y2 = Math.max(0, rect.top - pad);
37460
+ const maxWidth = window.innerWidth - x4;
37461
+ const maxHeight = window.innerHeight - y2;
37462
+ return {
37463
+ x: x4,
37464
+ y: y2,
37465
+ width: Math.max(1, Math.min(rect.width + pad * 2, maxWidth)),
37466
+ height: Math.max(1, Math.min(rect.height + pad * 2, maxHeight))
37467
+ };
37468
+ },
37469
+ opts.selector,
37470
+ opts.selectorIndex
37471
+ );
37214
37472
  }
37215
- const screenshot = await page.screenshot(
37216
- opts.format === "png" ? {
37217
- type: "png",
37218
- ...clip ? { clip } : {}
37219
- } : {
37220
- type: "jpeg",
37221
- quality: 80,
37222
- ...clip ? { clip } : {}
37223
- }
37224
- );
37473
+ const screenshot = await page.screenshot({
37474
+ type: "jpeg",
37475
+ quality: 80,
37476
+ ...clip ? { clip } : {}
37477
+ });
37225
37478
  return screenshot;
37226
37479
  } catch {
37227
37480
  return null;
@@ -37242,7 +37495,7 @@ function createStudioServer(options) {
37242
37495
  });
37243
37496
  app.get("/api/runtime.js", (c2) => {
37244
37497
  const serve4 = async () => {
37245
- const runtimeSource = await loadRuntimeSource() ?? (existsSync36(runtimePath) ? readFileSync26(runtimePath, "utf-8") : null);
37498
+ const runtimeSource = await loadRuntimeSource() ?? (existsSync37(runtimePath) ? readFileSync26(runtimePath, "utf-8") : null);
37246
37499
  if (!runtimeSource) return c2.text("runtime not available", 404);
37247
37500
  return c2.body(runtimeSource, 200, {
37248
37501
  "Content-Type": "text/javascript",
@@ -37278,7 +37531,7 @@ function createStudioServer(options) {
37278
37531
  });
37279
37532
  app.get("/assets/*", (c2) => {
37280
37533
  const filePath = resolve20(studioDir, c2.req.path.slice(1));
37281
- if (!existsSync36(filePath) || !statSync12(filePath).isFile()) return c2.text("not found", 404);
37534
+ if (!existsSync37(filePath) || !statSync14(filePath).isFile()) return c2.text("not found", 404);
37282
37535
  const content = readFileSync26(filePath);
37283
37536
  return new Response(content, {
37284
37537
  headers: { "Content-Type": getMimeType(filePath), "Cache-Control": "no-store" }
@@ -37286,7 +37539,7 @@ function createStudioServer(options) {
37286
37539
  });
37287
37540
  app.get("/icons/*", (c2) => {
37288
37541
  const filePath = resolve20(studioDir, c2.req.path.slice(1));
37289
- if (!existsSync36(filePath) || !statSync12(filePath).isFile()) return c2.text("not found", 404);
37542
+ if (!existsSync37(filePath) || !statSync14(filePath).isFile()) return c2.text("not found", 404);
37290
37543
  const content = readFileSync26(filePath);
37291
37544
  return new Response(content, {
37292
37545
  headers: { "Content-Type": getMimeType(filePath), "Cache-Control": "no-store" }
@@ -37294,7 +37547,7 @@ function createStudioServer(options) {
37294
37547
  });
37295
37548
  app.get("*", (c2) => {
37296
37549
  const indexPath = resolve20(studioDir, "index.html");
37297
- if (!existsSync36(indexPath)) {
37550
+ if (!existsSync37(indexPath)) {
37298
37551
  return c2.html(
37299
37552
  `<!doctype html>
37300
37553
  <html>
@@ -37374,20 +37627,20 @@ __export(preview_exports, {
37374
37627
  examples: () => examples
37375
37628
  });
37376
37629
  import { spawn as spawn9 } from "child_process";
37377
- import { existsSync as existsSync37, lstatSync, symlinkSync, unlinkSync as unlinkSync5, readlinkSync, mkdirSync as mkdirSync22 } from "fs";
37378
- import { resolve as resolve21, dirname as dirname14, basename as basename4, join as join39 } from "path";
37630
+ import { existsSync as existsSync38, lstatSync, symlinkSync, unlinkSync as unlinkSync5, readlinkSync, mkdirSync as mkdirSync22 } from "fs";
37631
+ import { resolve as resolve21, dirname as dirname14, basename as basename4, join as join40 } from "path";
37379
37632
  import { fileURLToPath as fileURLToPath4 } from "url";
37380
37633
  import { createRequire } from "module";
37381
37634
  async function runDevMode(dir, projectName) {
37382
37635
  const thisFile = fileURLToPath4(import.meta.url);
37383
37636
  const repoRoot = resolve21(dirname14(thisFile), "..", "..", "..", "..");
37384
- const projectsDir = join39(repoRoot, "packages", "studio", "data", "projects");
37637
+ const projectsDir = join40(repoRoot, "packages", "studio", "data", "projects");
37385
37638
  const pName = projectName ?? basename4(dir);
37386
- const symlinkPath = join39(projectsDir, pName);
37639
+ const symlinkPath = join40(projectsDir, pName);
37387
37640
  mkdirSync22(projectsDir, { recursive: true });
37388
37641
  let createdSymlink = false;
37389
37642
  if (dir !== symlinkPath) {
37390
- if (existsSync37(symlinkPath)) {
37643
+ if (existsSync38(symlinkPath)) {
37391
37644
  try {
37392
37645
  const stat3 = lstatSync(symlinkPath);
37393
37646
  if (stat3.isSymbolicLink()) {
@@ -37399,7 +37652,7 @@ async function runDevMode(dir, projectName) {
37399
37652
  } catch {
37400
37653
  }
37401
37654
  }
37402
- if (!existsSync37(symlinkPath)) {
37655
+ if (!existsSync38(symlinkPath)) {
37403
37656
  symlinkSync(dir, symlinkPath, "dir");
37404
37657
  createdSymlink = true;
37405
37658
  }
@@ -37407,7 +37660,7 @@ async function runDevMode(dir, projectName) {
37407
37660
  Wt2(c.bold("hyperframes preview"));
37408
37661
  const s2 = be();
37409
37662
  s2.start("Starting studio...");
37410
- const studioPkgDir = join39(repoRoot, "packages", "studio");
37663
+ const studioPkgDir = join40(repoRoot, "packages", "studio");
37411
37664
  const child = spawn9("pnpm", ["exec", "vite"], {
37412
37665
  cwd: studioPkgDir,
37413
37666
  stdio: ["ignore", "pipe", "pipe"]
@@ -37441,7 +37694,7 @@ async function runDevMode(dir, projectName) {
37441
37694
  if (createdSymlink) {
37442
37695
  process.on("exit", () => {
37443
37696
  try {
37444
- if (existsSync37(symlinkPath)) unlinkSync5(symlinkPath);
37697
+ if (existsSync38(symlinkPath)) unlinkSync5(symlinkPath);
37445
37698
  } catch {
37446
37699
  }
37447
37700
  });
@@ -37452,7 +37705,7 @@ async function runDevMode(dir, projectName) {
37452
37705
  }
37453
37706
  function hasLocalStudio(dir) {
37454
37707
  try {
37455
- const req = createRequire(join39(dir, "package.json"));
37708
+ const req = createRequire(join40(dir, "package.json"));
37456
37709
  req.resolve("@hyperframes/studio/package.json");
37457
37710
  return true;
37458
37711
  } catch {
@@ -37460,20 +37713,20 @@ function hasLocalStudio(dir) {
37460
37713
  }
37461
37714
  }
37462
37715
  async function runLocalStudioMode(dir, projectName) {
37463
- const req = createRequire(join39(dir, "package.json"));
37716
+ const req = createRequire(join40(dir, "package.json"));
37464
37717
  const studioPkgPath = dirname14(req.resolve("@hyperframes/studio/package.json"));
37465
37718
  const pName = projectName ?? basename4(dir);
37466
- const projectsDir = join39(studioPkgPath, "data", "projects");
37467
- const symlinkPath = join39(projectsDir, pName);
37719
+ const projectsDir = join40(studioPkgPath, "data", "projects");
37720
+ const symlinkPath = join40(projectsDir, pName);
37468
37721
  mkdirSync22(projectsDir, { recursive: true });
37469
37722
  let createdSymlink = false;
37470
37723
  if (dir !== symlinkPath) {
37471
- if (existsSync37(symlinkPath) && lstatSync(symlinkPath).isSymbolicLink()) {
37724
+ if (existsSync38(symlinkPath) && lstatSync(symlinkPath).isSymbolicLink()) {
37472
37725
  if (resolve21(readlinkSync(symlinkPath)) !== resolve21(dir)) {
37473
37726
  unlinkSync5(symlinkPath);
37474
37727
  }
37475
37728
  }
37476
- if (!existsSync37(symlinkPath)) {
37729
+ if (!existsSync38(symlinkPath)) {
37477
37730
  symlinkSync(dir, symlinkPath, "dir");
37478
37731
  createdSymlink = true;
37479
37732
  }
@@ -37512,7 +37765,7 @@ async function runLocalStudioMode(dir, projectName) {
37512
37765
  if (createdSymlink) {
37513
37766
  process.on("exit", () => {
37514
37767
  try {
37515
- if (existsSync37(symlinkPath)) unlinkSync5(symlinkPath);
37768
+ if (existsSync38(symlinkPath)) unlinkSync5(symlinkPath);
37516
37769
  } catch {
37517
37770
  }
37518
37771
  });
@@ -37688,8 +37941,8 @@ var init_preview2 = __esm({
37688
37941
  const dir = resolve21(rawArg ?? ".");
37689
37942
  const isImplicitCwd = !rawArg || rawArg === "." || rawArg === "./";
37690
37943
  const projectName = isImplicitCwd ? basename4(process.env.PWD ?? dir) : basename4(dir);
37691
- const indexPath = join39(dir, "index.html");
37692
- if (existsSync37(indexPath)) {
37944
+ const indexPath = join40(dir, "index.html");
37945
+ if (existsSync38(indexPath)) {
37693
37946
  const project = { dir, name: projectName, indexPath };
37694
37947
  const lintResult = lintProject(project);
37695
37948
  if (lintResult.totalErrors > 0 || lintResult.totalWarnings > 0) {
@@ -37718,20 +37971,20 @@ __export(init_exports, {
37718
37971
  examples: () => examples2
37719
37972
  });
37720
37973
  import {
37721
- existsSync as existsSync38,
37974
+ existsSync as existsSync39,
37722
37975
  mkdirSync as mkdirSync23,
37723
37976
  copyFileSync as copyFileSync3,
37724
37977
  cpSync,
37725
37978
  writeFileSync as writeFileSync18,
37726
37979
  readFileSync as readFileSync27,
37727
- readdirSync as readdirSync13
37980
+ readdirSync as readdirSync14
37728
37981
  } from "fs";
37729
- import { resolve as resolve22, basename as basename5, join as join40, dirname as dirname15 } from "path";
37982
+ import { resolve as resolve22, basename as basename5, join as join41, dirname as dirname15 } from "path";
37730
37983
  import { fileURLToPath as fileURLToPath5 } from "url";
37731
- import { execFileSync as execFileSync4, spawn as spawn10 } from "child_process";
37984
+ import { execFileSync as execFileSync5, spawn as spawn10 } from "child_process";
37732
37985
  function probeVideo(filePath) {
37733
37986
  try {
37734
- const raw = execFileSync4(
37987
+ const raw = execFileSync5(
37735
37988
  "ffprobe",
37736
37989
  ["-v", "quiet", "-print_format", "json", "-show_format", "-show_streams", filePath],
37737
37990
  { encoding: "utf-8", timeout: 15e3 }
@@ -37798,7 +38051,7 @@ function resolveAssetDir(devSegments, builtSegments) {
37798
38051
  const base = dirname15(fileURLToPath5(import.meta.url));
37799
38052
  const devPath = resolve22(base, ...devSegments);
37800
38053
  const builtPath = resolve22(base, ...builtSegments);
37801
- return existsSync38(devPath) ? devPath : builtPath;
38054
+ return existsSync39(devPath) ? devPath : builtPath;
37802
38055
  }
37803
38056
  function getStaticTemplateDir(templateId) {
37804
38057
  return resolveAssetDir(["..", "templates", templateId], ["templates", templateId]);
@@ -37807,7 +38060,7 @@ function getSharedTemplateDir() {
37807
38060
  return resolveAssetDir(["..", "templates", "_shared"], ["templates", "_shared"]);
37808
38061
  }
37809
38062
  function patchVideoSrc(dir, videoFilename, durationSeconds) {
37810
- const htmlFiles = readdirSync13(dir, { withFileTypes: true, recursive: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".html")).map((e2) => join40(e2.parentPath ?? e2.path, e2.name));
38063
+ const htmlFiles = readdirSync14(dir, { withFileTypes: true, recursive: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".html")).map((e2) => join41(e2.parentPath ?? e2.path, e2.name));
37811
38064
  for (const file of htmlFiles) {
37812
38065
  let content = readFileSync27(file, "utf-8");
37813
38066
  if (videoFilename) {
@@ -37914,7 +38167,7 @@ async function handleVideoFile(videoPath, destDir, interactive) {
37914
38167
  async function scaffoldProject(destDir, name, templateId, localVideoName, durationSeconds) {
37915
38168
  mkdirSync23(destDir, { recursive: true });
37916
38169
  const templateDir = getStaticTemplateDir(templateId);
37917
- if (existsSync38(templateDir)) {
38170
+ if (existsSync39(templateDir)) {
37918
38171
  cpSync(templateDir, destDir, { recursive: true });
37919
38172
  } else {
37920
38173
  await fetchRemoteTemplate(templateId, destDir);
@@ -37933,14 +38186,14 @@ async function scaffoldProject(destDir, name, templateId, localVideoName, durati
37933
38186
  ),
37934
38187
  "utf-8"
37935
38188
  );
37936
- if (!existsSync38(resolve22(destDir, "hyperframes.json"))) {
38189
+ if (!existsSync39(resolve22(destDir, "hyperframes.json"))) {
37937
38190
  const { writeProjectConfig: writeProjectConfig2, DEFAULT_PROJECT_CONFIG: DEFAULT_PROJECT_CONFIG2 } = await Promise.resolve().then(() => (init_projectConfig(), projectConfig_exports));
37938
38191
  writeProjectConfig2(destDir, DEFAULT_PROJECT_CONFIG2);
37939
38192
  }
37940
38193
  const sharedDir = getSharedTemplateDir();
37941
- if (existsSync38(sharedDir)) {
37942
- for (const entry of readdirSync13(sharedDir, { withFileTypes: true })) {
37943
- const src = join40(sharedDir, entry.name);
38194
+ if (existsSync39(sharedDir)) {
38195
+ for (const entry of readdirSync14(sharedDir, { withFileTypes: true })) {
38196
+ const src = join41(sharedDir, entry.name);
37944
38197
  const dest = resolve22(destDir, entry.name);
37945
38198
  if (entry.isFile() || entry.isSymbolicLink()) {
37946
38199
  copyFileSync3(src, dest);
@@ -38054,7 +38307,7 @@ var init_init = __esm({
38054
38307
  const templateId2 = exampleFlag ?? "blank";
38055
38308
  const name2 = args.name ?? "my-video";
38056
38309
  const destDir2 = resolve22(name2);
38057
- if (existsSync38(destDir2) && readdirSync13(destDir2).length > 0) {
38310
+ if (existsSync39(destDir2) && readdirSync14(destDir2).length > 0) {
38058
38311
  console.error(c.error(`Directory already exists and is not empty: ${name2}`));
38059
38312
  process.exit(1);
38060
38313
  }
@@ -38068,7 +38321,7 @@ var init_init = __esm({
38068
38321
  }
38069
38322
  if (videoFlag) {
38070
38323
  const videoPath = resolve22(videoFlag);
38071
- if (!existsSync38(videoPath)) {
38324
+ if (!existsSync39(videoPath)) {
38072
38325
  console.error(c.error(`Video file not found: ${videoFlag}`));
38073
38326
  process.exit(1);
38074
38327
  }
@@ -38082,7 +38335,7 @@ var init_init = __esm({
38082
38335
  }
38083
38336
  if (audioFlag) {
38084
38337
  const audioPath = resolve22(audioFlag);
38085
- if (!existsSync38(audioPath)) {
38338
+ if (!existsSync39(audioPath)) {
38086
38339
  console.error(c.error(`Audio file not found: ${audioFlag}`));
38087
38340
  process.exit(1);
38088
38341
  }
@@ -38128,11 +38381,11 @@ var init_init = __esm({
38128
38381
  }
38129
38382
  trackInitTemplate(templateId2);
38130
38383
  const transcriptFile2 = resolve22(destDir2, "transcript.json");
38131
- if (existsSync38(transcriptFile2)) {
38384
+ if (existsSync39(transcriptFile2)) {
38132
38385
  await patchTranscript(destDir2, transcriptFile2);
38133
38386
  }
38134
38387
  console.log(c.success(`Created ${c.accent(name2 + "/")}`));
38135
- for (const f3 of readdirSync13(destDir2).filter((f4) => !f4.startsWith("."))) {
38388
+ for (const f3 of readdirSync14(destDir2).filter((f4) => !f4.startsWith("."))) {
38136
38389
  console.log(` ${c.accent(f3)}`);
38137
38390
  }
38138
38391
  console.log();
@@ -38180,7 +38433,7 @@ var init_init = __esm({
38180
38433
  name = nameResult;
38181
38434
  }
38182
38435
  const destDir = resolve22(name);
38183
- if (existsSync38(destDir) && readdirSync13(destDir).length > 0) {
38436
+ if (existsSync39(destDir) && readdirSync14(destDir).length > 0) {
38184
38437
  const overwrite = await Rt({
38185
38438
  message: `Directory ${c.accent(name)} already exists and is not empty. Overwrite?`,
38186
38439
  initialValue: false
@@ -38195,7 +38448,7 @@ var init_init = __esm({
38195
38448
  let videoDuration;
38196
38449
  if (videoFlag) {
38197
38450
  const videoPath = resolve22(videoFlag);
38198
- if (!existsSync38(videoPath)) {
38451
+ if (!existsSync39(videoPath)) {
38199
38452
  R2.error(`File not found: ${videoFlag}`);
38200
38453
  Nt("Setup cancelled.");
38201
38454
  process.exit(1);
@@ -38207,7 +38460,7 @@ var init_init = __esm({
38207
38460
  videoDuration = result.meta.durationSeconds;
38208
38461
  } else if (audioFlag) {
38209
38462
  const audioPath = resolve22(audioFlag);
38210
- if (!existsSync38(audioPath)) {
38463
+ if (!existsSync39(audioPath)) {
38211
38464
  R2.error(`File not found: ${audioFlag}`);
38212
38465
  Nt("Setup cancelled.");
38213
38466
  process.exit(1);
@@ -38300,10 +38553,10 @@ ${c.dim("Use --example blank for offline use.")}`
38300
38553
  }
38301
38554
  trackInitTemplate(templateId);
38302
38555
  const transcriptFile = resolve22(destDir, "transcript.json");
38303
- if (existsSync38(transcriptFile)) {
38556
+ if (existsSync39(transcriptFile)) {
38304
38557
  await patchTranscript(destDir, transcriptFile);
38305
38558
  }
38306
- const files = readdirSync13(destDir);
38559
+ const files = readdirSync14(destDir);
38307
38560
  Vt2(files.map((f3) => c.accent(f3)).join("\n"), c.success(`Created ${name}/`));
38308
38561
  if (!skipSkills) {
38309
38562
  const installSkills = await Rt({
@@ -38332,9 +38585,9 @@ ${c.dim("Use --example blank for offline use.")}`
38332
38585
 
38333
38586
  // src/utils/clipboard.ts
38334
38587
  import { spawnSync as spawnSync2 } from "child_process";
38335
- import { platform as platform3 } from "os";
38588
+ import { platform as platform4 } from "os";
38336
38589
  function detectProvider() {
38337
- const os = platform3();
38590
+ const os = platform4();
38338
38591
  if (os === "darwin") {
38339
38592
  return { cmd: "pbcopy", args: [] };
38340
38593
  }
@@ -38386,7 +38639,7 @@ __export(add_exports, {
38386
38639
  remapTarget: () => remapTarget,
38387
38640
  runAdd: () => runAdd
38388
38641
  });
38389
- import { existsSync as existsSync39 } from "fs";
38642
+ import { existsSync as existsSync40 } from "fs";
38390
38643
  import { resolve as resolve23, relative as relative3 } from "path";
38391
38644
  function remapTarget(item, originalTarget, paths) {
38392
38645
  if (item.type === "hyperframes:block") {
@@ -38412,8 +38665,8 @@ function buildSnippet(item, relativeTarget) {
38412
38665
  async function runAdd(opts) {
38413
38666
  const projectDir = resolve23(opts.projectDir);
38414
38667
  let config = loadProjectConfig(projectDir);
38415
- const hasConfig = existsSync39(projectConfigPath(projectDir));
38416
- if (!hasConfig && existsSync39(resolve23(projectDir, "index.html"))) {
38668
+ const hasConfig = existsSync40(projectConfigPath(projectDir));
38669
+ if (!hasConfig && existsSync40(resolve23(projectDir, "index.html"))) {
38417
38670
  writeProjectConfig(projectDir, DEFAULT_PROJECT_CONFIG);
38418
38671
  config = DEFAULT_PROJECT_CONFIG;
38419
38672
  }
@@ -38512,10 +38765,10 @@ var init_add = __esm({
38512
38765
  const projectDir = resolve23(args.dir ?? process.cwd());
38513
38766
  const json = args.json === true;
38514
38767
  const skipClipboard = args["no-clipboard"] === true;
38515
- const hasConfigBefore = existsSync39(projectConfigPath(projectDir));
38768
+ const hasConfigBefore = existsSync40(projectConfigPath(projectDir));
38516
38769
  try {
38517
38770
  const result = await runAdd({ name: args.name, projectDir, skipClipboard });
38518
- const wroteConfig = !hasConfigBefore && existsSync39(projectConfigPath(projectDir));
38771
+ const wroteConfig = !hasConfigBefore && existsSync40(projectConfigPath(projectDir));
38519
38772
  if (json) {
38520
38773
  console.log(JSON.stringify(result));
38521
38774
  return;
@@ -38725,17 +38978,17 @@ var init_format = __esm({
38725
38978
  });
38726
38979
 
38727
38980
  // src/utils/project.ts
38728
- import { existsSync as existsSync40, statSync as statSync13 } from "fs";
38981
+ import { existsSync as existsSync41, statSync as statSync15 } from "fs";
38729
38982
  import { resolve as resolve25, basename as basename6 } from "path";
38730
38983
  function resolveProject(dirArg) {
38731
38984
  const dir = resolve25(dirArg ?? ".");
38732
38985
  const name = basename6(dir);
38733
38986
  const indexPath = resolve25(dir, "index.html");
38734
- if (!existsSync40(dir) || !statSync13(dir).isDirectory()) {
38987
+ if (!existsSync41(dir) || !statSync15(dir).isDirectory()) {
38735
38988
  errorBox("Not a directory: " + dir);
38736
38989
  process.exit(1);
38737
38990
  }
38738
- if (!existsSync40(indexPath)) {
38991
+ if (!existsSync41(indexPath)) {
38739
38992
  errorBox(
38740
38993
  "No composition found in " + dir,
38741
38994
  "No index.html file found.",
@@ -38758,7 +39011,7 @@ __export(play_exports, {
38758
39011
  default: () => play_default,
38759
39012
  examples: () => examples5
38760
39013
  });
38761
- import { existsSync as existsSync41, readFileSync as readFileSync28 } from "fs";
39014
+ import { existsSync as existsSync42, readFileSync as readFileSync28 } from "fs";
38762
39015
  import { resolve as resolve26, dirname as dirname16 } from "path";
38763
39016
  function commandDir() {
38764
39017
  return dirname16(new URL(import.meta.url).pathname);
@@ -38773,7 +39026,7 @@ function resolveRuntimePath2() {
38773
39026
  resolve26(d, "..", "..", "..", "core", "dist", "hyperframe.runtime.iife.js")
38774
39027
  ];
38775
39028
  for (const p of candidates) {
38776
- if (existsSync41(p)) return p;
39029
+ if (existsSync42(p)) return p;
38777
39030
  }
38778
39031
  return null;
38779
39032
  }
@@ -38787,7 +39040,7 @@ function resolvePlayerPath() {
38787
39040
  resolve26(d, "..", "hyperframes-player.global.js")
38788
39041
  ];
38789
39042
  for (const p of candidates) {
38790
- if (existsSync41(p)) return p;
39043
+ if (existsSync42(p)) return p;
38791
39044
  }
38792
39045
  return null;
38793
39046
  }
@@ -38889,7 +39142,7 @@ var init_play = __esm({
38889
39142
  const reqPath = ctx.req.path.replace("/composition/", "");
38890
39143
  const filePath = resolve26(project.dir, reqPath);
38891
39144
  if (!filePath.startsWith(project.dir)) return ctx.text("Forbidden", 403);
38892
- if (!existsSync41(filePath)) return ctx.text("Not found", 404);
39145
+ if (!existsSync42(filePath)) return ctx.text("Not found", 404);
38893
39146
  const content = readFileSync28(filePath, "utf-8");
38894
39147
  if (filePath.endsWith(".html")) {
38895
39148
  const injected = injectRuntime(content);
@@ -38965,14 +39218,14 @@ var init_play = __esm({
38965
39218
  });
38966
39219
 
38967
39220
  // src/utils/publishProject.ts
38968
- import { basename as basename7, join as join41, relative as relative4 } from "path";
38969
- import { readdirSync as readdirSync14, readFileSync as readFileSync29, statSync as statSync14 } from "fs";
39221
+ import { basename as basename7, join as join42, relative as relative4 } from "path";
39222
+ import { readdirSync as readdirSync15, readFileSync as readFileSync29, statSync as statSync16 } from "fs";
38970
39223
  import AdmZip from "adm-zip";
38971
- function isRecord(value) {
39224
+ function isRecord2(value) {
38972
39225
  return typeof value === "object" && value !== null && !Array.isArray(value);
38973
39226
  }
38974
39227
  function dataRecord(payload) {
38975
- if (!isRecord(payload) || !isRecord(payload["data"])) return null;
39228
+ if (!isRecord2(payload) || !isRecord2(payload["data"])) return null;
38976
39229
  return payload["data"];
38977
39230
  }
38978
39231
  function stringField(record, key2) {
@@ -39015,7 +39268,7 @@ function parseStagedUploadResponse(payload, archiveByteLength) {
39015
39268
  function getUploadHeaders(data, uploadUrl, contentType, archiveByteLength) {
39016
39269
  const headers = {};
39017
39270
  const uploadHeaders = data["upload_headers"];
39018
- if (isRecord(uploadHeaders)) {
39271
+ if (isRecord2(uploadHeaders)) {
39019
39272
  for (const [key2, value] of Object.entries(uploadHeaders)) {
39020
39273
  if (typeof value === "string" && key2.trim()) {
39021
39274
  headers[key2] = value;
@@ -39041,7 +39294,7 @@ async function readErrorMessage(response, fallback) {
39041
39294
  const contentType = response.headers.get("content-type") || "";
39042
39295
  if (contentType.includes("application/json")) {
39043
39296
  const payload = await readJson(response);
39044
- if (isRecord(payload) && typeof payload["message"] === "string") {
39297
+ if (isRecord2(payload) && typeof payload["message"] === "string") {
39045
39298
  return payload["message"];
39046
39299
  }
39047
39300
  }
@@ -39055,16 +39308,16 @@ function shouldIgnoreSegment(segment) {
39055
39308
  return segment.startsWith(".") || IGNORED_DIRS.has(segment) || IGNORED_FILES.has(segment);
39056
39309
  }
39057
39310
  function collectProjectFiles(rootDir, currentDir, paths) {
39058
- for (const entry of readdirSync14(currentDir, { withFileTypes: true })) {
39311
+ for (const entry of readdirSync15(currentDir, { withFileTypes: true })) {
39059
39312
  if (shouldIgnoreSegment(entry.name)) continue;
39060
- const absolutePath = join41(currentDir, entry.name);
39313
+ const absolutePath = join42(currentDir, entry.name);
39061
39314
  const relativePath = relative4(rootDir, absolutePath).replaceAll("\\", "/");
39062
39315
  if (!relativePath) continue;
39063
39316
  if (entry.isDirectory()) {
39064
39317
  collectProjectFiles(rootDir, absolutePath, paths);
39065
39318
  continue;
39066
39319
  }
39067
- if (!statSync14(absolutePath).isFile()) continue;
39320
+ if (!statSync16(absolutePath).isFile()) continue;
39068
39321
  paths.push(relativePath);
39069
39322
  }
39070
39323
  }
@@ -39076,7 +39329,7 @@ function createPublishArchive(projectDir) {
39076
39329
  }
39077
39330
  const archive = new AdmZip();
39078
39331
  for (const filePath of filePaths) {
39079
- archive.addFile(filePath, readFileSync29(join41(projectDir, filePath)));
39332
+ archive.addFile(filePath, readFileSync29(join42(projectDir, filePath)));
39080
39333
  }
39081
39334
  return {
39082
39335
  buffer: archive.toBuffer(),
@@ -39192,8 +39445,8 @@ __export(publish_exports, {
39192
39445
  examples: () => examples6
39193
39446
  });
39194
39447
  import { basename as basename8, resolve as resolve27 } from "path";
39195
- import { existsSync as existsSync42 } from "fs";
39196
- import { join as join42 } from "path";
39448
+ import { existsSync as existsSync43 } from "fs";
39449
+ import { join as join43 } from "path";
39197
39450
  var examples6, publish_default;
39198
39451
  var init_publish = __esm({
39199
39452
  "src/commands/publish.ts"() {
@@ -39228,8 +39481,8 @@ var init_publish = __esm({
39228
39481
  const dir = resolve27(rawArg ?? ".");
39229
39482
  const isImplicitCwd = !rawArg || rawArg === "." || rawArg === "./";
39230
39483
  const projectName = isImplicitCwd ? basename8(process.env["PWD"] ?? dir) : basename8(dir);
39231
- const indexPath = join42(dir, "index.html");
39232
- if (existsSync42(indexPath)) {
39484
+ const indexPath = join43(dir, "index.html");
39485
+ if (existsSync43(indexPath)) {
39233
39486
  const lintResult = lintProject({ dir, name: projectName, indexPath });
39234
39487
  if (lintResult.totalErrors > 0 || lintResult.totalWarnings > 0) {
39235
39488
  console.log();
@@ -39400,10 +39653,10 @@ __export(render_exports, {
39400
39653
  default: () => render_default,
39401
39654
  examples: () => examples7
39402
39655
  });
39403
- import { mkdirSync as mkdirSync24, readFileSync as readFileSync30, statSync as statSync15, writeFileSync as writeFileSync19, rmSync as rmSync9 } from "fs";
39656
+ import { mkdirSync as mkdirSync24, readFileSync as readFileSync30, statSync as statSync17, writeFileSync as writeFileSync19, rmSync as rmSync9 } from "fs";
39404
39657
  import { cpus as cpus3, freemem as freemem3, tmpdir as tmpdir4 } from "os";
39405
- import { resolve as resolve28, dirname as dirname17, join as join43, basename as basename9 } from "path";
39406
- import { execFileSync as execFileSync5, spawn as spawn11 } from "child_process";
39658
+ import { resolve as resolve28, dirname as dirname17, join as join44, basename as basename9 } from "path";
39659
+ import { execFileSync as execFileSync6, spawn as spawn11 } from "child_process";
39407
39660
  function dockerImageTag(version) {
39408
39661
  return `${DOCKER_IMAGE_PREFIX}:${version}`;
39409
39662
  }
@@ -39412,7 +39665,7 @@ function resolveDockerfilePath() {
39412
39665
  const devPath = resolve28(__dirname, "..", "src", "docker", "Dockerfile.render");
39413
39666
  for (const p of [builtPath, devPath]) {
39414
39667
  try {
39415
- statSync15(p);
39668
+ statSync17(p);
39416
39669
  return p;
39417
39670
  } catch {
39418
39671
  continue;
@@ -39422,7 +39675,7 @@ function resolveDockerfilePath() {
39422
39675
  }
39423
39676
  function dockerImageExists(tag) {
39424
39677
  try {
39425
- execFileSync5("docker", ["image", "inspect", tag], { stdio: "pipe", timeout: 1e4 });
39678
+ execFileSync6("docker", ["image", "inspect", tag], { stdio: "pipe", timeout: 1e4 });
39426
39679
  return true;
39427
39680
  } catch {
39428
39681
  return false;
@@ -39436,11 +39689,11 @@ function ensureDockerImage(version, quiet) {
39436
39689
  }
39437
39690
  if (!quiet) console.log(c.dim(` Building Docker image: ${tag}...`));
39438
39691
  const dockerfilePath = resolveDockerfilePath();
39439
- const tmpDir = join43(tmpdir4(), `hyperframes-docker-${Date.now()}`);
39692
+ const tmpDir = join44(tmpdir4(), `hyperframes-docker-${Date.now()}`);
39440
39693
  mkdirSync24(tmpDir, { recursive: true });
39441
- writeFileSync19(join43(tmpDir, "Dockerfile"), readFileSync30(dockerfilePath));
39694
+ writeFileSync19(join44(tmpDir, "Dockerfile"), readFileSync30(dockerfilePath));
39442
39695
  try {
39443
- execFileSync5(
39696
+ execFileSync6(
39444
39697
  "docker",
39445
39698
  [
39446
39699
  "build",
@@ -39625,7 +39878,7 @@ function printRenderComplete(outputPath, elapsedMs, quiet) {
39625
39878
  if (quiet) return;
39626
39879
  let fileSize = "unknown";
39627
39880
  try {
39628
- fileSize = formatBytes(statSync15(outputPath).size);
39881
+ fileSize = formatBytes(statSync17(outputPath).size);
39629
39882
  } catch {
39630
39883
  }
39631
39884
  const duration = formatDuration(elapsedMs);
@@ -39791,7 +40044,7 @@ var init_render2 = __esm({
39791
40044
  const now = /* @__PURE__ */ new Date();
39792
40045
  const datePart = now.toISOString().slice(0, 10);
39793
40046
  const timePart = now.toTimeString().slice(0, 8).replace(/:/g, "-");
39794
- const outputPath = args.output ? resolve28(args.output) : join43(rendersDir, `${project.name}_${datePart}_${timePart}${ext}`);
40047
+ const outputPath = args.output ? resolve28(args.output) : join44(rendersDir, `${project.name}_${datePart}_${timePart}${ext}`);
39795
40048
  mkdirSync24(dirname17(outputPath), { recursive: true });
39796
40049
  const useDocker = args.docker ?? false;
39797
40050
  const useGpu = args.gpu ?? false;
@@ -40270,8 +40523,8 @@ __export(layout_exports, {
40270
40523
  examples: () => examples9
40271
40524
  });
40272
40525
  import { createServer } from "http";
40273
- import { existsSync as existsSync43, readFileSync as readFileSync31 } from "fs";
40274
- import { dirname as dirname18, isAbsolute as isAbsolute6, join as join44, relative as relative5, resolve as resolve29 } from "path";
40526
+ import { existsSync as existsSync44, readFileSync as readFileSync31 } from "fs";
40527
+ import { dirname as dirname18, isAbsolute as isAbsolute6, join as join45, relative as relative5, resolve as resolve29 } from "path";
40275
40528
  import { fileURLToPath as fileURLToPath6 } from "url";
40276
40529
  async function getCompositionDuration2(page) {
40277
40530
  return page.evaluate(() => {
@@ -40341,7 +40594,7 @@ async function bundleProjectHtml(projectDir) {
40341
40594
  "dist",
40342
40595
  "hyperframe.runtime.iife.js"
40343
40596
  );
40344
- if (existsSync43(runtimePath)) {
40597
+ if (existsSync44(runtimePath)) {
40345
40598
  const runtimeSource = readFileSync31(runtimePath, "utf-8");
40346
40599
  html = html.replace(
40347
40600
  /<script[^>]*data-hyperframes-preview-runtime[^>]*src="[^"]*"[^>]*><\/script>/,
@@ -40366,7 +40619,7 @@ async function serveProject(projectDir, html) {
40366
40619
  res.end();
40367
40620
  return;
40368
40621
  }
40369
- if (existsSync43(filePath)) {
40622
+ if (existsSync44(filePath)) {
40370
40623
  res.writeHead(200, { "Content-Type": getMimeType2(filePath) });
40371
40624
  res.end(readFileSync31(filePath));
40372
40625
  return;
@@ -40470,11 +40723,11 @@ async function runLayoutAudit(projectDir, opts) {
40470
40723
  }
40471
40724
  function loadLayoutAuditScript() {
40472
40725
  const candidates = [
40473
- join44(__dirname2, "layout-audit.browser.js"),
40474
- join44(__dirname2, "commands", "layout-audit.browser.js")
40726
+ join45(__dirname2, "layout-audit.browser.js"),
40727
+ join45(__dirname2, "commands", "layout-audit.browser.js")
40475
40728
  ];
40476
40729
  for (const candidate of candidates) {
40477
- if (existsSync43(candidate)) return readFileSync31(candidate, "utf-8");
40730
+ if (existsSync44(candidate)) return readFileSync31(candidate, "utf-8");
40478
40731
  }
40479
40732
  throw new Error("Missing layout audit browser script");
40480
40733
  }
@@ -40694,16 +40947,16 @@ __export(info_exports, {
40694
40947
  default: () => info_default,
40695
40948
  examples: () => examples11
40696
40949
  });
40697
- import { readFileSync as readFileSync32, readdirSync as readdirSync15, statSync as statSync16 } from "fs";
40698
- import { join as join45 } from "path";
40950
+ import { readFileSync as readFileSync32, readdirSync as readdirSync16, statSync as statSync18 } from "fs";
40951
+ import { join as join46 } from "path";
40699
40952
  function totalSize(dir) {
40700
40953
  let total = 0;
40701
- for (const entry of readdirSync15(dir, { withFileTypes: true })) {
40702
- const path2 = join45(dir, entry.name);
40954
+ for (const entry of readdirSync16(dir, { withFileTypes: true })) {
40955
+ const path2 = join46(dir, entry.name);
40703
40956
  if (entry.isDirectory()) {
40704
40957
  total += totalSize(path2);
40705
40958
  } else {
40706
- total += statSync16(path2).size;
40959
+ total += statSync18(path2).size;
40707
40960
  }
40708
40961
  }
40709
40962
  return total;
@@ -40787,7 +41040,7 @@ __export(compositions_exports, {
40787
41040
  default: () => compositions_default,
40788
41041
  examples: () => examples12
40789
41042
  });
40790
- import { existsSync as existsSync44, readFileSync as readFileSync33 } from "fs";
41043
+ import { existsSync as existsSync45, readFileSync as readFileSync33 } from "fs";
40791
41044
  import { resolve as resolve30, dirname as dirname19 } from "path";
40792
41045
  function parseCompositions(html, baseDir) {
40793
41046
  const parser = new DOMParser();
@@ -40801,7 +41054,7 @@ function parseCompositions(html, baseDir) {
40801
41054
  const compositionSrc = div.getAttribute("data-composition-src");
40802
41055
  if (compositionSrc) {
40803
41056
  const subPath = resolve30(baseDir, compositionSrc);
40804
- if (existsSync44(subPath)) {
41057
+ if (existsSync45(subPath)) {
40805
41058
  const subHtml = readFileSync33(subPath, "utf-8");
40806
41059
  const subInfo = parseSubComposition(subHtml, id, width, height);
40807
41060
  compositions.push({ ...subInfo, source: compositionSrc });
@@ -40934,8 +41187,8 @@ __export(benchmark_exports, {
40934
41187
  default: () => benchmark_default,
40935
41188
  examples: () => examples13
40936
41189
  });
40937
- import { existsSync as existsSync45, statSync as statSync17 } from "fs";
40938
- import { resolve as resolve31, join as join46 } from "path";
41190
+ import { existsSync as existsSync46, statSync as statSync19 } from "fs";
41191
+ import { resolve as resolve31, join as join47 } from "path";
40939
41192
  var examples13, DEFAULT_CONFIGS, benchmark_default;
40940
41193
  var init_benchmark = __esm({
40941
41194
  "src/commands/benchmark.ts"() {
@@ -41010,7 +41263,7 @@ var init_benchmark = __esm({
41010
41263
  s2?.start(`Benchmarking ${config.label}...`);
41011
41264
  for (let i2 = 0; i2 < runsPerConfig; i2++) {
41012
41265
  s2?.message(`${config.label} \u2014 run ${i2 + 1}/${runsPerConfig}`);
41013
- const outputPath = join46(
41266
+ const outputPath = join47(
41014
41267
  benchDir,
41015
41268
  `${config.label.replace(/[^a-zA-Z0-9]/g, "_")}_run${i2}.mp4`
41016
41269
  );
@@ -41024,8 +41277,8 @@ var init_benchmark = __esm({
41024
41277
  await producer.executeRenderJob(job, project.dir, outputPath);
41025
41278
  const elapsedMs = Date.now() - startTime;
41026
41279
  let fileSize = null;
41027
- if (existsSync45(outputPath)) {
41028
- const stat3 = statSync17(outputPath);
41280
+ if (existsSync46(outputPath)) {
41281
+ const stat3 = statSync19(outputPath);
41029
41282
  fileSize = stat3.size;
41030
41283
  }
41031
41284
  runs.push({ elapsedMs, fileSize });
@@ -41240,8 +41493,8 @@ __export(transcribe_exports2, {
41240
41493
  default: () => transcribe_default,
41241
41494
  examples: () => examples15
41242
41495
  });
41243
- import { existsSync as existsSync46, writeFileSync as writeFileSync20 } from "fs";
41244
- import { resolve as resolve32, join as join47, extname as extname8 } from "path";
41496
+ import { existsSync as existsSync47, writeFileSync as writeFileSync20 } from "fs";
41497
+ import { resolve as resolve32, join as join48, extname as extname8 } from "path";
41245
41498
  async function importTranscript(inputPath, dir, json) {
41246
41499
  const { loadTranscript: loadTranscript2, patchCaptionHtml: patchCaptionHtml2 } = await Promise.resolve().then(() => (init_normalize(), normalize_exports));
41247
41500
  const { words, format } = loadTranscript2(inputPath);
@@ -41249,7 +41502,7 @@ async function importTranscript(inputPath, dir, json) {
41249
41502
  console.error(c.error("No words found in transcript."));
41250
41503
  process.exit(1);
41251
41504
  }
41252
- const outPath = join47(dir, "transcript.json");
41505
+ const outPath = join48(dir, "transcript.json");
41253
41506
  writeFileSync20(outPath, JSON.stringify(words, null, 2));
41254
41507
  patchCaptionHtml2(dir, words);
41255
41508
  if (json) {
@@ -41366,7 +41619,7 @@ var init_transcribe2 = __esm({
41366
41619
  },
41367
41620
  async run({ args }) {
41368
41621
  const inputPath = resolve32(args.input);
41369
- if (!existsSync46(inputPath)) {
41622
+ if (!existsSync47(inputPath)) {
41370
41623
  console.error(c.error(`File not found: ${args.input}`));
41371
41624
  process.exit(1);
41372
41625
  }
@@ -41387,9 +41640,9 @@ var init_transcribe2 = __esm({
41387
41640
  });
41388
41641
 
41389
41642
  // src/tts/manager.ts
41390
- import { existsSync as existsSync47, mkdirSync as mkdirSync25 } from "fs";
41391
- import { homedir as homedir8 } from "os";
41392
- import { join as join48 } from "path";
41643
+ import { existsSync as existsSync48, mkdirSync as mkdirSync25 } from "fs";
41644
+ import { homedir as homedir9 } from "os";
41645
+ import { join as join49 } from "path";
41393
41646
  function inferLangFromVoiceId(voiceId) {
41394
41647
  const first = voiceId.charAt(0).toLowerCase();
41395
41648
  return VOICE_PREFIX_LANG[first] ?? "en-us";
@@ -41398,8 +41651,8 @@ function isSupportedLang(value) {
41398
41651
  return SUPPORTED_LANGS.includes(value);
41399
41652
  }
41400
41653
  async function ensureModel2(model = DEFAULT_MODEL2, options) {
41401
- const modelPath = join48(MODELS_DIR2, `${model}.onnx`);
41402
- if (existsSync47(modelPath)) return modelPath;
41654
+ const modelPath = join49(MODELS_DIR2, `${model}.onnx`);
41655
+ if (existsSync48(modelPath)) return modelPath;
41403
41656
  const url = MODEL_URLS[model];
41404
41657
  if (!url) {
41405
41658
  throw new Error(
@@ -41409,18 +41662,18 @@ async function ensureModel2(model = DEFAULT_MODEL2, options) {
41409
41662
  mkdirSync25(MODELS_DIR2, { recursive: true });
41410
41663
  options?.onProgress?.(`Downloading TTS model ${model} (~311 MB)...`);
41411
41664
  await downloadFile(url, modelPath);
41412
- if (!existsSync47(modelPath)) {
41665
+ if (!existsSync48(modelPath)) {
41413
41666
  throw new Error(`Model download failed: ${model}`);
41414
41667
  }
41415
41668
  return modelPath;
41416
41669
  }
41417
41670
  async function ensureVoices(options) {
41418
- const voicesPath = join48(VOICES_DIR, "voices-v1.0.bin");
41419
- if (existsSync47(voicesPath)) return voicesPath;
41671
+ const voicesPath = join49(VOICES_DIR, "voices-v1.0.bin");
41672
+ if (existsSync48(voicesPath)) return voicesPath;
41420
41673
  mkdirSync25(VOICES_DIR, { recursive: true });
41421
41674
  options?.onProgress?.("Downloading voice data (~27 MB)...");
41422
41675
  await downloadFile(VOICES_URL, voicesPath);
41423
- if (!existsSync47(voicesPath)) {
41676
+ if (!existsSync48(voicesPath)) {
41424
41677
  throw new Error("Voice data download failed");
41425
41678
  }
41426
41679
  return voicesPath;
@@ -41430,9 +41683,9 @@ var init_manager3 = __esm({
41430
41683
  "src/tts/manager.ts"() {
41431
41684
  "use strict";
41432
41685
  init_download();
41433
- CACHE_DIR3 = join48(homedir8(), ".cache", "hyperframes", "tts");
41434
- MODELS_DIR2 = join48(CACHE_DIR3, "models");
41435
- VOICES_DIR = join48(CACHE_DIR3, "voices");
41686
+ CACHE_DIR3 = join49(homedir9(), ".cache", "hyperframes", "tts");
41687
+ MODELS_DIR2 = join49(CACHE_DIR3, "models");
41688
+ VOICES_DIR = join49(CACHE_DIR3, "voices");
41436
41689
  DEFAULT_MODEL2 = "kokoro-v1.0";
41437
41690
  MODEL_URLS = {
41438
41691
  "kokoro-v1.0": "https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0/kokoro-v1.0.onnx"
@@ -41492,22 +41745,22 @@ var synthesize_exports = {};
41492
41745
  __export(synthesize_exports, {
41493
41746
  synthesize: () => synthesize
41494
41747
  });
41495
- import { execFileSync as execFileSync6 } from "child_process";
41496
- import { existsSync as existsSync48, writeFileSync as writeFileSync21, mkdirSync as mkdirSync26, readdirSync as readdirSync16, unlinkSync as unlinkSync6 } from "fs";
41497
- import { join as join49, dirname as dirname20, basename as basename10 } from "path";
41498
- import { homedir as homedir9 } from "os";
41748
+ import { execFileSync as execFileSync7 } from "child_process";
41749
+ import { existsSync as existsSync49, writeFileSync as writeFileSync21, mkdirSync as mkdirSync26, readdirSync as readdirSync17, unlinkSync as unlinkSync6 } from "fs";
41750
+ import { join as join50, dirname as dirname20, basename as basename10 } from "path";
41751
+ import { homedir as homedir10 } from "os";
41499
41752
  function findPython() {
41500
41753
  for (const name of ["python3", "python"]) {
41501
41754
  try {
41502
41755
  const cmd = process.platform === "win32" ? "where" : "which";
41503
- const output = execFileSync6(cmd, [name], {
41756
+ const output = execFileSync7(cmd, [name], {
41504
41757
  encoding: "utf-8",
41505
41758
  stdio: ["pipe", "pipe", "pipe"],
41506
41759
  timeout: 5e3
41507
41760
  });
41508
41761
  const first = output.split(/\r?\n/).map((s2) => s2.trim()).find(Boolean);
41509
41762
  if (!first) continue;
41510
- const version = execFileSync6(first, ["--version"], {
41763
+ const version = execFileSync7(first, ["--version"], {
41511
41764
  encoding: "utf-8",
41512
41765
  stdio: ["pipe", "pipe", "pipe"],
41513
41766
  timeout: 5e3
@@ -41520,7 +41773,7 @@ function findPython() {
41520
41773
  }
41521
41774
  function hasPythonPackage(python, pkg) {
41522
41775
  try {
41523
- execFileSync6(python, ["-c", `import ${pkg}`], {
41776
+ execFileSync7(python, ["-c", `import ${pkg}`], {
41524
41777
  stdio: ["pipe", "pipe", "pipe"],
41525
41778
  timeout: 1e4
41526
41779
  });
@@ -41530,15 +41783,15 @@ function hasPythonPackage(python, pkg) {
41530
41783
  }
41531
41784
  }
41532
41785
  function ensureSynthScript() {
41533
- if (!existsSync48(SCRIPT_PATH)) {
41786
+ if (!existsSync49(SCRIPT_PATH)) {
41534
41787
  mkdirSync26(SCRIPT_DIR, { recursive: true });
41535
41788
  writeFileSync21(SCRIPT_PATH, SYNTH_SCRIPT);
41536
41789
  const currentName = basename10(SCRIPT_PATH);
41537
41790
  try {
41538
- for (const entry of readdirSync16(SCRIPT_DIR)) {
41791
+ for (const entry of readdirSync17(SCRIPT_DIR)) {
41539
41792
  if (entry !== currentName && /^synth(-v\d+)?\.py$/.test(entry)) {
41540
41793
  try {
41541
- unlinkSync6(join49(SCRIPT_DIR, entry));
41794
+ unlinkSync6(join50(SCRIPT_DIR, entry));
41542
41795
  } catch {
41543
41796
  }
41544
41797
  }
@@ -41575,7 +41828,7 @@ async function synthesize(text, outputPath, options) {
41575
41828
  mkdirSync26(dirname20(outputPath), { recursive: true });
41576
41829
  options?.onProgress?.(`Generating speech with voice ${voice} (${lang})...`);
41577
41830
  try {
41578
- const stdout2 = execFileSync6(
41831
+ const stdout2 = execFileSync7(
41579
41832
  python,
41580
41833
  [scriptPath, modelPath, voicesPath, text, voice, String(speed), outputPath, lang],
41581
41834
  {
@@ -41584,7 +41837,7 @@ async function synthesize(text, outputPath, options) {
41584
41837
  stdio: ["pipe", "pipe", "pipe"]
41585
41838
  }
41586
41839
  );
41587
- if (!existsSync48(outputPath)) {
41840
+ if (!existsSync49(outputPath)) {
41588
41841
  throw new Error("Synthesis completed but no output file was created");
41589
41842
  }
41590
41843
  const lines = stdout2.trim().split("\n");
@@ -41597,7 +41850,7 @@ async function synthesize(text, outputPath, options) {
41597
41850
  langApplied: result.langApplied
41598
41851
  };
41599
41852
  } catch (err) {
41600
- if (err instanceof SyntaxError && existsSync48(outputPath)) {
41853
+ if (err instanceof SyntaxError && existsSync49(outputPath)) {
41601
41854
  throw new Error(
41602
41855
  "Speech was generated but metadata could not be read. Check the output file manually."
41603
41856
  );
@@ -41648,8 +41901,8 @@ print(json.dumps({
41648
41901
  "langApplied": bool(lang and supports_lang),
41649
41902
  }))
41650
41903
  `;
41651
- SCRIPT_DIR = join49(homedir9(), ".cache", "hyperframes", "tts");
41652
- SCRIPT_PATH = join49(SCRIPT_DIR, "synth-v2.py");
41904
+ SCRIPT_DIR = join50(homedir10(), ".cache", "hyperframes", "tts");
41905
+ SCRIPT_PATH = join50(SCRIPT_DIR, "synth-v2.py");
41653
41906
  }
41654
41907
  });
41655
41908
 
@@ -41659,7 +41912,7 @@ __export(tts_exports, {
41659
41912
  default: () => tts_default,
41660
41913
  examples: () => examples16
41661
41914
  });
41662
- import { existsSync as existsSync49, readFileSync as readFileSync34 } from "fs";
41915
+ import { existsSync as existsSync50, readFileSync as readFileSync34 } from "fs";
41663
41916
  import { resolve as resolve33, extname as extname9 } from "path";
41664
41917
  function listVoices(json) {
41665
41918
  const rows = BUNDLED_VOICES.map((v) => ({ ...v, defaultLang: inferLangFromVoiceId(v.id) }));
@@ -41769,7 +42022,7 @@ var init_tts = __esm({
41769
42022
  }
41770
42023
  let text;
41771
42024
  const maybeFile = resolve33(args.input);
41772
- if (existsSync49(maybeFile) && extname9(maybeFile).toLowerCase() === ".txt") {
42025
+ if (existsSync50(maybeFile) && extname9(maybeFile).toLowerCase() === ".txt") {
41773
42026
  text = readFileSync34(maybeFile, "utf-8").trim();
41774
42027
  if (!text) {
41775
42028
  console.error(c.error("File is empty."));
@@ -41862,15 +42115,15 @@ __export(docs_exports, {
41862
42115
  default: () => docs_default,
41863
42116
  examples: () => examples17
41864
42117
  });
41865
- import { readFileSync as readFileSync35, existsSync as existsSync50 } from "fs";
41866
- import { resolve as resolve34, dirname as dirname21, join as join50 } from "path";
42118
+ import { readFileSync as readFileSync35, existsSync as existsSync51 } from "fs";
42119
+ import { resolve as resolve34, dirname as dirname21, join as join51 } from "path";
41867
42120
  import { fileURLToPath as fileURLToPath7 } from "url";
41868
42121
  function docsDir() {
41869
42122
  const thisFile = fileURLToPath7(import.meta.url);
41870
42123
  const dir = dirname21(thisFile);
41871
42124
  const devPath = resolve34(dir, "..", "docs");
41872
42125
  const builtPath = resolve34(dir, "docs");
41873
- return existsSync50(devPath) ? devPath : builtPath;
42126
+ return existsSync51(devPath) ? devPath : builtPath;
41874
42127
  }
41875
42128
  function formatInlineCode(line) {
41876
42129
  return line.replace(/`([^`]+)`/g, (_match, code) => c.accent(code));
@@ -41967,8 +42220,8 @@ var init_docs = __esm({
41967
42220
  }
41968
42221
  process.exit(1);
41969
42222
  }
41970
- const filePath = join50(docsDir(), entry.file);
41971
- if (!existsSync50(filePath)) {
42223
+ const filePath = join51(docsDir(), entry.file);
42224
+ if (!existsSync51(filePath)) {
41972
42225
  console.error(c.error(`Doc file not found: ${filePath}`));
41973
42226
  process.exit(1);
41974
42227
  }
@@ -41987,7 +42240,7 @@ __export(doctor_exports, {
41987
42240
  examples: () => examples18
41988
42241
  });
41989
42242
  import { execSync as execSync3 } from "child_process";
41990
- import { freemem as freemem4, platform as platform4 } from "os";
42243
+ import { freemem as freemem4, platform as platform5 } from "os";
41991
42244
  function checkFFmpeg() {
41992
42245
  const path2 = findFFmpeg();
41993
42246
  if (path2) {
@@ -42152,7 +42405,7 @@ var init_doctor = __esm({
42152
42405
  { name: "Memory", run: checkMemory },
42153
42406
  { name: "Disk", run: checkDisk }
42154
42407
  ];
42155
- if (platform4() === "linux") {
42408
+ if (platform5() === "linux") {
42156
42409
  checks.push({ name: "/dev/shm", run: checkShm });
42157
42410
  }
42158
42411
  checks.push(
@@ -42384,20 +42637,11 @@ Run ${c.accent("hyperframes telemetry --help")} for usage.`
42384
42637
  // src/commands/validate.ts
42385
42638
  var validate_exports = {};
42386
42639
  __export(validate_exports, {
42387
- default: () => validate_default,
42388
- shouldIgnoreRequestFailure: () => shouldIgnoreRequestFailure
42640
+ default: () => validate_default
42389
42641
  });
42390
- import { existsSync as existsSync51, readFileSync as readFileSync36 } from "fs";
42391
- import { resolve as resolve35, join as join51, dirname as dirname22 } from "path";
42642
+ import { existsSync as existsSync52, readFileSync as readFileSync36 } from "fs";
42643
+ import { resolve as resolve35, join as join52, dirname as dirname22 } from "path";
42392
42644
  import { fileURLToPath as fileURLToPath8 } from "url";
42393
- function shouldIgnoreRequestFailure(url, errorText) {
42394
- if (errorText !== "net::ERR_ABORTED") return false;
42395
- try {
42396
- return MEDIA_EXTENSIONS.test(new URL(url).pathname);
42397
- } catch {
42398
- return false;
42399
- }
42400
- }
42401
42645
  async function getCompositionDuration3(page) {
42402
42646
  return page.evaluate(() => {
42403
42647
  if (window.__hf?.duration && window.__hf.duration > 0) return window.__hf.duration;
@@ -42440,11 +42684,11 @@ async function runContrastAudit(page) {
42440
42684
  }
42441
42685
  function loadContrastAuditScript() {
42442
42686
  const candidates = [
42443
- join51(__dirname3, "contrast-audit.browser.js"),
42444
- join51(__dirname3, "commands", "contrast-audit.browser.js")
42687
+ join52(__dirname3, "contrast-audit.browser.js"),
42688
+ join52(__dirname3, "commands", "contrast-audit.browser.js")
42445
42689
  ];
42446
42690
  for (const candidate of candidates) {
42447
- if (existsSync51(candidate)) return readFileSync36(candidate, "utf-8");
42691
+ if (existsSync52(candidate)) return readFileSync36(candidate, "utf-8");
42448
42692
  }
42449
42693
  throw new Error("Missing contrast audit browser script");
42450
42694
  }
@@ -42461,7 +42705,7 @@ async function validateInBrowser(projectDir, opts) {
42461
42705
  "dist",
42462
42706
  "hyperframe.runtime.iife.js"
42463
42707
  );
42464
- if (existsSync51(runtimePath)) {
42708
+ if (existsSync52(runtimePath)) {
42465
42709
  const runtimeSource = readFileSync36(runtimePath, "utf-8");
42466
42710
  html = html.replace(
42467
42711
  /<script[^>]*data-hyperframes-preview-runtime[^>]*src="[^"]*"[^>]*><\/script>/,
@@ -42477,8 +42721,8 @@ async function validateInBrowser(projectDir, opts) {
42477
42721
  res.end(html);
42478
42722
  return;
42479
42723
  }
42480
- const filePath = join51(projectDir, decodeURIComponent(url));
42481
- if (existsSync51(filePath)) {
42724
+ const filePath = join52(projectDir, decodeURIComponent(url));
42725
+ if (existsSync52(filePath)) {
42482
42726
  res.writeHead(200, { "Content-Type": getMimeType2(filePath) });
42483
42727
  res.end(readFileSync36(filePath));
42484
42728
  return;
@@ -42522,12 +42766,10 @@ async function validateInBrowser(projectDir, opts) {
42522
42766
  page.on("requestfailed", (req) => {
42523
42767
  const url = req.url();
42524
42768
  if (url.includes("favicon") || url.startsWith("data:")) return;
42525
- const failureText = req.failure()?.errorText;
42526
- if (shouldIgnoreRequestFailure(url, failureText)) return;
42527
42769
  const path2 = decodeURIComponent(new URL(url).pathname).replace(/^\//, "");
42528
42770
  errors.push({
42529
42771
  level: "error",
42530
- text: `Failed to load ${path2}: ${failureText ?? "net::ERR_FAILED"}`,
42772
+ text: `Failed to load ${path2}: ${req.failure()?.errorText ?? "net::ERR_FAILED"}`,
42531
42773
  url
42532
42774
  });
42533
42775
  });
@@ -42560,7 +42802,7 @@ function printContrastFailures(failures) {
42560
42802
  );
42561
42803
  }
42562
42804
  }
42563
- var __filename2, __dirname3, CONTRAST_SAMPLES, SEEK_SETTLE_MS2, MEDIA_EXTENSIONS, validate_default;
42805
+ var __filename2, __dirname3, CONTRAST_SAMPLES, SEEK_SETTLE_MS2, validate_default;
42564
42806
  var init_validate = __esm({
42565
42807
  "src/commands/validate.ts"() {
42566
42808
  "use strict";
@@ -42572,7 +42814,6 @@ var init_validate = __esm({
42572
42814
  __dirname3 = dirname22(__filename2);
42573
42815
  CONTRAST_SAMPLES = 5;
42574
42816
  SEEK_SETTLE_MS2 = 150;
42575
- MEDIA_EXTENSIONS = /\.(aac|flac|m4a|mov|mp3|mp4|oga|ogg|wav|webm)$/i;
42576
42817
  validate_default = defineCommand({
42577
42818
  meta: {
42578
42819
  name: "validate",
@@ -42673,12 +42914,12 @@ __export(snapshot_exports, {
42673
42914
  examples: () => examples21
42674
42915
  });
42675
42916
  import { spawn as spawn12 } from "child_process";
42676
- import { existsSync as existsSync52, mkdtempSync as mkdtempSync3, readFileSync as readFileSync37, mkdirSync as mkdirSync27, rmSync as rmSync10 } from "fs";
42917
+ import { existsSync as existsSync53, mkdtempSync as mkdtempSync3, readFileSync as readFileSync37, mkdirSync as mkdirSync27, rmSync as rmSync10 } from "fs";
42677
42918
  import { tmpdir as tmpdir5 } from "os";
42678
- import { resolve as resolve36, join as join52, relative as relative6, isAbsolute as isAbsolute7 } from "path";
42919
+ import { resolve as resolve36, join as join53, relative as relative6, isAbsolute as isAbsolute7 } from "path";
42679
42920
  async function extractVideoFrameToBuffer(videoPath, timeSeconds, useVp9AlphaDecoder = false) {
42680
- const tmp = mkdtempSync3(join52(tmpdir5(), "hf-snapshot-frame-"));
42681
- const outPath = join52(tmp, "frame.png");
42921
+ const tmp = mkdtempSync3(join53(tmpdir5(), "hf-snapshot-frame-"));
42922
+ const outPath = join53(tmp, "frame.png");
42682
42923
  try {
42683
42924
  const result = await new Promise(
42684
42925
  (resolvePromise) => {
@@ -42718,7 +42959,7 @@ async function extractVideoFrameToBuffer(videoPath, timeSeconds, useVp9AlphaDeco
42718
42959
  });
42719
42960
  }
42720
42961
  );
42721
- if (result.code !== 0 || result.timedOut || !existsSync52(outPath)) return null;
42962
+ if (result.code !== 0 || result.timedOut || !existsSync53(outPath)) return null;
42722
42963
  return readFileSync37(outPath);
42723
42964
  } finally {
42724
42965
  try {
@@ -42756,7 +42997,7 @@ async function captureSnapshots(projectDir, opts) {
42756
42997
  res.end();
42757
42998
  return;
42758
42999
  }
42759
- if (existsSync52(filePath)) {
43000
+ if (existsSync53(filePath)) {
42760
43001
  res.writeHead(200, { "Content-Type": getMimeType2(filePath) });
42761
43002
  res.end(readFileSync37(filePath));
42762
43003
  return;
@@ -42831,7 +43072,7 @@ async function captureSnapshots(projectDir, opts) {
42831
43072
  return [];
42832
43073
  }
42833
43074
  const positions = opts.at?.length ? opts.at : numFrames === 1 ? [duration / 2] : Array.from({ length: numFrames }, (_2, i2) => i2 / (numFrames - 1) * duration);
42834
- const snapshotDir = join52(projectDir, "snapshots");
43075
+ const snapshotDir = join53(projectDir, "snapshots");
42835
43076
  mkdirSync27(snapshotDir, { recursive: true });
42836
43077
  let injectVideoFramesBatch2 = null;
42837
43078
  let syncVideoFrameVisibility2 = null;
@@ -42906,7 +43147,7 @@ async function captureSnapshots(projectDir, opts) {
42906
43147
  const decodedPath = decodeURIComponent(url.pathname).replace(/^\//, "");
42907
43148
  const candidate = resolve36(projectDir, decodedPath);
42908
43149
  const rel = relative6(projectDir, candidate);
42909
- if (!rel.startsWith("..") && !isAbsolute7(rel) && existsSync52(candidate)) {
43150
+ if (!rel.startsWith("..") && !isAbsolute7(rel) && existsSync53(candidate)) {
42910
43151
  filePath = candidate;
42911
43152
  }
42912
43153
  } catch {
@@ -42936,7 +43177,7 @@ async function captureSnapshots(projectDir, opts) {
42936
43177
  }
42937
43178
  const timeLabel = opts.at?.length ? `${time.toFixed(1)}s` : `${Math.round(time / duration * 100)}pct`;
42938
43179
  const filename = `frame-${String(i2).padStart(2, "0")}-at-${timeLabel}.png`;
42939
- const framePath = join52(snapshotDir, filename);
43180
+ const framePath = join53(snapshotDir, filename);
42940
43181
  await page.screenshot({ path: framePath, type: "png" });
42941
43182
  savedPaths.push(`snapshots/${filename}`);
42942
43183
  }
@@ -43020,13 +43261,13 @@ ${c.error("\u2717")} Snapshot failed: ${msg}`);
43020
43261
 
43021
43262
  // src/capture/assetDownloader.ts
43022
43263
  import { writeFileSync as writeFileSync22, mkdirSync as mkdirSync28 } from "fs";
43023
- import { join as join53, extname as extname10 } from "path";
43264
+ import { join as join54, extname as extname10 } from "path";
43024
43265
  async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks) {
43025
- const assetsDir = join53(outputDir, "assets");
43266
+ const assetsDir = join54(outputDir, "assets");
43026
43267
  mkdirSync28(assetsDir, { recursive: true });
43027
43268
  const assets = [];
43028
43269
  const downloadedUrls = /* @__PURE__ */ new Set();
43029
- mkdirSync28(join53(outputDir, "assets", "svgs"), { recursive: true });
43270
+ mkdirSync28(join54(outputDir, "assets", "svgs"), { recursive: true });
43030
43271
  for (let i2 = 0; i2 < tokens.svgs.length && i2 < 30; i2++) {
43031
43272
  const svg = tokens.svgs[i2];
43032
43273
  if (!svg.outerHTML || svg.outerHTML.length < 50) continue;
@@ -43034,7 +43275,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks)
43034
43275
  const name = label2 ? slugify(label2) + ".svg" : svg.isLogo ? `logo-${i2}.svg` : `icon-${i2}.svg`;
43035
43276
  const localPath = `assets/svgs/${name}`;
43036
43277
  try {
43037
- writeFileSync22(join53(outputDir, localPath), svg.outerHTML, "utf-8");
43278
+ writeFileSync22(join54(outputDir, localPath), svg.outerHTML, "utf-8");
43038
43279
  assets.push({ url: "", localPath, type: "svg" });
43039
43280
  } catch {
43040
43281
  }
@@ -43047,7 +43288,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks)
43047
43288
  const localPath = `assets/${name}`;
43048
43289
  const buffer = await fetchBuffer(icon.href);
43049
43290
  if (buffer) {
43050
- writeFileSync22(join53(outputDir, localPath), buffer);
43291
+ writeFileSync22(join54(outputDir, localPath), buffer);
43051
43292
  assets.push({ url: icon.href, localPath, type: "favicon" });
43052
43293
  break;
43053
43294
  }
@@ -43104,7 +43345,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks)
43104
43345
  const slug = isMeaningful ? slugify(rawName) : `${prefix}-${imgIdx}`;
43105
43346
  const name = `${slug}${ext}`;
43106
43347
  const localPath = `assets/${name}`;
43107
- writeFileSync22(join53(outputDir, localPath), buffer);
43348
+ writeFileSync22(join54(outputDir, localPath), buffer);
43108
43349
  assets.push({ url, localPath, type: "image" });
43109
43350
  imgIdx++;
43110
43351
  } catch {
@@ -43117,7 +43358,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks)
43117
43358
  const localPath = `assets/og-image${ext}`;
43118
43359
  const buffer = await fetchBuffer(tokens.ogImage);
43119
43360
  if (buffer && buffer.length > 5e3) {
43120
- writeFileSync22(join53(outputDir, localPath), buffer);
43361
+ writeFileSync22(join54(outputDir, localPath), buffer);
43121
43362
  assets.push({ url: tokens.ogImage, localPath, type: "image" });
43122
43363
  }
43123
43364
  } catch {
@@ -43140,7 +43381,7 @@ function normalizeUrl(u) {
43140
43381
  }
43141
43382
  }
43142
43383
  async function downloadAndRewriteFonts(css, outputDir) {
43143
- const assetsDir = join53(outputDir, "assets", "fonts");
43384
+ const assetsDir = join54(outputDir, "assets", "fonts");
43144
43385
  mkdirSync28(assetsDir, { recursive: true });
43145
43386
  const fontUrlRegex = /url\(['"]?(https?:\/\/[^'")\s]+\.(?:woff2?|ttf|otf)[^'")\s]*?)['"]?\)/g;
43146
43387
  const fontUrls = /* @__PURE__ */ new Set();
@@ -43176,7 +43417,7 @@ async function downloadAndRewriteFonts(css, outputDir) {
43176
43417
  try {
43177
43418
  const urlObj = new URL(fontUrl);
43178
43419
  const filename = urlObj.pathname.split("/").pop() || `font-${count}.woff2`;
43179
- const localPath = join53(assetsDir, filename);
43420
+ const localPath = join54(assetsDir, filename);
43180
43421
  const relativePath = `assets/fonts/${filename}`;
43181
43422
  const buffer = await fetchBuffer(fontUrl);
43182
43423
  if (buffer) {
@@ -43973,8 +44214,8 @@ var init_animationCataloger = __esm({
43973
44214
  });
43974
44215
 
43975
44216
  // src/capture/mediaCapture.ts
43976
- import { mkdirSync as mkdirSync29, writeFileSync as writeFileSync23, readdirSync as readdirSync17, readFileSync as readFileSync38, statSync as statSync18 } from "fs";
43977
- import { join as join54 } from "path";
44217
+ import { mkdirSync as mkdirSync29, writeFileSync as writeFileSync23, readdirSync as readdirSync18, readFileSync as readFileSync38, statSync as statSync20 } from "fs";
44218
+ import { join as join55 } from "path";
43978
44219
  async function saveLottieAnimations(discoveredLotties, lottieDir) {
43979
44220
  let savedCount = 0;
43980
44221
  const savedHashes = /* @__PURE__ */ new Set();
@@ -44007,7 +44248,7 @@ async function saveLottieAnimations(discoveredLotties, lottieDir) {
44007
44248
  const hash2 = buf.toString("base64").slice(0, 100);
44008
44249
  if (savedHashes.has(hash2)) continue;
44009
44250
  savedHashes.add(hash2);
44010
- writeFileSync23(join54(lottieDir, `animation-${savedCount}.lottie`), buf);
44251
+ writeFileSync23(join55(lottieDir, `animation-${savedCount}.lottie`), buf);
44011
44252
  savedCount++;
44012
44253
  continue;
44013
44254
  }
@@ -44025,7 +44266,7 @@ async function saveLottieAnimations(discoveredLotties, lottieDir) {
44025
44266
  } catch {
44026
44267
  continue;
44027
44268
  }
44028
- writeFileSync23(join54(lottieDir, `animation-${savedCount}.json`), jsonData, "utf-8");
44269
+ writeFileSync23(join55(lottieDir, `animation-${savedCount}.json`), jsonData, "utf-8");
44029
44270
  savedCount++;
44030
44271
  }
44031
44272
  } catch {
@@ -44035,22 +44276,22 @@ async function saveLottieAnimations(discoveredLotties, lottieDir) {
44035
44276
  }
44036
44277
  async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir) {
44037
44278
  const manifest = [];
44038
- const previewDir = join54(lottieDir, "previews");
44279
+ const previewDir = join55(lottieDir, "previews");
44039
44280
  mkdirSync29(previewDir, { recursive: true });
44040
- for (const file of readdirSync17(lottieDir)) {
44281
+ for (const file of readdirSync18(lottieDir)) {
44041
44282
  if (!file.endsWith(".json")) continue;
44042
44283
  try {
44043
- const raw = JSON.parse(readFileSync38(join54(lottieDir, file), "utf-8"));
44284
+ const raw = JSON.parse(readFileSync38(join55(lottieDir, file), "utf-8"));
44044
44285
  const fr = raw.fr || 30;
44045
44286
  const dur = ((raw.op || 0) - (raw.ip || 0)) / fr;
44046
44287
  const previewName = file.replace(".json", "-preview.png");
44047
- const fileSize = statSync18(join54(lottieDir, file)).size;
44288
+ const fileSize = statSync20(join55(lottieDir, file)).size;
44048
44289
  if (fileSize > 2e6) continue;
44049
44290
  let previewPage;
44050
44291
  try {
44051
44292
  previewPage = await chromeBrowser.newPage();
44052
44293
  await previewPage.setViewport({ width: 400, height: 400 });
44053
- const animData = JSON.parse(readFileSync38(join54(lottieDir, file), "utf-8"));
44294
+ const animData = JSON.parse(readFileSync38(join55(lottieDir, file), "utf-8"));
44054
44295
  const midFrame = Math.floor(((raw.op || 0) - (raw.ip || 0)) * 0.3);
44055
44296
  await previewPage.setContent(
44056
44297
  `<!DOCTYPE html>
@@ -44080,7 +44321,7 @@ async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir) {
44080
44321
  await previewPage.waitForFunction(() => window.__READY === true, { timeout: 5e3 }).catch(() => {
44081
44322
  });
44082
44323
  await previewPage.screenshot({
44083
- path: join54(previewDir, previewName),
44324
+ path: join55(previewDir, previewName),
44084
44325
  type: "png",
44085
44326
  omitBackground: true
44086
44327
  });
@@ -44104,7 +44345,7 @@ async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir) {
44104
44345
  }
44105
44346
  if (manifest.length > 0) {
44106
44347
  writeFileSync23(
44107
- join54(outputDir, "extracted", "lottie-manifest.json"),
44348
+ join55(outputDir, "extracted", "lottie-manifest.json"),
44108
44349
  JSON.stringify(manifest, null, 2),
44109
44350
  "utf-8"
44110
44351
  );
@@ -44166,15 +44407,15 @@ async function captureVideoManifest(page, outputDir, progress) {
44166
44407
  return true;
44167
44408
  });
44168
44409
  if (uniqueVideos.length > 0) {
44169
- const videoManifestDir = join54(outputDir, "assets", "videos");
44410
+ const videoManifestDir = join55(outputDir, "assets", "videos");
44170
44411
  mkdirSync29(videoManifestDir, { recursive: true });
44171
- const previewDir = join54(videoManifestDir, "previews");
44412
+ const previewDir = join55(videoManifestDir, "previews");
44172
44413
  mkdirSync29(previewDir, { recursive: true });
44173
44414
  const videoManifest = [];
44174
44415
  for (let vi = 0; vi < uniqueVideos.length && vi < 20; vi++) {
44175
44416
  const v = uniqueVideos[vi];
44176
44417
  const previewName = `video-${vi}-preview.png`;
44177
- const previewPath = join54(previewDir, previewName);
44418
+ const previewPath = join55(previewDir, previewName);
44178
44419
  try {
44179
44420
  await page.evaluate(`window.scrollTo(0, ${Math.max(0, v.top - 100)})`);
44180
44421
  await new Promise((r2) => setTimeout(r2, 300));
@@ -44213,7 +44454,7 @@ async function captureVideoManifest(page, outputDir, progress) {
44213
44454
  }
44214
44455
  if (videoManifest.length > 0) {
44215
44456
  writeFileSync23(
44216
- join54(outputDir, "extracted", "video-manifest.json"),
44457
+ join55(outputDir, "extracted", "video-manifest.json"),
44217
44458
  JSON.stringify(videoManifest, null, 2),
44218
44459
  "utf-8"
44219
44460
  );
@@ -51167,7 +51408,7 @@ var require_node_domexception = __commonJS({
51167
51408
  });
51168
51409
 
51169
51410
  // ../../node_modules/.bun/fetch-blob@3.2.0/node_modules/fetch-blob/from.js
51170
- import { statSync as statSync19, createReadStream as createReadStream2, promises as fs2 } from "fs";
51411
+ import { statSync as statSync21, createReadStream as createReadStream2, promises as fs2 } from "fs";
51171
51412
  import { basename as basename11 } from "path";
51172
51413
  var import_node_domexception, stat, blobFromSync, blobFrom, fileFrom, fileFromSync, fromBlob, fromFile, BlobDataItem;
51173
51414
  var init_from = __esm({
@@ -51177,10 +51418,10 @@ var init_from = __esm({
51177
51418
  init_file();
51178
51419
  init_fetch_blob();
51179
51420
  ({ stat } = fs2);
51180
- blobFromSync = (path2, type) => fromBlob(statSync19(path2), path2, type);
51421
+ blobFromSync = (path2, type) => fromBlob(statSync21(path2), path2, type);
51181
51422
  blobFrom = (path2, type) => stat(path2).then((stat3) => fromBlob(stat3, path2, type));
51182
51423
  fileFrom = (path2, type) => stat(path2).then((stat3) => fromFile(stat3, path2, type));
51183
- fileFromSync = (path2, type) => fromFile(statSync19(path2), path2, type);
51424
+ fileFromSync = (path2, type) => fromFile(statSync21(path2), path2, type);
51184
51425
  fromBlob = (stat3, path2, type = "") => new fetch_blob_default([new BlobDataItem({
51185
51426
  path: path2,
51186
51427
  size: stat3.size,
@@ -84668,8 +84909,8 @@ ${underline2}`);
84668
84909
  });
84669
84910
 
84670
84911
  // src/capture/contentExtractor.ts
84671
- import { readdirSync as readdirSync18, statSync as statSync20, readFileSync as readFileSync39 } from "fs";
84672
- import { join as join55 } from "path";
84912
+ import { readdirSync as readdirSync19, statSync as statSync22, readFileSync as readFileSync39 } from "fs";
84913
+ import { join as join56 } from "path";
84673
84914
  async function detectLibraries(page, capturedShaders) {
84674
84915
  let detectedLibraries = [];
84675
84916
  try {
@@ -84789,7 +85030,7 @@ async function captionImagesWithGemini(outputDir, progress, warnings) {
84789
85030
  try {
84790
85031
  const { GoogleGenAI: GoogleGenAI2 } = await Promise.resolve().then(() => (init_node4(), node_exports));
84791
85032
  const ai = new GoogleGenAI2({ apiKey: geminiKey });
84792
- const imageFiles = readdirSync18(join55(outputDir, "assets")).filter(
85033
+ const imageFiles = readdirSync19(join56(outputDir, "assets")).filter(
84793
85034
  (f3) => /\.(png|jpg|jpeg|webp|gif)$/i.test(f3)
84794
85035
  );
84795
85036
  const model = process.env.HYPERFRAMES_GEMINI_MODEL || "gemini-3.1-flash-lite-preview";
@@ -84798,8 +85039,8 @@ async function captionImagesWithGemini(outputDir, progress, warnings) {
84798
85039
  const batch = imageFiles.slice(i2, i2 + BATCH_SIZE);
84799
85040
  const results = await Promise.allSettled(
84800
85041
  batch.map(async (file) => {
84801
- const filePath = join55(outputDir, "assets", file);
84802
- const stat3 = statSync20(filePath);
85042
+ const filePath = join56(outputDir, "assets", file);
85043
+ const stat3 = statSync22(filePath);
84803
85044
  if (stat3.size > 4e6) return { file, caption: "" };
84804
85045
  const buffer = readFileSync39(filePath);
84805
85046
  const base64 = buffer.toString("base64");
@@ -84847,12 +85088,12 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
84847
85088
  const uncaptionedLines = [];
84848
85089
  const svgLines = [];
84849
85090
  const fontLines = [];
84850
- const assetsPath = join55(outputDir, "assets");
85091
+ const assetsPath = join56(outputDir, "assets");
84851
85092
  try {
84852
- for (const file of readdirSync18(assetsPath)) {
85093
+ for (const file of readdirSync19(assetsPath)) {
84853
85094
  if (file === "svgs" || file === "fonts" || file === "lottie" || file === "videos") continue;
84854
- const filePath = join55(assetsPath, file);
84855
- const stat3 = statSync20(filePath);
85095
+ const filePath = join56(assetsPath, file);
85096
+ const stat3 = statSync22(filePath);
84856
85097
  if (!stat3.isFile()) continue;
84857
85098
  const sizeKb = Math.round(stat3.size / 1024);
84858
85099
  const catalogMatch = catalogedAssets.find(
@@ -84880,8 +85121,8 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
84880
85121
  } catch {
84881
85122
  }
84882
85123
  try {
84883
- const svgsPath = join55(assetsPath, "svgs");
84884
- for (const file of readdirSync18(svgsPath)) {
85124
+ const svgsPath = join56(assetsPath, "svgs");
85125
+ for (const file of readdirSync19(svgsPath)) {
84885
85126
  if (!file.endsWith(".svg")) continue;
84886
85127
  const svgMatch = tokens.svgs.find(
84887
85128
  (s2) => s2.label && file.includes(
@@ -84895,8 +85136,8 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
84895
85136
  } catch {
84896
85137
  }
84897
85138
  try {
84898
- const fontsPath = join55(assetsPath, "fonts");
84899
- for (const file of readdirSync18(fontsPath)) {
85139
+ const fontsPath = join56(assetsPath, "fonts");
85140
+ for (const file of readdirSync19(fontsPath)) {
84900
85141
  fontLines.push(`fonts/${file} \u2014 font file`);
84901
85142
  }
84902
85143
  } catch {
@@ -84915,12 +85156,12 @@ __export(agentPromptGenerator_exports, {
84915
85156
  generateAgentPrompt: () => generateAgentPrompt
84916
85157
  });
84917
85158
  import { writeFileSync as writeFileSync24 } from "fs";
84918
- import { join as join56 } from "path";
85159
+ import { join as join57 } from "path";
84919
85160
  function generateAgentPrompt(outputDir, url, tokens, _animations, hasScreenshot, hasLottie, hasShaders, _catalogedAssets, detectedLibraries) {
84920
85161
  const prompt = buildPrompt(url, tokens, hasScreenshot, hasLottie, hasShaders, detectedLibraries);
84921
- writeFileSync24(join56(outputDir, "AGENTS.md"), prompt, "utf-8");
84922
- writeFileSync24(join56(outputDir, "CLAUDE.md"), prompt, "utf-8");
84923
- writeFileSync24(join56(outputDir, ".cursorrules"), prompt, "utf-8");
85162
+ writeFileSync24(join57(outputDir, "AGENTS.md"), prompt, "utf-8");
85163
+ writeFileSync24(join57(outputDir, "CLAUDE.md"), prompt, "utf-8");
85164
+ writeFileSync24(join57(outputDir, ".cursorrules"), prompt, "utf-8");
84924
85165
  }
84925
85166
  function buildPrompt(url, tokens, hasScreenshot, hasLottie, hasShaders, detectedLibraries) {
84926
85167
  const title = tokens.title || new URL(url).hostname.replace(/^www\./, "");
@@ -84987,8 +85228,8 @@ var init_agentPromptGenerator = __esm({
84987
85228
  });
84988
85229
 
84989
85230
  // src/capture/scaffolding.ts
84990
- import { existsSync as existsSync53, writeFileSync as writeFileSync25, readFileSync as readFileSync40 } from "fs";
84991
- import { join as join57, resolve as resolve37 } from "path";
85231
+ import { existsSync as existsSync54, writeFileSync as writeFileSync25, readFileSync as readFileSync40 } from "fs";
85232
+ import { join as join58, resolve as resolve37 } from "path";
84992
85233
  function loadEnvFile(startDir) {
84993
85234
  try {
84994
85235
  let dir = resolve37(startDir);
@@ -85014,8 +85255,8 @@ function loadEnvFile(startDir) {
85014
85255
  }
85015
85256
  }
85016
85257
  async function generateProjectScaffold(outputDir, url, tokens, animationCatalog, hasScreenshots, hasLotties, hasShaders, catalogedAssets, progress, warnings, detectedLibraries) {
85017
- const metaPath = join57(outputDir, "meta.json");
85018
- if (!existsSync53(metaPath)) {
85258
+ const metaPath = join58(outputDir, "meta.json");
85259
+ if (!existsSync54(metaPath)) {
85019
85260
  const hostname = new URL(url).hostname.replace(/^www\./, "");
85020
85261
  writeFileSync25(
85021
85262
  metaPath,
@@ -85053,9 +85294,9 @@ __export(screenshotCapture_exports, {
85053
85294
  captureScrollScreenshots: () => captureScrollScreenshots
85054
85295
  });
85055
85296
  import { writeFileSync as writeFileSync26, mkdirSync as mkdirSync30 } from "fs";
85056
- import { join as join58 } from "path";
85297
+ import { join as join59 } from "path";
85057
85298
  async function captureScrollScreenshots(page, outputDir) {
85058
- const screenshotsDir = join58(outputDir, "screenshots");
85299
+ const screenshotsDir = join59(outputDir, "screenshots");
85059
85300
  mkdirSync30(screenshotsDir, { recursive: true });
85060
85301
  const MAX_SCREENSHOTS = 20;
85061
85302
  const filePaths = [];
@@ -85089,7 +85330,7 @@ async function captureScrollScreenshots(page, outputDir) {
85089
85330
  finalPositions[i2] / Math.max(1, scrollHeight - viewportHeight) * 100
85090
85331
  );
85091
85332
  const filename = `scroll-${String(Math.min(pct, 100)).padStart(3, "0")}.png`;
85092
- const filePath = join58(screenshotsDir, filename);
85333
+ const filePath = join59(screenshotsDir, filename);
85093
85334
  const buffer = await page.screenshot({ type: "png" });
85094
85335
  writeFileSync26(filePath, buffer);
85095
85336
  filePaths.push(`screenshots/${filename}`);
@@ -85402,8 +85643,8 @@ var capture_exports = {};
85402
85643
  __export(capture_exports, {
85403
85644
  captureWebsite: () => captureWebsite
85404
85645
  });
85405
- import { mkdirSync as mkdirSync31, writeFileSync as writeFileSync27, existsSync as existsSync54 } from "fs";
85406
- import { join as join59 } from "path";
85646
+ import { mkdirSync as mkdirSync31, writeFileSync as writeFileSync27, existsSync as existsSync55 } from "fs";
85647
+ import { join as join60 } from "path";
85407
85648
  async function captureWebsite(opts, onProgress) {
85408
85649
  const {
85409
85650
  url,
@@ -85420,9 +85661,9 @@ async function captureWebsite(opts, onProgress) {
85420
85661
  onProgress?.(stage, detail);
85421
85662
  };
85422
85663
  loadEnvFile(outputDir);
85423
- mkdirSync31(join59(outputDir, "extracted"), { recursive: true });
85424
- mkdirSync31(join59(outputDir, "screenshots"), { recursive: true });
85425
- mkdirSync31(join59(outputDir, "assets"), { recursive: true });
85664
+ mkdirSync31(join60(outputDir, "extracted"), { recursive: true });
85665
+ mkdirSync31(join60(outputDir, "screenshots"), { recursive: true });
85666
+ mkdirSync31(join60(outputDir, "assets"), { recursive: true });
85426
85667
  progress("browser", "Launching headless Chrome...");
85427
85668
  const { ensureBrowser: ensureBrowser2 } = await Promise.resolve().then(() => (init_manager2(), manager_exports2));
85428
85669
  const browser = await ensureBrowser2();
@@ -85578,7 +85819,7 @@ async function captureWebsite(opts, onProgress) {
85578
85819
  } catch {
85579
85820
  }
85580
85821
  if (discoveredLotties.length > 0) {
85581
- const lottieDir = join59(outputDir, "assets", "lottie");
85822
+ const lottieDir = join60(outputDir, "assets", "lottie");
85582
85823
  mkdirSync31(lottieDir, { recursive: true });
85583
85824
  const savedCount = await saveLottieAnimations(discoveredLotties, lottieDir);
85584
85825
  if (savedCount > 0) {
@@ -85598,7 +85839,7 @@ async function captureWebsite(opts, onProgress) {
85598
85839
  });
85599
85840
  capturedShaders = unique;
85600
85841
  writeFileSync27(
85601
- join59(outputDir, "extracted", "shaders.json"),
85842
+ join60(outputDir, "extracted", "shaders.json"),
85602
85843
  JSON.stringify(unique, null, 2),
85603
85844
  "utf-8"
85604
85845
  );
@@ -85609,7 +85850,7 @@ async function captureWebsite(opts, onProgress) {
85609
85850
  progress("tokens", "Extracting design tokens...");
85610
85851
  const tokens = await extractTokens(page1);
85611
85852
  writeFileSync27(
85612
- join59(outputDir, "extracted", "tokens.json"),
85853
+ join60(outputDir, "extracted", "tokens.json"),
85613
85854
  JSON.stringify(tokens, null, 2),
85614
85855
  "utf-8"
85615
85856
  );
@@ -85683,7 +85924,7 @@ async function captureWebsite(opts, onProgress) {
85683
85924
  representativeAnimations: representativeAnims
85684
85925
  };
85685
85926
  writeFileSync27(
85686
- join59(outputDir, "extracted", "animations.json"),
85927
+ join60(outputDir, "extracted", "animations.json"),
85687
85928
  JSON.stringify(leanCatalog, null, 2),
85688
85929
  "utf-8"
85689
85930
  );
@@ -85694,18 +85935,18 @@ async function captureWebsite(opts, onProgress) {
85694
85935
  assets = await downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks);
85695
85936
  }
85696
85937
  if (visibleTextContent) {
85697
- writeFileSync27(join59(outputDir, "extracted", "visible-text.txt"), visibleTextContent, "utf-8");
85938
+ writeFileSync27(join60(outputDir, "extracted", "visible-text.txt"), visibleTextContent, "utf-8");
85698
85939
  }
85699
85940
  if (catalogedAssets.length > 0) {
85700
85941
  writeFileSync27(
85701
- join59(outputDir, "extracted", "assets-catalog.json"),
85942
+ join60(outputDir, "extracted", "assets-catalog.json"),
85702
85943
  JSON.stringify(catalogedAssets, null, 2),
85703
85944
  "utf-8"
85704
85945
  );
85705
85946
  }
85706
85947
  if (detectedLibraries.length > 0) {
85707
85948
  writeFileSync27(
85708
- join59(outputDir, "extracted", "detected-libraries.json"),
85949
+ join60(outputDir, "extracted", "detected-libraries.json"),
85709
85950
  JSON.stringify(detectedLibraries, null, 2),
85710
85951
  "utf-8"
85711
85952
  );
@@ -85716,7 +85957,7 @@ async function captureWebsite(opts, onProgress) {
85716
85957
  const lines = generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCaptions);
85717
85958
  if (lines.length > 0) {
85718
85959
  writeFileSync27(
85719
- join59(outputDir, "extracted", "asset-descriptions.md"),
85960
+ join60(outputDir, "extracted", "asset-descriptions.md"),
85720
85961
  "# Asset Descriptions\n\nOne line per file. Read this instead of opening every image individually.\n\n" + lines.map((l) => "- " + l).join("\n") + "\n",
85721
85962
  "utf-8"
85722
85963
  );
@@ -85732,7 +85973,7 @@ async function captureWebsite(opts, onProgress) {
85732
85973
  animationCatalog,
85733
85974
  screenshots.length > 0,
85734
85975
  discoveredLotties.length > 0,
85735
- existsSync54(join59(outputDir, "extracted", "shaders.json")),
85976
+ existsSync55(join60(outputDir, "extracted", "shaders.json")),
85736
85977
  catalogedAssets,
85737
85978
  progress,
85738
85979
  warnings,
@@ -86080,8 +86321,8 @@ __export(autoUpdate_exports, {
86080
86321
  });
86081
86322
  import { spawn as spawn13 } from "child_process";
86082
86323
  import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync32, openSync as openSync2 } from "fs";
86083
- import { homedir as homedir10 } from "os";
86084
- import { join as join60 } from "path";
86324
+ import { homedir as homedir11 } from "os";
86325
+ import { join as join61 } from "path";
86085
86326
  import { compareVersions as compareVersions2 } from "compare-versions";
86086
86327
  function isAutoInstallDisabled() {
86087
86328
  if (isDevMode()) return true;
@@ -86104,7 +86345,7 @@ function log(line) {
86104
86345
  }
86105
86346
  function launchDetachedInstall(installCommand, version) {
86106
86347
  mkdirSync32(CONFIG_DIR2, { recursive: true, mode: 448 });
86107
- const configFile = join60(CONFIG_DIR2, "config.json");
86348
+ const configFile = join61(CONFIG_DIR2, "config.json");
86108
86349
  const nodeScript = `
86109
86350
  const { exec } = require("node:child_process");
86110
86351
  const { readFileSync, renameSync, writeFileSync } = require("node:fs");
@@ -86223,8 +86464,8 @@ var init_autoUpdate = __esm({
86223
86464
  init_config();
86224
86465
  init_env();
86225
86466
  init_installerDetection();
86226
- CONFIG_DIR2 = join60(homedir10(), ".hyperframes");
86227
- LOG_FILE = join60(CONFIG_DIR2, "auto-update.log");
86467
+ CONFIG_DIR2 = join61(homedir11(), ".hyperframes");
86468
+ LOG_FILE = join61(CONFIG_DIR2, "auto-update.log");
86228
86469
  PENDING_TIMEOUT_MS = 10 * 60 * 1e3;
86229
86470
  }
86230
86471
  });