hyperframes 0.8.61 → 0.8.63

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
@@ -50,7 +50,7 @@ var VERSION;
50
50
  var init_version = __esm({
51
51
  "src/version.ts"() {
52
52
  "use strict";
53
- VERSION = true ? "0.8.61" : "0.0.0-dev";
53
+ VERSION = true ? "0.8.63" : "0.0.0-dev";
54
54
  }
55
55
  });
56
56
 
@@ -4887,7 +4887,7 @@ var require_util = __commonJS({
4887
4887
  return path;
4888
4888
  }
4889
4889
  exports.normalize = normalize3;
4890
- function join163(aRoot, aPath) {
4890
+ function join164(aRoot, aPath) {
4891
4891
  if (aRoot === "") {
4892
4892
  aRoot = ".";
4893
4893
  }
@@ -4919,7 +4919,7 @@ var require_util = __commonJS({
4919
4919
  }
4920
4920
  return joined;
4921
4921
  }
4922
- exports.join = join163;
4922
+ exports.join = join164;
4923
4923
  exports.isAbsolute = function(aPath) {
4924
4924
  return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
4925
4925
  };
@@ -5092,7 +5092,7 @@ var require_util = __commonJS({
5092
5092
  parsed.path = parsed.path.substring(0, index + 1);
5093
5093
  }
5094
5094
  }
5095
- sourceURL = join163(urlGenerate(parsed), sourceURL);
5095
+ sourceURL = join164(urlGenerate(parsed), sourceURL);
5096
5096
  }
5097
5097
  return normalize3(sourceURL);
5098
5098
  }
@@ -60906,8 +60906,8 @@ function redactKnownPaths(value, paths) {
60906
60906
  for (const path of paths) {
60907
60907
  if (typeof path !== "string" || path.length === 0)
60908
60908
  continue;
60909
- const basename34 = path.split(/[\\/]/).pop() ?? "";
60910
- for (const literal2 of [path, basename34].filter((v2) => v2.length > 2)) {
60909
+ const basename35 = path.split(/[\\/]/).pop() ?? "";
60910
+ for (const literal2 of [path, basename35].filter((v2) => v2.length > 2)) {
60911
60911
  out = out.split(literal2).join("[path]");
60912
60912
  }
60913
60913
  }
@@ -66117,17 +66117,22 @@ var BrowserLeasePool;
66117
66117
  var init_browserLeasePool = __esm({
66118
66118
  "../engine/src/services/browserLeasePool.ts"() {
66119
66119
  "use strict";
66120
- BrowserLeasePool = class {
66120
+ BrowserLeasePool = class _BrowserLeasePool {
66121
66121
  constructor(options) {
66122
66122
  this.options = options;
66123
66123
  }
66124
66124
  options;
66125
+ /** Under the CLI's 3s exit watchdog, alongside the render-wait bound it races. */
66126
+ static DRAIN_FORCE_CLOSE_MS = 1e3;
66125
66127
  available = /* @__PURE__ */ new Map();
66126
66128
  entries = /* @__PURE__ */ new Set();
66127
66129
  leasesByBrowser = /* @__PURE__ */ new Map();
66128
66130
  drainPromise = null;
66131
+ closed = false;
66129
66132
  async acquire(fingerprintInput, pooled) {
66133
+ if (this.closed) throw new Error("Browser pool is closed; no new browsers can be acquired");
66130
66134
  if (this.drainPromise) await this.drainPromise;
66135
+ if (this.closed) throw new Error("Browser pool is closed; no new browsers can be acquired");
66131
66136
  const fingerprint = freezeFingerprint(fingerprintInput);
66132
66137
  const entry = this.reserveEntry(fingerprint, pooled);
66133
66138
  return this.issueLease(entry);
@@ -66186,17 +66191,30 @@ var init_browserLeasePool = __esm({
66186
66191
  if (this.drainPromise) return this.drainPromise;
66187
66192
  const entries2 = [...this.entries];
66188
66193
  for (const entry of entries2) this.requestClose(entry, false);
66189
- this.drainPromise = Promise.all(entries2.map((entry) => entry.closePromise)).then(() => {
66194
+ const settle = Promise.all(entries2.map((entry) => entry.closePromise));
66195
+ const bound = new Promise(
66196
+ (resolve84) => setTimeout(resolve84, _BrowserLeasePool.DRAIN_FORCE_CLOSE_MS).unref()
66197
+ );
66198
+ this.drainPromise = Promise.race([settle, bound]).then(() => {
66199
+ for (const entry of entries2) if (this.entries.has(entry)) this.requestClose(entry, true);
66200
+ return settle;
66201
+ }).then(() => {
66190
66202
  this.drainPromise = null;
66191
66203
  });
66192
66204
  return this.drainPromise;
66193
66205
  }
66206
+ /** Terminal: drains every entry and refuses every `acquire()` after. */
66207
+ close() {
66208
+ this.closed = true;
66209
+ return this.drain();
66210
+ }
66194
66211
  /** Test-only state reset. Call `drain()` first when entries own real browsers. */
66195
66212
  reset() {
66196
66213
  this.available.clear();
66197
66214
  this.entries.clear();
66198
66215
  this.leasesByBrowser.clear();
66199
66216
  this.drainPromise = null;
66217
+ this.closed = false;
66200
66218
  }
66201
66219
  createEntry(key2, fingerprint, pooled) {
66202
66220
  const entry = {
@@ -66730,6 +66748,9 @@ function forceReleaseBrowser(browser) {
66730
66748
  async function drainBrowserPool() {
66731
66749
  await browserLeasePool.drain();
66732
66750
  }
66751
+ async function closeBrowserPool() {
66752
+ await browserLeasePool.close();
66753
+ }
66733
66754
  function probeNvidiaVramMb() {
66734
66755
  if (_cachedVramMb !== null) return _cachedVramMb;
66735
66756
  try {
@@ -73753,11 +73774,19 @@ async function extractMediaMetadata(filePath) {
73753
73774
  });
73754
73775
  return probePromise;
73755
73776
  }
73777
+ function resolveStreamWindow(filePath, metadata) {
73778
+ const videoStreamDurationSeconds = metadata.videoStreamDurationSeconds;
73779
+ const candidateStart = metadata.videoStreamStartSeconds ?? 0;
73780
+ const videoStreamStartSeconds = Number.isFinite(candidateStart) ? candidateStart : 0;
73781
+ const cacheKey = `${filePath}\0${String(videoStreamStartSeconds)}\0${String(videoStreamDurationSeconds)}`;
73782
+ return { videoStreamStartSeconds, videoStreamDurationSeconds, cacheKey };
73783
+ }
73756
73784
  async function extractFinalVideoFrameTimestamp(filePath, metadata, signal) {
73757
- const videoDurationSeconds = metadata.videoStreamDurationSeconds;
73758
- const candidateStreamStart = metadata.videoStreamStartSeconds ?? 0;
73759
- const videoStreamStartSeconds = Number.isFinite(candidateStreamStart) ? candidateStreamStart : 0;
73760
- const cacheKey = `${filePath}\0${String(videoStreamStartSeconds)}\0${String(videoDurationSeconds)}`;
73785
+ const {
73786
+ videoStreamStartSeconds,
73787
+ videoStreamDurationSeconds: videoDurationSeconds,
73788
+ cacheKey
73789
+ } = resolveStreamWindow(filePath, metadata);
73761
73790
  let probeCache = finalVideoFrameTimestampCache;
73762
73791
  if (signal) {
73763
73792
  probeCache = finalVideoFrameTimestampSignalCaches.get(signal) ?? /* @__PURE__ */ new Map();
@@ -73860,19 +73889,27 @@ async function extractAudioMetadata(filePath, options) {
73860
73889
  });
73861
73890
  return probePromise;
73862
73891
  }
73863
- async function analyzeKeyframeIntervals(filePath) {
73864
- const cached3 = keyframeCache.get(filePath);
73892
+ async function analyzeKeyframeIntervals(filePath, metadata) {
73893
+ const { videoStreamStartSeconds, videoStreamDurationSeconds, cacheKey } = resolveStreamWindow(
73894
+ filePath,
73895
+ metadata
73896
+ );
73897
+ const cached3 = keyframeCache.get(cacheKey);
73865
73898
  if (cached3) return cached3;
73866
- const promise = analyzeKeyframeIntervalsUncached(filePath);
73867
- keyframeCache.set(filePath, promise);
73899
+ const promise = analyzeKeyframeIntervalsUncached(
73900
+ filePath,
73901
+ videoStreamStartSeconds,
73902
+ videoStreamDurationSeconds
73903
+ );
73904
+ keyframeCache.set(cacheKey, promise);
73868
73905
  promise.catch(() => {
73869
- if (keyframeCache.get(filePath) === promise) {
73870
- keyframeCache.delete(filePath);
73906
+ if (keyframeCache.get(cacheKey) === promise) {
73907
+ keyframeCache.delete(cacheKey);
73871
73908
  }
73872
73909
  });
73873
73910
  return promise;
73874
73911
  }
73875
- async function analyzeKeyframeIntervalsUncached(filePath) {
73912
+ async function analyzeKeyframeIntervalsUncached(filePath, videoStreamStartSeconds, videoStreamDurationSeconds) {
73876
73913
  const stdout2 = await runFfprobe(filePath, [
73877
73914
  "-select_streams",
73878
73915
  "v:0",
@@ -73884,14 +73921,26 @@ async function analyzeKeyframeIntervalsUncached(filePath) {
73884
73921
  "csv=p=0"
73885
73922
  ]);
73886
73923
  const timestamps = stdout2.split("\n").map((line3) => parseFloat(line3.trim())).filter((t) => Number.isFinite(t));
73887
- if (timestamps.length < 2) {
73924
+ if (timestamps.length === 0) {
73888
73925
  return {
73889
73926
  avgIntervalSeconds: 0,
73890
73927
  maxIntervalSeconds: 0,
73891
- keyframeCount: timestamps.length,
73928
+ keyframeCount: 0,
73892
73929
  isProblematic: false
73893
73930
  };
73894
73931
  }
73932
+ if (timestamps.length === 1) {
73933
+ const streamEnd = videoStreamStartSeconds + videoStreamDurationSeconds;
73934
+ const rawInterval = streamEnd - (timestamps[0] ?? videoStreamStartSeconds);
73935
+ const singleGopInterval = Number.isFinite(rawInterval) ? Math.max(rawInterval, 0) : 0;
73936
+ const roundedInterval = Math.round(singleGopInterval * 100) / 100;
73937
+ return {
73938
+ avgIntervalSeconds: roundedInterval,
73939
+ maxIntervalSeconds: roundedInterval,
73940
+ keyframeCount: 1,
73941
+ isProblematic: singleGopInterval > PROBLEMATIC_KEYFRAME_INTERVAL_SECONDS
73942
+ };
73943
+ }
73895
73944
  let maxInterval = 0;
73896
73945
  let totalInterval = 0;
73897
73946
  for (let i = 1; i < timestamps.length; i++) {
@@ -73904,10 +73953,10 @@ async function analyzeKeyframeIntervalsUncached(filePath) {
73904
73953
  avgIntervalSeconds: Math.round(avgInterval * 100) / 100,
73905
73954
  maxIntervalSeconds: Math.round(maxInterval * 100) / 100,
73906
73955
  keyframeCount: timestamps.length,
73907
- isProblematic: maxInterval > 2
73956
+ isProblematic: maxInterval > PROBLEMATIC_KEYFRAME_INTERVAL_SECONDS
73908
73957
  };
73909
73958
  }
73910
- var FFPROBE_STDERR_MAX_BYTES, FFPROBE_STDOUT_MAX_CHARS, FFPROBE_ERROR_MAX_CHARS, videoMetadataCache, finalVideoFrameTimestampCache, finalVideoFrameTimestampSignalCaches, audioMetadataCache, mediaProbeOutputCache, mediaProbeOutputSignalCaches, MEDIA_PROBE_OUTPUT_CACHE_MAX_ENTRIES, AAC_LC_SAMPLES_PER_PACKET, STILL_IMAGE_DEMUXERS, StructurallyIncompletePngError, extractVideoMetadata, keyframeCache;
73959
+ var FFPROBE_STDERR_MAX_BYTES, FFPROBE_STDOUT_MAX_CHARS, FFPROBE_ERROR_MAX_CHARS, videoMetadataCache, finalVideoFrameTimestampCache, finalVideoFrameTimestampSignalCaches, audioMetadataCache, mediaProbeOutputCache, mediaProbeOutputSignalCaches, MEDIA_PROBE_OUTPUT_CACHE_MAX_ENTRIES, AAC_LC_SAMPLES_PER_PACKET, STILL_IMAGE_DEMUXERS, StructurallyIncompletePngError, extractVideoMetadata, keyframeCache, PROBLEMATIC_KEYFRAME_INTERVAL_SECONDS;
73911
73960
  var init_ffprobe = __esm({
73912
73961
  "../engine/src/utils/ffprobe.ts"() {
73913
73962
  "use strict";
@@ -73950,6 +73999,7 @@ var init_ffprobe = __esm({
73950
73999
  };
73951
74000
  extractVideoMetadata = extractMediaMetadata;
73952
74001
  keyframeCache = /* @__PURE__ */ new Map();
74002
+ PROBLEMATIC_KEYFRAME_INTERVAL_SECONDS = 2;
73953
74003
  }
73954
74004
  });
73955
74005
 
@@ -75140,10 +75190,10 @@ function collectSubCompositionSrcs(html) {
75140
75190
  function cleanAssetUrl(url) {
75141
75191
  return url.trim().split(/[?#]/, 1)[0] ?? "";
75142
75192
  }
75143
- function isWithinProjectRoot(projectDir, candidate) {
75144
- const projectRoot = resolve6(projectDir);
75145
- const relativePath = relative2(projectRoot, candidate);
75146
- return relativePath === "" || !relativePath.startsWith("..") && !isAbsolute2(relativePath);
75193
+ function isWithinProjectRoot(projectDir, candidate, pathModule = { resolve: resolve6, relative: relative2, isAbsolute: isAbsolute2 }) {
75194
+ const projectRoot = pathModule.resolve(projectDir);
75195
+ const relativePath = pathModule.relative(projectRoot, candidate);
75196
+ return relativePath === "" || !relativePath.startsWith("..") && !pathModule.isAbsolute(relativePath);
75147
75197
  }
75148
75198
  function addCandidate(candidates, candidate) {
75149
75199
  if (!candidates.includes(candidate)) candidates.push(candidate);
@@ -95460,6 +95510,14 @@ function readRegularFile(filePath) {
95460
95510
  closeSync4(fd);
95461
95511
  }
95462
95512
  }
95513
+ function resolveContainedPaths(projectDir, compiledDir, relativePath) {
95514
+ const projectPath = join20(projectDir, relativePath);
95515
+ if (!isWithinProjectRoot(projectDir, projectPath)) return null;
95516
+ if (!compiledDir) return { projectPath, compiledPath: null };
95517
+ const compiledPath = join20(compiledDir, relativePath);
95518
+ if (!isWithinProjectRoot(compiledDir, compiledPath)) return null;
95519
+ return { projectPath, compiledPath };
95520
+ }
95463
95521
  function createFileServer(options) {
95464
95522
  const { projectDir, compiledDir, port = 0, stripEmbeddedRuntime = true } = options;
95465
95523
  const headScripts = options.headScripts ?? [];
@@ -95469,8 +95527,9 @@ function createFileServer(options) {
95469
95527
  let requestPath = c3.req.path;
95470
95528
  if (requestPath === "/") requestPath = "/index.html";
95471
95529
  const relativePath = requestPath.replace(/^\//, "");
95472
- const compiledPath = compiledDir ? join20(compiledDir, relativePath) : null;
95473
- const content = (compiledPath ? readRegularFile(compiledPath) : null) ?? readRegularFile(join20(projectDir, relativePath));
95530
+ const resolved2 = resolveContainedPaths(projectDir, compiledDir, relativePath);
95531
+ if (!resolved2) return c3.text("Not found", 404);
95532
+ const content = (resolved2.compiledPath ? readRegularFile(resolved2.compiledPath) : null) ?? readRegularFile(resolved2.projectPath);
95474
95533
  if (content === null) return c3.text("Not found", 404);
95475
95534
  const ext = extname4(relativePath).toLowerCase();
95476
95535
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
@@ -95501,6 +95560,7 @@ var init_fileServer = __esm({
95501
95560
  "../engine/src/services/fileServer.ts"() {
95502
95561
  "use strict";
95503
95562
  init_compiler();
95563
+ init_assetResolution();
95504
95564
  MIME_TYPES = {
95505
95565
  ".html": "text/html; charset=utf-8",
95506
95566
  ".css": "text/css; charset=utf-8",
@@ -96654,6 +96714,7 @@ __export(src_exports, {
96654
96714
  classifyVideoExtractionError: () => classifyVideoExtractionError,
96655
96715
  cloneCaptureWarning: () => cloneCaptureWarning,
96656
96716
  cloneCaptureWarnings: () => cloneCaptureWarnings,
96717
+ closeBrowserPool: () => closeBrowserPool,
96657
96718
  closeCaptureSession: () => closeCaptureSession,
96658
96719
  completeDeferredDrawElementInit: () => completeDeferredDrawElementInit,
96659
96720
  compositionRequiresWebGpu: () => compositionRequiresWebGpu,
@@ -96981,6 +97042,18 @@ var init_agent_runtime = __esm({
96981
97042
  "use strict";
96982
97043
  init_platform();
96983
97044
  VENDOR_RULES = [
97045
+ // IBM Bob Shell — both shell execution paths stamp BOBSHELL_CLI=1 onto the
97046
+ // child environment: childProcessFallback's spawn env and executeWithPty's
97047
+ // PTY env. Keep this ahead of other agents because Bob inherits the parent
97048
+ // environment, so a Bob session launched from Codex or Claude can carry both
97049
+ // vendors' markers; BOBSHELL_CLI identifies the immediate executor.
97050
+ // BOBSHELL_API_KEY is deliberately ignored: it is an authentication setting,
97051
+ // not proof that Bob launched the current process.
97052
+ // Source: IBM Bob Shell 1.0.6 bundle (lb.childProcessFallback / executeWithPty).
97053
+ {
97054
+ name: "bob",
97055
+ check: (env) => typeof env["BOBSHELL_CLI"] === "string"
97056
+ },
96984
97057
  // Anthropic Claude Code — sets CLAUDECODE=1 on every Bash/PowerShell tool
96985
97058
  // spawn (Shell.ts:321) and CLAUDE_CODE_ENTRYPOINT at startup, inherited by
96986
97059
  // every child (main.tsx:527). Both propagate to spawned subprocesses.
@@ -133534,10 +133607,12 @@ var init_videoFrameCoverage = __esm({
133534
133607
  });
133535
133608
 
133536
133609
  // ../producer/src/services/renderMediaCollector.ts
133537
- function parseNumeric4(value) {
133538
- if (value == null || value === "") return null;
133539
- const parsed = Number.parseFloat(value);
133540
- return Number.isFinite(parsed) ? parsed : null;
133610
+ function resolveHostEnd(host, hostStart) {
133611
+ return resolveAuthoredTimingWindow({
133612
+ start: hostStart,
133613
+ duration: host.getAttribute("data-duration"),
133614
+ end: host.getAttribute("data-end")
133615
+ })?.end ?? null;
133541
133616
  }
133542
133617
  function resolveHostWindow(element, document2, startCache, visiting) {
133543
133618
  const hosts = [];
@@ -133549,7 +133624,7 @@ function resolveHostWindow(element, document2, startCache, visiting) {
133549
133624
  let limit = Infinity;
133550
133625
  for (const host of hosts.reverse()) {
133551
133626
  const hostStart = resolveReferencedStart(document2, host, startCache, visiting);
133552
- const hostEnd = parseNumeric4(host.getAttribute("data-end"));
133627
+ const hostEnd = resolveHostEnd(host, hostStart);
133553
133628
  if (hostEnd != null) limit = Math.min(limit, offset2 + hostEnd);
133554
133629
  offset2 += hostStart;
133555
133630
  }
@@ -139555,10 +139630,11 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
139555
139630
  if (isHttpUrl(video.src)) continue;
139556
139631
  const videoPath = resolve33(projectDir, video.src);
139557
139632
  const reencode = `ffmpeg -i "${video.src}" -c:v libx264 -r 30 -g 30 -keyint_min 30 -movflags +faststart -c:a copy output.mp4`;
139558
- Promise.all([
139559
- withMediaProbeSlot(() => analyzeKeyframeIntervals(videoPath)),
139560
- withMediaProbeSlot(() => extractMediaMetadata(videoPath))
139561
- ]).then(([analysis, metadata]) => {
139633
+ (async () => {
139634
+ const metadata = await withMediaProbeSlot(() => extractMediaMetadata(videoPath));
139635
+ const analysis = await withMediaProbeSlot(
139636
+ () => analyzeKeyframeIntervals(videoPath, metadata)
139637
+ );
139562
139638
  if (analysis.isProblematic) {
139563
139639
  defaultLogger.warn(
139564
139640
  `[Compiler] WARNING: Video "${video.id}" has sparse keyframes (max interval: ${analysis.maxIntervalSeconds}s). This causes seek failures and frame freezing. Re-encode with: ${reencode}`
@@ -139569,7 +139645,7 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
139569
139645
  `[Compiler] Video "${video.id}" is variable frame rate (VFR); the engine will normalize it to CFR before frame extraction. If rendering feels slow on this video, pre-encode once with: ${reencode}`
139570
139646
  );
139571
139647
  }
139572
- }).catch(() => {
139648
+ })().catch(() => {
139573
139649
  });
139574
139650
  }
139575
139651
  const { document: document2 } = parseHTML(html);
@@ -151340,7 +151416,6 @@ var init_pipeline = __esm({
151340
151416
  // src/server/studioServer.ts
151341
151417
  var studioServer_exports = {};
151342
151418
  __export(studioServer_exports, {
151343
- closeThumbnailBrowser: () => closeThumbnailBrowser,
151344
151419
  createStudioServer: () => createStudioServer,
151345
151420
  loadPreviewServerBuildSignature: () => loadPreviewServerBuildSignature,
151346
151421
  resolveStudioBundle: () => resolveStudioBundle
@@ -151448,7 +151523,8 @@ async function downloadRemoteGifImageSources(html, downloadDir, downloadToTemp2)
151448
151523
  );
151449
151524
  return sourceAssets;
151450
151525
  }
151451
- async function getThumbnailBrowser(requestedGpuMode) {
151526
+ async function getThumbnailBrowser(requestedGpuMode, isShuttingDown) {
151527
+ if (isShuttingDown()) return null;
151452
151528
  if (_thumbnailBrowserLease?.browser.connected && _thumbnailBrowserModes?.requested === requestedGpuMode) {
151453
151529
  return {
151454
151530
  browser: _thumbnailBrowserLease.browser,
@@ -151463,6 +151539,7 @@ async function getThumbnailBrowser(requestedGpuMode) {
151463
151539
  if (_thumbnailBrowserLease) await closeThumbnailBrowser();
151464
151540
  _thumbnailBrowserInitializing = (async () => {
151465
151541
  try {
151542
+ if (isShuttingDown()) return null;
151466
151543
  const { ensureBrowser: ensureBrowser2 } = await Promise.resolve().then(() => (init_manager2(), manager_exports2));
151467
151544
  const { acquireBrowser: acquireBrowser2, buildChromeArgs: buildChromeArgs2 } = await Promise.resolve().then(() => (init_src(), src_exports));
151468
151545
  let executablePath;
@@ -151503,6 +151580,8 @@ async function getThumbnailBrowser(requestedGpuMode) {
151503
151580
  return _thumbnailBrowserInitializing;
151504
151581
  }
151505
151582
  async function closeThumbnailBrowser() {
151583
+ if (_thumbnailBrowserInitializing) await _thumbnailBrowserInitializing.catch(() => {
151584
+ });
151506
151585
  if (!_thumbnailBrowserLease) return;
151507
151586
  const lease = _thumbnailBrowserLease;
151508
151587
  _thumbnailBrowserLease = null;
@@ -151566,6 +151645,8 @@ function createStudioServer(options) {
151566
151645
  cachedProjectSignature = null;
151567
151646
  }
151568
151647
  });
151648
+ const inFlightRenders = /* @__PURE__ */ new Map();
151649
+ let shuttingDown = false;
151569
151650
  const adapter2 = {
151570
151651
  // Explicit option wins (preview's resolved --proxy/--no-proxy + config);
151571
151652
  // otherwise honor the project's hyperframes.json media.autoProxy so every
@@ -151629,6 +151710,15 @@ function createStudioServer(options) {
151629
151710
  runtimeUrl: "/api/runtime.js",
151630
151711
  rendersDir: () => join91(projectDir, "renders"),
151631
151712
  startRender(opts) {
151713
+ if (shuttingDown) {
151714
+ return {
151715
+ id: opts.jobId,
151716
+ status: "failed",
151717
+ progress: 0,
151718
+ outputPath: opts.outputPath,
151719
+ error: "Studio server is shutting down"
151720
+ };
151721
+ }
151632
151722
  refreshTelemetryPosture();
151633
151723
  const abortController = new AbortController();
151634
151724
  const state = {
@@ -151639,7 +151729,7 @@ function createStudioServer(options) {
151639
151729
  cancel: () => abortController.abort()
151640
151730
  };
151641
151731
  const startTime = Date.now();
151642
- (async () => {
151732
+ const run2 = (async () => {
151643
151733
  let renderJob;
151644
151734
  const removeCancelledOutput = () => {
151645
151735
  state.status = "cancelled";
@@ -151711,6 +151801,9 @@ function createStudioServer(options) {
151711
151801
  }
151712
151802
  }
151713
151803
  })();
151804
+ inFlightRenders.set(abortController, run2);
151805
+ const forget = () => void inFlightRenders.delete(abortController);
151806
+ run2.then(forget, forget);
151714
151807
  return state;
151715
151808
  },
151716
151809
  startBackgroundRemoval(opts) {
@@ -151723,9 +151816,11 @@ function createStudioServer(options) {
151723
151816
  });
151724
151817
  },
151725
151818
  async generateThumbnail(opts) {
151726
- const session = await getThumbnailBrowser(browserGpuMode);
151819
+ const session = await getThumbnailBrowser(browserGpuMode, () => shuttingDown);
151727
151820
  if (!session) {
151728
- console.warn("[Studio] Thumbnail: no browser available \u2014 Chrome may not be installed");
151821
+ if (!shuttingDown) {
151822
+ console.warn("[Studio] Thumbnail: no browser available \u2014 Chrome may not be installed");
151823
+ }
151729
151824
  return null;
151730
151825
  }
151731
151826
  const sourcePath = join91(opts.project.dir, opts.compPath);
@@ -151943,6 +152038,7 @@ function createStudioServer(options) {
151943
152038
  });
151944
152039
  const serveStudioStaticFile = (cacheControl) => (c3) => {
151945
152040
  const filePath = resolve39(studioDir, c3.req.path.slice(1));
152041
+ if (!isWithinProjectRoot(studioDir, filePath)) return c3.text("not found", 404);
151946
152042
  const content = readBundleFile(filePath);
151947
152043
  if (content === null) return c3.text("not found", 404);
151948
152044
  return new Response(content, {
@@ -152028,9 +152124,27 @@ function createStudioServer(options) {
152028
152124
  }
152029
152125
  return c3.html(html, 200, { "Cache-Control": "no-cache" });
152030
152126
  });
152031
- return { app, watcher, adapter: adapter2 };
152127
+ const shutdown = async () => {
152128
+ shuttingDown = true;
152129
+ const renders = [...inFlightRenders];
152130
+ for (const [abortController] of renders) abortController.abort();
152131
+ const { killTrackedProcesses: killTrackedProcesses2, closeBrowserPool: closeBrowserPool2 } = await Promise.resolve().then(() => (init_src(), src_exports));
152132
+ killTrackedProcesses2();
152133
+ const closeBrowsers = Promise.allSettled([
152134
+ closeThumbnailBrowser().catch(() => {
152135
+ }),
152136
+ closeBrowserPool2().catch(() => {
152137
+ })
152138
+ ]);
152139
+ await Promise.race([
152140
+ Promise.allSettled(renders.map(([, done]) => done)),
152141
+ new Promise((resolve84) => setTimeout(resolve84, RENDER_SHUTDOWN_WAIT_MS).unref())
152142
+ ]);
152143
+ await closeBrowsers;
152144
+ };
152145
+ return { app, watcher, adapter: adapter2, shutdown };
152032
152146
  }
152033
- var STUDIO_MANUAL_EDITS_PATH2, IMMUTABLE_CACHE_CONTROL, REMOTE_GIF_IMG_SRC_RE, _thumbnailBrowserLease, _thumbnailBrowserInitializing, _thumbnailBrowserModes;
152147
+ var STUDIO_MANUAL_EDITS_PATH2, RENDER_SHUTDOWN_WAIT_MS, IMMUTABLE_CACHE_CONTROL, REMOTE_GIF_IMG_SRC_RE, _thumbnailBrowserLease, _thumbnailBrowserInitializing, _thumbnailBrowserModes;
152034
152148
  var init_studioServer = __esm({
152035
152149
  "src/server/studioServer.ts"() {
152036
152150
  "use strict";
@@ -152045,9 +152159,11 @@ var init_studioServer = __esm({
152045
152159
  init_dist9();
152046
152160
  init_projectConfig();
152047
152161
  init_screenshotClip();
152162
+ init_assetResolution();
152048
152163
  init_captureCompositionFrame();
152049
152164
  init_gpuPolicy();
152050
152165
  STUDIO_MANUAL_EDITS_PATH2 = ".hyperframes/studio-manual-edits.json";
152166
+ RENDER_SHUTDOWN_WAIT_MS = 2e3;
152051
152167
  IMMUTABLE_CACHE_CONTROL = "public, max-age=31536000, immutable";
152052
152168
  REMOTE_GIF_IMG_SRC_RE = /<img\b[^>]*?\bsrc\s*=\s*["'](https?:\/\/[^"']+\.gif(?:[?#][^"']*)?)["'][^>]*>/gi;
152053
152169
  _thumbnailBrowserLease = null;
@@ -152756,7 +152872,11 @@ async function runEmbeddedMode(dir, startPort, options) {
152756
152872
  return;
152757
152873
  }
152758
152874
  const serverBuildSignature = await loadPreviewServerBuildSignature2();
152759
- const { app, watcher } = createStudioServer2({
152875
+ const {
152876
+ app,
152877
+ watcher,
152878
+ shutdown: shutdownStudio
152879
+ } = createStudioServer2({
152760
152880
  projectDir: dir,
152761
152881
  projectName: pName,
152762
152882
  autoProxy: options?.autoProxy,
@@ -152845,28 +152965,17 @@ async function runEmbeddedMode(dir, startPort, options) {
152845
152965
  const shutdown = () => {
152846
152966
  if (shuttingDown) return;
152847
152967
  shuttingDown = true;
152848
- process.off("SIGINT", shutdown);
152849
- process.off("SIGTERM", shutdown);
152850
152968
  rl?.close();
152851
152969
  reportPreviewShutdown(Boolean(options?.json));
152852
152970
  setTimeout(() => requestCliExit(0), 3e3).unref();
152853
- const cleanup = async () => {
152854
- const { closeThumbnailBrowser: closeThumbnailBrowser2 } = await Promise.resolve().then(() => (init_studioServer(), studioServer_exports));
152855
- const { drainBrowserPool: drainBrowserPool2, killTrackedProcesses: killTrackedProcesses2 } = await Promise.resolve().then(() => (init_src(), src_exports));
152856
- killTrackedProcesses2();
152857
- await closeThumbnailBrowser2().catch(() => {
152858
- });
152859
- await drainBrowserPool2().catch(() => {
152860
- });
152861
- };
152862
- cleanup().catch(() => {
152971
+ shutdownStudio().catch(() => {
152863
152972
  }).finally(() => {
152864
152973
  watcher.close();
152865
152974
  result.server.close(() => resolveRun());
152866
152975
  });
152867
152976
  };
152868
- process.once("SIGINT", shutdown);
152869
- process.once("SIGTERM", shutdown);
152977
+ process.on("SIGINT", shutdown);
152978
+ process.on("SIGTERM", shutdown);
152870
152979
  Promise.resolve().then(() => (init_src(), src_exports)).then(({ killTrackedProcesses: killTrackedProcesses2 }) => {
152871
152980
  process.once("exit", () => {
152872
152981
  if (!shuttingDown) killTrackedProcesses2();
@@ -174692,9 +174801,27 @@ var init_events2 = __esm({
174692
174801
  });
174693
174802
 
174694
174803
  // src/capture/captureFile.ts
174695
- import { writeFileSync as writeFileSync47 } from "fs";
174804
+ var captureFile_exports = {};
174805
+ __export(captureFile_exports, {
174806
+ writeCaptureFileSync: () => writeCaptureFileSync
174807
+ });
174808
+ import { mkdtempSync as mkdtempSync24, renameSync as renameSync21, rmSync as rmSync37, writeFileSync as writeFileSync47 } from "fs";
174809
+ import { basename as basename27, dirname as dirname61, join as join126 } from "path";
174810
+ function publishCaptureFileSync(path, data2, options) {
174811
+ const stagingDir = mkdtempSync24(join126(dirname61(path), ".capture-"));
174812
+ try {
174813
+ const staged = join126(stagingDir, basename27(path));
174814
+ writeCaptureFileSync(staged, data2, { ...options, flag: "wx" });
174815
+ renameSync21(staged, path);
174816
+ } finally {
174817
+ rmSync37(stagingDir, { recursive: true, force: true });
174818
+ }
174819
+ }
174696
174820
  function writeCaptureFileSync(path, data2, options) {
174697
174821
  const normalized2 = typeof options === "string" ? { encoding: options } : options;
174822
+ if (normalized2?.flag === void 0 || normalized2.flag === "w") {
174823
+ return publishCaptureFileSync(path, data2, normalized2);
174824
+ }
174698
174825
  writeFileSync47(path, data2, { ...normalized2, mode: 384 });
174699
174826
  }
174700
174827
  var init_captureFile = __esm({
@@ -174714,7 +174841,7 @@ __export(contactSheet_exports, {
174714
174841
  });
174715
174842
  import sharp from "sharp";
174716
174843
  import { readdirSync as readdirSync39, readFileSync as readFileSync85, unlinkSync as unlinkSync10, existsSync as existsSync111 } from "fs";
174717
- import { join as join126, extname as extname19, basename as basename27, dirname as dirname61 } from "path";
174844
+ import { join as join127, extname as extname19, basename as basename28, dirname as dirname62 } from "path";
174718
174845
  async function createContactSheet(imagePaths, outputPath, opts = {}) {
174719
174846
  const {
174720
174847
  cols = 3,
@@ -174748,7 +174875,7 @@ async function createContactSheet(imagePaths, outputPath, opts = {}) {
174748
174875
  overlays.push({ input: resized, left: x2, top: y + labelH });
174749
174876
  let labelText = `${i + 1}`;
174750
174877
  if (labelMode === "filename") {
174751
- labelText = `${i + 1}. ${basename27(files[i]).replace(extname19(files[i]), "")}`;
174878
+ labelText = `${i + 1}. ${basename28(files[i]).replace(extname19(files[i]), "")}`;
174752
174879
  } else if (labelMode === "custom" && labels?.[i]) {
174753
174880
  labelText = `${i + 1}. ${labels[i]}`;
174754
174881
  }
@@ -174766,11 +174893,8 @@ async function createContactSheet(imagePaths, outputPath, opts = {}) {
174766
174893
  background: { r: 26, g: 26, b: 26 }
174767
174894
  }
174768
174895
  }).composite(overlays);
174769
- if (extname19(outputPath).toLowerCase() === ".png") {
174770
- await sheet.png().toFile(outputPath);
174771
- } else {
174772
- await sheet.jpeg({ quality }).toFile(outputPath);
174773
- }
174896
+ const encoded = extname19(outputPath).toLowerCase() === ".png" ? await sheet.png().toBuffer() : await sheet.jpeg({ quality }).toBuffer();
174897
+ writeCaptureFileSync(outputPath, encoded);
174774
174898
  return outputPath;
174775
174899
  }
174776
174900
  function escapeXml2(s) {
@@ -174803,7 +174927,7 @@ async function createScrollContactSheet(screenshotsDir, outputPath, budget = {})
174803
174927
  if (!existsSync111(screenshotsDir)) return [];
174804
174928
  const scrollFiles = readdirSync39(screenshotsDir).filter((f) => f.startsWith("scroll-") && f.endsWith(".png")).sort();
174805
174929
  if (scrollFiles.length === 0) return [];
174806
- const paths = scrollFiles.map((f) => join126(screenshotsDir, f));
174930
+ const paths = scrollFiles.map((f) => join127(screenshotsDir, f));
174807
174931
  const labels = scrollFiles.map((f) => {
174808
174932
  const m = f.match(/scroll-(\d+)\.png/);
174809
174933
  return m ? `${m[1]}% scroll` : f;
@@ -174820,7 +174944,7 @@ async function createSnapshotContactSheet(snapshotsDir, outputPath, budget = {})
174820
174944
  if (!existsSync111(snapshotsDir)) return [];
174821
174945
  const snapshotFiles = readdirSync39(snapshotsDir).filter((f) => f.startsWith("frame-") && f.endsWith(".png")).sort();
174822
174946
  if (snapshotFiles.length === 0) return [];
174823
- const paths = snapshotFiles.map((f) => join126(snapshotsDir, f));
174947
+ const paths = snapshotFiles.map((f) => join127(snapshotsDir, f));
174824
174948
  const labels = snapshotFiles.map((f) => {
174825
174949
  const m = f.match(/at-([\d.]+)s/);
174826
174950
  return m ? `${m[1]}s` : f;
@@ -174838,7 +174962,7 @@ async function createAssetContactSheet(assetsDir, outputPath, budget = {}) {
174838
174962
  const imageExts = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".webp"]);
174839
174963
  const assetFiles = readdirSync39(assetsDir).filter((f) => imageExts.has(extname19(f).toLowerCase()) && !f.includes("contact-sheet")).sort();
174840
174964
  if (assetFiles.length === 0) return [];
174841
- const paths = assetFiles.map((f) => join126(assetsDir, f));
174965
+ const paths = assetFiles.map((f) => join127(assetsDir, f));
174842
174966
  return createContactSheetPages(paths, outputPath, {
174843
174967
  cols: 4,
174844
174968
  cellWidth: 480,
@@ -174858,20 +174982,20 @@ async function createSvgContactSheet(svgsDir, outputPath, assetsRootDir, budget
174858
174982
  for (const f of readdirSync39(dir).filter((f2) => f2.endsWith(".svg")).sort()) {
174859
174983
  if (!seen.has(f)) {
174860
174984
  seen.add(f);
174861
- svgPaths.push(join126(dir, f));
174985
+ svgPaths.push(join127(dir, f));
174862
174986
  }
174863
174987
  }
174864
174988
  }
174865
174989
  if (svgPaths.length === 0) return [];
174866
174990
  const svgFileNames = svgPaths.map((p2) => p2.split("/").pop());
174867
174991
  const thumbSize = 200;
174868
- const tmpDir = dirname61(outputPath);
174992
+ const tmpDir = dirname62(outputPath);
174869
174993
  const tmpPaths = [];
174870
174994
  const labels = [];
174871
174995
  for (let i = 0; i < svgPaths.length; i++) {
174872
174996
  if ((budget.remainingMs?.() ?? 1) <= 0) break;
174873
174997
  const svgPath = svgPaths[i];
174874
- const tmpPath = join126(tmpDir, `.thumb-${i}.png`);
174998
+ const tmpPath = join127(tmpDir, `.thumb-${i}.png`);
174875
174999
  try {
174876
175000
  const svgBuf = readFileSync85(svgPath);
174877
175001
  const thumb = await sharp(svgBuf).resize(thumbSize, thumbSize, {
@@ -174930,9 +175054,9 @@ __export(snapshot_exports, {
174930
175054
  resolveSnapshotVideoRateSpec: () => resolveSnapshotVideoRateSpec,
174931
175055
  tailFrameTime: () => tailFrameTime
174932
175056
  });
174933
- import { existsSync as existsSync112, mkdtempSync as mkdtempSync24, readFileSync as readFileSync86, mkdirSync as mkdirSync56, rmSync as rmSync37, writeFileSync as writeFileSync48 } from "fs";
175057
+ import { existsSync as existsSync112, mkdtempSync as mkdtempSync25, readFileSync as readFileSync86, mkdirSync as mkdirSync56, rmSync as rmSync38, writeFileSync as writeFileSync48 } from "fs";
174934
175058
  import { tmpdir as tmpdir16 } from "os";
174935
- import { resolve as resolve72, join as join127, relative as relative31, isAbsolute as isAbsolute19, basename as basename28, posix as posix6 } from "path";
175059
+ import { resolve as resolve72, join as join128, relative as relative31, isAbsolute as isAbsolute19, basename as basename29, posix as posix6 } from "path";
174936
175060
  function orbitStageSource() {
174937
175061
  return `function(cam) {
174938
175062
  var root = document.querySelector("[data-composition-id]")
@@ -174986,8 +175110,8 @@ function requireSnapshotFfmpeg(ffmpegPath) {
174986
175110
  );
174987
175111
  }
174988
175112
  async function extractVideoFrameToBuffer(videoPath, timeSeconds, useVp9AlphaDecoder = false, accurateSeek = false) {
174989
- const tmp = mkdtempSync24(join127(tmpdir16(), "hf-snapshot-frame-"));
174990
- const outPath = join127(tmp, "frame.png");
175113
+ const tmp = mkdtempSync25(join128(tmpdir16(), "hf-snapshot-frame-"));
175114
+ const outPath = join128(tmp, "frame.png");
174991
175115
  try {
174992
175116
  const ffmpegPath = requireSnapshotFfmpeg(findFFmpeg());
174993
175117
  const args = ["-hide_banner", "-loglevel", "error"];
@@ -175009,7 +175133,7 @@ async function extractVideoFrameToBuffer(videoPath, timeSeconds, useVp9AlphaDeco
175009
175133
  return readFileSync86(outPath);
175010
175134
  } finally {
175011
175135
  try {
175012
- rmSync37(tmp, { recursive: true, force: true });
175136
+ rmSync38(tmp, { recursive: true, force: true });
175013
175137
  } catch {
175014
175138
  }
175015
175139
  }
@@ -175098,13 +175222,13 @@ async function captureSnapshots(projectDir, opts) {
175098
175222
  );
175099
175223
  }
175100
175224
  const cameraExpr = opts.angle && (opts.angle.yaw !== 0 || opts.angle.pitch !== 0) ? `(${orbitStageSource()})(${JSON.stringify(opts.angle)})` : null;
175101
- const snapshotDir = opts.outputDir ?? join127(projectDir, "snapshots");
175225
+ const snapshotDir = opts.outputDir ?? join128(projectDir, "snapshots");
175102
175226
  mkdirSync56(snapshotDir, { recursive: true });
175103
175227
  try {
175104
175228
  const { readdirSync: readdirSync46 } = await import("fs");
175105
175229
  for (const file of readdirSync46(snapshotDir)) {
175106
175230
  if (/\.(png|jpg|jpeg)$/i.test(file)) {
175107
- rmSync37(join127(snapshotDir, file), { force: true });
175231
+ rmSync38(join128(snapshotDir, file), { force: true });
175108
175232
  }
175109
175233
  }
175110
175234
  } catch {
@@ -175238,7 +175362,7 @@ async function captureSnapshots(projectDir, opts) {
175238
175362
  const timeLabel = formatSnapshotTimestamp(time);
175239
175363
  const index = String(i).padStart(2, "0");
175240
175364
  const filename = `frame-${index}-at-${timeLabel}.png`;
175241
- const framePath = join127(snapshotDir, filename);
175365
+ const framePath = join128(snapshotDir, filename);
175242
175366
  if (opts.zoom) {
175243
175367
  const canvas = await page.evaluate(() => ({
175244
175368
  width: window.innerWidth,
@@ -175267,9 +175391,9 @@ async function captureSnapshots(projectDir, opts) {
175267
175391
  ` ${c.warn("\u26A0")} --against has no frame at ${timeLabel} \u2014 reference pair skipped`
175268
175392
  );
175269
175393
  } else {
175270
- const refPath = join127(snapshotDir, `ref-${index}-at-${timeLabel}.png`);
175394
+ const refPath = join128(snapshotDir, `ref-${index}-at-${timeLabel}.png`);
175271
175395
  writeFileSync48(refPath, refPng);
175272
- const pairPath = join127(snapshotDir, `pair-${index}-at-${timeLabel}.jpg`);
175396
+ const pairPath = join128(snapshotDir, `pair-${index}-at-${timeLabel}.jpg`);
175273
175397
  const { createContactSheet: createContactSheet2 } = await Promise.resolve().then(() => (init_contactSheet(), contactSheet_exports));
175274
175398
  await createContactSheet2([framePath, refPath], pairPath, {
175275
175399
  cols: 2,
@@ -175436,7 +175560,7 @@ var init_snapshot = __esm({
175436
175560
  const angleLabel = camera && (camera.yaw !== 0 || camera.pitch !== 0) ? ` ${c.dim(`(angle yaw ${camera.yaw}\xB0 pitch ${camera.pitch}\xB0)`)}` : "";
175437
175561
  console.log(`${c.accent("\u25C6")} Capturing ${label2} from ${c.accent(project.name)}${angleLabel}`);
175438
175562
  try {
175439
- const snapshotDir = args.output ? resolve72(String(args.output)) : join127(project.dir, "snapshots");
175563
+ const snapshotDir = args.output ? resolve72(String(args.output)) : join128(project.dir, "snapshots");
175440
175564
  const paths = await captureSnapshots(project.dir, {
175441
175565
  frames,
175442
175566
  timeout,
@@ -175473,7 +175597,7 @@ ${c.success("\u25C7")} ${paths.length} snapshots saved to ${args.output ? snaps
175473
175597
  const { createSnapshotContactSheet: createSnapshotContactSheet2 } = await Promise.resolve().then(() => (init_contactSheet(), contactSheet_exports));
175474
175598
  const sheets = await createSnapshotContactSheet2(
175475
175599
  snapshotDir,
175476
- join127(snapshotDir, "contact-sheet.jpg")
175600
+ join128(snapshotDir, "contact-sheet.jpg")
175477
175601
  );
175478
175602
  if (sheets.length > 0) {
175479
175603
  const label3 = sheets.length === 1 ? "contact-sheet.jpg" : `contact-sheet-1..${sheets.length}.jpg`;
@@ -175508,8 +175632,8 @@ ${c.success("\u25C7")} ${paths.length} snapshots saved to ${args.output ? snaps
175508
175632
  }
175509
175633
  const results = await Promise.allSettled(
175510
175634
  paths.map(async (p2) => {
175511
- const filename = basename28(p2);
175512
- const filePath = join127(snapshotDir, filename);
175635
+ const filename = basename29(p2);
175636
+ const filePath = join128(snapshotDir, filename);
175513
175637
  if (!existsSync112(filePath)) return { filename, desc: "file not found" };
175514
175638
  const raw = readFileSync86(filePath);
175515
175639
  let imageData;
@@ -175547,7 +175671,7 @@ ${c.success("\u25C7")} ${paths.length} snapshots saved to ${args.output ? snaps
175547
175671
  descriptions.push(`## (error)`, `Gemini call failed: ${errMsg.slice(0, 120)}`, ``);
175548
175672
  }
175549
175673
  }
175550
- const descPath = join127(snapshotDir, "descriptions.md");
175674
+ const descPath = join128(snapshotDir, "descriptions.md");
175551
175675
  writeFileSync48(descPath, descriptions.join("\n"));
175552
175676
  console.log(` ${c.dim("descriptions.md")} (Gemini frame analysis)`);
175553
175677
  }
@@ -175601,7 +175725,7 @@ var init_pathArgs = __esm({
175601
175725
 
175602
175726
  // ../core/dist/mediaGradeAnalyzer.js
175603
175727
  import { execFileSync as execFileSync18 } from "child_process";
175604
- import { basename as basename29, extname as extname20 } from "path";
175728
+ import { basename as basename30, extname as extname20 } from "path";
175605
175729
  function clamp2(value, key2) {
175606
175730
  const limit = ADJUST_LIMITS2[key2];
175607
175731
  if (!Number.isFinite(value))
@@ -175881,7 +176005,7 @@ __export(media_treatment_exports, {
175881
176005
  resolveMediaTreatmentSource: () => resolveMediaTreatmentSource
175882
176006
  });
175883
176007
  import { existsSync as existsSync113, readFileSync as readFileSync87, writeFileSync as writeFileSync49 } from "fs";
175884
- import { join as join128, relative as relative34, resolve as resolve74 } from "path";
176008
+ import { join as join129, relative as relative34, resolve as resolve74 } from "path";
175885
176009
  function getMediaTreatmentCapabilityOverview() {
175886
176010
  const capabilities = getHfColorGradingCapabilities();
175887
176011
  const family = (id, label2, description) => ({
@@ -176259,7 +176383,7 @@ function resolveMediaTreatmentSource(projectDir, compositionFile, source) {
176259
176383
  }
176260
176384
  const cleanSource = cleanAssetUrl(sourceUrl);
176261
176385
  if (!cleanSource) throw new Error("Selected media has no analyzable local src");
176262
- const projectRelative = cleanSource.startsWith("/") ? cleanSource : rewriteAssetPath(compositionFile, cleanSource, (path) => existsSync113(join128(projectDir, path)));
176386
+ const projectRelative = cleanSource.startsWith("/") ? cleanSource : rewriteAssetPath(compositionFile, cleanSource, (path) => existsSync113(join129(projectDir, path)));
176263
176387
  const asset = resolveExistingLocalAsset(projectDir, projectRelative);
176264
176388
  if (!asset) throw new Error(`Media file not found: ${source}`);
176265
176389
  return asset.resolved;
@@ -176537,13 +176661,13 @@ import {
176537
176661
  copyFileSync as copyFileSync10,
176538
176662
  existsSync as existsSync114,
176539
176663
  mkdirSync as mkdirSync57,
176540
- mkdtempSync as mkdtempSync25,
176664
+ mkdtempSync as mkdtempSync26,
176541
176665
  readFileSync as readFileSync88,
176542
- rmSync as rmSync38,
176666
+ rmSync as rmSync39,
176543
176667
  writeFileSync as writeFileSync50
176544
176668
  } from "fs";
176545
176669
  import { tmpdir as tmpdir17 } from "os";
176546
- import { basename as basename30, dirname as dirname62, extname as extname21, join as join129, resolve as resolve75 } from "path";
176670
+ import { basename as basename31, dirname as dirname63, extname as extname21, join as join130, resolve as resolve75 } from "path";
176547
176671
  import sharp2 from "sharp";
176548
176672
  function isRecord15(value) {
176549
176673
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -176661,7 +176785,7 @@ function resolveLutCells(luts) {
176661
176785
  throw new Error("--luts must include at least one LUT path");
176662
176786
  }
176663
176787
  return paths.map(
176664
- (lutPath) => validateCell(basename30(lutPath, extname21(lutPath)), { lut: { src: lutPath } })
176788
+ (lutPath) => validateCell(basename31(lutPath, extname21(lutPath)), { lut: { src: lutPath } })
176665
176789
  );
176666
176790
  }
176667
176791
  function prependBaselineCell(cells) {
@@ -176799,10 +176923,10 @@ function frameFileNameForPath(framePath) {
176799
176923
  return "frame.png";
176800
176924
  }
176801
176925
  async function prepareGradeCompareTempProject(opts) {
176802
- const tempDir = mkdtempSync25(join129(tmpdir17(), "hf-grade-compare-"));
176926
+ const tempDir = mkdtempSync26(join130(tmpdir17(), "hf-grade-compare-"));
176803
176927
  try {
176804
176928
  const frameFileName2 = opts.frameFileName ?? frameFileNameForPath(opts.framePath);
176805
- writeFileSync50(join129(tempDir, frameFileName2), opts.frameBuffer);
176929
+ writeFileSync50(join130(tempDir, frameFileName2), opts.frameBuffer);
176806
176930
  let lutIndex = 0;
176807
176931
  const stagedCells = opts.cells.map((cell) => {
176808
176932
  const lutSrc = lutSrcFromGrading(cell.grading);
@@ -176822,7 +176946,7 @@ async function prepareGradeCompareTempProject(opts) {
176822
176946
  const lutExt = extname21(sourcePath) || ".cube";
176823
176947
  const stagedName = `lut-${lutIndex}${lutExt}`;
176824
176948
  lutIndex += 1;
176825
- copyFileSync10(sourcePath, join129(tempDir, stagedName));
176949
+ copyFileSync10(sourcePath, join130(tempDir, stagedName));
176826
176950
  return {
176827
176951
  label: cell.label,
176828
176952
  grading: rewriteGradingLutSrc(cell.grading, stagedName)
@@ -176834,10 +176958,10 @@ async function prepareGradeCompareTempProject(opts) {
176834
176958
  frameWidth: opts.frameWidth,
176835
176959
  frameHeight: opts.frameHeight
176836
176960
  });
176837
- writeFileSync50(join129(tempDir, "index.html"), html);
176961
+ writeFileSync50(join130(tempDir, "index.html"), html);
176838
176962
  return { tempDir, html, cells: stagedCells };
176839
176963
  } catch (err) {
176840
- rmSync38(tempDir, { recursive: true, force: true });
176964
+ rmSync39(tempDir, { recursive: true, force: true });
176841
176965
  throw err;
176842
176966
  }
176843
176967
  }
@@ -176846,8 +176970,8 @@ function isVideoPath2(filePath) {
176846
176970
  return [".mp4", ".mov", ".m4v", ".webm", ".mkv", ".avi", ".mpeg", ".mpg", ".ogv"].includes(ext);
176847
176971
  }
176848
176972
  async function extractVideoFrameToBuffer2(videoPath) {
176849
- const tmp = mkdtempSync25(join129(tmpdir17(), "hf-grade-compare-frame-"));
176850
- const outPath = join129(tmp, "frame.png");
176973
+ const tmp = mkdtempSync26(join130(tmpdir17(), "hf-grade-compare-frame-"));
176974
+ const outPath = join130(tmp, "frame.png");
176851
176975
  try {
176852
176976
  const ffmpegPath = findFFmpeg();
176853
176977
  if (!ffmpegPath) return null;
@@ -176876,7 +177000,7 @@ async function extractVideoFrameToBuffer2(videoPath) {
176876
177000
  }
176877
177001
  return readFileSync88(outPath);
176878
177002
  } finally {
176879
- rmSync38(tmp, { recursive: true, force: true });
177003
+ rmSync39(tmp, { recursive: true, force: true });
176880
177004
  }
176881
177005
  }
176882
177006
  async function loadReferenceFrame(framePath) {
@@ -176902,7 +177026,7 @@ async function captureGradeCompareSheet(projectDir, timeoutMs) {
176902
177026
  const { bundleToSingleHtml: bundleToSingleHtml2 } = await Promise.resolve().then(() => (init_compiler(), compiler_exports));
176903
177027
  const html = await bundleToSingleHtml2(projectDir);
176904
177028
  const server = await serveStaticProjectHtml(projectDir, html);
176905
- const sheetPath = join129(projectDir, "grade-compare.png");
177029
+ const sheetPath = join130(projectDir, "grade-compare.png");
176906
177030
  try {
176907
177031
  const {
176908
177032
  browser: chromeBrowser,
@@ -177025,7 +177149,7 @@ var init_grade_compare = __esm({
177025
177149
  }
177026
177150
  if (!parsed.json) {
177027
177151
  console.log(
177028
- `${c.accent("\u25C6")} Rendering ${cells.length} grade candidates from ${c.accent(basename30(parsed.framePath))}`
177152
+ `${c.accent("\u25C6")} Rendering ${cells.length} grade candidates from ${c.accent(basename31(parsed.framePath))}`
177029
177153
  );
177030
177154
  }
177031
177155
  const frame = await loadReferenceFrame(parsed.framePath);
@@ -177043,7 +177167,7 @@ var init_grade_compare = __esm({
177043
177167
  prepared.tempDir,
177044
177168
  parsed.timeoutMs
177045
177169
  );
177046
- mkdirSync57(dirname62(parsed.outPath), { recursive: true });
177170
+ mkdirSync57(dirname63(parsed.outPath), { recursive: true });
177047
177171
  copyFileSync10(tempSheet, parsed.outPath);
177048
177172
  trackCompareSheet({
177049
177173
  command: "grade-compare",
@@ -177070,7 +177194,7 @@ ${c.error("\u2717")} Grade compare failed: ${message}`);
177070
177194
  failCommand();
177071
177195
  } finally {
177072
177196
  if (preparedDir) {
177073
- rmSync38(preparedDir, { recursive: true, force: true });
177197
+ rmSync39(preparedDir, { recursive: true, force: true });
177074
177198
  }
177075
177199
  }
177076
177200
  }
@@ -177088,12 +177212,12 @@ __export(compare_exports, {
177088
177212
  parseCompareArgs: () => parseCompareArgs,
177089
177213
  prepareCompareVariantProjects: () => prepareCompareVariantProjects
177090
177214
  });
177091
- import { cpSync as cpSync6, existsSync as existsSync115, mkdirSync as mkdirSync58, mkdtempSync as mkdtempSync26, renameSync as renameSync21, rmSync as rmSync39, statSync as statSync43 } from "fs";
177215
+ import { cpSync as cpSync6, existsSync as existsSync115, mkdirSync as mkdirSync58, mkdtempSync as mkdtempSync27, renameSync as renameSync23, rmSync as rmSync40, statSync as statSync43 } from "fs";
177092
177216
  import { tmpdir as tmpdir18 } from "os";
177093
- import { basename as basename31, dirname as dirname63, extname as extname23, join as join130 } from "path";
177217
+ import { basename as basename32, dirname as dirname64, extname as extname23, join as join131 } from "path";
177094
177218
  function defaultLabelForPath(input2) {
177095
- const name = basename31(input2);
177096
- return extname23(name).toLowerCase() === ".html" ? basename31(name, extname23(name)) : name;
177219
+ const name = basename32(input2);
177220
+ return extname23(name).toLowerCase() === ".html" ? basename32(name, extname23(name)) : name;
177097
177221
  }
177098
177222
  function parsePathArgs(args) {
177099
177223
  return (args._ ?? []).filter((value) => typeof value === "string").map((value) => value.trim()).filter(Boolean);
@@ -177189,18 +177313,18 @@ function inputError(variant) {
177189
177313
  );
177190
177314
  }
177191
177315
  function stageHtmlVariant(variant) {
177192
- const stagedDir = mkdtempSync26(join130(tmpdir18(), "hf-compare-variant-"));
177316
+ const stagedDir = mkdtempSync27(join131(tmpdir18(), "hf-compare-variant-"));
177193
177317
  try {
177194
- cpSync6(dirname63(variant.inputPath), stagedDir, {
177318
+ cpSync6(dirname64(variant.inputPath), stagedDir, {
177195
177319
  recursive: true,
177196
177320
  filter: (src) => {
177197
- const base2 = basename31(src);
177321
+ const base2 = basename32(src);
177198
177322
  return base2 !== "node_modules" && base2 !== ".git";
177199
177323
  }
177200
177324
  });
177201
- const sourceName = basename31(variant.inputPath);
177325
+ const sourceName = basename32(variant.inputPath);
177202
177326
  if (sourceName !== "index.html") {
177203
- renameSync21(join130(stagedDir, sourceName), join130(stagedDir, "index.html"));
177327
+ renameSync23(join131(stagedDir, sourceName), join131(stagedDir, "index.html"));
177204
177328
  }
177205
177329
  return {
177206
177330
  ...variant,
@@ -177208,7 +177332,7 @@ function stageHtmlVariant(variant) {
177208
177332
  stagedDir
177209
177333
  };
177210
177334
  } catch (err) {
177211
- rmSync39(stagedDir, { recursive: true, force: true });
177335
+ rmSync40(stagedDir, { recursive: true, force: true });
177212
177336
  throw err;
177213
177337
  }
177214
177338
  }
@@ -177220,7 +177344,7 @@ function prepareCompareVariantProjects(variants) {
177220
177344
  throw inputError(variant);
177221
177345
  }
177222
177346
  const stat = statSync43(variant.inputPath);
177223
- if (stat.isDirectory() && existsSync115(join130(variant.inputPath, "index.html"))) {
177347
+ if (stat.isDirectory() && existsSync115(join131(variant.inputPath, "index.html"))) {
177224
177348
  prepared.push({
177225
177349
  ...variant,
177226
177350
  projectDir: variant.inputPath
@@ -177237,7 +177361,7 @@ function prepareCompareVariantProjects(variants) {
177237
177361
  } catch (err) {
177238
177362
  for (const variant of prepared) {
177239
177363
  if (variant.stagedDir) {
177240
- rmSync39(variant.stagedDir, { recursive: true, force: true });
177364
+ rmSync40(variant.stagedDir, { recursive: true, force: true });
177241
177365
  }
177242
177366
  }
177243
177367
  throw err;
@@ -177246,7 +177370,7 @@ function prepareCompareVariantProjects(variants) {
177246
177370
  function cleanupPreparedCompareVariants(variants) {
177247
177371
  for (const variant of variants) {
177248
177372
  if (variant.stagedDir) {
177249
- rmSync39(variant.stagedDir, { recursive: true, force: true });
177373
+ rmSync40(variant.stagedDir, { recursive: true, force: true });
177250
177374
  }
177251
177375
  }
177252
177376
  }
@@ -177286,13 +177410,13 @@ async function renderCompareSheet(parsed) {
177286
177410
  const capResult = capCompareVariants(parsed.variants);
177287
177411
  const variants = capResult.variants;
177288
177412
  const prepared = prepareCompareVariantProjects(variants);
177289
- const frameDir = mkdtempSync26(join130(tmpdir18(), "hf-compare-frames-"));
177413
+ const frameDir = mkdtempSync27(join131(tmpdir18(), "hf-compare-frames-"));
177290
177414
  const framePaths = [];
177291
177415
  try {
177292
177416
  let renderReadyTimedOut = false;
177293
177417
  for (let i = 0; i < prepared.length; i++) {
177294
177418
  const variant = prepared[i];
177295
- const framePath = join130(frameDir, `variant-${String(i + 1).padStart(2, "0")}.png`);
177419
+ const framePath = join131(frameDir, `variant-${String(i + 1).padStart(2, "0")}.png`);
177296
177420
  const rendered = await renderCompareVariant(variant, {
177297
177421
  atSeconds: parsed.atSeconds,
177298
177422
  framePath,
@@ -177301,7 +177425,7 @@ async function renderCompareSheet(parsed) {
177301
177425
  framePaths.push(rendered.framePath);
177302
177426
  renderReadyTimedOut = renderReadyTimedOut || rendered.renderReadyTimedOut;
177303
177427
  }
177304
- mkdirSync58(dirname63(parsed.outPath), { recursive: true });
177428
+ mkdirSync58(dirname64(parsed.outPath), { recursive: true });
177305
177429
  await createContactSheet(framePaths, parsed.outPath, {
177306
177430
  cols: parsed.cols ?? defaultCompareCols(framePaths.length),
177307
177431
  maxImages: framePaths.length,
@@ -177318,7 +177442,7 @@ async function renderCompareSheet(parsed) {
177318
177442
  return buildCompareSuccessPayload(parsed.outPath, variants, capResult);
177319
177443
  } finally {
177320
177444
  cleanupPreparedCompareVariants(prepared);
177321
- rmSync39(frameDir, { recursive: true, force: true });
177445
+ rmSync40(frameDir, { recursive: true, force: true });
177322
177446
  }
177323
177447
  }
177324
177448
  function printJson2(payload) {
@@ -177674,9 +177798,9 @@ function captureFontExtension(bytes) {
177674
177798
  }
177675
177799
  }
177676
177800
  function preferredFontFilename(url, extension) {
177677
- const basename34 = new URL(url).pathname.split("/").pop() ?? "";
177678
- const dot = basename34.lastIndexOf(".");
177679
- const stem = dot > 0 ? basename34.slice(0, dot) : basename34;
177801
+ const basename35 = new URL(url).pathname.split("/").pop() ?? "";
177802
+ const dot = basename35.lastIndexOf(".");
177803
+ const stem = dot > 0 ? basename35.slice(0, dot) : basename35;
177680
177804
  if (/^[a-zA-Z0-9_-][a-zA-Z0-9_.-]{0,119}$/.test(stem) && !/[. ]$/.test(stem) && !/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(stem)) {
177681
177805
  return `${stem}${extension}`;
177682
177806
  }
@@ -177857,7 +177981,7 @@ var init_captureImageValidation = __esm({
177857
177981
 
177858
177982
  // src/capture/assetDownloader.ts
177859
177983
  import { mkdirSync as mkdirSync59 } from "fs";
177860
- import { join as join131, extname as extname24 } from "path";
177984
+ import { join as join133, extname as extname24 } from "path";
177861
177985
  import { createHash as createHash27 } from "crypto";
177862
177986
  function noDrops() {
177863
177987
  return { "size-floor": 0, "budget-exhausted": 0, "cap-reached": 0, unavailable: 0 };
@@ -177919,7 +178043,7 @@ async function fetchAndInspectIcon(icon, rank, stem, outputDir, timeoutMs, byteB
177919
178043
  const ext = await captureImageExtension(buffer);
177920
178044
  if (!ext) return null;
177921
178045
  const file = `assets/${stem}${ext}`;
177922
- writeCaptureFileSync(join131(outputDir, file), buffer);
178046
+ writeCaptureFileSync(join133(outputDir, file), buffer);
177923
178047
  const verdict = await classifyIcon(buffer, ext);
177924
178048
  return {
177925
178049
  buffer,
@@ -177941,7 +178065,7 @@ function promoteHeadline(manifest, bytesByFile, outputDir) {
177941
178065
  )[0];
177942
178066
  if (!chosen) return null;
177943
178067
  const file = `assets/favicon${extname24(chosen.file)}`;
177944
- writeCaptureFileSync(join131(outputDir, file), bytesByFile.get(chosen.file));
178068
+ writeCaptureFileSync(join133(outputDir, file), bytesByFile.get(chosen.file));
177945
178069
  manifest.headline = {
177946
178070
  file,
177947
178071
  source: chosen.file,
@@ -177996,13 +178120,13 @@ async function downloadDeclaredIcons(faviconLinks, outputDir, drops, options) {
177996
178120
  }
177997
178121
  async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks, options = {}) {
177998
178122
  options = { ...options, byteBudget: options.byteBudget ?? createCaptureDownloadBudget() };
177999
- const assetsDir = join131(outputDir, "assets");
178123
+ const assetsDir = join133(outputDir, "assets");
178000
178124
  mkdirSync59(assetsDir, { recursive: true });
178001
178125
  const assets = [];
178002
178126
  const drops = noDrops();
178003
178127
  const downloadedUrls = /* @__PURE__ */ new Set();
178004
178128
  let icons = emptyIconManifest();
178005
- mkdirSync59(join131(outputDir, "assets", "svgs"), { recursive: true });
178129
+ mkdirSync59(join133(outputDir, "assets", "svgs"), { recursive: true });
178006
178130
  const usedSvgNames = /* @__PURE__ */ new Set();
178007
178131
  const MAX_INLINE_SVGS = 30;
178008
178132
  drops["cap-reached"] += Math.max(0, tokens.svgs.length - MAX_INLINE_SVGS);
@@ -178024,7 +178148,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks,
178024
178148
  const name = `${finalSlug}.svg`;
178025
178149
  const localPath = `assets/svgs/${name}`;
178026
178150
  try {
178027
- writeCaptureFileSync(join131(outputDir, localPath), svgFile, "utf-8");
178151
+ writeCaptureFileSync(join133(outputDir, localPath), svgFile, "utf-8");
178028
178152
  assets.push({ url: "", localPath, type: "svg" });
178029
178153
  } catch {
178030
178154
  drops.unavailable++;
@@ -178114,7 +178238,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks,
178114
178238
  const name = `${slug}${ext}`;
178115
178239
  usedNames.add(slug);
178116
178240
  const localPath = `assets/${name}`;
178117
- writeCaptureFileSync(join131(outputDir, localPath), buffer);
178241
+ writeCaptureFileSync(join133(outputDir, localPath), buffer);
178118
178242
  assets.push({ url, localPath, type: "image" });
178119
178243
  imgIdx++;
178120
178244
  } catch {
@@ -178141,7 +178265,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks,
178141
178265
  drops["size-floor"]++;
178142
178266
  } else {
178143
178267
  const localPath = `assets/og-image${ext}`;
178144
- writeCaptureFileSync(join131(outputDir, localPath), buffer);
178268
+ writeCaptureFileSync(join133(outputDir, localPath), buffer);
178145
178269
  assets.push({ url: tokens.ogImage, localPath, type: "image" });
178146
178270
  }
178147
178271
  }
@@ -178167,7 +178291,7 @@ function normalizeUrl(u) {
178167
178291
  }
178168
178292
  async function downloadAndRewriteFonts(css, outputDir, options = {}) {
178169
178293
  options = { ...options, byteBudget: options.byteBudget ?? createCaptureDownloadBudget() };
178170
- const assetsDir = join131(outputDir, "assets", "fonts");
178294
+ const assetsDir = join133(outputDir, "assets", "fonts");
178171
178295
  mkdirSync59(assetsDir, { recursive: true });
178172
178296
  const drops = noDrops();
178173
178297
  const fontUrlRegex = /url\(['"]?(https?:\/\/[^'")\s]+\.(?:woff2?|ttf|otf)[^'")\s]*?)['"]?\)/g;
@@ -178225,7 +178349,7 @@ async function downloadAndRewriteFonts(css, outputDir, options = {}) {
178225
178349
  const extension = buffer && captureFontExtension(buffer);
178226
178350
  if (buffer && extension) {
178227
178351
  const filename = captureFontFilename(fontUrl, extension, usedFontNames);
178228
- const localPath = join131(assetsDir, filename);
178352
+ const localPath = join133(assetsDir, filename);
178229
178353
  const relativePath = `assets/fonts/${filename}`;
178230
178354
  writeCaptureFileSync(localPath, buffer);
178231
178355
  rewritten = rewritten.split(fontUrl).join(relativePath);
@@ -178349,7 +178473,7 @@ __export(video_exports, {
178349
178473
  safeFilename: () => safeFilename
178350
178474
  });
178351
178475
  import { createWriteStream as createWriteStream3, existsSync as existsSync116, mkdirSync as mkdirSync60, readFileSync as readFileSync89, unlinkSync as unlinkSync11 } from "fs";
178352
- import { resolve as resolve76, join as join133, basename as basename32 } from "path";
178476
+ import { resolve as resolve76, join as join134, basename as basename33 } from "path";
178353
178477
  async function streamToFile(url, destPath) {
178354
178478
  const r = await safeFetch(url, {
178355
178479
  signal: AbortSignal.timeout(12e4),
@@ -178427,9 +178551,9 @@ function safeFilename(name) {
178427
178551
  return decoded.replace(/[^A-Za-z0-9._-]+/g, "_");
178428
178552
  }
178429
178553
  function findFilenameCollision(manifest, selected) {
178430
- const selectedName = safeFilename(selected.filename || basename32(selected.url));
178554
+ const selectedName = safeFilename(selected.filename || basename33(selected.url));
178431
178555
  return manifest.filter(
178432
- (e2) => e2.index !== selected.index && safeFilename(e2.filename || basename32(e2.url)) === selectedName
178556
+ (e2) => e2.index !== selected.index && safeFilename(e2.filename || basename33(e2.url)) === selectedName
178433
178557
  );
178434
178558
  }
178435
178559
  function pickManifestEntry(manifest, args) {
@@ -178468,8 +178592,8 @@ function pickManifestEntry(manifest, args) {
178468
178592
  }
178469
178593
  async function runVideoMode(args) {
178470
178594
  const projectDir = resolve76(args.project);
178471
- const directPath = join133(projectDir, "extracted", "video-manifest.json");
178472
- const w2hPath = join133(projectDir, "capture", "extracted", "video-manifest.json");
178595
+ const directPath = join134(projectDir, "extracted", "video-manifest.json");
178596
+ const w2hPath = join134(projectDir, "capture", "extracted", "video-manifest.json");
178473
178597
  const manifestPath3 = existsSync116(directPath) ? directPath : w2hPath;
178474
178598
  const isW2hLayout = manifestPath3 === w2hPath;
178475
178599
  if (!existsSync116(manifestPath3)) {
@@ -178518,15 +178642,15 @@ async function runVideoMode(args) {
178518
178642
  const collisions = findFilenameCollision(manifest, entry);
178519
178643
  if (collisions.length > 0) {
178520
178644
  console.error(
178521
- `${c.error("\u2717")} filename "${safeFilename(entry.filename || basename32(entry.url))}" collides with manifest entr${collisions.length === 1 ? "y" : "ies"} ${collisions.map((co) => `[${co.index}]`).join(", ")}. Refusing to download \u2014 the on-disk file's bytes would not match the requested entry.`
178645
+ `${c.error("\u2717")} filename "${safeFilename(entry.filename || basename33(entry.url))}" collides with manifest entr${collisions.length === 1 ? "y" : "ies"} ${collisions.map((co) => `[${co.index}]`).join(", ")}. Refusing to download \u2014 the on-disk file's bytes would not match the requested entry.`
178522
178646
  );
178523
178647
  setCommandExitCode(1);
178524
178648
  return;
178525
178649
  }
178526
- const outDir = isW2hLayout ? join133(projectDir, "capture", "assets", "videos") : join133(projectDir, "assets", "videos");
178650
+ const outDir = isW2hLayout ? join134(projectDir, "capture", "assets", "videos") : join134(projectDir, "assets", "videos");
178527
178651
  mkdirSync60(outDir, { recursive: true });
178528
- const fname = safeFilename(entry.filename || basename32(entry.url));
178529
- const outPath = join133(outDir, fname);
178652
+ const fname = safeFilename(entry.filename || basename33(entry.url));
178653
+ const outPath = join134(outDir, fname);
178530
178654
  const relPath = isW2hLayout ? `capture/assets/videos/${fname}` : `assets/videos/${fname}`;
178531
178655
  console.log(
178532
178656
  `${c.accent("\u25B8")} downloading [${entry.index}] ${entry.filename} (${entry.sourceWidth || entry.width}\xD7${entry.sourceHeight || entry.height})`
@@ -178567,7 +178691,7 @@ var init_video = __esm({
178567
178691
 
178568
178692
  // src/capture/partialCapture.ts
178569
178693
  import { mkdirSync as mkdirSync61 } from "fs";
178570
- import { join as join134 } from "path";
178694
+ import { join as join135 } from "path";
178571
178695
  function serializeTokensForCapture(tokens) {
178572
178696
  const tokensForDisk = {
178573
178697
  ...tokens,
@@ -178616,15 +178740,15 @@ function createPartialCaptureState(opts) {
178616
178740
  function writePartialCaptureBundle(opts, state, lastPhase) {
178617
178741
  const hostname2 = new URL(opts.url).hostname.replace(/^www\./, "");
178618
178742
  mkdirSync61(opts.outputDir, { recursive: true });
178619
- const extractedDir = join134(opts.outputDir, "extracted");
178743
+ const extractedDir = join135(opts.outputDir, "extracted");
178620
178744
  mkdirSync61(extractedDir, { recursive: true });
178621
- writeCaptureFileSync(join134(extractedDir, "tokens.json"), serializeTokensForCapture(state.tokens));
178745
+ writeCaptureFileSync(join135(extractedDir, "tokens.json"), serializeTokensForCapture(state.tokens));
178622
178746
  writeCaptureFileSync(
178623
- join134(extractedDir, "design-styles.json"),
178747
+ join135(extractedDir, "design-styles.json"),
178624
178748
  JSON.stringify(state.designStyles, null, 2)
178625
178749
  );
178626
- writeCaptureFileSync(join134(extractedDir, "page.html"), state.pageHtml);
178627
- const metaPath = join134(opts.outputDir, "meta.json");
178750
+ writeCaptureFileSync(join135(extractedDir, "page.html"), state.pageHtml);
178751
+ const metaPath = join135(opts.outputDir, "meta.json");
178628
178752
  try {
178629
178753
  writeCaptureFileSync(
178630
178754
  metaPath,
@@ -179263,7 +179387,7 @@ __export(agentPromptGenerator_exports, {
179263
179387
  generateAgentPrompt: () => generateAgentPrompt
179264
179388
  });
179265
179389
  import { readdirSync as readdirSync40, existsSync as existsSync117 } from "fs";
179266
- import { join as join135 } from "path";
179390
+ import { join as join136 } from "path";
179267
179391
  function inferColorRole(hex) {
179268
179392
  const r = parseInt(hex.slice(1, 3), 16) / 255;
179269
179393
  const g = parseInt(hex.slice(3, 5), 16) / 255;
@@ -179282,9 +179406,9 @@ function inferColorRole(hex) {
179282
179406
  }
179283
179407
  function generateAgentPrompt(outputDir, url, tokens, _animations, hasScreenshot, hasLottie, hasShaders, _catalogedAssets, _detectedLibraries) {
179284
179408
  const prompt = buildPrompt(outputDir, url, tokens, hasScreenshot, hasLottie, hasShaders);
179285
- writeCaptureFileSync(join135(outputDir, "AGENTS.md"), prompt, "utf-8");
179286
- writeCaptureFileSync(join135(outputDir, "CLAUDE.md"), prompt, "utf-8");
179287
- writeCaptureFileSync(join135(outputDir, ".cursorrules"), prompt, "utf-8");
179409
+ writeCaptureFileSync(join136(outputDir, "AGENTS.md"), prompt, "utf-8");
179410
+ writeCaptureFileSync(join136(outputDir, "CLAUDE.md"), prompt, "utf-8");
179411
+ writeCaptureFileSync(join136(outputDir, ".cursorrules"), prompt, "utf-8");
179288
179412
  }
179289
179413
  function buildPrompt(outputDir, url, tokens, hasScreenshot, hasLottie, hasShaders) {
179290
179414
  const title = tokens.title || new URL(url).hostname.replace(/^www\./, "");
@@ -179293,7 +179417,7 @@ function buildPrompt(outputDir, url, tokens, hasScreenshot, hasLottie, hasShader
179293
179417
  (f) => f.family + (f.variable && f.weightRange ? ` (${f.weightRange[0]}-${f.weightRange[1]} variable)` : f.weights.length > 0 ? ` (${f.weights.join(",")})` : "")
179294
179418
  ).join(", ") || "none detected";
179295
179419
  function contactSheetRows(dir, baseFile, label2) {
179296
- const fullDir = join135(outputDir, dir);
179420
+ const fullDir = join136(outputDir, dir);
179297
179421
  if (!existsSync117(fullDir)) return [];
179298
179422
  const baseName = baseFile.replace(/\.jpg$/, "");
179299
179423
  const escapedBase = baseName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -179326,7 +179450,7 @@ function buildPrompt(outputDir, url, tokens, hasScreenshot, hasLottie, hasShader
179326
179450
  tableRows.push(
179327
179451
  `| \`extracted/tokens.json\` | Design tokens: ${tokens.colors.length} colors, ${tokens.fonts.length} fonts, ${tokens.headings?.length ?? 0} headings, ${tokens.ctas?.length ?? 0} CTAs |`
179328
179452
  );
179329
- if (existsSync117(join135(outputDir, "extracted", "design-styles.json"))) {
179453
+ if (existsSync117(join136(outputDir, "extracted", "design-styles.json"))) {
179330
179454
  tableRows.push(
179331
179455
  "| `extracted/design-styles.json` | Computed styles from live DOM: typography hierarchy, button/card/nav styles, spacing scale, border-radius, box shadows. Primary data source for DESIGN.md. |"
179332
179456
  );
@@ -179399,7 +179523,7 @@ var init_agentPromptGenerator = __esm({
179399
179523
 
179400
179524
  // src/capture/scaffolding.ts
179401
179525
  import { existsSync as existsSync118, readFileSync as readFileSync90 } from "fs";
179402
- import { join as join136, resolve as resolve77 } from "path";
179526
+ import { join as join137, resolve as resolve77 } from "path";
179403
179527
  function loadEnvFile(startDir) {
179404
179528
  try {
179405
179529
  let dir = resolve77(startDir);
@@ -179425,7 +179549,7 @@ function loadEnvFile(startDir) {
179425
179549
  }
179426
179550
  }
179427
179551
  async function generateProjectScaffold(outputDir, url, tokens, animationCatalog, hasScreenshots, hasLotties, hasShaders, catalogedAssets, progress, warnings, detectedLibraries) {
179428
- const metaPath = join136(outputDir, "meta.json");
179552
+ const metaPath = join137(outputDir, "meta.json");
179429
179553
  if (!existsSync118(metaPath)) {
179430
179554
  const hostname2 = new URL(url).hostname.replace(/^www\./, "");
179431
179555
  writeNewFileSync(
@@ -179566,10 +179690,10 @@ var init_navigateForCapture = __esm({
179566
179690
  });
179567
179691
 
179568
179692
  // src/capture/responseRecord.ts
179569
- import { join as join137 } from "path";
179693
+ import { join as join138 } from "path";
179570
179694
  function writeResponseRecord(extractedDir, record2) {
179571
179695
  writeCaptureFileSync(
179572
- join137(extractedDir, RESPONSE_RECORD_FILENAME),
179696
+ join138(extractedDir, RESPONSE_RECORD_FILENAME),
179573
179697
  JSON.stringify(record2, null, 2),
179574
179698
  "utf-8"
179575
179699
  );
@@ -179584,7 +179708,7 @@ var init_responseRecord = __esm({
179584
179708
  });
179585
179709
 
179586
179710
  // src/capture/navigationPhase.ts
179587
- import { join as join138 } from "path";
179711
+ import { join as join139 } from "path";
179588
179712
  async function navigatePage(context) {
179589
179713
  let navigation;
179590
179714
  try {
@@ -179638,7 +179762,7 @@ async function checkPageContent(context) {
179638
179762
  function persistNavigationResult(context, navigationResponse, pageContentCheck) {
179639
179763
  context.httpStatus = navigationResponse ? navigationResponse.status() : null;
179640
179764
  if (context.canWrite()) {
179641
- writeResponseRecord(join138(context.outputDir, "extracted"), { status: context.httpStatus });
179765
+ writeResponseRecord(join139(context.outputDir, "extracted"), { status: context.httpStatus });
179642
179766
  }
179643
179767
  const blockedReason = detectBlockedPage({
179644
179768
  httpStatus: context.httpStatus,
@@ -179683,7 +179807,7 @@ import {
179683
179807
  fstatSync as fstatSync9,
179684
179808
  closeSync as closeSync15
179685
179809
  } from "fs";
179686
- import { basename as basename33, join as join139 } from "path";
179810
+ import { basename as basename34, join as join140 } from "path";
179687
179811
  function resolveVisionPhaseCompletion(outcome, remainingMs) {
179688
179812
  if (outcome.budgetExhausted || remainingMs <= 0) {
179689
179813
  return { status: "degraded", reason: "budget-exhausted" };
@@ -179964,7 +180088,7 @@ async function captionImagesWithGemini(outputDir, progress, warnings, options =
179964
180088
  return response.text?.trim() || "";
179965
180089
  };
179966
180090
  }
179967
- const imageFiles = readdirSync41(join139(outputDir, "assets")).filter(
180091
+ const imageFiles = readdirSync41(join140(outputDir, "assets")).filter(
179968
180092
  (f) => /\.(png|jpg|jpeg|webp|gif)$/i.test(f)
179969
180093
  );
179970
180094
  const BATCH_SIZE = 20;
@@ -179992,7 +180116,7 @@ async function captionImagesWithGemini(outputDir, progress, warnings, options =
179992
180116
  const batch = imageFiles.slice(i, i + BATCH_SIZE);
179993
180117
  const results = await Promise.allSettled(
179994
180118
  batch.map(async (file) => {
179995
- const filePath = join139(outputDir, "assets", file);
180119
+ const filePath = join140(outputDir, "assets", file);
179996
180120
  const fd = openSync14(filePath, "r", 384);
179997
180121
  let buffer;
179998
180122
  try {
@@ -180031,11 +180155,11 @@ async function captionImagesWithGemini(outputDir, progress, warnings, options =
180031
180155
  `${Object.keys(geminiCaptions).length} images captioned with ${providerName}`
180032
180156
  );
180033
180157
  const svgFiles = [];
180034
- const assetsDir = join139(outputDir, "assets");
180158
+ const assetsDir = join140(outputDir, "assets");
180035
180159
  for (const f of readdirSync41(assetsDir)) {
180036
180160
  if (/\.svg$/i.test(f)) svgFiles.push({ file: f, relPath: f });
180037
180161
  }
180038
- const svgsSubdir = join139(assetsDir, "svgs");
180162
+ const svgsSubdir = join140(assetsDir, "svgs");
180039
180163
  if (existsSync119(svgsSubdir)) {
180040
180164
  for (const f of readdirSync41(svgsSubdir)) {
180041
180165
  if (/\.svg$/i.test(f)) svgFiles.push({ file: f, relPath: `svgs/${f}` });
@@ -180069,7 +180193,7 @@ async function captionImagesWithGemini(outputDir, progress, warnings, options =
180069
180193
  sharp5.concurrency(1);
180070
180194
  try {
180071
180195
  for (const { relPath } of batch) {
180072
- const filePath = join139(assetsDir, relPath);
180196
+ const filePath = join140(assetsDir, relPath);
180073
180197
  try {
180074
180198
  const svgSource = readFileSync91(filePath, "utf-8");
180075
180199
  const lightFillHits = (svgSource.match(/fill\s*=\s*["'](#fff(fff)?|white|#[ef][ef][ef]|#[ef]{6})["']/gi) || []).length;
@@ -180144,11 +180268,11 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
180144
180268
  const uncaptionedLines = [];
180145
180269
  const svgLines = [];
180146
180270
  const fontLines = [];
180147
- const assetsPath = join139(outputDir, "assets");
180271
+ const assetsPath = join140(outputDir, "assets");
180148
180272
  try {
180149
180273
  for (const file of readdirSync41(assetsPath)) {
180150
180274
  if (file === "svgs" || file === "fonts" || file === "lottie" || file === "videos") continue;
180151
- const filePath = join139(assetsPath, file);
180275
+ const filePath = join140(assetsPath, file);
180152
180276
  const stat = statSync44(filePath);
180153
180277
  if (!stat.isFile()) continue;
180154
180278
  const sizeKb = Math.round(stat.size / 1024);
@@ -180177,7 +180301,7 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
180177
180301
  } catch {
180178
180302
  }
180179
180303
  try {
180180
- const svgsPath = join139(assetsPath, "svgs");
180304
+ const svgsPath = join140(assetsPath, "svgs");
180181
180305
  for (const file of readdirSync41(svgsPath)) {
180182
180306
  if (!file.endsWith(".svg")) continue;
180183
180307
  const svgMatch = tokens.svgs.find(
@@ -180196,7 +180320,7 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
180196
180320
  } catch {
180197
180321
  }
180198
180322
  try {
180199
- const fontsPath = join139(assetsPath, "fonts");
180323
+ const fontsPath = join140(assetsPath, "fonts");
180200
180324
  for (const file of readdirSync41(fontsPath)) {
180201
180325
  fontLines.push(`fonts/${file} \u2014 font file`);
180202
180326
  }
@@ -180205,11 +180329,11 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
180205
180329
  const videoLines = [];
180206
180330
  try {
180207
180331
  const manifest = JSON.parse(
180208
- readFileSync91(join139(outputDir, "extracted", "video-manifest.json"), "utf-8")
180332
+ readFileSync91(join140(outputDir, "extracted", "video-manifest.json"), "utf-8")
180209
180333
  );
180210
180334
  for (const v2 of manifest) {
180211
180335
  if (!v2.localPath) continue;
180212
- const base2 = basename33(v2.localPath) || v2.filename || "";
180336
+ const base2 = basename34(v2.localPath) || v2.filename || "";
180213
180337
  if (!base2) continue;
180214
180338
  const desc = (v2.caption || v2.heading || "").trim().replace(/\s+/g, " ").slice(0, 140) || "motion clip";
180215
180339
  const dimW = v2.sourceWidth || v2.width;
@@ -181481,7 +181605,7 @@ var init_lottiePreviewRequests = __esm({
181481
181605
 
181482
181606
  // src/capture/mediaCapture.ts
181483
181607
  import { mkdirSync as mkdirSync63, readdirSync as readdirSync43, readFileSync as readFileSync93, statSync as statSync45 } from "fs";
181484
- import { join as join140, extname as extname25 } from "path";
181608
+ import { join as join141, extname as extname25 } from "path";
181485
181609
  function liveRemainingMs(budget, fallbackMs) {
181486
181610
  return budget.remainingMs?.() ?? fallbackMs;
181487
181611
  }
@@ -181519,7 +181643,7 @@ async function saveLottieAnimations(discoveredLotties, lottieDir, budget = {}) {
181519
181643
  if (savedHashes.has(hash2)) continue;
181520
181644
  savedHashes.add(hash2);
181521
181645
  if (!validLottieJson(jsonData)) continue;
181522
- writeCaptureFileSync(join140(lottieDir, `animation-${savedCount}.json`), jsonData, "utf-8");
181646
+ writeCaptureFileSync(join141(lottieDir, `animation-${savedCount}.json`), jsonData, "utf-8");
181523
181647
  savedCount++;
181524
181648
  }
181525
181649
  } catch {
@@ -181529,18 +181653,18 @@ async function saveLottieAnimations(discoveredLotties, lottieDir, budget = {}) {
181529
181653
  }
181530
181654
  async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir, budget = {}) {
181531
181655
  const manifest = [];
181532
- const previewDir = join140(lottieDir, "previews");
181656
+ const previewDir = join141(lottieDir, "previews");
181533
181657
  mkdirSync63(previewDir, { recursive: true });
181534
181658
  for (const file of readdirSync43(lottieDir)) {
181535
181659
  if (!file.endsWith(".json")) continue;
181536
181660
  if (liveRemainingMs(budget, 1) <= 0) break;
181537
181661
  try {
181538
- const raw = JSON.parse(readFileSync93(join140(lottieDir, file), "utf-8"));
181662
+ const raw = JSON.parse(readFileSync93(join141(lottieDir, file), "utf-8"));
181539
181663
  const fr = raw.fr || 30;
181540
181664
  const dur = ((raw.op || 0) - (raw.ip || 0)) / fr;
181541
181665
  const previewName = file.replace(".json", "-preview.png");
181542
181666
  let preview;
181543
- const fileSize = statSync45(join140(lottieDir, file)).size;
181667
+ const fileSize = statSync45(join141(lottieDir, file)).size;
181544
181668
  if (fileSize > 2e6) continue;
181545
181669
  let previewPage;
181546
181670
  try {
@@ -181549,7 +181673,7 @@ async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir, budget
181549
181673
  if (liveRemainingMs(budget, 1) <= 0) break;
181550
181674
  await guardLottiePreviewRequests(previewPage);
181551
181675
  await previewPage.setViewport({ width: 400, height: 400 });
181552
- const animData = JSON.parse(readFileSync93(join140(lottieDir, file), "utf-8"));
181676
+ const animData = JSON.parse(readFileSync93(join141(lottieDir, file), "utf-8"));
181553
181677
  const midFrame = Math.floor(((raw.op || 0) - (raw.ip || 0)) * 0.3);
181554
181678
  await previewPage.setContent(
181555
181679
  `<!DOCTYPE html>
@@ -181579,11 +181703,8 @@ async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir, budget
181579
181703
  await previewPage.waitForFunction(() => window.__READY === true, { timeout: 5e3 }).catch(() => {
181580
181704
  });
181581
181705
  if (liveRemainingMs(budget, 1) > 0) {
181582
- await previewPage.screenshot({
181583
- path: join140(previewDir, previewName),
181584
- type: "png",
181585
- omitBackground: true
181586
- });
181706
+ const shot = await previewPage.screenshot({ type: "png", omitBackground: true });
181707
+ writeCaptureFileSync(join141(previewDir, previewName), shot);
181587
181708
  preview = `assets/lottie/previews/${previewName}`;
181588
181709
  }
181589
181710
  } catch {
@@ -181606,7 +181727,7 @@ async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir, budget
181606
181727
  }
181607
181728
  if (manifest.length > 0) {
181608
181729
  writeCaptureFileSync(
181609
- join140(outputDir, "extracted", "lottie-manifest.json"),
181730
+ join141(outputDir, "extracted", "lottie-manifest.json"),
181610
181731
  JSON.stringify(manifest, null, 2),
181611
181732
  "utf-8"
181612
181733
  );
@@ -181644,7 +181765,7 @@ async function downloadVideoBody(srcUrl, filename, videosDir, timeoutMs) {
181644
181765
  }
181645
181766
  if (total < 1024) return null;
181646
181767
  const safe = /\.[a-z0-9]+$/i.test(filename) ? filename.replace(/[^\w.-]/g, "_") : `video${ext}`;
181647
- writeCaptureFileSync(join140(videosDir, safe), Buffer.concat(chunks));
181768
+ writeCaptureFileSync(join141(videosDir, safe), Buffer.concat(chunks));
181648
181769
  return `assets/videos/${safe}`;
181649
181770
  } catch {
181650
181771
  return null;
@@ -181711,9 +181832,9 @@ async function captureVideoManifest(page, outputDir, progress, opts) {
181711
181832
  }
181712
181833
  const merged = [...byKey.values()];
181713
181834
  if (merged.length === 0) return;
181714
- const videoManifestDir = join140(outputDir, "assets", "videos");
181835
+ const videoManifestDir = join141(outputDir, "assets", "videos");
181715
181836
  mkdirSync63(videoManifestDir, { recursive: true });
181716
- const previewDir = join140(videoManifestDir, "previews");
181837
+ const previewDir = join141(videoManifestDir, "previews");
181717
181838
  mkdirSync63(previewDir, { recursive: true });
181718
181839
  const videoManifest = [];
181719
181840
  const dlStart = Date.now();
@@ -181741,8 +181862,8 @@ async function captureVideoManifest(page, outputDir, progress, opts) {
181741
181862
  if (rect && rect.width >= 10) {
181742
181863
  await new Promise((r) => setTimeout(r, 200));
181743
181864
  if (liveRemainingMs(opts ?? {}, 1) > 0) {
181744
- await page.screenshot({
181745
- path: join140(previewDir, previewName),
181865
+ const shot = await page.screenshot({
181866
+ type: "png",
181746
181867
  clip: {
181747
181868
  x: Math.max(0, rect.x),
181748
181869
  y: Math.max(0, rect.y),
@@ -181750,6 +181871,7 @@ async function captureVideoManifest(page, outputDir, progress, opts) {
181750
181871
  height: Math.min(rect.height, 1080)
181751
181872
  }
181752
181873
  });
181874
+ writeCaptureFileSync(join141(previewDir, previewName), shot);
181753
181875
  preview = `assets/videos/previews/${previewName}`;
181754
181876
  }
181755
181877
  }
@@ -181779,7 +181901,7 @@ async function captureVideoManifest(page, outputDir, progress, opts) {
181779
181901
  }
181780
181902
  if (videoManifest.length > 0) {
181781
181903
  writeCaptureFileSync(
181782
- join140(outputDir, "extracted", "video-manifest.json"),
181904
+ join141(outputDir, "extracted", "video-manifest.json"),
181783
181905
  JSON.stringify(videoManifest, null, 2),
181784
181906
  "utf-8"
181785
181907
  );
@@ -181902,7 +182024,7 @@ __export(screenshotCapture_exports, {
181902
182024
  pngHeight: () => pngHeight
181903
182025
  });
181904
182026
  import { mkdirSync as mkdirSync64 } from "fs";
181905
- import { join as join141 } from "path";
182027
+ import { join as join143 } from "path";
181906
182028
  function pngHeight(buf) {
181907
182029
  if (buf.length < 24) return null;
181908
182030
  if (buf[12] !== 73 || buf[13] !== 72 || buf[14] !== 68 || buf[15] !== 82) return null;
@@ -181927,7 +182049,7 @@ async function captureFullPagePlate(page, screenshotsDir, budget = {}) {
181927
182049
  const buffer = await page.screenshot({ type: "png", fullPage: true });
181928
182050
  const produced = pngHeight(buffer);
181929
182051
  if (produced != null && produced > MAX_PLATE_HEIGHT_PX) return null;
181930
- writeCaptureFileSync(join141(screenshotsDir, "full-page.png"), buffer);
182052
+ writeCaptureFileSync(join143(screenshotsDir, "full-page.png"), buffer);
181931
182053
  return "screenshots/full-page.png";
181932
182054
  } finally {
181933
182055
  try {
@@ -181942,7 +182064,7 @@ async function captureFullPagePlate(page, screenshotsDir, budget = {}) {
181942
182064
  }
181943
182065
  }
181944
182066
  async function captureScrollScreenshots(page, outputDir, budget = {}) {
181945
- const screenshotsDir = join141(outputDir, "screenshots");
182067
+ const screenshotsDir = join143(outputDir, "screenshots");
181946
182068
  mkdirSync64(screenshotsDir, { recursive: true });
181947
182069
  const MAX_SCREENSHOTS = 20;
181948
182070
  const filePaths = [];
@@ -182039,7 +182161,7 @@ async function captureScrollScreenshots(page, outputDir, budget = {}) {
182039
182161
  finalPositions[i] / Math.max(1, scrollHeight - viewportHeight) * 100
182040
182162
  );
182041
182163
  const filename = `scroll-${String(Math.min(pct, 100)).padStart(3, "0")}.png`;
182042
- const filePath = join141(screenshotsDir, filename);
182164
+ const filePath = join143(screenshotsDir, filename);
182043
182165
  if ((budget.remainingMs?.() ?? 1) <= 0) break;
182044
182166
  const buffer = await page.screenshot({ type: "png" });
182045
182167
  writeCaptureFileSync(filePath, buffer);
@@ -182397,7 +182519,7 @@ var init_assetCataloger = __esm({
182397
182519
 
182398
182520
  // src/capture/coreExtractionPhase.ts
182399
182521
  import { mkdirSync as mkdirSync65 } from "fs";
182400
- import { join as join143 } from "path";
182522
+ import { join as join144 } from "path";
182401
182523
  async function runCoreExtraction(input2) {
182402
182524
  let {
182403
182525
  page1,
@@ -182487,7 +182609,7 @@ async function runCoreExtraction(input2) {
182487
182609
  await collectNetworkLotties();
182488
182610
  const saveDiscoveredLotties = async () => {
182489
182611
  if (discoveredLotties.length > 0 && remainingMs() > 0) {
182490
- const lottieDir = join143(outputDir, "assets", "lottie");
182612
+ const lottieDir = join144(outputDir, "assets", "lottie");
182491
182613
  if (!canWrite()) return;
182492
182614
  mkdirSync65(lottieDir, { recursive: true });
182493
182615
  const lottieBudget = { remainingMs, byteBudget: downloadByteBudget };
@@ -182514,7 +182636,7 @@ async function runCoreExtraction(input2) {
182514
182636
  capturedShaders = unique;
182515
182637
  if (canWrite()) {
182516
182638
  writeCaptureFileSync(
182517
- join143(outputDir, "extracted", "shaders.json"),
182639
+ join144(outputDir, "extracted", "shaders.json"),
182518
182640
  JSON.stringify(unique, null, 2),
182519
182641
  "utf-8"
182520
182642
  );
@@ -182531,7 +182653,7 @@ async function runCoreExtraction(input2) {
182531
182653
  state.tokens = tokens;
182532
182654
  if (canWrite()) {
182533
182655
  writeCaptureFileSync(
182534
- join143(outputDir, "extracted", "tokens.json"),
182656
+ join144(outputDir, "extracted", "tokens.json"),
182535
182657
  serializeTokensForCapture(tokens),
182536
182658
  "utf-8"
182537
182659
  );
@@ -182542,7 +182664,7 @@ async function runCoreExtraction(input2) {
182542
182664
  state.designStyles = designStyles;
182543
182665
  if (canWrite()) {
182544
182666
  writeCaptureFileSync(
182545
- join143(outputDir, "extracted", "design-styles.json"),
182667
+ join144(outputDir, "extracted", "design-styles.json"),
182546
182668
  JSON.stringify(designStyles, null, 2),
182547
182669
  "utf-8"
182548
182670
  );
@@ -182685,7 +182807,7 @@ var init_coreExtractionPhase = __esm({
182685
182807
 
182686
182808
  // src/capture/fontMetadataExtractor.ts
182687
182809
  import { readdirSync as readdirSync44, readFileSync as readFileSync94, existsSync as existsSync120 } from "fs";
182688
- import { join as join144 } from "path";
182810
+ import { join as join145 } from "path";
182689
182811
  import * as fontkit from "fontkit";
182690
182812
  function isFontCollection(value) {
182691
182813
  return value.type === "TTC" || value.type === "DFont";
@@ -182696,7 +182818,7 @@ function extractFontMetadata(fontsDir, outputPath) {
182696
182818
  if (existsSync120(fontsDir)) {
182697
182819
  const fontFiles = readdirSync44(fontsDir).filter((f) => /\.(woff2?|ttf|otf)$/i.test(f));
182698
182820
  for (const filename of fontFiles) {
182699
- const fullPath = join144(fontsDir, filename);
182821
+ const fullPath = join145(fontsDir, filename);
182700
182822
  const meta = readSingleFont(fullPath, filename);
182701
182823
  if (meta.identified) {
182702
182824
  files.push(meta);
@@ -182871,7 +182993,7 @@ var init_fontMetadataExtractor = __esm({
182871
182993
 
182872
182994
  // src/capture/postExtractionPhase.ts
182873
182995
  import { existsSync as existsSync121 } from "fs";
182874
- import { join as join145 } from "path";
182996
+ import { join as join146 } from "path";
182875
182997
  function hasVisionCredentials(skipVision) {
182876
182998
  return Boolean(
182877
182999
  !skipVision && (process.env.OPENROUTER_API_KEY || process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY || process.env.HYPERFRAMES_VERTEX_PROJECT_ID && process.env.HYPERFRAMES_VERTEX_SERVICE_ACCOUNT)
@@ -182879,7 +183001,7 @@ function hasVisionCredentials(skipVision) {
182879
183001
  }
182880
183002
  function writeAssetDescriptionsFile(outputDir, lines, header2) {
182881
183003
  writeCaptureFileSync(
182882
- join145(outputDir, "extracted", "asset-descriptions.md"),
183004
+ join146(outputDir, "extracted", "asset-descriptions.md"),
182883
183005
  header2 + lines.map((line3) => `- ${line3}`).join("\n") + "\n",
182884
183006
  "utf-8"
182885
183007
  );
@@ -182922,8 +183044,8 @@ async function runPostExtraction(input2) {
182922
183044
  );
182923
183045
  try {
182924
183046
  const fontsManifest = extractFontMetadata(
182925
- join145(outputDir, "assets", "fonts"),
182926
- join145(outputDir, "extracted", "fonts-manifest.json")
183047
+ join146(outputDir, "assets", "fonts"),
183048
+ join146(outputDir, "extracted", "fonts-manifest.json")
182927
183049
  );
182928
183050
  if (fontsManifest.families.length > 0) {
182929
183051
  const summary = fontsManifest.families.map((f) => `${f.family}${f.variable ? " (variable)" : ""} \xD7 ${f.fileCount}`).join(", ");
@@ -182954,7 +183076,7 @@ async function runPostExtraction(input2) {
182954
183076
  };
182955
183077
  if (canWrite()) {
182956
183078
  writeCaptureFileSync(
182957
- join145(outputDir, "extracted", "animations.json"),
183079
+ join146(outputDir, "extracted", "animations.json"),
182958
183080
  JSON.stringify(leanCatalog, null, 2),
182959
183081
  "utf-8"
182960
183082
  );
@@ -182977,7 +183099,7 @@ async function runPostExtraction(input2) {
182977
183099
  state.assets = assets;
182978
183100
  if (canWrite()) {
182979
183101
  writeCaptureFileSync(
182980
- join145(outputDir, "extracted", "icons-manifest.json"),
183102
+ join146(outputDir, "extracted", "icons-manifest.json"),
182981
183103
  JSON.stringify(assetPass.icons, null, 2),
182982
183104
  "utf-8"
182983
183105
  );
@@ -183029,7 +183151,7 @@ async function runPostExtraction(input2) {
183029
183151
  if (assets.length && Array.isArray(tokens.sections)) {
183030
183152
  if (canWrite()) {
183031
183153
  writeCaptureFileSync(
183032
- join145(outputDir, "extracted", "tokens.json"),
183154
+ join146(outputDir, "extracted", "tokens.json"),
183033
183155
  serializeTokensForCapture(tokens),
183034
183156
  "utf-8"
183035
183157
  );
@@ -183052,14 +183174,14 @@ ${extracted.bodyHtml}
183052
183174
  `;
183053
183175
  state.pageHtml = pageHtml;
183054
183176
  if (canWrite())
183055
- writeCaptureFileSync(join145(outputDir, "extracted", "page.html"), pageHtml, "utf-8");
183177
+ writeCaptureFileSync(join146(outputDir, "extracted", "page.html"), pageHtml, "utf-8");
183056
183178
  } catch (err) {
183057
183179
  warnings.push(`page.html write failed: ${err}`);
183058
183180
  }
183059
183181
  if (visibleTextContent) {
183060
183182
  if (canWrite()) {
183061
183183
  writeCaptureFileSync(
183062
- join145(outputDir, "extracted", "visible-text.txt"),
183184
+ join146(outputDir, "extracted", "visible-text.txt"),
183063
183185
  visibleTextContent,
183064
183186
  "utf-8"
183065
183187
  );
@@ -183121,8 +183243,8 @@ ${extracted.bodyHtml}
183121
183243
  const contactSheetBudget = { remainingMs };
183122
183244
  const runScrollContactSheet = async () => {
183123
183245
  const scrollSheets = await createScrollContactSheet2(
183124
- join145(outputDir, "screenshots"),
183125
- join145(outputDir, "screenshots", "contact-sheet.jpg"),
183246
+ join146(outputDir, "screenshots"),
183247
+ join146(outputDir, "screenshots", "contact-sheet.jpg"),
183126
183248
  contactSheetBudget
183127
183249
  );
183128
183250
  if (scrollSheets.length > 0)
@@ -183133,11 +183255,11 @@ ${extracted.bodyHtml}
183133
183255
  };
183134
183256
  await runScrollContactSheet();
183135
183257
  const runAssetContactSheet = async () => {
183136
- const assetsImgDir = join145(outputDir, "assets");
183258
+ const assetsImgDir = join146(outputDir, "assets");
183137
183259
  if (existsSync121(assetsImgDir)) {
183138
183260
  const assetSheets = await createAssetContactSheet2(
183139
183261
  assetsImgDir,
183140
- join145(outputDir, "assets", "contact-sheet.jpg"),
183262
+ join146(outputDir, "assets", "contact-sheet.jpg"),
183141
183263
  contactSheetBudget
183142
183264
  );
183143
183265
  if (assetSheets.length > 0)
@@ -183149,9 +183271,9 @@ ${extracted.bodyHtml}
183149
183271
  };
183150
183272
  await runAssetContactSheet();
183151
183273
  const runSvgContactSheet = async () => {
183152
- const svgsDir = join145(outputDir, "assets", "svgs");
183153
- const assetsRootDir = join145(outputDir, "assets");
183154
- const svgOutputPath = existsSync121(svgsDir) ? join145(outputDir, "assets", "svgs", "contact-sheet.jpg") : join145(outputDir, "assets", "contact-sheet-svgs.jpg");
183274
+ const svgsDir = join146(outputDir, "assets", "svgs");
183275
+ const assetsRootDir = join146(outputDir, "assets");
183276
+ const svgOutputPath = existsSync121(svgsDir) ? join146(outputDir, "assets", "svgs", "contact-sheet.jpg") : join146(outputDir, "assets", "contact-sheet-svgs.jpg");
183155
183277
  const svgSheets = await createSvgContactSheet2(
183156
183278
  svgsDir,
183157
183279
  svgOutputPath,
@@ -183197,7 +183319,7 @@ var init_postExtractionPhase = __esm({
183197
183319
 
183198
183320
  // src/capture/captureAttempt.ts
183199
183321
  import { mkdirSync as mkdirSync66, existsSync as existsSync122 } from "fs";
183200
- import { join as join146 } from "path";
183322
+ import { join as join147 } from "path";
183201
183323
  async function captureWebsiteAttempt(opts, onProgress, disableWebgl, watchdog, state) {
183202
183324
  const {
183203
183325
  url,
@@ -183242,9 +183364,9 @@ async function captureWebsiteAttempt(opts, onProgress, disableWebgl, watchdog, s
183242
183364
  };
183243
183365
  phase("browser", "started");
183244
183366
  loadEnvFile(outputDir);
183245
- mkdirSync66(join146(outputDir, "extracted"), { recursive: true });
183246
- mkdirSync66(join146(outputDir, "screenshots"), { recursive: true });
183247
- mkdirSync66(join146(outputDir, "assets"), { recursive: true });
183367
+ mkdirSync66(join147(outputDir, "extracted"), { recursive: true });
183368
+ mkdirSync66(join147(outputDir, "screenshots"), { recursive: true });
183369
+ mkdirSync66(join147(outputDir, "assets"), { recursive: true });
183248
183370
  progress("browser", "Launching headless Chrome...");
183249
183371
  const { ensureBrowser: ensureBrowser2 } = await Promise.resolve().then(() => (init_manager2(), manager_exports2));
183250
183372
  const browser = await ensureBrowser2();
@@ -183411,7 +183533,7 @@ async function captureWebsiteAttempt(opts, onProgress, disableWebgl, watchdog, s
183411
183533
  animationCatalog,
183412
183534
  screenshots.length > 0,
183413
183535
  discoveredLotties.length > 0,
183414
- existsSync122(join146(outputDir, "extracted", "shaders.json")),
183536
+ existsSync122(join147(outputDir, "extracted", "shaders.json")),
183415
183537
  catalogedAssets,
183416
183538
  progress,
183417
183539
  warnings,
@@ -183765,11 +183887,12 @@ var init_capture2 = __esm({
183765
183887
  } catch (err) {
183766
183888
  const errMsg = normalizeErrorMessage(err);
183767
183889
  try {
183768
- const { mkdirSync: mkdirSync76, writeFileSync: writeFileSync60 } = await import("fs");
183890
+ const { mkdirSync: mkdirSync76 } = await import("fs");
183769
183891
  const { formatCaptureFailureReason: formatCaptureFailureReason2 } = await Promise.resolve().then(() => (init_captureTimeout(), captureTimeout_exports));
183892
+ const { writeCaptureFileSync: writeCaptureFileSync2 } = await Promise.resolve().then(() => (init_captureFile(), captureFile_exports));
183770
183893
  mkdirSync76(outputDir, { recursive: true });
183771
183894
  const reason = formatCaptureFailureReason2(errMsg);
183772
- writeFileSync60(
183895
+ writeCaptureFileSync2(
183773
183896
  `${outputDir}/BLOCKED.md`,
183774
183897
  `# Capture Failed
183775
183898
 
@@ -183833,14 +183956,14 @@ __export(state_exports, {
183833
183956
  stateFilePath: () => stateFilePath,
183834
183957
  writeStackOutputs: () => writeStackOutputs
183835
183958
  });
183836
- import { existsSync as existsSync123, mkdirSync as mkdirSync67, readdirSync as readdirSync45, readFileSync as readFileSync95, rmSync as rmSync40, writeFileSync as writeFileSync51 } from "fs";
183837
- import { dirname as dirname64, join as join147 } from "path";
183959
+ import { existsSync as existsSync123, mkdirSync as mkdirSync67, readdirSync as readdirSync45, readFileSync as readFileSync95, rmSync as rmSync41, writeFileSync as writeFileSync51 } from "fs";
183960
+ import { dirname as dirname65, join as join148 } from "path";
183838
183961
  function stateFilePath(stackName = DEFAULT_STACK_NAME, cwd = process.cwd()) {
183839
- return join147(cwd, STATE_DIR_NAME, `${STATE_FILE_PREFIX}${stackName}.json`);
183962
+ return join148(cwd, STATE_DIR_NAME, `${STATE_FILE_PREFIX}${stackName}.json`);
183840
183963
  }
183841
183964
  function writeStackOutputs(outputs, cwd = process.cwd()) {
183842
183965
  const path = stateFilePath(outputs.stackName, cwd);
183843
- mkdirSync67(dirname64(path), { recursive: true });
183966
+ mkdirSync67(dirname65(path), { recursive: true });
183844
183967
  writeFileSync51(path, JSON.stringify(outputs, null, 2) + "\n");
183845
183968
  return path;
183846
183969
  }
@@ -183855,10 +183978,10 @@ function readStackOutputs(stackName = DEFAULT_STACK_NAME, cwd = process.cwd()) {
183855
183978
  }
183856
183979
  function deleteStackOutputs(stackName = DEFAULT_STACK_NAME, cwd = process.cwd()) {
183857
183980
  const path = stateFilePath(stackName, cwd);
183858
- if (existsSync123(path)) rmSync40(path);
183981
+ if (existsSync123(path)) rmSync41(path);
183859
183982
  }
183860
183983
  function listStackNames(cwd = process.cwd()) {
183861
- const dir = join147(cwd, STATE_DIR_NAME);
183984
+ const dir = join148(cwd, STATE_DIR_NAME);
183862
183985
  if (!existsSync123(dir)) return [];
183863
183986
  return readdirSync45(dir).filter((f) => f.startsWith(STATE_FILE_PREFIX) && f.endsWith(".json")).map((f) => f.slice(STATE_FILE_PREFIX.length, -".json".length));
183864
183987
  }
@@ -183891,7 +184014,7 @@ var init_state = __esm({
183891
184014
  // src/commands/lambda/sam.ts
183892
184015
  import { execFileSync as execFileSync19, spawnSync as spawnSync5 } from "child_process";
183893
184016
  import { existsSync as existsSync124 } from "fs";
183894
- import { join as join148 } from "path";
184017
+ import { join as join149 } from "path";
183895
184018
  function assertSamAvailable() {
183896
184019
  try {
183897
184020
  execFileSync19("sam", ["--version"], { stdio: "ignore" });
@@ -183911,7 +184034,7 @@ function assertAwsCliAvailable() {
183911
184034
  }
183912
184035
  }
183913
184036
  function locateSamTemplate(repoRoot2) {
183914
- const candidate = join148(repoRoot2, "examples", "aws-lambda", "template.yaml");
184037
+ const candidate = join149(repoRoot2, "examples", "aws-lambda", "template.yaml");
183915
184038
  if (!existsSync124(candidate)) {
183916
184039
  throw new Error(
183917
184040
  `[lambda] SAM template not found at ${candidate}. If you're running from an installed package, point --sam-template at your local copy of examples/aws-lambda/template.yaml.`
@@ -183945,7 +184068,7 @@ function samDeploy(opts) {
183945
184068
  if (opts.awsProfile) {
183946
184069
  args.push("--profile", opts.awsProfile);
183947
184070
  }
183948
- const samDir = join148(opts.repoRoot, "examples", "aws-lambda");
184071
+ const samDir = join149(opts.repoRoot, "examples", "aws-lambda");
183949
184072
  const result = spawnSync5("sam", args, { cwd: samDir, stdio: opts.stdio ?? "inherit" });
183950
184073
  if (result.status !== 0) {
183951
184074
  throw new Error(
@@ -183963,7 +184086,7 @@ function samDelete(opts) {
183963
184086
  if (opts.awsProfile) {
183964
184087
  args.push("--profile", opts.awsProfile);
183965
184088
  }
183966
- const samDir = join148(opts.repoRoot, "examples", "aws-lambda");
184089
+ const samDir = join149(opts.repoRoot, "examples", "aws-lambda");
183967
184090
  const result = spawnSync5("sam", args, { cwd: samDir, stdio: opts.stdio ?? "inherit" });
183968
184091
  if (result.status !== 0) {
183969
184092
  throw new Error(`[lambda] sam delete exited with code ${result.status ?? "unknown"}`);
@@ -184014,19 +184137,19 @@ var init_sam = __esm({
184014
184137
 
184015
184138
  // src/commands/lambda/repoRoot.ts
184016
184139
  import { existsSync as existsSync125 } from "fs";
184017
- import { dirname as dirname65, resolve as resolve79 } from "path";
184140
+ import { dirname as dirname66, resolve as resolve79 } from "path";
184018
184141
  import { fileURLToPath as fileURLToPath17 } from "url";
184019
184142
  function repoRoot() {
184020
184143
  const override = process.env.HYPERFRAMES_REPO_ROOT;
184021
184144
  if (override && existsSync125(resolve79(override, "packages", "aws-lambda", "package.json"))) {
184022
184145
  return override;
184023
184146
  }
184024
- let dir = dirname65(fileURLToPath17(import.meta.url));
184147
+ let dir = dirname66(fileURLToPath17(import.meta.url));
184025
184148
  for (let depth = 0; depth < 12; depth++) {
184026
184149
  if (existsSync125(resolve79(dir, "packages", "aws-lambda", "package.json"))) {
184027
184150
  return dir;
184028
184151
  }
184029
- const parent = dirname65(dir);
184152
+ const parent = dirname66(dir);
184030
184153
  if (parent === dir) break;
184031
184154
  dir = parent;
184032
184155
  }
@@ -184047,7 +184170,7 @@ __export(deploy_exports, {
184047
184170
  });
184048
184171
  import { spawnSync as spawnSync6 } from "child_process";
184049
184172
  import { existsSync as existsSync126 } from "fs";
184050
- import { join as join149, resolve as resolve80 } from "path";
184173
+ import { join as join150, resolve as resolve80 } from "path";
184051
184174
  async function runDeploy(args = {}) {
184052
184175
  const resolved2 = {
184053
184176
  stackName: args.stackName ?? DEFAULT_STACK_NAME,
@@ -184064,7 +184187,7 @@ async function runDeploy(args = {}) {
184064
184187
  console.log(c.dim("\u2192 Building handler ZIP"));
184065
184188
  buildHandlerZip(root);
184066
184189
  } else {
184067
- const zip = join149(root, "packages", "aws-lambda", "dist", "handler.zip");
184190
+ const zip = join150(root, "packages", "aws-lambda", "dist", "handler.zip");
184068
184191
  if (!existsSync126(zip)) {
184069
184192
  throw new Error(
184070
184193
  `--skip-build set but ${zip} does not exist. Run \`bun run --cwd packages/aws-lambda build:zip\` first or drop --skip-build.`
@@ -184108,7 +184231,7 @@ async function runDeploy(args = {}) {
184108
184231
  function buildHandlerZip(root) {
184109
184232
  const result = spawnSync6(
184110
184233
  "bun",
184111
- ["run", "--cwd", join149(root, "packages", "aws-lambda"), "build:zip"],
184234
+ ["run", "--cwd", join150(root, "packages", "aws-lambda"), "build:zip"],
184112
184235
  { stdio: "inherit" }
184113
184236
  );
184114
184237
  if (result.status !== 0) {
@@ -184178,13 +184301,13 @@ var init_sites = __esm({
184178
184301
 
184179
184302
  // src/commands/lambda/_dimensions.ts
184180
184303
  import { readFileSync as readFileSync96 } from "fs";
184181
- import { join as join150 } from "path";
184304
+ import { join as join151 } from "path";
184182
184305
  function warnOnDimensionMismatch(args) {
184183
184306
  if (args.quiet) return;
184184
184307
  if (args.outputResolution) return;
184185
184308
  let html;
184186
184309
  try {
184187
- html = readFileSync96(join150(args.projectDir, "index.html"), "utf-8");
184310
+ html = readFileSync96(join151(args.projectDir, "index.html"), "utf-8");
184188
184311
  } catch {
184189
184312
  return;
184190
184313
  }
@@ -184214,7 +184337,7 @@ __export(render_exports2, {
184214
184337
  runRender: () => runRender
184215
184338
  });
184216
184339
  import { existsSync as existsSync127 } from "fs";
184217
- import { join as join151, resolve as resolvePath2 } from "path";
184340
+ import { join as join152, resolve as resolvePath2 } from "path";
184218
184341
  async function loadSDK2() {
184219
184342
  return import("@hyperframes/aws-lambda/sdk");
184220
184343
  }
@@ -184230,7 +184353,7 @@ async function runRender(args) {
184230
184353
  });
184231
184354
  const variables = resolveVariablesArg(args.variables, args.variablesFile);
184232
184355
  if (variables && Object.keys(variables).length > 0) {
184233
- const indexPath2 = join151(projectDir, "index.html");
184356
+ const indexPath2 = join152(projectDir, "index.html");
184234
184357
  if (existsSync127(indexPath2)) {
184235
184358
  const issues = validateVariablesAgainstProject(indexPath2, variables);
184236
184359
  reportVariableIssues(issues, { strict: args.strictVariables ?? false, quiet: args.json });
@@ -184365,7 +184488,7 @@ __export(render_batch_exports, {
184365
184488
  runWithConcurrencyLimit: () => runWithConcurrencyLimit
184366
184489
  });
184367
184490
  import { existsSync as existsSync128, readFileSync as readFileSync97 } from "fs";
184368
- import { join as join152, resolve as resolvePath3 } from "path";
184491
+ import { join as join153, resolve as resolvePath3 } from "path";
184369
184492
  async function loadSDK3() {
184370
184493
  return import("@hyperframes/aws-lambda/sdk");
184371
184494
  }
@@ -184389,7 +184512,7 @@ async function runRenderBatch(args) {
184389
184512
  outputResolution: args.outputResolution,
184390
184513
  quiet: args.json
184391
184514
  });
184392
- const schema = loadProjectVariableSchema(join152(projectDir, "index.html"));
184515
+ const schema = loadProjectVariableSchema(join153(projectDir, "index.html"));
184393
184516
  const strict = args.strictVariables ?? false;
184394
184517
  let hadStrictIssue = false;
184395
184518
  for (const { entry, lineNumber } of entries2) {
@@ -185437,7 +185560,7 @@ __export(cloudrun_exports, {
185437
185560
  import { spawnSync as spawnSync7 } from "child_process";
185438
185561
  import { existsSync as existsSync129, mkdirSync as mkdirSync68, readFileSync as readFileSync99, writeFileSync as writeFileSync53 } from "fs";
185439
185562
  import { homedir as homedir24 } from "os";
185440
- import { join as join153, resolve as resolve81 } from "path";
185563
+ import { join as join154, resolve as resolve81 } from "path";
185441
185564
  function loadCloudRunAdapter() {
185442
185565
  cloudRunAdapterPromise ??= Promise.all([
185443
185566
  import("@hyperframes/gcp-cloud-run/sdk"),
@@ -185456,10 +185579,10 @@ Or, for an opt-in project setup:
185456
185579
  ${c.accent("npm install @hyperframes/gcp-cloud-run")}`;
185457
185580
  }
185458
185581
  function stateDir() {
185459
- return join153(homedir24(), ".hyperframes");
185582
+ return join154(homedir24(), ".hyperframes");
185460
185583
  }
185461
185584
  function statePath() {
185462
- return join153(stateDir(), "cloudrun-state.json");
185585
+ return join154(stateDir(), "cloudrun-state.json");
185463
185586
  }
185464
185587
  function writeState2(state) {
185465
185588
  mkdirSync68(stateDir(), { recursive: true });
@@ -185616,11 +185739,11 @@ function machineVars(args, project, region, image) {
185616
185739
  }
185617
185740
  function findRepoRoot(tfDir) {
185618
185741
  const candidate = resolve81(tfDir, "..", "..", "..");
185619
- if (existsSync129(join153(candidate, "packages", "gcp-cloud-run", "Dockerfile"))) return candidate;
185742
+ if (existsSync129(join154(candidate, "packages", "gcp-cloud-run", "Dockerfile"))) return candidate;
185620
185743
  return null;
185621
185744
  }
185622
185745
  function writeCloudBuildConfig(image) {
185623
- const cfgPath = join153(stateDir(), "cloudrun-cloudbuild.yaml");
185746
+ const cfgPath = join154(stateDir(), "cloudrun-cloudbuild.yaml");
185624
185747
  mkdirSync68(stateDir(), { recursive: true });
185625
185748
  writeFileSync53(
185626
185749
  cfgPath,
@@ -185912,7 +186035,7 @@ function resolveAndValidateVariables(args, projectDir) {
185912
186035
  args["variables-file"]
185913
186036
  );
185914
186037
  if (variables && Object.keys(variables).length > 0) {
185915
- const indexPath2 = join153(projectDir, "index.html");
186038
+ const indexPath2 = join154(projectDir, "index.html");
185916
186039
  if (existsSync129(indexPath2)) {
185917
186040
  const issues = validateVariablesAgainstProject(indexPath2, variables);
185918
186041
  reportVariableIssues(issues, {
@@ -186279,13 +186402,13 @@ import {
186279
186402
  createWriteStream as createWriteStream4,
186280
186403
  lstatSync as lstatSync11,
186281
186404
  mkdirSync as mkdirSync69,
186282
- mkdtempSync as mkdtempSync27,
186405
+ mkdtempSync as mkdtempSync28,
186283
186406
  readlinkSync as readlinkSync2,
186284
- renameSync as renameSync23,
186285
- rmSync as rmSync41,
186407
+ renameSync as renameSync24,
186408
+ rmSync as rmSync43,
186286
186409
  statSync as statSync46
186287
186410
  } from "fs";
186288
- import { dirname as dirname66, join as join154, resolve as resolve82 } from "path";
186411
+ import { dirname as dirname67, join as join155, resolve as resolve82 } from "path";
186289
186412
  import { pipeline as pipeline3 } from "stream/promises";
186290
186413
  async function downloadToFile(url, destPath, options = {}) {
186291
186414
  const fetchImpl = options.fetchImpl ?? fetch;
@@ -186296,14 +186419,14 @@ async function downloadToFile(url, destPath, options = {}) {
186296
186419
  if (!res.body) {
186297
186420
  throw new Error(`Failed to download ${url}: empty response body`);
186298
186421
  }
186299
- mkdirSync69(dirname66(destPath), { recursive: true });
186422
+ mkdirSync69(dirname67(destPath), { recursive: true });
186300
186423
  const totalHeader = res.headers.get("content-length");
186301
186424
  const total = totalHeader ? Number.parseInt(totalHeader, 10) : void 0;
186302
186425
  const totalOpt = total !== void 0 && Number.isFinite(total) ? total : void 0;
186303
186426
  const destination = resolveDownloadDestination(destPath);
186304
186427
  const previous = statSync46(destination, { throwIfNoEntry: false });
186305
- const stage = mkdtempSync27(join154(dirname66(destination), ".hf-download-"));
186306
- const stagedFile = join154(stage, "download");
186428
+ const stage = mkdtempSync28(join155(dirname67(destination), ".hf-download-"));
186429
+ const stagedFile = join155(stage, "download");
186307
186430
  let bytes = 0;
186308
186431
  try {
186309
186432
  await pipeline3(
@@ -186324,13 +186447,13 @@ async function downloadToFile(url, destPath, options = {}) {
186324
186447
  );
186325
186448
  options.signal?.throwIfAborted();
186326
186449
  if (previous?.isFile()) chmodSync2(stagedFile, previous.mode & 511);
186327
- renameSync23(stagedFile, destination);
186450
+ renameSync24(stagedFile, destination);
186328
186451
  } catch (error) {
186329
186452
  const reason = options.signal?.reason;
186330
186453
  if (options.signal?.aborted && reason instanceof Error) throw reason;
186331
186454
  throw error;
186332
186455
  } finally {
186333
- rmSync41(stage, { recursive: true, force: true });
186456
+ rmSync43(stage, { recursive: true, force: true });
186334
186457
  }
186335
186458
  return { path: destPath, bytes };
186336
186459
  }
@@ -186338,7 +186461,7 @@ function resolveDownloadDestination(destPath) {
186338
186461
  let destination = resolve82(destPath);
186339
186462
  for (let hops = 0; hops < 40; hops++) {
186340
186463
  if (!lstatSync11(destination, { throwIfNoEntry: false })?.isSymbolicLink()) return destination;
186341
- destination = resolve82(dirname66(destination), readlinkSync2(destination));
186464
+ destination = resolve82(dirname67(destination), readlinkSync2(destination));
186342
186465
  }
186343
186466
  throw new Error(`Too many symbolic links in download destination: ${destPath}`);
186344
186467
  }
@@ -188939,8 +189062,8 @@ var init_parseFigmaRef = __esm({
188939
189062
  });
188940
189063
 
188941
189064
  // ../core/dist/figma/freeze.js
188942
- import { copyFileSync as copyFileSync11, mkdirSync as mkdirSync70, rmSync as rmSync43, statSync as statSync47, writeFileSync as writeFileSync54 } from "fs";
188943
- import { dirname as dirname67 } from "path";
189065
+ import { copyFileSync as copyFileSync11, mkdirSync as mkdirSync70, rmSync as rmSync44, statSync as statSync47, writeFileSync as writeFileSync54 } from "fs";
189066
+ import { dirname as dirname68 } from "path";
188944
189067
  function exceedsFreezeCap(byteLength) {
188945
189068
  return byteLength > MAX_FREEZE_BYTES;
188946
189069
  }
@@ -188949,13 +189072,13 @@ function freezeBytes(bytes, destPath) {
188949
189072
  throw new Error("freeze failed: empty bytes");
188950
189073
  if (exceedsFreezeCap(bytes.length))
188951
189074
  throw new Error(`freeze failed: ${bytes.length} bytes exceeds ${MAX_FREEZE_BYTES} cap`);
188952
- mkdirSync70(dirname67(destPath), { recursive: true });
189075
+ mkdirSync70(dirname68(destPath), { recursive: true });
188953
189076
  try {
188954
189077
  writeFileSync54(destPath, bytes, { flag: "wx" });
188955
189078
  } catch (err) {
188956
189079
  if (err.code !== "EEXIST")
188957
189080
  throw err;
188958
- rmSync43(destPath);
189081
+ rmSync44(destPath);
188959
189082
  writeFileSync54(destPath, bytes, { flag: "wx" });
188960
189083
  }
188961
189084
  return bytes.length;
@@ -188994,15 +189117,15 @@ var init_jsonl = __esm({
188994
189117
 
188995
189118
  // ../core/dist/figma/manifest.js
188996
189119
  import { appendFileSync as appendFileSync2, existsSync as existsSync132, mkdirSync as mkdirSync71, readFileSync as readFileSync103, writeFileSync as writeFileSync55 } from "fs";
188997
- import { join as join155 } from "path";
189120
+ import { join as join156 } from "path";
188998
189121
  function mediaDir(projectDir) {
188999
- return join155(projectDir, ".media");
189122
+ return join156(projectDir, ".media");
189000
189123
  }
189001
189124
  function manifestPath2(projectDir) {
189002
- return join155(mediaDir(projectDir), MANIFEST_FILE2);
189125
+ return join156(mediaDir(projectDir), MANIFEST_FILE2);
189003
189126
  }
189004
189127
  function typeDirPath(projectDir, type) {
189005
- return join155(mediaDir(projectDir), TYPE_DIRS[type]);
189128
+ return join156(mediaDir(projectDir), TYPE_DIRS[type]);
189006
189129
  }
189007
189130
  function isFigmaManifestRecord(value) {
189008
189131
  if (typeof value !== "object" || value === null)
@@ -189089,12 +189212,12 @@ var init_manifest = __esm({
189089
189212
 
189090
189213
  // ../core/dist/figma/mediaIndex.js
189091
189214
  import { mkdirSync as mkdirSync72, writeFileSync as writeFileSync56 } from "fs";
189092
- import { dirname as dirname68, join as join156 } from "path";
189215
+ import { dirname as dirname69, join as join157 } from "path";
189093
189216
  function isRow(value) {
189094
189217
  return typeof value === "object" && value !== null;
189095
189218
  }
189096
189219
  function indexPath(projectDir) {
189097
- return join156(mediaDir(projectDir), "index.md");
189220
+ return join157(mediaDir(projectDir), "index.md");
189098
189221
  }
189099
189222
  function pad(str, len2) {
189100
189223
  return String(str ?? "").padEnd(len2);
@@ -189139,7 +189262,7 @@ function regenerateIndex(projectDir) {
189139
189262
  const records = readJsonlValues(manifestPath2(projectDir)).filter(isRow);
189140
189263
  const content = generateIndexContent(records);
189141
189264
  const p2 = indexPath(projectDir);
189142
- mkdirSync72(dirname68(p2), { recursive: true });
189265
+ mkdirSync72(dirname69(p2), { recursive: true });
189143
189266
  writeFileSync56(p2, content);
189144
189267
  return content;
189145
189268
  }
@@ -189206,9 +189329,9 @@ var init_sanitizeSvg = __esm({
189206
189329
 
189207
189330
  // ../core/dist/figma/bindings.js
189208
189331
  import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync73, writeFileSync as writeFileSync57 } from "fs";
189209
- import { join as join157 } from "path";
189332
+ import { join as join158 } from "path";
189210
189333
  function bindingsPath(projectDir) {
189211
- return join157(mediaDir(projectDir), BINDINGS_FILE);
189334
+ return join158(mediaDir(projectDir), BINDINGS_FILE);
189212
189335
  }
189213
189336
  function isRecord17(value) {
189214
189337
  return typeof value === "object" && value !== null;
@@ -189809,7 +189932,7 @@ __export(asset_exports, {
189809
189932
  runAssetImportMany: () => runAssetImportMany
189810
189933
  });
189811
189934
  import { existsSync as existsSync133 } from "fs";
189812
- import { join as join158, relative as relative36 } from "path";
189935
+ import { join as join159, relative as relative36 } from "path";
189813
189936
  function gatherAssetRefs(positionals) {
189814
189937
  return positionals.flatMap((r) => /^https?:/i.test(r.trim()) ? [r] : r.split(",")).map((r) => r.trim()).filter((r) => r.length > 0);
189815
189938
  }
@@ -189823,7 +189946,7 @@ function requireNodeRef(refInput) {
189823
189946
  }
189824
189947
  function reuseExisting(fileKey2, nodeId, opts, version2, deps, description, entity) {
189825
189948
  const existing = findAllByFigmaNode(deps.projectDir, fileKey2, nodeId).find(
189826
- (r) => r.provenance.format === opts.format && (r.provenance.scale ?? 1) === (opts.scale ?? 1) && r.provenance.version === version2 && existsSync133(join158(deps.projectDir, r.path))
189949
+ (r) => r.provenance.format === opts.format && (r.provenance.scale ?? 1) === (opts.scale ?? 1) && r.provenance.version === version2 && existsSync133(join159(deps.projectDir, r.path))
189827
189950
  );
189828
189951
  if (!existing) return null;
189829
189952
  let record2 = existing;
@@ -189846,7 +189969,7 @@ async function freezeAndRecord(fileKey2, nodeId, url, ext, opts, version2, deps,
189846
189969
  bytes = new TextEncoder().encode(sanitizeSvg(new TextDecoder().decode(bytes)));
189847
189970
  }
189848
189971
  const id = nextId(deps.projectDir, "image");
189849
- const destAbs = join158(typeDirPath(deps.projectDir, "image"), `${id}.${ext}`);
189972
+ const destAbs = join159(typeDirPath(deps.projectDir, "image"), `${id}.${ext}`);
189850
189973
  freezeBytes(bytes, destAbs);
189851
189974
  const record2 = {
189852
189975
  id,
@@ -190016,11 +190139,11 @@ __export(tokens_exports, {
190016
190139
  runTokensImport: () => runTokensImport
190017
190140
  });
190018
190141
  import { writeFileSync as writeFileSync58 } from "fs";
190019
- import { join as join159 } from "path";
190142
+ import { join as join160 } from "path";
190020
190143
  async function runTokensImport(refInput, deps) {
190021
190144
  const { fileKey: fileKey2 } = parseFigmaRef(refInput);
190022
190145
  const { version: version2 } = await deps.client.fileVersion(fileKey2);
190023
- const sidecarPath = join159(deps.projectDir, "figma-tokens.json");
190146
+ const sidecarPath = join160(deps.projectDir, "figma-tokens.json");
190024
190147
  let vars = null;
190025
190148
  try {
190026
190149
  vars = await deps.client.variables(fileKey2);
@@ -190095,7 +190218,7 @@ __export(component_exports, {
190095
190218
  runComponentImport: () => runComponentImport
190096
190219
  });
190097
190220
  import { existsSync as existsSync134, mkdirSync as mkdirSync74, writeFileSync as writeFileSync59 } from "fs";
190098
- import { join as join160, relative as relative37 } from "path";
190221
+ import { join as join161, relative as relative37 } from "path";
190099
190222
  function escapeAttr2(value) {
190100
190223
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
190101
190224
  }
@@ -190106,7 +190229,7 @@ async function runComponentImport(refInput, deps) {
190106
190229
  const bindings = resolveBindings(tree, readBindings(deps.projectDir));
190107
190230
  const mapped = nodeToHtml(tree, bindings, { rootName: deps.name });
190108
190231
  const name = slugify(deps.name ?? tree.name);
190109
- const componentDir = join160(deps.projectDir, "compositions", "components", name);
190232
+ const componentDir = join161(deps.projectDir, "compositions", "components", name);
190110
190233
  if (existsSync134(componentDir))
190111
190234
  console.warn(
190112
190235
  `component dir compositions/components/${name} already exists \u2014 overwriting (rename the figma frame for a separate import)`
@@ -190118,7 +190241,7 @@ async function runComponentImport(refInput, deps) {
190118
190241
  componentDir,
190119
190242
  deps
190120
190243
  );
190121
- const htmlFile = join160(componentDir, `${name}.html`);
190244
+ const htmlFile = join161(componentDir, `${name}.html`);
190122
190245
  writeFileSync59(htmlFile, html + "\n");
190123
190246
  const registryItem = {
190124
190247
  name,
@@ -190140,7 +190263,7 @@ async function runComponentImport(refInput, deps) {
190140
190263
  ]
190141
190264
  };
190142
190265
  writeFileSync59(
190143
- join160(componentDir, "registry-item.json"),
190266
+ join161(componentDir, "registry-item.json"),
190144
190267
  JSON.stringify(registryItem, null, 2) + "\n"
190145
190268
  );
190146
190269
  return {
@@ -190165,7 +190288,7 @@ async function rasterizeFallback(mapped, fileKey2, componentDir, deps) {
190165
190288
  continue;
190166
190289
  }
190167
190290
  frozenAssets.push(asset.record.path);
190168
- const srcRel = relative37(componentDir, join160(deps.projectDir, asset.record.path)).replaceAll(
190291
+ const srcRel = relative37(componentDir, join161(deps.projectDir, asset.record.path)).replaceAll(
190169
190292
  "\\",
190170
190293
  "/"
190171
190294
  );
@@ -190314,7 +190437,7 @@ __export(autoUpdate_exports, {
190314
190437
  import { spawn as spawn19 } from "child_process";
190315
190438
  import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync75, openSync as openSync15 } from "fs";
190316
190439
  import { homedir as homedir25 } from "os";
190317
- import { join as join161 } from "path";
190440
+ import { join as join162 } from "path";
190318
190441
  import { compareVersions as compareVersions3 } from "compare-versions";
190319
190442
  function isAutoInstallDisabled() {
190320
190443
  if (isDevMode()) return true;
@@ -190337,7 +190460,7 @@ function log(line3) {
190337
190460
  }
190338
190461
  function launchDetachedInstall(invocation, displayCommand, version2) {
190339
190462
  mkdirSync75(CONFIG_DIR3, { recursive: true, mode: 448 });
190340
- const configFile = join161(CONFIG_DIR3, "config.json");
190463
+ const configFile = join162(CONFIG_DIR3, "config.json");
190341
190464
  const nodeScript = `
190342
190465
  const { execFile } = require("node:child_process");
190343
190466
  const { readFileSync, renameSync, writeFileSync } = require("node:fs");
@@ -190458,8 +190581,8 @@ var init_autoUpdate = __esm({
190458
190581
  init_config();
190459
190582
  init_env();
190460
190583
  init_installerDetection();
190461
- CONFIG_DIR3 = join161(homedir25(), ".hyperframes");
190462
- LOG_FILE = join161(CONFIG_DIR3, "auto-update.log");
190584
+ CONFIG_DIR3 = join162(homedir25(), ".hyperframes");
190585
+ LOG_FILE = join162(CONFIG_DIR3, "auto-update.log");
190463
190586
  PENDING_TIMEOUT_MS = 10 * 60 * 1e3;
190464
190587
  }
190465
190588
  });
@@ -190655,7 +190778,7 @@ var init_help = __esm({
190655
190778
  init_version();
190656
190779
  init_dist();
190657
190780
  init_runId();
190658
- import { dirname as dirname69, join as join162, resolve as resolve83 } from "path";
190781
+ import { dirname as dirname70, join as join163, resolve as resolve83 } from "path";
190659
190782
  import { fileURLToPath as fileURLToPath18 } from "url";
190660
190783
  import { existsSync as existsSync135 } from "fs";
190661
190784
 
@@ -190774,8 +190897,8 @@ for (const stream of [process.stdout, process.stderr]) {
190774
190897
  });
190775
190898
  }
190776
190899
  (() => {
190777
- const here = dirname69(fileURLToPath18(import.meta.url));
190778
- const shader = join162(here, "shaderTransitionWorker.js");
190900
+ const here = dirname70(fileURLToPath18(import.meta.url));
190901
+ const shader = join163(here, "shaderTransitionWorker.js");
190779
190902
  if (!process.env.HF_SHADER_WORKER_ENTRY && existsSync135(shader)) {
190780
190903
  process.env.HF_SHADER_WORKER_ENTRY = shader;
190781
190904
  }
@@ -190816,7 +190939,7 @@ try {
190816
190939
  var isHelp = process.argv.includes("--help") || process.argv.includes("-h");
190817
190940
  async function assertStudioWorkspaceBuilt() {
190818
190941
  if (!isDevMode()) return;
190819
- const repoRoot2 = resolve83(dirname69(fileURLToPath18(import.meta.url)), "..", "..", "..");
190942
+ const repoRoot2 = resolve83(dirname70(fileURLToPath18(import.meta.url)), "..", "..", "..");
190820
190943
  const { checkStudioWorkspaceBuild: checkStudioWorkspaceBuild2, formatWorkspaceBuildProblems: formatWorkspaceBuildProblems2 } = await Promise.resolve().then(() => (init_workspaceBuildCheck(), workspaceBuildCheck_exports));
190821
190944
  const problems = checkStudioWorkspaceBuild2(repoRoot2);
190822
190945
  if (problems.length === 0) return;