hyperframes 0.8.57 → 0.8.58

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.57" : "0.0.0-dev";
53
+ VERSION = true ? "0.8.58" : "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 join156(aRoot, aPath) {
4890
+ function join157(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 = join156;
4922
+ exports.join = join157;
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 = join156(urlGenerate(parsed), sourceURL);
5095
+ sourceURL = join157(urlGenerate(parsed), sourceURL);
5096
5096
  }
5097
5097
  return normalize3(sourceURL);
5098
5098
  }
@@ -13450,8 +13450,8 @@ var require_lines = __commonJS({
13450
13450
  if (isOnlyWhitespace(sliced)) {
13451
13451
  continue;
13452
13452
  }
13453
- var diff = Math.abs(info.indent - lastIndent);
13454
- counts[diff] = ~~counts[diff] + 1;
13453
+ var diff2 = Math.abs(info.indent - lastIndent);
13454
+ counts[diff2] = ~~counts[diff2] + 1;
13455
13455
  lastIndent = info.indent;
13456
13456
  }
13457
13457
  var maxCount = -1;
@@ -13477,12 +13477,12 @@ var require_lines = __commonJS({
13477
13477
  Lines2.prototype.isPrecededOnlyByWhitespace = function(pos) {
13478
13478
  var info = this.infos[pos.line - 1];
13479
13479
  var indent = Math.max(info.indent, 0);
13480
- var diff = pos.column - indent;
13481
- if (diff <= 0) {
13480
+ var diff2 = pos.column - indent;
13481
+ if (diff2 <= 0) {
13482
13482
  return true;
13483
13483
  }
13484
13484
  var start = info.sliceStart;
13485
- var end = Math.min(start + diff, info.sliceEnd);
13485
+ var end = Math.min(start + diff2, info.sliceEnd);
13486
13486
  var prefix = info.line.slice(start, end);
13487
13487
  return isOnlyWhitespace(prefix);
13488
13488
  };
@@ -67717,9 +67717,9 @@ function motionBlurWindowIsStatic(plan2, frameIndex, staticFrames) {
67717
67717
  }
67718
67718
  return true;
67719
67719
  }
67720
- function adaptiveSampleCount(diff) {
67720
+ function adaptiveSampleCount(diff2) {
67721
67721
  for (const step of ADAPTIVE_SAMPLE_STEPS) {
67722
- if (diff <= step.maxDiff) return step.samples;
67722
+ if (diff2 <= step.maxDiff) return step.samples;
67723
67723
  }
67724
67724
  return MAX_SAMPLES_PER_FRAME;
67725
67725
  }
@@ -93681,10 +93681,10 @@ function simplifyVolumeKeyframes(keyframes) {
93681
93681
  return sampled;
93682
93682
  }
93683
93683
  function buildVolumeExpression(track, ignoreKeyframes = false) {
93684
- const trimDuration = track.end - track.start;
93684
+ const trimDuration2 = track.end - track.start;
93685
93685
  const staticVolume = clampVolume(track.volume);
93686
93686
  const keyframes = (ignoreKeyframes ? [] : track.volumeKeyframes ?? []).filter((keyframe) => Number.isFinite(keyframe.time) && Number.isFinite(keyframe.volume)).map((keyframe) => ({
93687
- time: Math.max(0, Math.min(trimDuration, keyframe.time - track.start)),
93687
+ time: Math.max(0, Math.min(trimDuration2, keyframe.time - track.start)),
93688
93688
  volume: clampVolume(keyframe.volume)
93689
93689
  })).sort((a, b2) => a.time - b2.time);
93690
93690
  if (keyframes.length === 0) return `volume=${formatFilterNumber(staticVolume)}`;
@@ -94044,10 +94044,10 @@ async function mixAudioTracks(tracks, outputPath, totalDuration, signal, config)
94044
94044
  const filterParts = [];
94045
94045
  tracks.forEach((track, i) => {
94046
94046
  const delayMs = Math.round(track.start * 1e3);
94047
- const trimDuration = track.end - track.start + (track.tailSeconds ?? 0);
94047
+ const trimDuration2 = track.end - track.start + (track.tailSeconds ?? 0);
94048
94048
  const volumeFilter = buildVolumeExpression(track, ignoreAutomation);
94049
94049
  filterParts.push(
94050
- `[${i}:a]atrim=0:${formatFilterNumber(trimDuration)},${volumeFilter},adelay=${delayMs}|${delayMs},apad,asetpts=N/SR/TB,atrim=0:${formatFilterNumber(totalDuration)}[a${i}]`
94050
+ `[${i}:a]atrim=0:${formatFilterNumber(trimDuration2)},${volumeFilter},adelay=${delayMs}|${delayMs},apad,asetpts=N/SR/TB,atrim=0:${formatFilterNumber(totalDuration)}[a${i}]`
94051
94051
  );
94052
94052
  });
94053
94053
  const mixInputs = tracks.map((_, i) => `[a${i}]`).join("");
@@ -94150,9 +94150,9 @@ async function mixGroupMembers(memberTracks, outputPath, totalDuration, signal,
94150
94150
  if (!existsSync16(outputDir)) mkdirSync8(outputDir, { recursive: true });
94151
94151
  const buildInputFilters = (ignoreKeyframes) => memberTracks.map((track, i) => {
94152
94152
  const delayMs = Math.round(track.start * 1e3);
94153
- const trimDuration = track.end - track.start + (track.tailSeconds ?? 0);
94153
+ const trimDuration2 = track.end - track.start + (track.tailSeconds ?? 0);
94154
94154
  const volumeFilter = buildVolumeExpression(track, ignoreKeyframes);
94155
- return `[${i}:a]atrim=0:${formatFilterNumber(trimDuration)},${volumeFilter},adelay=${delayMs}|${delayMs},apad,asetpts=N/SR/TB,atrim=0:${formatFilterNumber(totalDuration)}[a${i}]`;
94155
+ return `[${i}:a]atrim=0:${formatFilterNumber(trimDuration2)},${volumeFilter},adelay=${delayMs}|${delayMs},apad,asetpts=N/SR/TB,atrim=0:${formatFilterNumber(totalDuration)}[a${i}]`;
94156
94156
  });
94157
94157
  const mixInputs = memberTracks.map((_, i) => `[a${i}]`).join("");
94158
94158
  const runOnce = async (useNormalize2, ignoreKeyframes = false) => {
@@ -95579,27 +95579,27 @@ function diffGpuParityPngs(pngA, pngB, options = {}) {
95579
95579
  return diffGpuParityFrames(a, b2, options);
95580
95580
  }
95581
95581
  function verifyGpuParity(a, b2, options = {}) {
95582
- const diff = diffGpuParityFrames(a, b2, options);
95582
+ const diff2 = diffGpuParityFrames(a, b2, options);
95583
95583
  const threshold = options.blackOnlyFractionThreshold ?? DEFAULT_BLACK_ONLY_FRACTION_THRESHOLD;
95584
- if (diff.blackOnlyInA.fraction > threshold) {
95585
- const bb = diff.blackOnlyInA.boundingBox;
95584
+ if (diff2.blackOnlyInA.fraction > threshold) {
95585
+ const bb = diff2.blackOnlyInA.boundingBox;
95586
95586
  const region = bb ? ` (bbox ${bb.x},${bb.y} ${bb.width}x${bb.height})` : "";
95587
95587
  return {
95588
95588
  ok: false,
95589
- reason: `hardware-GPU frame has ${diff.blackOnlyInA.pixels} pixel(s) (${(diff.blackOnlyInA.fraction * 100).toFixed(3)}%) that are solid-black while software-GPU frame has content there${region} \u2014 likely shape-dependent hardware-GPU capture bug`,
95590
- diff
95589
+ reason: `hardware-GPU frame has ${diff2.blackOnlyInA.pixels} pixel(s) (${(diff2.blackOnlyInA.fraction * 100).toFixed(3)}%) that are solid-black while software-GPU frame has content there${region} \u2014 likely shape-dependent hardware-GPU capture bug`,
95590
+ diff: diff2
95591
95591
  };
95592
95592
  }
95593
- if (diff.blackOnlyInB.fraction > threshold) {
95594
- const bb = diff.blackOnlyInB.boundingBox;
95593
+ if (diff2.blackOnlyInB.fraction > threshold) {
95594
+ const bb = diff2.blackOnlyInB.boundingBox;
95595
95595
  const region = bb ? ` (bbox ${bb.x},${bb.y} ${bb.width}x${bb.height})` : "";
95596
95596
  return {
95597
95597
  ok: false,
95598
- reason: `software-GPU frame has ${diff.blackOnlyInB.pixels} pixel(s) (${(diff.blackOnlyInB.fraction * 100).toFixed(3)}%) that are solid-black while hardware-GPU frame has content there${region} \u2014 unexpected inverse pattern, worth investigating`,
95599
- diff
95598
+ reason: `software-GPU frame has ${diff2.blackOnlyInB.pixels} pixel(s) (${(diff2.blackOnlyInB.fraction * 100).toFixed(3)}%) that are solid-black while hardware-GPU frame has content there${region} \u2014 unexpected inverse pattern, worth investigating`,
95599
+ diff: diff2
95600
95600
  };
95601
95601
  }
95602
- return { ok: true, reason: "", diff };
95602
+ return { ok: true, reason: "", diff: diff2 };
95603
95603
  }
95604
95604
  var DEFAULT_PIXEL_CHANNEL_TOLERANCE, DEFAULT_BLACK_SUM_THRESHOLD, DEFAULT_CONTENT_SUM_THRESHOLD, DEFAULT_BLACK_ONLY_FRACTION_THRESHOLD;
95605
95605
  var init_gpuParityDiff = __esm({
@@ -103585,7 +103585,7 @@ var require_compile = __commonJS({
103585
103585
  }
103586
103586
  }
103587
103587
  exports.compileSchema = compileSchema;
103588
- function resolveRef(root, baseId, ref2) {
103588
+ function resolveRef2(root, baseId, ref2) {
103589
103589
  var _a3;
103590
103590
  ref2 = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, ref2);
103591
103591
  const schOrFunc = root.refs[ref2];
@@ -103602,7 +103602,7 @@ var require_compile = __commonJS({
103602
103602
  return;
103603
103603
  return root.refs[ref2] = inlineOrCompile.call(this, _sch);
103604
103604
  }
103605
- exports.resolveRef = resolveRef;
103605
+ exports.resolveRef = resolveRef2;
103606
103606
  function inlineOrCompile(sch) {
103607
103607
  if ((0, resolve_1.inlineRef)(sch.schema, this.opts.inlineRefs))
103608
103608
  return sch.schema;
@@ -111061,7 +111061,7 @@ async function checkSkills(opts = {}) {
111061
111061
  const skillNames = Object.keys(latest.skills);
111062
111062
  const root = locateInstall(skillNames, { dir: opts.dir, cwd: opts.cwd, home: opts.home });
111063
111063
  const installed = root ? hashInstalled(root, skillNames) : {};
111064
- const diff = diffSkills(installed, latest);
111064
+ const diff2 = diffSkills(installed, latest);
111065
111065
  const removedResult = root ? detectRemoved(root, latest, { cwd: opts.cwd, home: opts.home }) : { removed: [], lockMissing: false };
111066
111066
  const { removed, lockMissing } = removedResult;
111067
111067
  return {
@@ -111070,9 +111070,9 @@ async function checkSkills(opts = {}) {
111070
111070
  scope: root?.scope ?? null,
111071
111071
  // Removed skills also mean the install isn't reconciled with the manifest —
111072
111072
  // `skills update` now prunes them, so they count toward "update available".
111073
- updateAvailable: diff.updateAvailable || removed.length > 0,
111074
- summary: { ...diff.summary, removed: removed.length },
111075
- skills: [...diff.skills, ...removed],
111073
+ updateAvailable: diff2.updateAvailable || removed.length > 0,
111074
+ summary: { ...diff2.summary, removed: removed.length },
111075
+ skills: [...diff2.skills, ...removed],
111076
111076
  lockMissing
111077
111077
  };
111078
111078
  }
@@ -121471,8 +121471,8 @@ import {
121471
121471
  existsSync as existsSync37,
121472
121472
  ftruncateSync as ftruncateSync2,
121473
121473
  openSync as openSync8,
121474
- readFileSync as readFileSync42,
121475
- writeFileSync as writeFileSync42,
121474
+ readFileSync as readFileSync52,
121475
+ writeFileSync as writeFileSync52,
121476
121476
  writeSync as writeSync2,
121477
121477
  mkdirSync as mkdirSync32,
121478
121478
  unlinkSync as unlinkSync23,
@@ -121502,14 +121502,15 @@ import { mkdirSync as mkdirSync22, readdirSync as readdirSync32, readFileSync as
121502
121502
  import { Buffer as Buffer2 } from "buffer";
121503
121503
  import { join as join52, relative as relative22 } from "path";
121504
121504
  import { createHash as createHash22, randomUUID as randomUUID9 } from "crypto";
121505
- import { existsSync as existsSync211, readFileSync as readFileSync32, realpathSync as realpathSync8 } from "fs";
121505
+ import { readFileSync as readFileSync32, writeFileSync as writeFileSync42 } from "fs";
121506
+ import { existsSync as existsSync211, readFileSync as readFileSync42, realpathSync as realpathSync8 } from "fs";
121506
121507
  import { randomUUID as randomUUID22 } from "crypto";
121507
121508
  import { dirname as dirname22, relative as relative32, resolve as resolve24, sep as sep22 } from "path";
121508
- import { createReadStream as createReadStream3, existsSync as existsSync52, readFileSync as readFileSync72, statSync as statSync32 } from "fs";
121509
+ import { createReadStream as createReadStream3, existsSync as existsSync52, readFileSync as readFileSync82, statSync as statSync32 } from "fs";
121509
121510
  import { Readable as Readable2 } from "stream";
121510
121511
  import { join as join82, resolve as resolve42 } from "path";
121511
121512
  import { createHash as createHash32 } from "crypto";
121512
- import { existsSync as existsSync42, readFileSync as readFileSync52 } from "fs";
121513
+ import { existsSync as existsSync42, readFileSync as readFileSync62 } from "fs";
121513
121514
  import { join as join72 } from "path";
121514
121515
  import {
121515
121516
  closeSync as closeSync22,
@@ -121517,35 +121518,35 @@ import {
121517
121518
  fstatSync as fstatSync4,
121518
121519
  ftruncateSync as ftruncateSync22,
121519
121520
  openSync as openSync22,
121520
- readFileSync as readFileSync62,
121521
- writeFileSync as writeFileSync52,
121521
+ readFileSync as readFileSync72,
121522
+ writeFileSync as writeFileSync62,
121522
121523
  writeSync as writeSync22
121523
121524
  } from "fs";
121524
- import { readFileSync as readFileSync82 } from "fs";
121525
+ import { readFileSync as readFileSync92 } from "fs";
121525
121526
  import { join as join92 } from "path";
121526
121527
  import { streamSSE } from "hono/streaming";
121527
- import { existsSync as existsSync62, readFileSync as readFileSync92, mkdirSync as mkdirSync42, unlinkSync as unlinkSync32, readdirSync as readdirSync52, statSync as statSync42 } from "fs";
121528
+ import { existsSync as existsSync62, readFileSync as readFileSync102, mkdirSync as mkdirSync42, unlinkSync as unlinkSync32, readdirSync as readdirSync52, statSync as statSync42 } from "fs";
121528
121529
  import { join as join102 } from "path";
121529
121530
  import {
121530
121531
  existsSync as existsSync72,
121531
121532
  mkdirSync as mkdirSync52,
121532
- readFileSync as readFileSync102,
121533
+ readFileSync as readFileSync112,
121533
121534
  readdirSync as readdirSync62,
121534
121535
  renameSync as renameSync32,
121535
121536
  rmSync as rmSync42,
121536
121537
  statSync as statSync52,
121537
121538
  unlinkSync as unlinkSync42,
121538
- writeFileSync as writeFileSync62
121539
+ writeFileSync as writeFileSync72
121539
121540
  } from "fs";
121540
121541
  import { join as join112 } from "path";
121541
121542
  import { createHash as createHash42, randomUUID as randomUUID32 } from "crypto";
121542
- import { existsSync as existsSync82, readFileSync as readFileSync112, statSync as statSync62 } from "fs";
121543
+ import { existsSync as existsSync82, readFileSync as readFileSync122, statSync as statSync62 } from "fs";
121543
121544
  import { join as join122 } from "path";
121544
121545
  import { closeSync as closeSync32, constants as constants22, fstatSync as fstatSync22, openSync as openSync32, readSync as readSync2 } from "fs";
121545
121546
  import { streamSSE as streamSSE2 } from "hono/streaming";
121546
121547
  import { existsSync as existsSync92, mkdirSync as mkdirSync62 } from "fs";
121547
121548
  import { basename as basename22, dirname as dirname42, extname as extname22, join as join132 } from "path";
121548
- import { existsSync as existsSync102, readFileSync as readFileSync122 } from "fs";
121549
+ import { existsSync as existsSync102, readFileSync as readFileSync132 } from "fs";
121549
121550
  import { homedir as homedir10 } from "os";
121550
121551
  import { join as join142 } from "path";
121551
121552
  function shouldIgnoreDir(rel) {
@@ -121956,6 +121957,13 @@ function recordFileWriteReceipt(absPath, receipt) {
121956
121957
  current2.push({ ...receipt, recordedAt: now });
121957
121958
  receipts.set(absPath, current2);
121958
121959
  }
121960
+ function clearFileWriteReceipt(absPath, version2, writeToken) {
121961
+ const current2 = (receipts.get(absPath) ?? []).filter(
121962
+ (entry) => entry.version !== version2 || entry.writeToken !== writeToken
121963
+ );
121964
+ if (current2.length > 0) receipts.set(absPath, current2);
121965
+ else receipts.delete(absPath);
121966
+ }
121959
121967
  function identifyFileWrite(absPath, expectedVersion) {
121960
121968
  const now = Date.now();
121961
121969
  const current2 = (receipts.get(absPath) ?? []).filter(
@@ -121971,6 +121979,76 @@ function identifyFileWrite(absPath, expectedVersion) {
121971
121979
  const { path, version: version2, writeToken } = receipt;
121972
121980
  return { path, version: version2, writeToken };
121973
121981
  }
121982
+ function applyFileMutations(projectDir, mutations, requestToken, writeFile3 = writeFileSync42) {
121983
+ const prepared = mutations.map((mutation) => ({
121984
+ ...mutation,
121985
+ before: mutation.before ?? readFileSync32(mutation.absPath, "utf-8")
121986
+ }));
121987
+ const results = [];
121988
+ const attempted = [];
121989
+ try {
121990
+ for (const mutation of prepared) {
121991
+ results.push(applyOneMutation(projectDir, mutation, requestToken, writeFile3, attempted));
121992
+ }
121993
+ return results;
121994
+ } catch (error) {
121995
+ const rollbackErrors = [];
121996
+ for (const mutation of attempted.reverse()) {
121997
+ try {
121998
+ writeFile3(mutation.absPath, mutation.before, "utf-8");
121999
+ clearFileWriteReceipt(mutation.absPath, mutation.version, mutation.writeToken);
122000
+ } catch (rollbackError) {
122001
+ rollbackErrors.push(rollbackError);
122002
+ }
122003
+ }
122004
+ if (rollbackErrors.length > 0) {
122005
+ throw new AggregateError(
122006
+ [error, ...rollbackErrors],
122007
+ "File mutation failed and rollback did not complete"
122008
+ );
122009
+ }
122010
+ throw error;
122011
+ }
122012
+ }
122013
+ function applyOneMutation(projectDir, mutation, requestToken, writeFile3, attempted) {
122014
+ const current2 = readFileSync32(mutation.absPath, "utf-8");
122015
+ assertExpectedVersion(mutation.expectedVersion, current2);
122016
+ if (mutation.after === mutation.before) {
122017
+ return {
122018
+ ...mutation,
122019
+ before: current2,
122020
+ changed: false,
122021
+ backupPath: null,
122022
+ version: fileContentVersion(mutation.before),
122023
+ writeToken: null
122024
+ };
122025
+ }
122026
+ const backup = snapshotBeforeWrite(projectDir, mutation.absPath);
122027
+ if (backup.error) throw new Error(`backup failed: ${backup.error}`);
122028
+ const before3 = current2;
122029
+ const version2 = fileContentVersion(mutation.after);
122030
+ const writeToken = createWriteToken(requestToken);
122031
+ attempted.push({ ...mutation, before: before3, version: version2, writeToken });
122032
+ writeFile3(mutation.absPath, mutation.after, "utf-8");
122033
+ recordFileWriteReceipt(mutation.absPath, {
122034
+ path: mutation.sourceFile,
122035
+ version: version2,
122036
+ writeToken
122037
+ });
122038
+ return {
122039
+ ...mutation,
122040
+ before: before3,
122041
+ changed: true,
122042
+ backupPath: backupPathForResponse(projectDir, backup.backupPath),
122043
+ version: version2,
122044
+ writeToken
122045
+ };
122046
+ }
122047
+ function assertExpectedVersion(expectedVersion, current2) {
122048
+ if (expectedVersion !== void 0 && fileContentVersion(current2) !== expectedVersion) {
122049
+ throw new Error("file changed since the timeline was read");
122050
+ }
122051
+ }
121974
122052
  function descendants(root, selector) {
121975
122053
  const found = Array.from(root.querySelectorAll(selector));
121976
122054
  for (const template of root.querySelectorAll("template")) {
@@ -122032,7 +122110,7 @@ function validateDependencyGraph(projectDir, targetAbs, sourceAbs) {
122032
122110
  }
122033
122111
  if (visited.has(file)) return;
122034
122112
  visiting.add(file);
122035
- const source = readFileSync32(file, "utf-8");
122113
+ const source = readFileSync42(file, "utf-8");
122036
122114
  const { document: document2 } = compositionRoot(source);
122037
122115
  for (const host of descendants(document2, "[data-composition-src]")) {
122038
122116
  const dependency = host.getAttribute("data-composition-src");
@@ -122096,7 +122174,7 @@ function insertCompositionIntoSource(input2) {
122096
122174
  const targetAbs = canonicalProjectFile(input2.projectDir, input2.targetPath);
122097
122175
  const sourceAbs = canonicalProjectFile(input2.projectDir, input2.sourcePath);
122098
122176
  validateDependencyGraph(input2.projectDir, targetAbs, sourceAbs);
122099
- const source = readFileSync32(sourceAbs, "utf-8");
122177
+ const source = readFileSync42(sourceAbs, "utf-8");
122100
122178
  const sourceComposition = compositionRoot(source).root;
122101
122179
  const duration = positiveAttribute(
122102
122180
  sourceComposition,
@@ -122224,7 +122302,7 @@ function foldElementPatches(originalContent, patches) {
122224
122302
  }
122225
122303
  return { content, matched };
122226
122304
  }
122227
- function commitElementPatchBatches(projectDir, batches, writeFile3 = writeFileSync42) {
122305
+ function commitElementPatchBatches(projectDir, batches, writeFile3 = writeFileSync52, requestToken) {
122228
122306
  const resolvedPaths = /* @__PURE__ */ new Set();
122229
122307
  const prepared = [];
122230
122308
  for (const batch of batches) {
@@ -122234,7 +122312,7 @@ function commitElementPatchBatches(projectDir, batches, writeFile3 = writeFileSy
122234
122312
  resolvedPaths.add(absPath);
122235
122313
  let before3;
122236
122314
  try {
122237
- before3 = readFileSync42(absPath, "utf-8");
122315
+ before3 = readFileSync52(absPath, "utf-8");
122238
122316
  } catch {
122239
122317
  return { error: "not-found", sourceFile: batch.sourceFile };
122240
122318
  }
@@ -122260,64 +122338,34 @@ function commitElementPatchBatches(projectDir, batches, writeFile3 = writeFileSy
122260
122338
  }))
122261
122339
  };
122262
122340
  }
122263
- const files = [];
122264
- const attemptedWrites = [];
122265
- try {
122266
- for (const file of prepared) {
122267
- if (file.after === file.before) {
122268
- files.push({
122269
- sourceFile: file.sourceFile,
122270
- changed: false,
122271
- matched: file.matched,
122272
- before: file.before,
122273
- after: file.before
122274
- });
122275
- continue;
122276
- }
122277
- const backup = snapshotBeforeWrite(projectDir, file.absPath);
122278
- if (backup.error) {
122279
- throw new Error(`Failed to create backup for ${file.sourceFile}: ${backup.error}`);
122280
- }
122281
- attemptedWrites.push(file);
122282
- writeFile3(file.absPath, file.after, "utf-8");
122283
- files.push({
122284
- sourceFile: file.sourceFile,
122285
- changed: true,
122286
- matched: file.matched,
122287
- before: file.before,
122288
- after: file.after,
122289
- backupPath: backupPathForResponse(projectDir, backup.backupPath)
122290
- });
122291
- }
122292
- } catch (error) {
122293
- const rollbackErrors = [];
122294
- for (const file of attemptedWrites.reverse()) {
122295
- try {
122296
- writeFile3(file.absPath, file.before, "utf-8");
122297
- } catch (rollbackError) {
122298
- rollbackErrors.push(rollbackError);
122299
- }
122300
- }
122301
- if (rollbackErrors.length > 0) {
122302
- throw new AggregateError(
122303
- [error, ...rollbackErrors],
122304
- "Element patch batch failed and rollback did not complete"
122305
- );
122306
- }
122307
- throw error;
122308
- }
122341
+ const applied = applyFileMutations(
122342
+ projectDir,
122343
+ prepared.map(({ sourceFile, absPath, before: before3, after: after2 }) => ({
122344
+ sourceFile,
122345
+ absPath,
122346
+ before: before3,
122347
+ after: after2
122348
+ })),
122349
+ requestToken,
122350
+ writeFile3
122351
+ );
122352
+ const files = applied.map((file, index) => ({
122353
+ sourceFile: file.sourceFile,
122354
+ changed: file.changed,
122355
+ matched: prepared[index]?.matched ?? [],
122356
+ before: file.before,
122357
+ after: file.after,
122358
+ backupPath: file.backupPath ?? void 0
122359
+ }));
122309
122360
  return { durable: true, files };
122310
122361
  }
122311
122362
  function commitElementPatchBatchesWithReceipts(c3, projectDir, batches) {
122312
- const result = commitElementPatchBatches(projectDir, batches);
122313
- if ("error" in result || !result.durable) return result;
122314
- for (const file of result.files) {
122315
- if (!file.changed) continue;
122316
- const absPath = resolveWithinProject(projectDir, file.sourceFile);
122317
- if (!absPath) throw new Error(`Committed element patch escaped project: ${file.sourceFile}`);
122318
- recordMutationReceipt(c3, file.sourceFile, absPath, file.after);
122319
- }
122320
- return result;
122363
+ return commitElementPatchBatches(
122364
+ projectDir,
122365
+ batches,
122366
+ writeFileSync52,
122367
+ c3.req.header("X-Hyperframes-Write-Token")
122368
+ );
122321
122369
  }
122322
122370
  function recordMutationReceipt(c3, filePath, absPath, html) {
122323
122371
  const version2 = fileContentVersion(html);
@@ -122326,7 +122374,7 @@ function recordMutationReceipt(c3, filePath, absPath, html) {
122326
122374
  return { version: version2, writeToken };
122327
122375
  }
122328
122376
  function writeFileWithReceipt(c3, filePath, absPath, html) {
122329
- writeFileSync42(absPath, html, "utf-8");
122377
+ writeFileSync52(absPath, html, "utf-8");
122330
122378
  return recordMutationReceipt(c3, filePath, absPath, html);
122331
122379
  }
122332
122380
  function writeMutationResult(c3, projectDir, filePath, absPath, html) {
@@ -122339,9 +122387,9 @@ function writeIfChanged(c3, projectDir, filePath, absPath, original, next) {
122339
122387
  if (next === original) {
122340
122388
  return c3.json({ ok: true, changed: false, content: original, path: filePath });
122341
122389
  }
122342
- const mutationResult = writeMutationResult(c3, projectDir, filePath, absPath, next);
122343
- if (mutationResult instanceof Response) return mutationResult;
122344
- const { backupPath } = mutationResult;
122390
+ const mutationResult2 = writeMutationResult(c3, projectDir, filePath, absPath, next);
122391
+ if (mutationResult2 instanceof Response) return mutationResult2;
122392
+ const { backupPath } = mutationResult2;
122345
122393
  return c3.json({
122346
122394
  ok: true,
122347
122395
  changed: true,
@@ -122411,11 +122459,11 @@ function updateReferences(projectDir, oldPath, newPath) {
122411
122459
  let updatedCount = 0;
122412
122460
  for (const file of textFiles) {
122413
122461
  if (!isSafePath(projectDir, file)) continue;
122414
- const content = readFileSync42(file, "utf-8");
122462
+ const content = readFileSync52(file, "utf-8");
122415
122463
  if (!content.includes(oldPath)) continue;
122416
122464
  const updated = content.split(oldPath).join(newPath);
122417
122465
  if (updated !== content) {
122418
- writeFileSync42(file, updated, "utf-8");
122466
+ writeFileSync52(file, updated, "utf-8");
122419
122467
  updatedCount++;
122420
122468
  }
122421
122469
  }
@@ -122654,7 +122702,7 @@ function containsRawGsapExpression(value) {
122654
122702
  return Object.values(value).some(containsRawGsapExpression);
122655
122703
  }
122656
122704
  async function prepareGsapMutationScript(c3, res, firstMutation) {
122657
- const beforeHtml = readFileSync42(res.absPath, "utf-8");
122705
+ const beforeHtml = readFileSync52(res.absPath, "utf-8");
122658
122706
  let html = beforeHtml;
122659
122707
  let block = extractGsapScriptBlock(html);
122660
122708
  if (!block && (firstMutation.type === "add" || firstMutation.type === "add-with-keyframes")) {
@@ -122721,19 +122769,19 @@ async function applyGsapMutations(c3, res, mutations) {
122721
122769
  const changed = block.scriptText !== initialScript;
122722
122770
  const newHtml = changed ? block.replaceScript(block.scriptText) : html;
122723
122771
  let backupPath = null;
122724
- if (readFileSync42(res.absPath, "utf-8") !== beforeHtml) {
122772
+ if (readFileSync52(res.absPath, "utf-8") !== beforeHtml) {
122725
122773
  return c3.json({ error: "file changed during GSAP mutation", conflict: true }, 409);
122726
122774
  }
122727
122775
  if (changed) {
122728
- const mutationResult = writeMutationResult(
122776
+ const mutationResult2 = writeMutationResult(
122729
122777
  c3,
122730
122778
  res.project.dir,
122731
122779
  res.filePath,
122732
122780
  res.absPath,
122733
122781
  newHtml
122734
122782
  );
122735
- if (mutationResult instanceof Response) return mutationResult;
122736
- backupPath = mutationResult.backupPath;
122783
+ if (mutationResult2 instanceof Response) return mutationResult2;
122784
+ backupPath = mutationResult2.backupPath;
122737
122785
  }
122738
122786
  const responsePayload = {
122739
122787
  ok: true,
@@ -123504,7 +123552,7 @@ async function processUploadedFiles(formData, targetDir, projectDir) {
123504
123552
  try {
123505
123553
  const fd = openSync8(finalPath, "wx");
123506
123554
  try {
123507
- writeFileSync42(fd, buffer);
123555
+ writeFileSync52(fd, buffer);
123508
123556
  } finally {
123509
123557
  closeSync8(fd);
123510
123558
  }
@@ -123542,7 +123590,7 @@ function registerFileRoutes(api, adapter2) {
123542
123590
  }
123543
123591
  return c3.json({ error: "not found" }, 404);
123544
123592
  }
123545
- const content = readFileSync42(res.absPath);
123593
+ const content = readFileSync52(res.absPath);
123546
123594
  const version2 = fileContentVersion(content);
123547
123595
  c3.header("ETag", version2);
123548
123596
  return c3.json({ filename: res.filePath, content: content.toString("utf-8"), version: version2 });
@@ -123556,7 +123604,7 @@ function registerFileRoutes(api, adapter2) {
123556
123604
  if (expectedVersion === null && !createOnly) {
123557
123605
  let currentContent = null;
123558
123606
  try {
123559
- currentContent = readFileSync42(res.absPath);
123607
+ currentContent = readFileSync52(res.absPath);
123560
123608
  } catch (error) {
123561
123609
  if (!error || typeof error !== "object" || !("code" in error) || error.code !== "ENOENT") {
123562
123610
  throw error;
@@ -123582,7 +123630,7 @@ function registerFileRoutes(api, adapter2) {
123582
123630
  if (!error || typeof error !== "object" || !("code" in error) || error.code !== "EEXIST") {
123583
123631
  throw error;
123584
123632
  }
123585
- const currentContent = readFileSync42(res.absPath);
123633
+ const currentContent = readFileSync52(res.absPath);
123586
123634
  return c3.json(
123587
123635
  {
123588
123636
  error: "file conflict",
@@ -123617,7 +123665,7 @@ function registerFileRoutes(api, adapter2) {
123617
123665
  );
123618
123666
  }
123619
123667
  try {
123620
- const currentContent = readFileSync42(fd);
123668
+ const currentContent = readFileSync52(fd);
123621
123669
  const currentVersion = fileContentVersion(currentContent);
123622
123670
  if (expectedVersion !== currentVersion) {
123623
123671
  return c3.json(
@@ -123656,7 +123704,7 @@ function registerFileRoutes(api, adapter2) {
123656
123704
  ensureDir(res.absPath);
123657
123705
  const body = Buffer.from(await c3.req.arrayBuffer());
123658
123706
  try {
123659
- writeFileSync42(res.absPath, body, { flag: "wx" });
123707
+ writeFileSync52(res.absPath, body, { flag: "wx" });
123660
123708
  } catch (error) {
123661
123709
  if (!error || typeof error !== "object" || !("code" in error) || error.code !== "EEXIST") {
123662
123710
  throw error;
@@ -123690,7 +123738,7 @@ function registerFileRoutes(api, adapter2) {
123690
123738
  }
123691
123739
  let before3;
123692
123740
  try {
123693
- before3 = readFileSync42(ctx.absPath, "utf-8");
123741
+ before3 = readFileSync52(ctx.absPath, "utf-8");
123694
123742
  } catch (error) {
123695
123743
  if (!error || typeof error !== "object" || !("code" in error) || error.code !== "ENOENT") {
123696
123744
  throw error;
@@ -123747,7 +123795,7 @@ function registerFileRoutes(api, adapter2) {
123747
123795
  }
123748
123796
  const parsed = await parseMutationBody(c3);
123749
123797
  if ("error" in parsed) return parsed.error;
123750
- const originalContent = readFileSync42(ctx.absPath, "utf-8");
123798
+ const originalContent = readFileSync52(ctx.absPath, "utf-8");
123751
123799
  return writeIfChanged(
123752
123800
  c3,
123753
123801
  ctx.project.dir,
@@ -123768,7 +123816,7 @@ function registerFileRoutes(api, adapter2) {
123768
123816
  if (!Array.isArray(targets) || targets.length === 0) {
123769
123817
  return c3.json({ error: "targets required" }, 400);
123770
123818
  }
123771
- const originalContent = readFileSync42(ctx.absPath, "utf-8");
123819
+ const originalContent = readFileSync52(ctx.absPath, "utf-8");
123772
123820
  let next = originalContent;
123773
123821
  for (const target of targets) {
123774
123822
  next = removeElementFromHtml2(next, target);
@@ -123801,7 +123849,7 @@ function registerFileRoutes(api, adapter2) {
123801
123849
  seen.add(absPath);
123802
123850
  let before3;
123803
123851
  try {
123804
- before3 = readFileSync42(absPath, "utf-8");
123852
+ before3 = readFileSync52(absPath, "utf-8");
123805
123853
  } catch {
123806
123854
  return c3.json({ error: `not found: ${file.path}` }, 404);
123807
123855
  }
@@ -123828,7 +123876,7 @@ function registerFileRoutes(api, adapter2) {
123828
123876
  prepared.push(folded);
123829
123877
  }
123830
123878
  for (const file of prepared) {
123831
- const current2 = readFileSync42(file.absPath, "utf-8");
123879
+ const current2 = readFileSync52(file.absPath, "utf-8");
123832
123880
  if (current2 !== file.before) {
123833
123881
  return c3.json(
123834
123882
  {
@@ -123858,7 +123906,7 @@ function registerFileRoutes(api, adapter2) {
123858
123906
  const written = [];
123859
123907
  try {
123860
123908
  for (const file of prepared) {
123861
- writeFileSync42(file.absPath, file.after, "utf-8");
123909
+ writeFileSync52(file.absPath, file.after, "utf-8");
123862
123910
  written.push(file);
123863
123911
  recordFileWriteReceipt(file.absPath, {
123864
123912
  path: file.path,
@@ -123870,12 +123918,12 @@ function registerFileRoutes(api, adapter2) {
123870
123918
  const conflicts = [];
123871
123919
  for (const file of written.reverse()) {
123872
123920
  try {
123873
- const current2 = readFileSync42(file.absPath, "utf-8");
123921
+ const current2 = readFileSync52(file.absPath, "utf-8");
123874
123922
  if (current2 !== file.after) {
123875
123923
  conflicts.push(file.path);
123876
123924
  continue;
123877
123925
  }
123878
- writeFileSync42(file.absPath, file.before, "utf-8");
123926
+ writeFileSync52(file.absPath, file.before, "utf-8");
123879
123927
  recordFileWriteReceipt(file.absPath, {
123880
123928
  path: file.path,
123881
123929
  version: fileContentVersion(file.before),
@@ -123918,7 +123966,7 @@ function registerFileRoutes(api, adapter2) {
123918
123966
  const fallbackTiming = typeof parsed.body.elementStart === "number" && typeof parsed.body.elementDuration === "number" ? { start: parsed.body.elementStart, duration: parsed.body.elementDuration } : void 0;
123919
123967
  let originalContent;
123920
123968
  try {
123921
- originalContent = readFileSync42(ctx.absPath, "utf-8");
123969
+ originalContent = readFileSync52(ctx.absPath, "utf-8");
123922
123970
  } catch {
123923
123971
  return c3.json({ error: "not found" }, 404);
123924
123972
  }
@@ -123940,15 +123988,15 @@ function registerFileRoutes(api, adapter2) {
123940
123988
  version: version22
123941
123989
  });
123942
123990
  }
123943
- const mutationResult = writeMutationResult(
123991
+ const mutationResult2 = writeMutationResult(
123944
123992
  c3,
123945
123993
  ctx.project.dir,
123946
123994
  ctx.filePath,
123947
123995
  ctx.absPath,
123948
123996
  result.html
123949
123997
  );
123950
- if (mutationResult instanceof Response) return mutationResult;
123951
- const { version: version2, backupPath } = mutationResult;
123998
+ if (mutationResult2 instanceof Response) return mutationResult2;
123999
+ const { version: version2, backupPath } = mutationResult2;
123952
124000
  c3.header("ETag", version2);
123953
124001
  return c3.json({
123954
124002
  ok: true,
@@ -123974,7 +124022,7 @@ function registerFileRoutes(api, adapter2) {
123974
124022
  }
123975
124023
  let originalContent;
123976
124024
  try {
123977
- originalContent = readFileSync42(ctx.absPath, "utf-8");
124025
+ originalContent = readFileSync52(ctx.absPath, "utf-8");
123978
124026
  } catch {
123979
124027
  return c3.json({ error: "not found" }, 404);
123980
124028
  }
@@ -123995,15 +124043,15 @@ function registerFileRoutes(api, adapter2) {
123995
124043
  version: version22
123996
124044
  });
123997
124045
  }
123998
- const mutationResult = writeMutationResult(
124046
+ const mutationResult2 = writeMutationResult(
123999
124047
  c3,
124000
124048
  ctx.project.dir,
124001
124049
  ctx.filePath,
124002
124050
  ctx.absPath,
124003
124051
  patched
124004
124052
  );
124005
- if (mutationResult instanceof Response) return mutationResult;
124006
- const { backupPath, version: version2 } = mutationResult;
124053
+ if (mutationResult2 instanceof Response) return mutationResult2;
124054
+ const { backupPath, version: version2 } = mutationResult2;
124007
124055
  c3.header("ETag", version2);
124008
124056
  return c3.json({
124009
124057
  ok: true,
@@ -124075,7 +124123,7 @@ function registerFileRoutes(api, adapter2) {
124075
124123
  }
124076
124124
  let originalContent;
124077
124125
  try {
124078
- originalContent = readFileSync42(ctx.absPath, "utf-8");
124126
+ originalContent = readFileSync52(ctx.absPath, "utf-8");
124079
124127
  } catch {
124080
124128
  return c3.json({ error: "not found" }, 404);
124081
124129
  }
@@ -124098,15 +124146,15 @@ function registerFileRoutes(api, adapter2) {
124098
124146
  result.error === "grouped elements must share a single parent" ? 422 : 400
124099
124147
  );
124100
124148
  }
124101
- const mutationResult = writeMutationResult(
124149
+ const mutationResult2 = writeMutationResult(
124102
124150
  c3,
124103
124151
  ctx.project.dir,
124104
124152
  ctx.filePath,
124105
124153
  ctx.absPath,
124106
124154
  result.html
124107
124155
  );
124108
- if (mutationResult instanceof Response) return mutationResult;
124109
- const { backupPath } = mutationResult;
124156
+ if (mutationResult2 instanceof Response) return mutationResult2;
124157
+ const { backupPath } = mutationResult2;
124110
124158
  return c3.json({
124111
124159
  ok: true,
124112
124160
  changed: true,
@@ -124130,7 +124178,7 @@ function registerFileRoutes(api, adapter2) {
124130
124178
  ).map((entry) => ({ target: entry.target, track: entry.track }));
124131
124179
  let originalContent;
124132
124180
  try {
124133
- originalContent = readFileSync42(ctx.absPath, "utf-8");
124181
+ originalContent = readFileSync52(ctx.absPath, "utf-8");
124134
124182
  } catch {
124135
124183
  return c3.json({ error: "not found" }, 404);
124136
124184
  }
@@ -124159,7 +124207,7 @@ function registerFileRoutes(api, adapter2) {
124159
124207
  if ("error" in parsed) return parsed.error;
124160
124208
  let content;
124161
124209
  try {
124162
- content = readFileSync42(ctx.absPath, "utf-8");
124210
+ content = readFileSync52(ctx.absPath, "utf-8");
124163
124211
  } catch {
124164
124212
  return c3.json({ exists: false });
124165
124213
  }
@@ -124202,7 +124250,7 @@ function registerFileRoutes(api, adapter2) {
124202
124250
  return c3.json({ error: "forbidden" }, 403);
124203
124251
  }
124204
124252
  ensureDir(destAbs);
124205
- writeFileSync42(destAbs, readFileSync42(srcAbs));
124253
+ writeFileSync52(destAbs, readFileSync52(srcAbs));
124206
124254
  return c3.json({ ok: true, path: copyPath }, 201);
124207
124255
  });
124208
124256
  const MAX_UPLOAD_BYTES = 500 * 1024 * 1024;
@@ -124232,7 +124280,7 @@ function registerFileRoutes(api, adapter2) {
124232
124280
  mustExist: true
124233
124281
  });
124234
124282
  if ("error" in res) return res.error;
124235
- const html = readFileSync42(res.absPath, "utf-8");
124283
+ const html = readFileSync52(res.absPath, "utf-8");
124236
124284
  const block = extractGsapScriptBlock(html);
124237
124285
  if (!block) {
124238
124286
  return c3.json({
@@ -124291,11 +124339,11 @@ function registerFileRoutes(api, adapter2) {
124291
124339
  if (!body || typeof body.expected !== "string" || typeof body.restore !== "string") {
124292
124340
  return c3.json({ error: "expected and restore contents required" }, 400);
124293
124341
  }
124294
- const current2 = readFileSync42(res.absPath, "utf-8");
124342
+ const current2 = readFileSync52(res.absPath, "utf-8");
124295
124343
  if (current2 !== body.expected) {
124296
124344
  return c3.json({ ok: true, restored: false, conflict: true });
124297
124345
  }
124298
- writeFileSync42(res.absPath, body.restore, "utf-8");
124346
+ writeFileSync52(res.absPath, body.restore, "utf-8");
124299
124347
  return c3.json({ ok: true, restored: true, conflict: false });
124300
124348
  });
124301
124349
  }
@@ -124400,7 +124448,7 @@ function tagRootCompositionFile(bodyHtml, compPath) {
124400
124448
  function buildSubCompositionHtml(projectDir, compPath, runtimeUrl, baseHref, rawOverride) {
124401
124449
  const compFile = join72(projectDir, compPath);
124402
124450
  if (!existsSync42(compFile)) return null;
124403
- const rawComp = rawOverride ?? readFileSync52(compFile, "utf-8");
124451
+ const rawComp = rawOverride ?? readFileSync62(compFile, "utf-8");
124404
124452
  let compHeadContent = "";
124405
124453
  let rewrittenContent;
124406
124454
  let htmlAttrs = "";
@@ -124433,7 +124481,7 @@ function buildSubCompositionHtml(projectDir, compPath, runtimeUrl, baseHref, raw
124433
124481
  const indexPath2 = join72(projectDir, "index.html");
124434
124482
  let headContent = "";
124435
124483
  if (existsSync42(indexPath2)) {
124436
- const indexHtml = readFileSync52(indexPath2, "utf-8");
124484
+ const indexHtml = readFileSync62(indexPath2, "utf-8");
124437
124485
  const headMatch = indexHtml.match(/<head[^>]*>([\s\S]*?)<\/head>/i);
124438
124486
  headContent = headMatch?.[1] ?? "";
124439
124487
  }
@@ -124471,9 +124519,9 @@ function persistHfIdsIfNeeded(filePath, html) {
124471
124519
  const idsAfter = (normalized2.match(/\bdata-hf-id=/g) ?? []).length;
124472
124520
  if (idsAfter > idsBefore) {
124473
124521
  try {
124474
- const current2 = readFileSync62(filePath, "utf-8");
124522
+ const current2 = readFileSync72(filePath, "utf-8");
124475
124523
  if (current2 === html) {
124476
- writeFileSync52(filePath, normalized2, "utf-8");
124524
+ writeFileSync62(filePath, normalized2, "utf-8");
124477
124525
  }
124478
124526
  } catch (err) {
124479
124527
  console.warn("[hyperframes] persistHfIdsIfNeeded: failed to write ids to disk:", err);
@@ -124499,7 +124547,7 @@ function stampFileHfIds(filePath) {
124499
124547
  if (fd === null) return null;
124500
124548
  try {
124501
124549
  if (!fstatSync4(fd).isFile()) return null;
124502
- const html = readFileSync62(fd, "utf-8");
124550
+ const html = readFileSync72(fd, "utf-8");
124503
124551
  const normalized2 = ensureHfIds(html);
124504
124552
  const idsBefore = (html.match(/\bdata-hf-id=/g) ?? []).length;
124505
124553
  const idsAfter = (normalized2.match(/\bdata-hf-id=/g) ?? []).length;
@@ -124549,7 +124597,7 @@ function readStudioMotionManifestContent(projectDir) {
124549
124597
  const manifestPath3 = join82(projectDir, STUDIO_MOTION_PATH);
124550
124598
  if (!existsSync52(manifestPath3)) return "";
124551
124599
  try {
124552
- return readFileSync72(manifestPath3, "utf-8");
124600
+ return readFileSync82(manifestPath3, "utf-8");
124553
124601
  } catch {
124554
124602
  return "";
124555
124603
  }
@@ -124675,14 +124723,14 @@ function resolveProjectMainHtml(projectDir, projectId) {
124675
124723
  const indexPath2 = join82(projectDir, "index.html");
124676
124724
  if (existsSync52(indexPath2)) {
124677
124725
  return {
124678
- html: readFileSync72(indexPath2, "utf-8"),
124726
+ html: readFileSync82(indexPath2, "utf-8"),
124679
124727
  compositionPath: "index.html"
124680
124728
  };
124681
124729
  }
124682
124730
  const blockHtmlPath = join82(projectDir, `${projectId}.html`);
124683
124731
  if (existsSync52(blockHtmlPath)) {
124684
124732
  return {
124685
- html: readFileSync72(blockHtmlPath, "utf-8"),
124733
+ html: readFileSync82(blockHtmlPath, "utf-8"),
124686
124734
  compositionPath: `${projectId}.html`
124687
124735
  };
124688
124736
  }
@@ -124876,7 +124924,7 @@ ${runtimeTag}`;
124876
124924
  }
124877
124925
  servedContentType = PROXY_VARIANT_CONFIG[proxyVariant].contentType;
124878
124926
  }
124879
- const textBuffer = isText2 ? Buffer.from(readFileSync72(file, "utf-8"), "utf-8") : null;
124927
+ const textBuffer = isText2 ? Buffer.from(readFileSync82(file, "utf-8"), "utf-8") : null;
124880
124928
  const totalSize2 = textBuffer ? textBuffer.length : statSync32(servedPath).size;
124881
124929
  const bodyFor = (start, end) => textBuffer ? new Uint8Array(textBuffer.subarray(start, end + 1)) : (
124882
124930
  // Node's web stream type and the DOM one do not overlap for tsc on
@@ -124935,7 +124983,7 @@ async function lintUncoveredHtml(adapter2, projectDir, htmlFiles, coveredFiles)
124935
124983
  const findings = [];
124936
124984
  for (const file of htmlFiles) {
124937
124985
  if (coveredFiles.has(file)) continue;
124938
- const content = readFileSync82(join92(projectDir, file), "utf-8");
124986
+ const content = readFileSync92(join92(projectDir, file), "utf-8");
124939
124987
  const result = await adapter2.lint(content, { filePath: file, isSubComposition: true });
124940
124988
  for (const finding of result?.findings ?? []) {
124941
124989
  findings.push({ ...finding, file });
@@ -125098,7 +125146,7 @@ function registerRenderRoutes(api, adapter2) {
125098
125146
  }
125099
125147
  const contentType = renderContentType(job.outputPath);
125100
125148
  const filename = job.outputPath.split("/").pop() ?? `render.mp4`;
125101
- const content = readFileSync92(job.outputPath);
125149
+ const content = readFileSync102(job.outputPath);
125102
125150
  return new Response(content, {
125103
125151
  headers: {
125104
125152
  "Content-Type": contentType,
@@ -125116,7 +125164,7 @@ function registerRenderRoutes(api, adapter2) {
125116
125164
  }
125117
125165
  const contentType = renderContentType(job.outputPath);
125118
125166
  const filename = job.outputPath.split("/").pop() ?? `render.mp4`;
125119
- const content = readFileSync92(job.outputPath);
125167
+ const content = readFileSync102(job.outputPath);
125120
125168
  return new Response(content, {
125121
125169
  headers: {
125122
125170
  "Content-Type": contentType,
@@ -125149,7 +125197,7 @@ function registerRenderRoutes(api, adapter2) {
125149
125197
  if (!fp) return c3.json({ error: "forbidden" }, 403);
125150
125198
  if (!existsSync62(fp)) return c3.json({ error: "not found" }, 404);
125151
125199
  const contentType = renderContentType(fp);
125152
- const content = readFileSync92(fp);
125200
+ const content = readFileSync102(fp);
125153
125201
  return new Response(content, {
125154
125202
  headers: {
125155
125203
  "Content-Type": contentType,
@@ -125173,7 +125221,7 @@ function registerRenderRoutes(api, adapter2) {
125173
125221
  let durationMs;
125174
125222
  if (existsSync62(metaPath)) {
125175
125223
  try {
125176
- const meta = JSON.parse(readFileSync92(metaPath, "utf-8"));
125224
+ const meta = JSON.parse(readFileSync102(metaPath, "utf-8"));
125177
125225
  if (meta.status === "failed" && !existsSync62(fp)) status = "failed";
125178
125226
  if (meta.durationMs) durationMs = meta.durationMs;
125179
125227
  } catch {
@@ -125236,7 +125284,7 @@ function pruneThumbnailCache(cacheDir, protectedPaths, now = Date.now()) {
125236
125284
  function writeThumbnailAtomically(path, buffer) {
125237
125285
  const temporaryPath = `${path}.${process.pid}.${randomUUID32()}.tmp`;
125238
125286
  try {
125239
- writeFileSync62(temporaryPath, buffer, { flag: "wx" });
125287
+ writeFileSync72(temporaryPath, buffer, { flag: "wx" });
125240
125288
  renameSync32(temporaryPath, path);
125241
125289
  } finally {
125242
125290
  rmSync42(temporaryPath, { force: true });
@@ -125274,7 +125322,7 @@ function registerThumbnailRoutes(api, adapter2) {
125274
125322
  let sourceKey = "";
125275
125323
  const htmlFile = join112(project.dir, compPath);
125276
125324
  if (existsSync72(htmlFile)) {
125277
- const html = readFileSync102(htmlFile, "utf-8");
125325
+ const html = readFileSync112(htmlFile, "utf-8");
125278
125326
  sourceKey = `_${createHash42("sha1").update(html).digest("hex").slice(0, 16)}`;
125279
125327
  sourceMtime = Math.round(statSync52(htmlFile).mtimeMs);
125280
125328
  if (!vpWidth) {
@@ -125287,14 +125335,14 @@ function registerThumbnailRoutes(api, adapter2) {
125287
125335
  const manualEditsFile = join112(project.dir, STUDIO_MANUAL_EDITS_PATH);
125288
125336
  let manualEditsKey = "";
125289
125337
  if (existsSync72(manualEditsFile)) {
125290
- const manualEditsContent = readFileSync102(manualEditsFile, "utf-8");
125338
+ const manualEditsContent = readFileSync112(manualEditsFile, "utf-8");
125291
125339
  manualEditsKey = `_${createHash42("sha1").update(manualEditsContent).digest("hex").slice(0, 16)}`;
125292
125340
  sourceMtime = Math.max(sourceMtime, Math.round(statSync52(manualEditsFile).mtimeMs));
125293
125341
  }
125294
125342
  const motionFile = join112(project.dir, STUDIO_MOTION_PATH);
125295
125343
  let motionKey = "";
125296
125344
  if (existsSync72(motionFile)) {
125297
- const motionContent = readFileSync102(motionFile, "utf-8");
125345
+ const motionContent = readFileSync112(motionFile, "utf-8");
125298
125346
  motionKey = `_${createHash42("sha1").update(motionContent).digest("hex").slice(0, 16)}`;
125299
125347
  sourceMtime = Math.max(sourceMtime, Math.round(statSync52(motionFile).mtimeMs));
125300
125348
  }
@@ -125316,7 +125364,7 @@ function registerThumbnailRoutes(api, adapter2) {
125316
125364
  );
125317
125365
  }
125318
125366
  if (existsSync72(cachePath2)) {
125319
- return new Response(new Uint8Array(readFileSync102(cachePath2)), {
125367
+ return new Response(new Uint8Array(readFileSync112(cachePath2)), {
125320
125368
  headers: { "Content-Type": contentType, "Cache-Control": "no-cache" }
125321
125369
  });
125322
125370
  }
@@ -125383,7 +125431,7 @@ function registerWaveformRoutes(api, adapter2) {
125383
125431
  const cachePath2 = join122(cacheDir, buildWaveformCacheKey(assetPath, stats));
125384
125432
  try {
125385
125433
  if (isWaveformCacheDirectory(cacheDir) && existsSync82(cachePath2)) {
125386
- const peaks2 = JSON.parse(readFileSync112(cachePath2, "utf-8"));
125434
+ const peaks2 = JSON.parse(readFileSync122(cachePath2, "utf-8"));
125387
125435
  return c3.json({ peaks: peaks2 });
125388
125436
  }
125389
125437
  } catch {
@@ -125809,7 +125857,7 @@ function readGlobalAssets(home = homedir10()) {
125809
125857
  const manifestPath3 = join142(home, ".media", "manifest.jsonl");
125810
125858
  if (!existsSync102(manifestPath3)) return [];
125811
125859
  const out = [];
125812
- for (const line3 of readFileSync122(manifestPath3, "utf8").split("\n")) {
125860
+ for (const line3 of readFileSync132(manifestPath3, "utf8").split("\n")) {
125813
125861
  if (!line3.trim()) continue;
125814
125862
  try {
125815
125863
  const rec = JSON.parse(line3);
@@ -155540,6 +155588,7 @@ __export(studio_api_exports, {
155540
155588
  STUDIO_MANUAL_EDITS_PATH: () => STUDIO_MANUAL_EDITS_PATH,
155541
155589
  STUDIO_MOTION_PATH: () => STUDIO_MOTION_PATH,
155542
155590
  affectsProjectSignature: () => affectsProjectSignature,
155591
+ applyFileMutations: () => applyFileMutations,
155543
155592
  buildSubCompositionHtml: () => buildSubCompositionHtml,
155544
155593
  consumeFileWriteReceipt: () => consumeFileWriteReceipt,
155545
155594
  createBackgroundRemovalJob: () => createBackgroundRemovalJob,
@@ -155553,6 +155602,9 @@ __export(studio_api_exports, {
155553
155602
  getMimeType: () => getMimeType,
155554
155603
  identifyFileWrite: () => identifyFileWrite,
155555
155604
  isSafePath: () => isSafePath,
155605
+ patchElementInHtml: () => patchElementInHtml,
155606
+ removeElementFromHtml: () => removeElementFromHtml2,
155607
+ splitElementInHtml: () => splitElementInHtml,
155556
155608
  thumbnailDeviceScaleFactor: () => thumbnailDeviceScaleFactor,
155557
155609
  walkDir: () => walkDir
155558
155610
  });
@@ -170293,27 +170345,32 @@ function mainTimelineStart(scope, el, start) {
170293
170345
  ordinaryStart
170294
170346
  });
170295
170347
  }
170296
- async function describeRow(scope, node, depth) {
170348
+ async function resolveRowTiming(scope, node, depth) {
170297
170349
  const { el } = node;
170298
- const { doc, startCache } = scope;
170299
- const start = resolveReferencedStart(doc, el, startCache, /* @__PURE__ */ new Set());
170300
- const authored = resolveReferencedDuration(doc, el, startCache, /* @__PURE__ */ new Set());
170350
+ const start = resolveReferencedStart(scope.doc, el, scope.startCache, /* @__PURE__ */ new Set());
170351
+ const authored = resolveReferencedDuration(scope.doc, el, scope.startCache, /* @__PURE__ */ new Set());
170301
170352
  const host = el.getAttribute("data-composition-src");
170302
170353
  const absStart = mainTimelineStart(scope, el, start);
170303
170354
  const children = host && depth === 0 ? await readSubComposition(host, scope, absStart) : [];
170304
170355
  const kind = el.tagName.toLowerCase();
170305
- const { duration, durationSource, pendingReason: pendingReason2 } = MEDIA_TAG.test(kind) ? await resolveMediaRowDuration(scope, el, kind, authored) : resolveContainerDuration(authored, children);
170356
+ const duration = MEDIA_TAG.test(kind) ? await resolveMediaRowDuration(scope, el, kind, authored) : resolveContainerDuration(authored, children);
170357
+ return { start, authored, absStart, children, duration };
170358
+ }
170359
+ function describeRowFields(scope, node, timing2) {
170360
+ const { el } = node;
170361
+ const kind = el.tagName.toLowerCase();
170362
+ const host = el.getAttribute("data-composition-src");
170306
170363
  const rate = parseNumeric2(el.getAttribute("data-playback-rate"));
170307
170364
  return {
170308
170365
  id: el.id || el.getAttribute("data-composition-id") || kind,
170309
170366
  label: null,
170310
170367
  kind,
170311
170368
  trackKind: trackKindOf(node).kind,
170312
- start,
170313
- duration,
170314
- end: start + duration,
170315
- absStart: roundMs(absStart),
170316
- absEnd: roundMs(absStart + duration),
170369
+ start: timing2.start,
170370
+ duration: timing2.duration.duration,
170371
+ end: timing2.start + timing2.duration.duration,
170372
+ absStart: roundMs(timing2.absStart),
170373
+ absEnd: roundMs(timing2.absStart + timing2.duration.duration),
170317
170374
  file: scope.file,
170318
170375
  trackIndex: parseNumeric2(el.getAttribute("data-track-index")) ?? 0,
170319
170376
  src: el.getAttribute("src") ?? host,
@@ -170323,17 +170380,26 @@ async function describeRow(scope, node, depth) {
170323
170380
  playbackRate: rate === 1 ? null : rate,
170324
170381
  audioGroup: el.getAttribute(HF_AUDIO_GROUP_ATTR),
170325
170382
  role: null,
170326
- durationAuthored: authored !== null,
170327
- durationSource,
170328
- pendingReason: pendingReason2,
170329
- children
170383
+ durationAuthored: timing2.authored !== null,
170384
+ durationSource: timing2.duration.durationSource,
170385
+ pendingReason: timing2.duration.pendingReason,
170386
+ children: timing2.children,
170387
+ ref: "",
170388
+ warnings: [],
170389
+ elementId: el.id || null,
170390
+ hfId: el.getAttribute("data-hf-id")
170330
170391
  };
170331
170392
  }
170393
+ async function describeRow(scope, node, depth) {
170394
+ return describeRowFields(scope, node, await resolveRowTiming(scope, node, depth));
170395
+ }
170332
170396
  async function readSubComposition(src, parent, origin) {
170333
170397
  const authored = resolve64(parent.dir, src);
170334
170398
  const file = realFileInside(parent.projectDir, authored);
170335
170399
  if (!file) return [];
170336
- const doc = new DOMParser().parseFromString(readFileSync76(file, "utf-8"), "text/html");
170400
+ const relativeFile = relative30(parent.projectDir, authored).split(sep17).join("/");
170401
+ const source = parent.sourceOverrides.get(relativeFile) ?? readFileSync76(file, "utf-8");
170402
+ const doc = new DOMParser().parseFromString(source, "text/html");
170337
170403
  const template = doc.querySelector("template");
170338
170404
  const root = (template?.content ?? doc).querySelector("[data-composition-id]");
170339
170405
  if (!root) return [];
@@ -170345,7 +170411,8 @@ async function readSubComposition(src, parent, origin) {
170345
170411
  origin,
170346
170412
  file: relative30(parent.projectDir, authored).split(sep17).join("/"),
170347
170413
  withProbeSlot: parent.withProbeSlot,
170348
- measure: parent.measure
170414
+ measure: parent.measure,
170415
+ sourceOverrides: parent.sourceOverrides
170349
170416
  };
170350
170417
  const rows = await Promise.all(
170351
170418
  topLevelElements(toNode2(root)).map((node) => describeRow(scope, node, 1))
@@ -170375,22 +170442,42 @@ function flatten(top) {
170375
170442
  byKind.flatMap((t) => t.entries.map((e2, index) => [e2.clip, { kind: t.kind, index }]))
170376
170443
  );
170377
170444
  const at4 = (clip) => pointers.get(clip);
170445
+ const fileIds = /* @__PURE__ */ new Map();
170446
+ for (const { entries: entries3 } of byKind) {
170447
+ for (const { clip } of entries3) {
170448
+ if (!clip.elementId) continue;
170449
+ const ids = fileIds.get(clip.file) ?? /* @__PURE__ */ new Map();
170450
+ ids.set(clip.elementId, (ids.get(clip.elementId) ?? 0) + 1);
170451
+ fileIds.set(clip.file, ids);
170452
+ }
170453
+ }
170378
170454
  return byKind.map(({ kind, entries: kindEntries }) => ({
170379
170455
  kind,
170380
- rows: kindEntries.map(({ clip, host }, index) => ({
170381
- ...clip,
170382
- index,
170383
- nested: host !== null,
170384
- host: host && host.id,
170385
- hostRow: host && at4(host),
170386
- children: clip.children.map(at4)
170387
- }))
170456
+ rows: kindEntries.map(({ clip, host }, index) => {
170457
+ const uniqueId = clip.elementId && fileIds.get(clip.file)?.get(clip.elementId) === 1;
170458
+ const ref2 = uniqueId ? `#${clip.elementId}` : clip.hfId ? `hf:${clip.hfId}` : `${kind}/${index}`;
170459
+ const warnings = uniqueId || clip.hfId ? [] : [
170460
+ 'row has no stable name; add id="<name>" to the element in this file, then retry with #<name>'
170461
+ ];
170462
+ return {
170463
+ ...clip,
170464
+ index,
170465
+ ref: ref2,
170466
+ warnings,
170467
+ nested: host !== null,
170468
+ host: host && host.id,
170469
+ hostRow: host && at4(host),
170470
+ children: clip.children.map(at4)
170471
+ };
170472
+ })
170388
170473
  }));
170389
170474
  }
170390
- async function describeProject(indexPath2, measure = measureWithFfprobe) {
170391
- const doc = new DOMParser().parseFromString(readFileSync76(indexPath2, "utf-8"), "text/html");
170475
+ async function describeProject(indexPath2, measure = measureWithFfprobe, sourceOverrides = /* @__PURE__ */ new Map()) {
170476
+ const projectDir = dirname55(indexPath2);
170477
+ const source = sourceOverrides.get(basename24(indexPath2)) ?? readFileSync76(indexPath2, "utf-8");
170478
+ const doc = new DOMParser().parseFromString(source, "text/html");
170392
170479
  const root = doc.querySelector("[data-composition-id]") ?? doc.body;
170393
- const dir = dirname55(indexPath2);
170480
+ const dir = projectDir;
170394
170481
  const scope = {
170395
170482
  doc,
170396
170483
  dir,
@@ -170399,7 +170486,8 @@ async function describeProject(indexPath2, measure = measureWithFfprobe) {
170399
170486
  origin: 0,
170400
170487
  file: basename24(indexPath2),
170401
170488
  withProbeSlot: createProbeGate(PROBE_CONCURRENCY3),
170402
- measure
170489
+ measure,
170490
+ sourceOverrides
170403
170491
  };
170404
170492
  const rows = (await Promise.all(topLevelElements(toNode2(root)).map((node) => describeRow(scope, node, 0)))).sort(byStart);
170405
170493
  const declared = parseNumeric2(root.getAttribute("data-duration"));
@@ -170470,7 +170558,7 @@ function details(row) {
170470
170558
  }
170471
170559
  function line2(row, total) {
170472
170560
  const times = row.host ? `${span(row.absStart, row.absEnd)} (local ${span(row.start, row.end)}) nested in ${row.host} ${row.file}` : span(row.start, row.end);
170473
- return ` |${bar(row, total)}| ${row.id} ${times} ${details(row)}`.trimEnd();
170561
+ return ` |${bar(row, total)}| ${row.ref} ${row.id} ${times} ${details(row)}`.trimEnd();
170474
170562
  }
170475
170563
  function header({ kind, rows }) {
170476
170564
  const nested = rows.filter((r) => r.nested).length;
@@ -170495,36 +170583,554 @@ var init_formatTimeline = __esm({
170495
170583
  }
170496
170584
  });
170497
170585
 
170586
+ // src/timeline/resolveRef.ts
170587
+ function targetFor(row) {
170588
+ if (row.ref.startsWith("#")) return { id: row.ref.slice(1) };
170589
+ if (row.ref.startsWith("hf:")) return { hfId: row.ref.slice(3) };
170590
+ return row.id && row.id !== row.kind ? { id: row.id } : null;
170591
+ }
170592
+ function resolveRef(timeline, ref2) {
170593
+ const rows = timeline.tracks.flatMap((track) => track.rows);
170594
+ const matches2 = ref2.startsWith("#") ? rows.filter((row2) => row2.ref === ref2 || row2.id === ref2.slice(1)) : ref2.startsWith("hf:") ? rows.filter((row2) => row2.ref === ref2) : (() => {
170595
+ const match = /^(video|graphics|captions|audio)\/(\d+)$/.exec(ref2);
170596
+ if (!match) return [];
170597
+ const row2 = timeline.tracks.find((track) => track.kind === match[1])?.rows.find((candidate) => candidate.index === Number(match[2]));
170598
+ return row2 ? [row2] : [];
170599
+ })();
170600
+ if (matches2.length !== 1) {
170601
+ return {
170602
+ ok: false,
170603
+ reason: matches2.length === 0 ? `${ref2} was not found` : `${ref2} matches ${matches2.length} rows`,
170604
+ fix: 'add id="<name>" to the element in its source file, then retry with #<name>'
170605
+ };
170606
+ }
170607
+ const row = matches2[0];
170608
+ const target = targetFor(row);
170609
+ if (!target) {
170610
+ return {
170611
+ ok: false,
170612
+ reason: `${ref2} has no stable element id in ${row.file}`,
170613
+ fix: `add id="<name>" to the element in ${row.file}, then retry with #<name>`
170614
+ };
170615
+ }
170616
+ return { ok: true, row, target };
170617
+ }
170618
+ var init_resolveRef = __esm({
170619
+ "src/timeline/resolveRef.ts"() {
170620
+ "use strict";
170621
+ }
170622
+ });
170623
+
170624
+ // src/timeline/timeExpr.ts
170625
+ function parseTimeExpression(expression, context) {
170626
+ const raw = expression.trim();
170627
+ if (raw === "end") return { ok: true, seconds: context.duration };
170628
+ const frames = /^(-?\d+(?:\.\d+)?)f$/.exec(raw);
170629
+ if (frames) return { ok: true, seconds: Number(frames[1]) / context.fps };
170630
+ const absolute = /^\d+(?:\.\d+)?$/.test(raw);
170631
+ const relative38 = /^[+-]\d+(?:\.\d+)?$/.test(raw);
170632
+ if (absolute) return { ok: true, seconds: Number(raw) };
170633
+ if (relative38) return { ok: true, seconds: context.row.start + Number(raw) };
170634
+ const anchor = /^(after|before|start-of|end-of):(.+)$/.exec(raw);
170635
+ if (!anchor) return { ok: false, reason: `invalid time expression: ${expression}` };
170636
+ const row = context.resolveAnchor(anchor[2]);
170637
+ if (!row) return { ok: false, reason: `anchor ref not found: ${anchor[2]}` };
170638
+ switch (anchor[1]) {
170639
+ case "after":
170640
+ case "end-of":
170641
+ return { ok: true, seconds: row.end };
170642
+ case "before":
170643
+ case "start-of":
170644
+ return { ok: true, seconds: row.start };
170645
+ }
170646
+ return { ok: false, reason: `invalid time anchor: ${anchor[1]}` };
170647
+ }
170648
+ var init_timeExpr = __esm({
170649
+ "src/timeline/timeExpr.ts"() {
170650
+ "use strict";
170651
+ }
170652
+ });
170653
+
170498
170654
  // src/commands/timeline.ts
170499
170655
  var timeline_exports = {};
170500
170656
  __export(timeline_exports, {
170501
170657
  default: () => timeline_default,
170502
170658
  examples: () => examples19
170503
170659
  });
170504
- var examples19, timeline_default;
170660
+ import { readFileSync as readFileSync77 } from "fs";
170661
+ import { join as join116 } from "path";
170662
+ function refusal(reason, fix, json) {
170663
+ setCommandExitCode(2);
170664
+ const payload = { ok: false, reason, fix };
170665
+ console.error(json ? JSON.stringify(payload, null, 2) : `${reason}; ${fix}.`);
170666
+ }
170667
+ function fpsFor(indexPath2) {
170668
+ const parsed = parseFpsWithDefault(
170669
+ readCompositionFps(readFileSync77(indexPath2, "utf-8")) ?? void 0
170670
+ );
170671
+ return fpsToNumber(parsed.ok ? parsed.value : { num: 30, den: 1 });
170672
+ }
170673
+ function declaredFps(source) {
170674
+ const raw = readCompositionFps(source);
170675
+ if (raw === null) return null;
170676
+ const parsed = parseFpsWithDefault(raw);
170677
+ return parsed.ok ? fpsToNumber(parsed.value) : null;
170678
+ }
170679
+ function splitBaseId(row) {
170680
+ return row.ref.startsWith("hf:") ? row.ref.slice(3) : row.id;
170681
+ }
170682
+ function nextFreeSplitId(source, base2) {
170683
+ const document2 = new DOMParser().parseFromString(source, "text/html");
170684
+ const existing = new Set(Array.from(document2.querySelectorAll("[id]"), (element) => element.id));
170685
+ const match = /^(.*)-(\d+)$/.exec(base2);
170686
+ const prefix = match && existing.has(match[1]) ? match[1] : base2;
170687
+ let suffix = 2;
170688
+ while (existing.has(`${prefix}-${suffix}`)) suffix += 1;
170689
+ return `${prefix}-${suffix}`;
170690
+ }
170691
+ function parseMutationTime(context, expression, fix) {
170692
+ const value = context.parseTime(expression);
170693
+ if (!value.ok) return { ok: false, reason: value.reason, fix };
170694
+ if (value.seconds < 0 || value.seconds > context.duration) {
170695
+ return {
170696
+ ok: false,
170697
+ reason: `time ${value.seconds} is outside the composition duration`,
170698
+ fix: "pass a time between 0 and the composition duration"
170699
+ };
170700
+ }
170701
+ return { ok: true, seconds: value.seconds };
170702
+ }
170703
+ function diff(before3, after2) {
170704
+ if (before3 === after2) return "";
170705
+ const beforeLines = before3.split("\n");
170706
+ const afterLines = after2.split("\n");
170707
+ let prefix = 0;
170708
+ while (prefix < beforeLines.length && prefix < afterLines.length && beforeLines[prefix] === afterLines[prefix]) {
170709
+ prefix += 1;
170710
+ }
170711
+ let suffix = 0;
170712
+ while (suffix < beforeLines.length - prefix && suffix < afterLines.length - prefix && beforeLines[beforeLines.length - suffix - 1] === afterLines[afterLines.length - suffix - 1]) {
170713
+ suffix += 1;
170714
+ }
170715
+ const changedBefore = beforeLines.slice(prefix, beforeLines.length - suffix);
170716
+ const changedAfter = afterLines.slice(prefix, afterLines.length - suffix);
170717
+ const lines = [
170718
+ "--- before",
170719
+ "+++ after",
170720
+ ...changedBefore.map((line3) => `-${line3}`),
170721
+ ...changedAfter.map((line3) => `+${line3}`)
170722
+ ];
170723
+ const output = lines.join("\n");
170724
+ return output.length <= 8e3 ? output : `${output.slice(0, 7997)}...`;
170725
+ }
170726
+ function overlap(row, timeline, start, end) {
170727
+ return allRows(timeline).find(
170728
+ (candidate) => candidate !== row && candidate.file === row.file && candidate.trackIndex === row.trackIndex && Math.max(start, candidate.start) < Math.min(end, candidate.end)
170729
+ );
170730
+ }
170731
+ function moveMutation(context, args) {
170732
+ const expression = typeof args.time === "string" ? args.time : "";
170733
+ const time = parseMutationTime(context, expression, "pass a valid time expression");
170734
+ if (!time.ok) return time;
170735
+ const end = time.seconds + context.row.duration;
170736
+ if (end > context.duration) {
170737
+ return {
170738
+ ok: false,
170739
+ reason: `move would end at ${end}, beyond composition duration ${context.duration}`,
170740
+ fix: `choose a start at or before the latest valid start ${context.duration - context.row.duration}`
170741
+ };
170742
+ }
170743
+ const patched = patchElementInHtml(context.before, context.resolved.target, [
170744
+ { type: "html-attribute", property: "data-start", value: String(time.seconds) }
170745
+ ]);
170746
+ if (!patched.matched) {
170747
+ return { ok: false, reason: `${context.ref} was not found`, fix: "choose an existing clip" };
170748
+ }
170749
+ return {
170750
+ ok: true,
170751
+ after: patched.html,
170752
+ nextStart: time.seconds,
170753
+ nextDuration: context.row.duration
170754
+ };
170755
+ }
170756
+ function splitMutation(context, args) {
170757
+ const expression = typeof args.time === "string" ? args.time : "";
170758
+ const time = parseMutationTime(context, expression, "pass a valid time expression");
170759
+ if (!time.ok) return time;
170760
+ const split = splitElementInHtml(
170761
+ context.before,
170762
+ context.resolved.target,
170763
+ time.seconds,
170764
+ nextFreeSplitId(context.before, splitBaseId(context.row)),
170765
+ {
170766
+ start: context.row.start,
170767
+ duration: context.row.duration,
170768
+ track: context.row.trackIndex
170769
+ }
170770
+ );
170771
+ if (!split.matched || !split.newId) {
170772
+ return {
170773
+ ok: false,
170774
+ reason: `${context.ref} cannot be split at ${time.seconds}`,
170775
+ fix: "choose a time inside the clip"
170776
+ };
170777
+ }
170778
+ return {
170779
+ ok: true,
170780
+ after: split.html,
170781
+ nextStart: context.row.start,
170782
+ nextDuration: context.row.duration
170783
+ };
170784
+ }
170785
+ function trimMutation(context, args) {
170786
+ const bounds = trimBounds(context, args);
170787
+ if (!bounds.ok) return bounds;
170788
+ const patched = patchElementInHtml(context.before, context.resolved.target, [
170789
+ { type: "html-attribute", property: "data-start", value: String(bounds.nextStart) },
170790
+ { type: "html-attribute", property: "data-duration", value: String(bounds.nextDuration) }
170791
+ ]);
170792
+ if (!patched.matched) {
170793
+ return { ok: false, reason: `${context.ref} was not found`, fix: "choose an existing clip" };
170794
+ }
170795
+ return {
170796
+ ok: true,
170797
+ after: patched.html,
170798
+ nextStart: bounds.nextStart,
170799
+ nextDuration: bounds.nextDuration
170800
+ };
170801
+ }
170802
+ function trimBounds(context, args) {
170803
+ const input2 = trimInput(args);
170804
+ if (!input2.ok) return input2;
170805
+ const start = trimStart(context, input2.start);
170806
+ if (!start.ok) return start;
170807
+ return finishTrim(
170808
+ context,
170809
+ start.seconds,
170810
+ trimEnd(context, input2.end),
170811
+ trimDuration(context, input2.duration)
170812
+ );
170813
+ }
170814
+ function trimInput(args) {
170815
+ const input2 = {
170816
+ start: typeof args.start === "string" ? args.start : void 0,
170817
+ end: typeof args.end === "string" ? args.end : void 0,
170818
+ duration: typeof args.duration === "string" ? args.duration : void 0
170819
+ };
170820
+ if (input2.start || input2.end || input2.duration) return { ok: true, ...input2 };
170821
+ return {
170822
+ ok: false,
170823
+ reason: "trim requires --start, --end, or --duration",
170824
+ fix: "pass one trim option"
170825
+ };
170826
+ }
170827
+ function finishTrim(context, nextStart, end, duration) {
170828
+ if (end && !end.ok) return end;
170829
+ if (duration && !duration.ok) return duration;
170830
+ const nextDuration = duration?.seconds ?? (end ? end.seconds - nextStart : context.row.duration);
170831
+ if (nextDuration <= 0) {
170832
+ return {
170833
+ ok: false,
170834
+ reason: "trim duration must be positive",
170835
+ fix: "choose a later end or positive duration"
170836
+ };
170837
+ }
170838
+ return { ok: true, nextStart, nextDuration };
170839
+ }
170840
+ function trimStart(context, expression) {
170841
+ if (!expression) return { ok: true, seconds: context.row.start };
170842
+ return parseMutationTime(context, expression, "pass a valid time expression");
170843
+ }
170844
+ function trimEnd(context, expression) {
170845
+ if (!expression) return void 0;
170846
+ return parseMutationTime(context, expression, "pass a valid time expression");
170847
+ }
170848
+ function trimDuration(context, expression) {
170849
+ if (!expression) return void 0;
170850
+ return parseMutationTime(context, expression, "pass a valid duration");
170851
+ }
170852
+ function deleteMutation(context) {
170853
+ return {
170854
+ ok: true,
170855
+ after: removeElementFromHtml2(context.before, context.resolved.target),
170856
+ nextStart: context.row.start,
170857
+ nextDuration: context.row.duration
170858
+ };
170859
+ }
170860
+ function decideMutation(verb, context, args) {
170861
+ switch (verb) {
170862
+ case "move":
170863
+ return moveMutation(context, args);
170864
+ case "trim":
170865
+ return trimMutation(context, args);
170866
+ case "split":
170867
+ return splitMutation(context, args);
170868
+ case "delete":
170869
+ return deleteMutation(context);
170870
+ }
170871
+ }
170872
+ async function runMutation(verb, args) {
170873
+ const setup = await prepareMutation(args);
170874
+ if (!setup.ok) return refusal(setup.reason, setup.fix, setup.json);
170875
+ const decision = decideMutation(verb, setup.context, args);
170876
+ if (!decision.ok) return refusal(decision.reason, decision.fix, setup.json);
170877
+ await finishMutation(setup, verb, decision);
170878
+ }
170879
+ async function prepareMutation(args) {
170880
+ const project = resolveProject(typeof args.dir === "string" ? args.dir : void 0);
170881
+ const ref2 = typeof args.ref === "string" ? args.ref : "";
170882
+ const json = args.json === true;
170883
+ const plan2 = args.plan === true;
170884
+ const overwrite = args.overwrite === true;
170885
+ const snap = args.snap === true;
170886
+ ensureDOMParser();
170887
+ const indexSource = readFileSync77(project.indexPath, "utf-8");
170888
+ const initialTimeline = await describeProject(
170889
+ project.indexPath,
170890
+ void 0,
170891
+ /* @__PURE__ */ new Map([["index.html", indexSource]])
170892
+ );
170893
+ const projectFps = declaredFps(indexSource);
170894
+ if (snap && projectFps === null) {
170895
+ return {
170896
+ ok: false,
170897
+ reason: "project fps is unknown",
170898
+ fix: "set data-fps on the project, then rerun with --snap",
170899
+ json
170900
+ };
170901
+ }
170902
+ const initialResolved = resolveRef(initialTimeline, ref2);
170903
+ if (!initialResolved.ok)
170904
+ return { ok: false, reason: initialResolved.reason, fix: initialResolved.fix, json };
170905
+ const initialRow = initialResolved.row;
170906
+ const filePath = join116(project.dir, initialRow.file);
170907
+ const before3 = readFileSync77(filePath, "utf-8");
170908
+ const expectedVersion = fileContentVersion(before3);
170909
+ const sources = /* @__PURE__ */ new Map([
170910
+ ["index.html", indexSource],
170911
+ [initialRow.file, before3]
170912
+ ]);
170913
+ const timeline = await describeProject(project.indexPath, void 0, sources);
170914
+ const resolved2 = resolveRef(timeline, ref2);
170915
+ if (!resolved2.ok) return { ok: false, reason: resolved2.reason, fix: resolved2.fix, json };
170916
+ const row = resolved2.row;
170917
+ const anchor = (anchorRef) => {
170918
+ const found = resolveRef(timeline, anchorRef);
170919
+ return found.ok ? found.row : void 0;
170920
+ };
170921
+ const parseTime = (expression) => {
170922
+ const parsed = parseTimeExpression(expression, {
170923
+ row,
170924
+ duration: timeline.duration,
170925
+ fps: fpsFor(project.indexPath),
170926
+ resolveAnchor: anchor
170927
+ });
170928
+ if (!parsed.ok || !snap) return parsed;
170929
+ return { ok: true, seconds: Math.round(parsed.seconds * projectFps) / projectFps };
170930
+ };
170931
+ return {
170932
+ ok: true,
170933
+ project,
170934
+ ref: ref2,
170935
+ json,
170936
+ plan: plan2,
170937
+ overwrite,
170938
+ timeline,
170939
+ indexSource,
170940
+ row,
170941
+ resolved: resolved2,
170942
+ filePath,
170943
+ before: before3,
170944
+ expectedVersion,
170945
+ context: {
170946
+ ref: ref2,
170947
+ row,
170948
+ before: before3,
170949
+ resolved: resolved2,
170950
+ parseTime,
170951
+ // Nested rows use the visible host clip duration as their composition bound.
170952
+ duration: row.nested && row.hostRow ? rowAt(timeline, row.hostRow).duration : timeline.duration
170953
+ }
170954
+ };
170955
+ }
170956
+ async function finishMutation(setup, verb, decision) {
170957
+ const { after: after2, nextStart, nextDuration } = decision;
170958
+ const { ref: ref2, row, timeline, json, overwrite, project, before: before3 } = setup;
170959
+ const refusalMessage = mutationRefusal(verb, after2, before3, ref2);
170960
+ if (refusalMessage) return refusal(refusalMessage.reason, refusalMessage.fix, json);
170961
+ const conflict = mutationConflict(verb, overwrite, row, timeline, nextStart, nextDuration);
170962
+ if (conflict) return refusal(conflict.reason, conflict.fix, json);
170963
+ const describeSource2 = (source) => describeProject(
170964
+ project.indexPath,
170965
+ void 0,
170966
+ /* @__PURE__ */ new Map([
170967
+ ["index.html", setup.indexSource],
170968
+ [row.file, source]
170969
+ ])
170970
+ );
170971
+ const describedBefore = await describeSource2(before3);
170972
+ const result = mutationResult(row, describedBefore, setup.plan);
170973
+ if (setup.plan) return printPlan(result, json, timeline, describeSource2, after2, before3);
170974
+ return applyAndPrint({
170975
+ setup,
170976
+ verb,
170977
+ json,
170978
+ row,
170979
+ nextStart,
170980
+ nextDuration,
170981
+ after: after2,
170982
+ before: before3,
170983
+ result,
170984
+ describeSource: describeSource2
170985
+ });
170986
+ }
170987
+ async function applyAndPrint(args) {
170988
+ const receipt = applyMutation(args.setup, args.after);
170989
+ if (receipt && "error" in receipt)
170990
+ return refusal(receipt.error, "re-run hyperframes timeline", args.json);
170991
+ if (!receipt && args.after !== args.before) {
170992
+ return refusal("mutation produced no receipt", "re-run hyperframes timeline", args.json);
170993
+ }
170994
+ args.result.after = rowsForFile(await args.describeSource(args.after), args.row.file);
170995
+ args.result.receipt = receipt ? {
170996
+ file: receipt.sourceFile,
170997
+ version: receipt.version,
170998
+ writeToken: receipt.writeToken,
170999
+ changed: receipt.changed,
171000
+ backupPath: receipt.backupPath
171001
+ } : null;
171002
+ if (args.json) {
171003
+ console.log(JSON.stringify(withMeta(args.result), null, 2));
171004
+ return;
171005
+ }
171006
+ console.log(
171007
+ `${args.verb} ${args.row.ref}: ${args.row.start}-${args.row.end}s -> ${args.nextStart}-${args.nextStart + args.nextDuration}s
171008
+ receipt: ${receipt?.version ?? "unchanged"}`
171009
+ );
171010
+ }
171011
+ function rowsForFile(timeline, file) {
171012
+ return allRows(timeline).filter((candidate) => candidate.file === file);
171013
+ }
171014
+ function rowAt(timeline, pointer) {
171015
+ const track = timeline.tracks.find((candidate) => candidate.kind === pointer.kind);
171016
+ if (!track) throw new Error(`missing track ${pointer.kind}`);
171017
+ const row = track.rows[pointer.index];
171018
+ if (!row) throw new Error(`missing row ${pointer.kind}/${pointer.index}`);
171019
+ return row;
171020
+ }
171021
+ function mutationResult(row, before3, planned) {
171022
+ return {
171023
+ ok: true,
171024
+ receipt: null,
171025
+ file: row.file,
171026
+ before: rowsForFile(before3, row.file),
171027
+ after: [],
171028
+ warnings: row.warnings,
171029
+ planned
171030
+ };
171031
+ }
171032
+ async function printPlan(result, json, timeline, describeSource2, after2, before3) {
171033
+ const plannedTimeline = await describeSource2(after2);
171034
+ result.after = rowsForFile(plannedTimeline, result.file);
171035
+ if (json) console.log(JSON.stringify(withMeta(result), null, 2));
171036
+ else
171037
+ console.log(
171038
+ `${formatTimeline(timeline)}
171039
+
171040
+ planned:
171041
+ ${formatTimeline(plannedTimeline)}
171042
+
171043
+ diff:
171044
+ ${diff(before3, after2)}`
171045
+ );
171046
+ }
171047
+ function applyMutation(setup, after2) {
171048
+ try {
171049
+ return applyFileMutations(setup.project.dir, [
171050
+ {
171051
+ sourceFile: setup.row.file,
171052
+ absPath: setup.filePath,
171053
+ before: setup.before,
171054
+ after: after2,
171055
+ expectedVersion: setup.expectedVersion
171056
+ }
171057
+ ])[0];
171058
+ } catch (error) {
171059
+ if (error instanceof Error && error.message === "file changed since the timeline was read") {
171060
+ return { error: error.message };
171061
+ }
171062
+ throw error;
171063
+ }
171064
+ }
171065
+ function mutationRefusal(verb, after2, before3, ref2) {
171066
+ if (verb !== "delete" || after2 !== before3) return null;
171067
+ return { reason: `${ref2} was not found`, fix: "choose an existing clip" };
171068
+ }
171069
+ function mutationConflict(verb, overwrite, row, timeline, nextStart, nextDuration) {
171070
+ if (verb !== "move" && verb !== "trim" || overwrite) return null;
171071
+ const conflict = overlap(row, timeline, nextStart, nextStart + nextDuration);
171072
+ if (!conflict) return null;
171073
+ return {
171074
+ reason: `${row.ref} would overlap ${conflict.ref} at ${nextStart}-${nextStart + nextDuration}`,
171075
+ fix: "pass --overwrite or move the named neighbour"
171076
+ };
171077
+ }
171078
+ function mutationCommand(verb) {
171079
+ return defineCommand({
171080
+ meta: { name: verb, description: `${verb} a timeline clip` },
171081
+ args: {
171082
+ ref: { type: "positional", required: true },
171083
+ time: { type: "positional", required: verb === "move" || verb === "split" },
171084
+ dir: { type: "string" },
171085
+ start: { type: "string" },
171086
+ end: { type: "string" },
171087
+ duration: { type: "string" },
171088
+ plan: { type: "boolean", default: false },
171089
+ json: { type: "boolean", default: false },
171090
+ overwrite: { type: "boolean", default: false },
171091
+ snap: { type: "boolean", default: false }
171092
+ },
171093
+ async run({ args }) {
171094
+ await runMutation(verb, args);
171095
+ }
171096
+ });
171097
+ }
171098
+ var examples19, allRows, timeline_default;
170505
171099
  var init_timeline = __esm({
170506
171100
  "src/commands/timeline.ts"() {
170507
171101
  "use strict";
171102
+ init_dist9();
171103
+ init_dist3();
171104
+ init_compositionFps();
170508
171105
  init_dist();
170509
171106
  init_describeProject();
170510
171107
  init_formatTimeline();
171108
+ init_resolveRef();
171109
+ init_timeExpr();
170511
171110
  init_dom();
171111
+ init_commandResult();
170512
171112
  init_project();
170513
171113
  init_updateCheck();
170514
171114
  examples19 = [
170515
171115
  ["Show every track and clip of the project in the current directory", "hyperframes timeline"],
170516
- ["Same, as JSON for an agent", "hyperframes timeline ./my-video --json"]
171116
+ ["Move a clip without writing", "hyperframes timeline move '#hero' +2 --plan"],
171117
+ ["Delete a clip and return a receipt", "hyperframes timeline delete '#hero' --json"]
170517
171118
  ];
171119
+ allRows = (timeline) => timeline.tracks.flatMap((track) => track.rows);
170518
171120
  timeline_default = defineCommand({
170519
- meta: {
170520
- name: "timeline",
170521
- description: "Print the project's tracks and clips (start, duration, source, volume, rate)"
170522
- },
171121
+ meta: { name: "timeline", description: "Print and edit the project's tracks and clips" },
170523
171122
  args: {
170524
171123
  dir: { type: "positional", description: "Project directory", required: false },
170525
171124
  json: { type: "boolean", description: "Output as JSON", default: false }
170526
171125
  },
171126
+ subCommands: {
171127
+ move: () => mutationCommand("move"),
171128
+ trim: () => mutationCommand("trim"),
171129
+ split: () => mutationCommand("split"),
171130
+ delete: () => mutationCommand("delete")
171131
+ },
170527
171132
  async run({ args }) {
171133
+ if (args._?.[0]) return;
170528
171134
  const project = resolveProject(args.dir);
170529
171135
  ensureDOMParser();
170530
171136
  const timeline = await describeProject(project.indexPath);
@@ -170543,7 +171149,7 @@ __export(benchmark_exports, {
170543
171149
  examples: () => examples20
170544
171150
  });
170545
171151
  import { existsSync as existsSync99, statSync as statSync41 } from "fs";
170546
- import { resolve as resolve65, join as join116 } from "path";
171152
+ import { resolve as resolve65, join as join117 } from "path";
170547
171153
  var examples20, FPS_30, FPS_60, DEFAULT_CONFIGS, benchmark_default;
170548
171154
  var init_benchmark = __esm({
170549
171155
  "src/commands/benchmark.ts"() {
@@ -170622,7 +171228,7 @@ var init_benchmark = __esm({
170622
171228
  s?.start(`Benchmarking ${config.label}...`);
170623
171229
  for (let i = 0; i < runsPerConfig; i++) {
170624
171230
  s?.message(`${config.label} \u2014 run ${i + 1}/${runsPerConfig}`);
170625
- const outputPath = join116(
171231
+ const outputPath = join117(
170626
171232
  benchDir,
170627
171233
  `${config.label.replace(/[^a-zA-Z0-9]/g, "_")}_run${i}.mp4`
170628
171234
  );
@@ -171124,9 +171730,9 @@ var init_remove_background = __esm({
171124
171730
 
171125
171731
  // src/whisper/parakeet.ts
171126
171732
  import { execFileSync as execFileSync13 } from "child_process";
171127
- import { existsSync as existsSync101, mkdtempSync as mkdtempSync23, readFileSync as readFileSync77, rmSync as rmSync36, writeFileSync as writeFileSync44 } from "fs";
171733
+ import { existsSync as existsSync101, mkdtempSync as mkdtempSync23, readFileSync as readFileSync78, rmSync as rmSync36, writeFileSync as writeFileSync44 } from "fs";
171128
171734
  import { homedir as homedir21, tmpdir as tmpdir15 } from "os";
171129
- import { basename as basename25, extname as extname16, join as join117 } from "path";
171735
+ import { basename as basename25, extname as extname16, join as join118 } from "path";
171130
171736
  function isRunnable(bin) {
171131
171737
  try {
171132
171738
  execFileSync13(bin, ["--help"], { stdio: ["ignore", "ignore", "ignore"], timeout: 1e4 });
@@ -171138,7 +171744,7 @@ function isRunnable(bin) {
171138
171744
  function findParakeet() {
171139
171745
  const candidates = [
171140
171746
  process.env.HYPERFRAMES_PARAKEET,
171141
- join117(homedir21(), ".venvs", "parakeet", "bin", "parakeet-mlx")
171747
+ join118(homedir21(), ".venvs", "parakeet", "bin", "parakeet-mlx")
171142
171748
  ].filter((p2) => Boolean(p2));
171143
171749
  for (const path of candidates) {
171144
171750
  if (existsSync101(path) && isRunnable(path)) return path;
@@ -171189,20 +171795,20 @@ function transcribeWithParakeet(inputPath, dir, options) {
171189
171795
  }
171190
171796
  const model = options?.model ?? DEFAULT_MODEL3;
171191
171797
  const cached3 = existsSync101(
171192
- join117(homedir21(), ".cache", "huggingface", "hub", `models--${model.replace(/\//g, "--")}`)
171798
+ join118(homedir21(), ".cache", "huggingface", "hub", `models--${model.replace(/\//g, "--")}`)
171193
171799
  );
171194
171800
  options?.onProgress?.(
171195
171801
  cached3 ? "Transcribing with Parakeet..." : "Downloading Parakeet model (first run, ~600MB)..."
171196
171802
  );
171197
- const workDir = mkdtempSync23(join117(tmpdir15(), "hyperframes-parakeet-"));
171803
+ const workDir = mkdtempSync23(join118(tmpdir15(), "hyperframes-parakeet-"));
171198
171804
  try {
171199
171805
  const argv2 = [inputPath, "--model", model, "--output-format", "json", "--output-dir", workDir];
171200
171806
  if (options?.language) argv2.push("--language", options.language);
171201
171807
  execFileSync13(runner, argv2, { stdio: ["ignore", "pipe", "pipe"], timeout: 18e5 });
171202
- const produced = join117(workDir, `${basename25(inputPath, extname16(inputPath))}.json`);
171808
+ const produced = join118(workDir, `${basename25(inputPath, extname16(inputPath))}.json`);
171203
171809
  if (!existsSync101(produced)) throw new Error("Parakeet did not produce output.");
171204
- const words = mergeTokensToWords(JSON.parse(readFileSync77(produced, "utf-8")));
171205
- const transcriptPath = join117(dir, "transcript.json");
171810
+ const words = mergeTokensToWords(JSON.parse(readFileSync78(produced, "utf-8")));
171811
+ const transcriptPath = join118(dir, "transcript.json");
171206
171812
  writeFileSync44(transcriptPath, JSON.stringify(words, null, 2));
171207
171813
  const durationSeconds = words.length > 0 ? words[words.length - 1].end : 0;
171208
171814
  return { transcriptPath, wordCount: words.length, durationSeconds, speechOnsetSeconds: null };
@@ -171226,7 +171832,7 @@ __export(transcribe_exports2, {
171226
171832
  examples: () => examples23
171227
171833
  });
171228
171834
  import { existsSync as existsSync103, writeFileSync as writeFileSync45 } from "fs";
171229
- import { resolve as resolve67, join as join118, extname as extname17, dirname as dirname56 } from "path";
171835
+ import { resolve as resolve67, join as join119, extname as extname17, dirname as dirname56 } from "path";
171230
171836
  function parseTimeoutMs(raw, json) {
171231
171837
  const source = raw ?? process.env["HYPERFRAMES_TRANSCRIBE_TIMEOUT_MS"];
171232
171838
  if (source == null || source === "") return void 0;
@@ -171262,7 +171868,7 @@ async function importTranscript(inputPath, dir, json) {
171262
171868
  const { loadTranscript: loadTranscript2, patchCaptionHtml: patchCaptionHtml2 } = await Promise.resolve().then(() => (init_normalize(), normalize_exports));
171263
171869
  const { words, format } = loadTranscript2(inputPath);
171264
171870
  if (words.length === 0) exitNoWords(json);
171265
- const outPath = join118(dir, "transcript.json");
171871
+ const outPath = join119(dir, "transcript.json");
171266
171872
  writeFileSync45(outPath, JSON.stringify(words, null, 2));
171267
171873
  patchCaptionHtml2(dir, words);
171268
171874
  if (json) {
@@ -171280,7 +171886,7 @@ async function exportTranscript(inputPath, dir, to, output, json, preserveCues)
171280
171886
  const { words, format } = loadTranscript2(inputPath);
171281
171887
  if (words.length === 0) exitNoWords(json);
171282
171888
  const preGrouped = preserveCues || format === "srt" || format === "vtt" || void 0;
171283
- const outPath = resolve67(output ?? join118(dir, `transcript.${to}`));
171889
+ const outPath = resolve67(output ?? join119(dir, `transcript.${to}`));
171284
171890
  const content = to === "srt" ? formatSrt2(words, { preGrouped }) : formatVtt2(words, { preGrouped });
171285
171891
  writeFileSync45(outPath, content);
171286
171892
  if (json) {
@@ -171503,7 +172109,7 @@ var init_transcribe2 = __esm({
171503
172109
  // src/tts/manager.ts
171504
172110
  import { existsSync as existsSync104, mkdirSync as mkdirSync54 } from "fs";
171505
172111
  import { homedir as homedir22 } from "os";
171506
- import { join as join119 } from "path";
172112
+ import { join as join120 } from "path";
171507
172113
  function inferLangFromVoiceId(voiceId) {
171508
172114
  const first = voiceId.charAt(0).toLowerCase();
171509
172115
  return VOICE_PREFIX_LANG[first] ?? "en-us";
@@ -171512,7 +172118,7 @@ function isSupportedLang(value) {
171512
172118
  return SUPPORTED_LANGS.includes(value);
171513
172119
  }
171514
172120
  async function ensureModel3(model = DEFAULT_MODEL4, options) {
171515
- const modelPath2 = join119(MODELS_DIR4, `${model}.onnx`);
172121
+ const modelPath2 = join120(MODELS_DIR4, `${model}.onnx`);
171516
172122
  if (existsSync104(modelPath2)) return modelPath2;
171517
172123
  const url = MODEL_URLS2[model];
171518
172124
  if (!url) {
@@ -171529,7 +172135,7 @@ async function ensureModel3(model = DEFAULT_MODEL4, options) {
171529
172135
  return modelPath2;
171530
172136
  }
171531
172137
  async function ensureVoices(options) {
171532
- const voicesPath = join119(VOICES_DIR, "voices-v1.0.bin");
172138
+ const voicesPath = join120(VOICES_DIR, "voices-v1.0.bin");
171533
172139
  if (existsSync104(voicesPath)) return voicesPath;
171534
172140
  mkdirSync54(VOICES_DIR, { recursive: true });
171535
172141
  options?.onProgress?.("Downloading voice data (~27 MB)...");
@@ -171544,9 +172150,9 @@ var init_manager4 = __esm({
171544
172150
  "src/tts/manager.ts"() {
171545
172151
  "use strict";
171546
172152
  init_download();
171547
- CACHE_DIR4 = join119(homedir22(), ".cache", "hyperframes", "tts");
171548
- MODELS_DIR4 = join119(CACHE_DIR4, "models");
171549
- VOICES_DIR = join119(CACHE_DIR4, "voices");
172153
+ CACHE_DIR4 = join120(homedir22(), ".cache", "hyperframes", "tts");
172154
+ MODELS_DIR4 = join120(CACHE_DIR4, "models");
172155
+ VOICES_DIR = join120(CACHE_DIR4, "voices");
171550
172156
  DEFAULT_MODEL4 = "kokoro-v1.0";
171551
172157
  MODEL_URLS2 = {
171552
172158
  "kokoro-v1.0": "https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0/kokoro-v1.0.onnx"
@@ -171689,7 +172295,7 @@ __export(synthesize_exports, {
171689
172295
  });
171690
172296
  import { execFileSync as execFileSync15 } from "child_process";
171691
172297
  import { existsSync as existsSync105, mkdirSync as mkdirSync55, readdirSync as readdirSync37, unlinkSync as unlinkSync9 } from "fs";
171692
- import { join as join120, dirname as dirname57, basename as basename26 } from "path";
172298
+ import { join as join121, dirname as dirname57, basename as basename26 } from "path";
171693
172299
  import { homedir as homedir23 } from "os";
171694
172300
  function ensureSynthScript() {
171695
172301
  if (!existsSync105(SCRIPT_PATH)) {
@@ -171700,7 +172306,7 @@ function ensureSynthScript() {
171700
172306
  for (const entry of readdirSync37(SCRIPT_DIR)) {
171701
172307
  if (entry !== currentName && /^synth(-v\d+)?\.py$/.test(entry)) {
171702
172308
  try {
171703
- unlinkSync9(join120(SCRIPT_DIR, entry));
172309
+ unlinkSync9(join121(SCRIPT_DIR, entry));
171704
172310
  } catch {
171705
172311
  }
171706
172312
  }
@@ -171848,8 +172454,8 @@ print(json.dumps({
171848
172454
  ESPEAK_LANG_OVERRIDES = {
171849
172455
  zh: "cmn"
171850
172456
  };
171851
- SCRIPT_DIR = join120(homedir23(), ".cache", "hyperframes", "tts");
171852
- SCRIPT_PATH = join120(SCRIPT_DIR, "synth-v3.py");
172457
+ SCRIPT_DIR = join121(homedir23(), ".cache", "hyperframes", "tts");
172458
+ SCRIPT_PATH = join121(SCRIPT_DIR, "synth-v3.py");
171853
172459
  }
171854
172460
  });
171855
172461
 
@@ -171859,7 +172465,7 @@ __export(tts_exports, {
171859
172465
  default: () => tts_default,
171860
172466
  examples: () => examples24
171861
172467
  });
171862
- import { existsSync as existsSync106, readFileSync as readFileSync78 } from "fs";
172468
+ import { existsSync as existsSync106, readFileSync as readFileSync79 } from "fs";
171863
172469
  import { resolve as resolve68, extname as extname18 } from "path";
171864
172470
  function listVoices(json) {
171865
172471
  const rows = BUNDLED_VOICES.map((v2) => ({ ...v2, defaultLang: inferLangFromVoiceId(v2.id) }));
@@ -171977,7 +172583,7 @@ var init_tts = __esm({
171977
172583
  let text2;
171978
172584
  const maybeFile = resolve68(input2);
171979
172585
  if (existsSync106(maybeFile) && extname18(maybeFile).toLowerCase() === ".txt") {
171980
- text2 = readFileSync78(maybeFile, "utf-8").trim();
172586
+ text2 = readFileSync79(maybeFile, "utf-8").trim();
171981
172587
  if (!text2) {
171982
172588
  console.error(c.error("File is empty."));
171983
172589
  failCommand();
@@ -172069,8 +172675,8 @@ __export(docs_exports, {
172069
172675
  default: () => docs_default,
172070
172676
  examples: () => examples25
172071
172677
  });
172072
- import { readFileSync as readFileSync79, existsSync as existsSync107 } from "fs";
172073
- import { resolve as resolve69, dirname as dirname58, join as join121 } from "path";
172678
+ import { readFileSync as readFileSync80, existsSync as existsSync107 } from "fs";
172679
+ import { resolve as resolve69, dirname as dirname58, join as join123 } from "path";
172074
172680
  import { fileURLToPath as fileURLToPath15 } from "url";
172075
172681
  function docsDir() {
172076
172682
  const thisFile = fileURLToPath15(import.meta.url);
@@ -172175,12 +172781,12 @@ var init_docs = __esm({
172175
172781
  }
172176
172782
  failCommand();
172177
172783
  }
172178
- const filePath = join121(docsDir(), entry.file);
172784
+ const filePath = join123(docsDir(), entry.file);
172179
172785
  if (!existsSync107(filePath)) {
172180
172786
  console.error(c.error(`Doc file not found: ${filePath}`));
172181
172787
  failCommand();
172182
172788
  }
172183
- const content = readFileSync79(filePath, "utf-8");
172789
+ const content = readFileSync80(filePath, "utf-8");
172184
172790
  console.log();
172185
172791
  renderMarkdown(content);
172186
172792
  }
@@ -172594,7 +173200,7 @@ __export(upgrade_exports, {
172594
173200
  upgradeProjectPins: () => upgradeProjectPins
172595
173201
  });
172596
173202
  import { execFileSync as execFileSync17 } from "child_process";
172597
- import { existsSync as existsSync109, readFileSync as readFileSync80, writeFileSync as writeFileSync46, renameSync as renameSync20, readdirSync as readdirSync38 } from "fs";
173203
+ import { existsSync as existsSync109, readFileSync as readFileSync81, writeFileSync as writeFileSync46, renameSync as renameSync20, readdirSync as readdirSync38 } from "fs";
172598
173204
  import { resolve as resolve70 } from "path";
172599
173205
  async function confirmUpgrade() {
172600
173206
  const shouldUpgrade = await ue({ message: "Upgrade now?" });
@@ -172657,14 +173263,14 @@ async function upgradeProjectPins(dir, opts) {
172657
173263
  if (!existsSync109(pkgPath)) {
172658
173264
  return { changed: false, from: [], to: "", path: pkgPath };
172659
173265
  }
172660
- const raw = JSON.parse(readFileSync80(pkgPath, "utf-8"));
173266
+ const raw = JSON.parse(readFileSync81(pkgPath, "utf-8"));
172661
173267
  const scripts = raw.scripts ?? {};
172662
173268
  const { latest } = await checkForUpdate(true);
172663
173269
  if (!isSafeVersion(latest)) return { changed: false, from: [], to: latest, path: pkgPath };
172664
173270
  const rewrite = rewriteProjectPinnedScripts(scripts, latest);
172665
173271
  const wrapperRewrites = readdirSync38(dir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".sh")).map((entry) => {
172666
173272
  const path = resolve70(dir, entry.name);
172667
- return { path, rewrite: rewritePinnedHyperframesText(readFileSync80(path, "utf-8"), latest) };
173273
+ return { path, rewrite: rewritePinnedHyperframesText(readFileSync81(path, "utf-8"), latest) };
172668
173274
  });
172669
173275
  if (rewrite.changed && !opts.check) {
172670
173276
  raw.scripts = rewrite.scripts;
@@ -173378,8 +173984,8 @@ __export(contactSheet_exports, {
173378
173984
  createSvgContactSheet: () => createSvgContactSheet
173379
173985
  });
173380
173986
  import sharp from "sharp";
173381
- import { readdirSync as readdirSync39, readFileSync as readFileSync81, writeFileSync as writeFileSync47, unlinkSync as unlinkSync10, existsSync as existsSync110 } from "fs";
173382
- import { join as join123, extname as extname19, basename as basename27, dirname as dirname60 } from "path";
173987
+ import { readdirSync as readdirSync39, readFileSync as readFileSync83, writeFileSync as writeFileSync47, unlinkSync as unlinkSync10, existsSync as existsSync110 } from "fs";
173988
+ import { join as join124, extname as extname19, basename as basename27, dirname as dirname60 } from "path";
173383
173989
  async function createContactSheet(imagePaths, outputPath, opts = {}) {
173384
173990
  const {
173385
173991
  cols = 3,
@@ -173468,7 +174074,7 @@ async function createScrollContactSheet(screenshotsDir, outputPath, budget = {})
173468
174074
  if (!existsSync110(screenshotsDir)) return [];
173469
174075
  const scrollFiles = readdirSync39(screenshotsDir).filter((f) => f.startsWith("scroll-") && f.endsWith(".png")).sort();
173470
174076
  if (scrollFiles.length === 0) return [];
173471
- const paths = scrollFiles.map((f) => join123(screenshotsDir, f));
174077
+ const paths = scrollFiles.map((f) => join124(screenshotsDir, f));
173472
174078
  const labels = scrollFiles.map((f) => {
173473
174079
  const m = f.match(/scroll-(\d+)\.png/);
173474
174080
  return m ? `${m[1]}% scroll` : f;
@@ -173485,7 +174091,7 @@ async function createSnapshotContactSheet(snapshotsDir, outputPath, budget = {})
173485
174091
  if (!existsSync110(snapshotsDir)) return [];
173486
174092
  const snapshotFiles = readdirSync39(snapshotsDir).filter((f) => f.startsWith("frame-") && f.endsWith(".png")).sort();
173487
174093
  if (snapshotFiles.length === 0) return [];
173488
- const paths = snapshotFiles.map((f) => join123(snapshotsDir, f));
174094
+ const paths = snapshotFiles.map((f) => join124(snapshotsDir, f));
173489
174095
  const labels = snapshotFiles.map((f) => {
173490
174096
  const m = f.match(/at-([\d.]+)s/);
173491
174097
  return m ? `${m[1]}s` : f;
@@ -173503,7 +174109,7 @@ async function createAssetContactSheet(assetsDir, outputPath, budget = {}) {
173503
174109
  const imageExts = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".webp"]);
173504
174110
  const assetFiles = readdirSync39(assetsDir).filter((f) => imageExts.has(extname19(f).toLowerCase()) && !f.includes("contact-sheet")).sort();
173505
174111
  if (assetFiles.length === 0) return [];
173506
- const paths = assetFiles.map((f) => join123(assetsDir, f));
174112
+ const paths = assetFiles.map((f) => join124(assetsDir, f));
173507
174113
  return createContactSheetPages(paths, outputPath, {
173508
174114
  cols: 4,
173509
174115
  cellWidth: 480,
@@ -173523,7 +174129,7 @@ async function createSvgContactSheet(svgsDir, outputPath, assetsRootDir, budget
173523
174129
  for (const f of readdirSync39(dir).filter((f2) => f2.endsWith(".svg")).sort()) {
173524
174130
  if (!seen.has(f)) {
173525
174131
  seen.add(f);
173526
- svgPaths.push(join123(dir, f));
174132
+ svgPaths.push(join124(dir, f));
173527
174133
  }
173528
174134
  }
173529
174135
  }
@@ -173536,9 +174142,9 @@ async function createSvgContactSheet(svgsDir, outputPath, assetsRootDir, budget
173536
174142
  for (let i = 0; i < svgPaths.length; i++) {
173537
174143
  if ((budget.remainingMs?.() ?? 1) <= 0) break;
173538
174144
  const svgPath = svgPaths[i];
173539
- const tmpPath = join123(tmpDir, `.thumb-${i}.png`);
174145
+ const tmpPath = join124(tmpDir, `.thumb-${i}.png`);
173540
174146
  try {
173541
- const svgBuf = readFileSync81(svgPath);
174147
+ const svgBuf = readFileSync83(svgPath);
173542
174148
  const thumb = await sharp(svgBuf).resize(thumbSize, thumbSize, {
173543
174149
  fit: "contain",
173544
174150
  background: { r: 245, g: 245, b: 245, alpha: 1 }
@@ -173594,9 +174200,9 @@ __export(snapshot_exports, {
173594
174200
  resolveSnapshotVideoRateSpec: () => resolveSnapshotVideoRateSpec,
173595
174201
  tailFrameTime: () => tailFrameTime
173596
174202
  });
173597
- import { existsSync as existsSync111, mkdtempSync as mkdtempSync24, readFileSync as readFileSync83, mkdirSync as mkdirSync56, rmSync as rmSync37, writeFileSync as writeFileSync48 } from "fs";
174203
+ import { existsSync as existsSync111, mkdtempSync as mkdtempSync24, readFileSync as readFileSync84, mkdirSync as mkdirSync56, rmSync as rmSync37, writeFileSync as writeFileSync48 } from "fs";
173598
174204
  import { tmpdir as tmpdir16 } from "os";
173599
- import { resolve as resolve72, join as join124, relative as relative31, isAbsolute as isAbsolute19, basename as basename28, posix as posix6 } from "path";
174205
+ import { resolve as resolve72, join as join125, relative as relative31, isAbsolute as isAbsolute19, basename as basename28, posix as posix6 } from "path";
173600
174206
  function orbitStageSource() {
173601
174207
  return `function(cam) {
173602
174208
  var root = document.querySelector("[data-composition-id]")
@@ -173650,8 +174256,8 @@ function requireSnapshotFfmpeg(ffmpegPath) {
173650
174256
  );
173651
174257
  }
173652
174258
  async function extractVideoFrameToBuffer(videoPath, timeSeconds, useVp9AlphaDecoder = false, accurateSeek = false) {
173653
- const tmp = mkdtempSync24(join124(tmpdir16(), "hf-snapshot-frame-"));
173654
- const outPath = join124(tmp, "frame.png");
174259
+ const tmp = mkdtempSync24(join125(tmpdir16(), "hf-snapshot-frame-"));
174260
+ const outPath = join125(tmp, "frame.png");
173655
174261
  try {
173656
174262
  const ffmpegPath = requireSnapshotFfmpeg(findFFmpeg());
173657
174263
  const args = ["-hide_banner", "-loglevel", "error"];
@@ -173670,7 +174276,7 @@ async function extractVideoFrameToBuffer(videoPath, timeSeconds, useVp9AlphaDeco
173670
174276
  );
173671
174277
  const result = await runFfmpegOnce(ffmpegPath, args, FFMPEG_EXTRACT_TIMEOUT_MS);
173672
174278
  if (result.code !== 0 || result.timedOut || !existsSync111(outPath)) return null;
173673
- return readFileSync83(outPath);
174279
+ return readFileSync84(outPath);
173674
174280
  } finally {
173675
174281
  try {
173676
174282
  rmSync37(tmp, { recursive: true, force: true });
@@ -173762,13 +174368,13 @@ async function captureSnapshots(projectDir, opts) {
173762
174368
  );
173763
174369
  }
173764
174370
  const cameraExpr = opts.angle && (opts.angle.yaw !== 0 || opts.angle.pitch !== 0) ? `(${orbitStageSource()})(${JSON.stringify(opts.angle)})` : null;
173765
- const snapshotDir = opts.outputDir ?? join124(projectDir, "snapshots");
174371
+ const snapshotDir = opts.outputDir ?? join125(projectDir, "snapshots");
173766
174372
  mkdirSync56(snapshotDir, { recursive: true });
173767
174373
  try {
173768
174374
  const { readdirSync: readdirSync46 } = await import("fs");
173769
174375
  for (const file of readdirSync46(snapshotDir)) {
173770
174376
  if (/\.(png|jpg|jpeg)$/i.test(file)) {
173771
- rmSync37(join124(snapshotDir, file), { force: true });
174377
+ rmSync37(join125(snapshotDir, file), { force: true });
173772
174378
  }
173773
174379
  }
173774
174380
  } catch {
@@ -173902,7 +174508,7 @@ async function captureSnapshots(projectDir, opts) {
173902
174508
  const timeLabel = formatSnapshotTimestamp(time);
173903
174509
  const index = String(i).padStart(2, "0");
173904
174510
  const filename = `frame-${index}-at-${timeLabel}.png`;
173905
- const framePath = join124(snapshotDir, filename);
174511
+ const framePath = join125(snapshotDir, filename);
173906
174512
  if (opts.zoom) {
173907
174513
  const canvas = await page.evaluate(() => ({
173908
174514
  width: window.innerWidth,
@@ -173931,9 +174537,9 @@ async function captureSnapshots(projectDir, opts) {
173931
174537
  ` ${c.warn("\u26A0")} --against has no frame at ${timeLabel} \u2014 reference pair skipped`
173932
174538
  );
173933
174539
  } else {
173934
- const refPath = join124(snapshotDir, `ref-${index}-at-${timeLabel}.png`);
174540
+ const refPath = join125(snapshotDir, `ref-${index}-at-${timeLabel}.png`);
173935
174541
  writeFileSync48(refPath, refPng);
173936
- const pairPath = join124(snapshotDir, `pair-${index}-at-${timeLabel}.jpg`);
174542
+ const pairPath = join125(snapshotDir, `pair-${index}-at-${timeLabel}.jpg`);
173937
174543
  const { createContactSheet: createContactSheet2 } = await Promise.resolve().then(() => (init_contactSheet(), contactSheet_exports));
173938
174544
  await createContactSheet2([framePath, refPath], pairPath, {
173939
174545
  cols: 2,
@@ -174100,7 +174706,7 @@ var init_snapshot = __esm({
174100
174706
  const angleLabel = camera && (camera.yaw !== 0 || camera.pitch !== 0) ? ` ${c.dim(`(angle yaw ${camera.yaw}\xB0 pitch ${camera.pitch}\xB0)`)}` : "";
174101
174707
  console.log(`${c.accent("\u25C6")} Capturing ${label2} from ${c.accent(project.name)}${angleLabel}`);
174102
174708
  try {
174103
- const snapshotDir = args.output ? resolve72(String(args.output)) : join124(project.dir, "snapshots");
174709
+ const snapshotDir = args.output ? resolve72(String(args.output)) : join125(project.dir, "snapshots");
174104
174710
  const paths = await captureSnapshots(project.dir, {
174105
174711
  frames,
174106
174712
  timeout,
@@ -174137,7 +174743,7 @@ ${c.success("\u25C7")} ${paths.length} snapshots saved to ${args.output ? snaps
174137
174743
  const { createSnapshotContactSheet: createSnapshotContactSheet2 } = await Promise.resolve().then(() => (init_contactSheet(), contactSheet_exports));
174138
174744
  const sheets = await createSnapshotContactSheet2(
174139
174745
  snapshotDir,
174140
- join124(snapshotDir, "contact-sheet.jpg")
174746
+ join125(snapshotDir, "contact-sheet.jpg")
174141
174747
  );
174142
174748
  if (sheets.length > 0) {
174143
174749
  const label3 = sheets.length === 1 ? "contact-sheet.jpg" : `contact-sheet-1..${sheets.length}.jpg`;
@@ -174173,9 +174779,9 @@ ${c.success("\u25C7")} ${paths.length} snapshots saved to ${args.output ? snaps
174173
174779
  const results = await Promise.allSettled(
174174
174780
  paths.map(async (p2) => {
174175
174781
  const filename = basename28(p2);
174176
- const filePath = join124(snapshotDir, filename);
174782
+ const filePath = join125(snapshotDir, filename);
174177
174783
  if (!existsSync111(filePath)) return { filename, desc: "file not found" };
174178
- const raw = readFileSync83(filePath);
174784
+ const raw = readFileSync84(filePath);
174179
174785
  let imageData;
174180
174786
  let mimeType = "image/png";
174181
174787
  if (sharpFn) {
@@ -174211,7 +174817,7 @@ ${c.success("\u25C7")} ${paths.length} snapshots saved to ${args.output ? snaps
174211
174817
  descriptions.push(`## (error)`, `Gemini call failed: ${errMsg.slice(0, 120)}`, ``);
174212
174818
  }
174213
174819
  }
174214
- const descPath = join124(snapshotDir, "descriptions.md");
174820
+ const descPath = join125(snapshotDir, "descriptions.md");
174215
174821
  writeFileSync48(descPath, descriptions.join("\n"));
174216
174822
  console.log(` ${c.dim("descriptions.md")} (Gemini frame analysis)`);
174217
174823
  }
@@ -174544,8 +175150,8 @@ __export(media_treatment_exports, {
174544
175150
  mediaTreatmentCommand: () => mediaTreatmentCommand,
174545
175151
  resolveMediaTreatmentSource: () => resolveMediaTreatmentSource
174546
175152
  });
174547
- import { existsSync as existsSync112, readFileSync as readFileSync84, writeFileSync as writeFileSync49 } from "fs";
174548
- import { join as join125, relative as relative34, resolve as resolve74 } from "path";
175153
+ import { existsSync as existsSync112, readFileSync as readFileSync85, writeFileSync as writeFileSync49 } from "fs";
175154
+ import { join as join126, relative as relative34, resolve as resolve74 } from "path";
174549
175155
  function getMediaTreatmentCapabilityOverview() {
174550
175156
  const capabilities = getHfColorGradingCapabilities();
174551
175157
  const family = (id, label2, description) => ({
@@ -174923,7 +175529,7 @@ function resolveMediaTreatmentSource(projectDir, compositionFile, source) {
174923
175529
  }
174924
175530
  const cleanSource = cleanAssetUrl(sourceUrl);
174925
175531
  if (!cleanSource) throw new Error("Selected media has no analyzable local src");
174926
- const projectRelative = cleanSource.startsWith("/") ? cleanSource : rewriteAssetPath(compositionFile, cleanSource, (path) => existsSync112(join125(projectDir, path)));
175532
+ const projectRelative = cleanSource.startsWith("/") ? cleanSource : rewriteAssetPath(compositionFile, cleanSource, (path) => existsSync112(join126(projectDir, path)));
174927
175533
  const asset = resolveExistingLocalAsset(projectDir, projectRelative);
174928
175534
  if (!asset) throw new Error(`Media file not found: ${source}`);
174929
175535
  return asset.resolved;
@@ -174988,7 +175594,7 @@ function analyzeTarget(args) {
174988
175594
  }
174989
175595
  const selectorIndex = parseSelectorIndex(readOptionalString(args["selector-index"]));
174990
175596
  const { element, tag } = selectMediaElement(
174991
- readFileSync84(filePath, "utf8"),
175597
+ readFileSync85(filePath, "utf8"),
174992
175598
  selector,
174993
175599
  selectorIndex
174994
175600
  );
@@ -175006,14 +175612,14 @@ function analyzeTarget(args) {
175006
175612
  ...analyzeMediaTreatment(mediaPath)
175007
175613
  };
175008
175614
  }
175009
- function prepareMutation(args) {
175615
+ function prepareMutation2(args) {
175010
175616
  const { project, filePath } = resolveMutationFile(args);
175011
175617
  const selector = readOptionalString(args.selector);
175012
175618
  if (!selector) throw new Error("--selector is required");
175013
175619
  const clear = args.clear === true;
175014
175620
  const apply = args.apply === true;
175015
175621
  const selectorIndex = parseSelectorIndex(readOptionalString(args["selector-index"]));
175016
- const result = applyMediaTreatmentToHtml(readFileSync84(filePath, "utf8"), {
175622
+ const result = applyMediaTreatmentToHtml(readFileSync85(filePath, "utf8"), {
175017
175623
  selector,
175018
175624
  selectorIndex,
175019
175625
  grading: parseGrading(readOptionalString(args.grading), apply, clear),
@@ -175055,7 +175661,7 @@ function printAnalysis(args) {
175055
175661
  console.log(` suggested patch: ${JSON.stringify(result.suggestedPatch)}`);
175056
175662
  }
175057
175663
  function printMutation(args) {
175058
- const { action, result, selector, payload } = prepareMutation(args);
175664
+ const { action, result, selector, payload } = prepareMutation2(args);
175059
175665
  if (args.json === true) {
175060
175666
  console.log(JSON.stringify(withMeta(payload), null, 2));
175061
175667
  return;
@@ -175202,12 +175808,12 @@ import {
175202
175808
  existsSync as existsSync113,
175203
175809
  mkdirSync as mkdirSync57,
175204
175810
  mkdtempSync as mkdtempSync25,
175205
- readFileSync as readFileSync85,
175811
+ readFileSync as readFileSync86,
175206
175812
  rmSync as rmSync38,
175207
175813
  writeFileSync as writeFileSync50
175208
175814
  } from "fs";
175209
175815
  import { tmpdir as tmpdir17 } from "os";
175210
- import { basename as basename30, dirname as dirname61, extname as extname21, join as join126, resolve as resolve75 } from "path";
175816
+ import { basename as basename30, dirname as dirname61, extname as extname21, join as join127, resolve as resolve75 } from "path";
175211
175817
  import sharp2 from "sharp";
175212
175818
  function isRecord14(value) {
175213
175819
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -175298,7 +175904,7 @@ function parseGradesFile(filePath) {
175298
175904
  }
175299
175905
  let parsed;
175300
175906
  try {
175301
- parsed = JSON.parse(readFileSync85(filePath, "utf-8"));
175907
+ parsed = JSON.parse(readFileSync86(filePath, "utf-8"));
175302
175908
  } catch (err) {
175303
175909
  throw new Error(`Could not parse grades JSON: ${normalizeErrorMessage(err)}`);
175304
175910
  }
@@ -175463,10 +176069,10 @@ function frameFileNameForPath(framePath) {
175463
176069
  return "frame.png";
175464
176070
  }
175465
176071
  async function prepareGradeCompareTempProject(opts) {
175466
- const tempDir = mkdtempSync25(join126(tmpdir17(), "hf-grade-compare-"));
176072
+ const tempDir = mkdtempSync25(join127(tmpdir17(), "hf-grade-compare-"));
175467
176073
  try {
175468
176074
  const frameFileName2 = opts.frameFileName ?? frameFileNameForPath(opts.framePath);
175469
- writeFileSync50(join126(tempDir, frameFileName2), opts.frameBuffer);
176075
+ writeFileSync50(join127(tempDir, frameFileName2), opts.frameBuffer);
175470
176076
  let lutIndex = 0;
175471
176077
  const stagedCells = opts.cells.map((cell) => {
175472
176078
  const lutSrc = lutSrcFromGrading(cell.grading);
@@ -175475,7 +176081,7 @@ async function prepareGradeCompareTempProject(opts) {
175475
176081
  if (!existsSync113(sourcePath)) {
175476
176082
  throw new Error(`LUT file not found for "${cell.label}": ${sourcePath}`);
175477
176083
  }
175478
- const lutText = readFileSync85(sourcePath, "utf-8");
176084
+ const lutText = readFileSync86(sourcePath, "utf-8");
175479
176085
  try {
175480
176086
  parseCubeLut(lutText, { maxSize: 64 });
175481
176087
  } catch (err) {
@@ -175486,7 +176092,7 @@ async function prepareGradeCompareTempProject(opts) {
175486
176092
  const lutExt = extname21(sourcePath) || ".cube";
175487
176093
  const stagedName = `lut-${lutIndex}${lutExt}`;
175488
176094
  lutIndex += 1;
175489
- copyFileSync10(sourcePath, join126(tempDir, stagedName));
176095
+ copyFileSync10(sourcePath, join127(tempDir, stagedName));
175490
176096
  return {
175491
176097
  label: cell.label,
175492
176098
  grading: rewriteGradingLutSrc(cell.grading, stagedName)
@@ -175498,7 +176104,7 @@ async function prepareGradeCompareTempProject(opts) {
175498
176104
  frameWidth: opts.frameWidth,
175499
176105
  frameHeight: opts.frameHeight
175500
176106
  });
175501
- writeFileSync50(join126(tempDir, "index.html"), html);
176107
+ writeFileSync50(join127(tempDir, "index.html"), html);
175502
176108
  return { tempDir, html, cells: stagedCells };
175503
176109
  } catch (err) {
175504
176110
  rmSync38(tempDir, { recursive: true, force: true });
@@ -175510,8 +176116,8 @@ function isVideoPath2(filePath) {
175510
176116
  return [".mp4", ".mov", ".m4v", ".webm", ".mkv", ".avi", ".mpeg", ".mpg", ".ogv"].includes(ext);
175511
176117
  }
175512
176118
  async function extractVideoFrameToBuffer2(videoPath) {
175513
- const tmp = mkdtempSync25(join126(tmpdir17(), "hf-grade-compare-frame-"));
175514
- const outPath = join126(tmp, "frame.png");
176119
+ const tmp = mkdtempSync25(join127(tmpdir17(), "hf-grade-compare-frame-"));
176120
+ const outPath = join127(tmp, "frame.png");
175515
176121
  try {
175516
176122
  const ffmpegPath = findFFmpeg();
175517
176123
  if (!ffmpegPath) return null;
@@ -175538,7 +176144,7 @@ async function extractVideoFrameToBuffer2(videoPath) {
175538
176144
  const detail = result.stderr.trim() ? `: ${result.stderr.trim()}` : "";
175539
176145
  throw new Error(`ffmpeg could not extract first frame from ${videoPath}${detail}`);
175540
176146
  }
175541
- return readFileSync85(outPath);
176147
+ return readFileSync86(outPath);
175542
176148
  } finally {
175543
176149
  rmSync38(tmp, { recursive: true, force: true });
175544
176150
  }
@@ -175547,7 +176153,7 @@ async function loadReferenceFrame(framePath) {
175547
176153
  if (!existsSync113(framePath)) {
175548
176154
  throw new Error(`Reference frame not found: ${framePath}`);
175549
176155
  }
175550
- const buffer = isVideoPath2(framePath) ? await extractVideoFrameToBuffer2(framePath) : readFileSync85(framePath);
176156
+ const buffer = isVideoPath2(framePath) ? await extractVideoFrameToBuffer2(framePath) : readFileSync86(framePath);
175551
176157
  if (!buffer) {
175552
176158
  throw new Error(`Could not extract a frame from video: ${framePath}`);
175553
176159
  }
@@ -175566,7 +176172,7 @@ async function captureGradeCompareSheet(projectDir, timeoutMs) {
175566
176172
  const { bundleToSingleHtml: bundleToSingleHtml2 } = await Promise.resolve().then(() => (init_compiler(), compiler_exports));
175567
176173
  const html = await bundleToSingleHtml2(projectDir);
175568
176174
  const server = await serveStaticProjectHtml(projectDir, html);
175569
- const sheetPath = join126(projectDir, "grade-compare.png");
176175
+ const sheetPath = join127(projectDir, "grade-compare.png");
175570
176176
  try {
175571
176177
  const {
175572
176178
  browser: chromeBrowser,
@@ -175754,7 +176360,7 @@ __export(compare_exports, {
175754
176360
  });
175755
176361
  import { cpSync as cpSync6, existsSync as existsSync114, mkdirSync as mkdirSync58, mkdtempSync as mkdtempSync26, renameSync as renameSync21, rmSync as rmSync39, statSync as statSync43 } from "fs";
175756
176362
  import { tmpdir as tmpdir18 } from "os";
175757
- import { basename as basename31, dirname as dirname62, extname as extname23, join as join127 } from "path";
176363
+ import { basename as basename31, dirname as dirname62, extname as extname23, join as join128 } from "path";
175758
176364
  function defaultLabelForPath(input2) {
175759
176365
  const name = basename31(input2);
175760
176366
  return extname23(name).toLowerCase() === ".html" ? basename31(name, extname23(name)) : name;
@@ -175853,7 +176459,7 @@ function inputError(variant) {
175853
176459
  );
175854
176460
  }
175855
176461
  function stageHtmlVariant(variant) {
175856
- const stagedDir = mkdtempSync26(join127(tmpdir18(), "hf-compare-variant-"));
176462
+ const stagedDir = mkdtempSync26(join128(tmpdir18(), "hf-compare-variant-"));
175857
176463
  try {
175858
176464
  cpSync6(dirname62(variant.inputPath), stagedDir, {
175859
176465
  recursive: true,
@@ -175864,7 +176470,7 @@ function stageHtmlVariant(variant) {
175864
176470
  });
175865
176471
  const sourceName = basename31(variant.inputPath);
175866
176472
  if (sourceName !== "index.html") {
175867
- renameSync21(join127(stagedDir, sourceName), join127(stagedDir, "index.html"));
176473
+ renameSync21(join128(stagedDir, sourceName), join128(stagedDir, "index.html"));
175868
176474
  }
175869
176475
  return {
175870
176476
  ...variant,
@@ -175884,7 +176490,7 @@ function prepareCompareVariantProjects(variants) {
175884
176490
  throw inputError(variant);
175885
176491
  }
175886
176492
  const stat = statSync43(variant.inputPath);
175887
- if (stat.isDirectory() && existsSync114(join127(variant.inputPath, "index.html"))) {
176493
+ if (stat.isDirectory() && existsSync114(join128(variant.inputPath, "index.html"))) {
175888
176494
  prepared.push({
175889
176495
  ...variant,
175890
176496
  projectDir: variant.inputPath
@@ -175950,13 +176556,13 @@ async function renderCompareSheet(parsed) {
175950
176556
  const capResult = capCompareVariants(parsed.variants);
175951
176557
  const variants = capResult.variants;
175952
176558
  const prepared = prepareCompareVariantProjects(variants);
175953
- const frameDir = mkdtempSync26(join127(tmpdir18(), "hf-compare-frames-"));
176559
+ const frameDir = mkdtempSync26(join128(tmpdir18(), "hf-compare-frames-"));
175954
176560
  const framePaths = [];
175955
176561
  try {
175956
176562
  let renderReadyTimedOut = false;
175957
176563
  for (let i = 0; i < prepared.length; i++) {
175958
176564
  const variant = prepared[i];
175959
- const framePath = join127(frameDir, `variant-${String(i + 1).padStart(2, "0")}.png`);
176565
+ const framePath = join128(frameDir, `variant-${String(i + 1).padStart(2, "0")}.png`);
175960
176566
  const rendered = await renderCompareVariant(variant, {
175961
176567
  atSeconds: parsed.atSeconds,
175962
176568
  framePath,
@@ -176467,7 +177073,7 @@ var init_captureImageValidation = __esm({
176467
177073
 
176468
177074
  // src/capture/assetDownloader.ts
176469
177075
  import { writeFileSync as writeFileSync51, mkdirSync as mkdirSync59 } from "fs";
176470
- import { join as join128, extname as extname24 } from "path";
177076
+ import { join as join129, extname as extname24 } from "path";
176471
177077
  import { createHash as createHash27 } from "crypto";
176472
177078
  function noDrops() {
176473
177079
  return { "size-floor": 0, "budget-exhausted": 0, "cap-reached": 0, unavailable: 0 };
@@ -176529,7 +177135,7 @@ async function fetchAndInspectIcon(icon, rank, stem, outputDir, timeoutMs, byteB
176529
177135
  const ext = await captureImageExtension(buffer);
176530
177136
  if (!ext) return null;
176531
177137
  const file = `assets/${stem}${ext}`;
176532
- writeFileSync51(join128(outputDir, file), buffer);
177138
+ writeFileSync51(join129(outputDir, file), buffer);
176533
177139
  const verdict = await classifyIcon(buffer, ext);
176534
177140
  return {
176535
177141
  buffer,
@@ -176551,7 +177157,7 @@ function promoteHeadline(manifest, bytesByFile, outputDir) {
176551
177157
  )[0];
176552
177158
  if (!chosen) return null;
176553
177159
  const file = `assets/favicon${extname24(chosen.file)}`;
176554
- writeFileSync51(join128(outputDir, file), bytesByFile.get(chosen.file));
177160
+ writeFileSync51(join129(outputDir, file), bytesByFile.get(chosen.file));
176555
177161
  manifest.headline = {
176556
177162
  file,
176557
177163
  source: chosen.file,
@@ -176606,13 +177212,13 @@ async function downloadDeclaredIcons(faviconLinks, outputDir, drops, options) {
176606
177212
  }
176607
177213
  async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks, options = {}) {
176608
177214
  options = { ...options, byteBudget: options.byteBudget ?? createCaptureDownloadBudget() };
176609
- const assetsDir = join128(outputDir, "assets");
177215
+ const assetsDir = join129(outputDir, "assets");
176610
177216
  mkdirSync59(assetsDir, { recursive: true });
176611
177217
  const assets = [];
176612
177218
  const drops = noDrops();
176613
177219
  const downloadedUrls = /* @__PURE__ */ new Set();
176614
177220
  let icons = emptyIconManifest();
176615
- mkdirSync59(join128(outputDir, "assets", "svgs"), { recursive: true });
177221
+ mkdirSync59(join129(outputDir, "assets", "svgs"), { recursive: true });
176616
177222
  const usedSvgNames = /* @__PURE__ */ new Set();
176617
177223
  const MAX_INLINE_SVGS = 30;
176618
177224
  drops["cap-reached"] += Math.max(0, tokens.svgs.length - MAX_INLINE_SVGS);
@@ -176634,7 +177240,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks,
176634
177240
  const name = `${finalSlug}.svg`;
176635
177241
  const localPath = `assets/svgs/${name}`;
176636
177242
  try {
176637
- writeFileSync51(join128(outputDir, localPath), svgFile, "utf-8");
177243
+ writeFileSync51(join129(outputDir, localPath), svgFile, "utf-8");
176638
177244
  assets.push({ url: "", localPath, type: "svg" });
176639
177245
  } catch {
176640
177246
  drops.unavailable++;
@@ -176724,7 +177330,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks,
176724
177330
  const name = `${slug}${ext}`;
176725
177331
  usedNames.add(slug);
176726
177332
  const localPath = `assets/${name}`;
176727
- writeFileSync51(join128(outputDir, localPath), buffer);
177333
+ writeFileSync51(join129(outputDir, localPath), buffer);
176728
177334
  assets.push({ url, localPath, type: "image" });
176729
177335
  imgIdx++;
176730
177336
  } catch {
@@ -176751,7 +177357,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks,
176751
177357
  drops["size-floor"]++;
176752
177358
  } else {
176753
177359
  const localPath = `assets/og-image${ext}`;
176754
- writeFileSync51(join128(outputDir, localPath), buffer);
177360
+ writeFileSync51(join129(outputDir, localPath), buffer);
176755
177361
  assets.push({ url: tokens.ogImage, localPath, type: "image" });
176756
177362
  }
176757
177363
  }
@@ -176777,7 +177383,7 @@ function normalizeUrl(u) {
176777
177383
  }
176778
177384
  async function downloadAndRewriteFonts(css, outputDir, options = {}) {
176779
177385
  options = { ...options, byteBudget: options.byteBudget ?? createCaptureDownloadBudget() };
176780
- const assetsDir = join128(outputDir, "assets", "fonts");
177386
+ const assetsDir = join129(outputDir, "assets", "fonts");
176781
177387
  mkdirSync59(assetsDir, { recursive: true });
176782
177388
  const drops = noDrops();
176783
177389
  const fontUrlRegex = /url\(['"]?(https?:\/\/[^'")\s]+\.(?:woff2?|ttf|otf)[^'")\s]*?)['"]?\)/g;
@@ -176835,7 +177441,7 @@ async function downloadAndRewriteFonts(css, outputDir, options = {}) {
176835
177441
  const extension = buffer && captureFontExtension(buffer);
176836
177442
  if (buffer && extension) {
176837
177443
  const filename = captureFontFilename(fontUrl, extension, usedFontNames);
176838
- const localPath = join128(assetsDir, filename);
177444
+ const localPath = join129(assetsDir, filename);
176839
177445
  const relativePath = `assets/fonts/${filename}`;
176840
177446
  writeFileSync51(localPath, buffer);
176841
177447
  rewritten = rewritten.split(fontUrl).join(relativePath);
@@ -176957,8 +177563,8 @@ __export(video_exports, {
176957
177563
  runVideoMode: () => runVideoMode,
176958
177564
  safeFilename: () => safeFilename
176959
177565
  });
176960
- import { createWriteStream as createWriteStream3, existsSync as existsSync115, mkdirSync as mkdirSync60, readFileSync as readFileSync86, unlinkSync as unlinkSync11 } from "fs";
176961
- import { resolve as resolve76, join as join129, basename as basename32 } from "path";
177566
+ import { createWriteStream as createWriteStream3, existsSync as existsSync115, mkdirSync as mkdirSync60, readFileSync as readFileSync87, unlinkSync as unlinkSync11 } from "fs";
177567
+ import { resolve as resolve76, join as join130, basename as basename32 } from "path";
176962
177568
  async function streamToFile(url, destPath) {
176963
177569
  const r = await safeFetch(url, {
176964
177570
  signal: AbortSignal.timeout(12e4),
@@ -177077,8 +177683,8 @@ function pickManifestEntry(manifest, args) {
177077
177683
  }
177078
177684
  async function runVideoMode(args) {
177079
177685
  const projectDir = resolve76(args.project);
177080
- const directPath = join129(projectDir, "extracted", "video-manifest.json");
177081
- const w2hPath = join129(projectDir, "capture", "extracted", "video-manifest.json");
177686
+ const directPath = join130(projectDir, "extracted", "video-manifest.json");
177687
+ const w2hPath = join130(projectDir, "capture", "extracted", "video-manifest.json");
177082
177688
  const manifestPath3 = existsSync115(directPath) ? directPath : w2hPath;
177083
177689
  const isW2hLayout = manifestPath3 === w2hPath;
177084
177690
  if (!existsSync115(manifestPath3)) {
@@ -177091,7 +177697,7 @@ async function runVideoMode(args) {
177091
177697
  }
177092
177698
  let manifest;
177093
177699
  try {
177094
- manifest = JSON.parse(readFileSync86(manifestPath3, "utf-8"));
177700
+ manifest = JSON.parse(readFileSync87(manifestPath3, "utf-8"));
177095
177701
  } catch (e2) {
177096
177702
  console.error(`${c.error("\u2717")} video-manifest.json is malformed: ${e2.message}`);
177097
177703
  setCommandExitCode(1);
@@ -177132,10 +177738,10 @@ async function runVideoMode(args) {
177132
177738
  setCommandExitCode(1);
177133
177739
  return;
177134
177740
  }
177135
- const outDir = isW2hLayout ? join129(projectDir, "capture", "assets", "videos") : join129(projectDir, "assets", "videos");
177741
+ const outDir = isW2hLayout ? join130(projectDir, "capture", "assets", "videos") : join130(projectDir, "assets", "videos");
177136
177742
  mkdirSync60(outDir, { recursive: true });
177137
177743
  const fname = safeFilename(entry.filename || basename32(entry.url));
177138
- const outPath = join129(outDir, fname);
177744
+ const outPath = join130(outDir, fname);
177139
177745
  const relPath = isW2hLayout ? `capture/assets/videos/${fname}` : `assets/videos/${fname}`;
177140
177746
  console.log(
177141
177747
  `${c.accent("\u25B8")} downloading [${entry.index}] ${entry.filename} (${entry.sourceWidth || entry.width}\xD7${entry.sourceHeight || entry.height})`
@@ -178632,8 +179238,8 @@ var init_designStyleExtractor = __esm({
178632
179238
  });
178633
179239
 
178634
179240
  // src/capture/fontMetadataExtractor.ts
178635
- import { readdirSync as readdirSync40, readFileSync as readFileSync87, writeFileSync as writeFileSync53, existsSync as existsSync116 } from "fs";
178636
- import { join as join130 } from "path";
179241
+ import { readdirSync as readdirSync40, readFileSync as readFileSync88, writeFileSync as writeFileSync53, existsSync as existsSync116 } from "fs";
179242
+ import { join as join131 } from "path";
178637
179243
  import * as fontkit from "fontkit";
178638
179244
  function isFontCollection(value) {
178639
179245
  return value.type === "TTC" || value.type === "DFont";
@@ -178644,7 +179250,7 @@ function extractFontMetadata(fontsDir, outputPath) {
178644
179250
  if (existsSync116(fontsDir)) {
178645
179251
  const fontFiles = readdirSync40(fontsDir).filter((f) => /\.(woff2?|ttf|otf)$/i.test(f));
178646
179252
  for (const filename of fontFiles) {
178647
- const fullPath = join130(fontsDir, filename);
179253
+ const fullPath = join131(fontsDir, filename);
178648
179254
  const meta = readSingleFont(fullPath, filename);
178649
179255
  if (meta.identified) {
178650
179256
  files.push(meta);
@@ -178683,7 +179289,7 @@ function readSingleFont(fullPath, filename) {
178683
179289
  isIcon: false
178684
179290
  };
178685
179291
  try {
178686
- const buf = readFileSync87(fullPath);
179292
+ const buf = readFileSync88(fullPath);
178687
179293
  const created = fontkit.create(buf);
178688
179294
  const font = isFontCollection(created) ? created.fonts[0] : created;
178689
179295
  if (!font) return empty2;
@@ -179199,8 +179805,8 @@ var init_lottiePreviewRequests = __esm({
179199
179805
  });
179200
179806
 
179201
179807
  // src/capture/mediaCapture.ts
179202
- import { mkdirSync as mkdirSync61, writeFileSync as writeFileSync54, readdirSync as readdirSync41, readFileSync as readFileSync88, statSync as statSync44 } from "fs";
179203
- import { join as join131, extname as extname25 } from "path";
179808
+ import { mkdirSync as mkdirSync61, writeFileSync as writeFileSync54, readdirSync as readdirSync41, readFileSync as readFileSync89, statSync as statSync44 } from "fs";
179809
+ import { join as join133, extname as extname25 } from "path";
179204
179810
  function liveRemainingMs(budget, fallbackMs) {
179205
179811
  return budget.remainingMs?.() ?? fallbackMs;
179206
179812
  }
@@ -179238,7 +179844,7 @@ async function saveLottieAnimations(discoveredLotties, lottieDir, budget = {}) {
179238
179844
  if (savedHashes.has(hash2)) continue;
179239
179845
  savedHashes.add(hash2);
179240
179846
  if (!validLottieJson(jsonData)) continue;
179241
- writeFileSync54(join131(lottieDir, `animation-${savedCount}.json`), jsonData, "utf-8");
179847
+ writeFileSync54(join133(lottieDir, `animation-${savedCount}.json`), jsonData, "utf-8");
179242
179848
  savedCount++;
179243
179849
  }
179244
179850
  } catch {
@@ -179248,18 +179854,18 @@ async function saveLottieAnimations(discoveredLotties, lottieDir, budget = {}) {
179248
179854
  }
179249
179855
  async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir, budget = {}) {
179250
179856
  const manifest = [];
179251
- const previewDir = join131(lottieDir, "previews");
179857
+ const previewDir = join133(lottieDir, "previews");
179252
179858
  mkdirSync61(previewDir, { recursive: true });
179253
179859
  for (const file of readdirSync41(lottieDir)) {
179254
179860
  if (!file.endsWith(".json")) continue;
179255
179861
  if (liveRemainingMs(budget, 1) <= 0) break;
179256
179862
  try {
179257
- const raw = JSON.parse(readFileSync88(join131(lottieDir, file), "utf-8"));
179863
+ const raw = JSON.parse(readFileSync89(join133(lottieDir, file), "utf-8"));
179258
179864
  const fr = raw.fr || 30;
179259
179865
  const dur = ((raw.op || 0) - (raw.ip || 0)) / fr;
179260
179866
  const previewName = file.replace(".json", "-preview.png");
179261
179867
  let preview;
179262
- const fileSize = statSync44(join131(lottieDir, file)).size;
179868
+ const fileSize = statSync44(join133(lottieDir, file)).size;
179263
179869
  if (fileSize > 2e6) continue;
179264
179870
  let previewPage;
179265
179871
  try {
@@ -179268,7 +179874,7 @@ async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir, budget
179268
179874
  if (liveRemainingMs(budget, 1) <= 0) break;
179269
179875
  await guardLottiePreviewRequests(previewPage);
179270
179876
  await previewPage.setViewport({ width: 400, height: 400 });
179271
- const animData = JSON.parse(readFileSync88(join131(lottieDir, file), "utf-8"));
179877
+ const animData = JSON.parse(readFileSync89(join133(lottieDir, file), "utf-8"));
179272
179878
  const midFrame = Math.floor(((raw.op || 0) - (raw.ip || 0)) * 0.3);
179273
179879
  await previewPage.setContent(
179274
179880
  `<!DOCTYPE html>
@@ -179299,7 +179905,7 @@ async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir, budget
179299
179905
  });
179300
179906
  if (liveRemainingMs(budget, 1) > 0) {
179301
179907
  await previewPage.screenshot({
179302
- path: join131(previewDir, previewName),
179908
+ path: join133(previewDir, previewName),
179303
179909
  type: "png",
179304
179910
  omitBackground: true
179305
179911
  });
@@ -179325,7 +179931,7 @@ async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir, budget
179325
179931
  }
179326
179932
  if (manifest.length > 0) {
179327
179933
  writeFileSync54(
179328
- join131(outputDir, "extracted", "lottie-manifest.json"),
179934
+ join133(outputDir, "extracted", "lottie-manifest.json"),
179329
179935
  JSON.stringify(manifest, null, 2),
179330
179936
  "utf-8"
179331
179937
  );
@@ -179363,7 +179969,7 @@ async function downloadVideoBody(srcUrl, filename, videosDir, timeoutMs) {
179363
179969
  }
179364
179970
  if (total < 1024) return null;
179365
179971
  const safe = /\.[a-z0-9]+$/i.test(filename) ? filename.replace(/[^\w.-]/g, "_") : `video${ext}`;
179366
- writeFileSync54(join131(videosDir, safe), Buffer.concat(chunks));
179972
+ writeFileSync54(join133(videosDir, safe), Buffer.concat(chunks));
179367
179973
  return `assets/videos/${safe}`;
179368
179974
  } catch {
179369
179975
  return null;
@@ -179430,9 +180036,9 @@ async function captureVideoManifest(page, outputDir, progress, opts) {
179430
180036
  }
179431
180037
  const merged = [...byKey.values()];
179432
180038
  if (merged.length === 0) return;
179433
- const videoManifestDir = join131(outputDir, "assets", "videos");
180039
+ const videoManifestDir = join133(outputDir, "assets", "videos");
179434
180040
  mkdirSync61(videoManifestDir, { recursive: true });
179435
- const previewDir = join131(videoManifestDir, "previews");
180041
+ const previewDir = join133(videoManifestDir, "previews");
179436
180042
  mkdirSync61(previewDir, { recursive: true });
179437
180043
  const videoManifest = [];
179438
180044
  const dlStart = Date.now();
@@ -179461,7 +180067,7 @@ async function captureVideoManifest(page, outputDir, progress, opts) {
179461
180067
  await new Promise((r) => setTimeout(r, 200));
179462
180068
  if (liveRemainingMs(opts ?? {}, 1) > 0) {
179463
180069
  await page.screenshot({
179464
- path: join131(previewDir, previewName),
180070
+ path: join133(previewDir, previewName),
179465
180071
  clip: {
179466
180072
  x: Math.max(0, rect.x),
179467
180073
  y: Math.max(0, rect.y),
@@ -179498,7 +180104,7 @@ async function captureVideoManifest(page, outputDir, progress, opts) {
179498
180104
  }
179499
180105
  if (videoManifest.length > 0) {
179500
180106
  writeFileSync54(
179501
- join131(outputDir, "extracted", "video-manifest.json"),
180107
+ join133(outputDir, "extracted", "video-manifest.json"),
179502
180108
  JSON.stringify(videoManifest, null, 2),
179503
180109
  "utf-8"
179504
180110
  );
@@ -179575,12 +180181,12 @@ import {
179575
180181
  existsSync as existsSync117,
179576
180182
  readdirSync as readdirSync43,
179577
180183
  statSync as statSync45,
179578
- readFileSync as readFileSync89,
180184
+ readFileSync as readFileSync90,
179579
180185
  openSync as openSync14,
179580
180186
  fstatSync as fstatSync9,
179581
180187
  closeSync as closeSync15
179582
180188
  } from "fs";
179583
- import { basename as basename33, join as join133 } from "path";
180189
+ import { basename as basename33, join as join134 } from "path";
179584
180190
  function resolveVisionPhaseCompletion(outcome, remainingMs) {
179585
180191
  if (outcome.budgetExhausted || remainingMs <= 0) {
179586
180192
  return { status: "degraded", reason: "budget-exhausted" };
@@ -179861,7 +180467,7 @@ async function captionImagesWithGemini(outputDir, progress, warnings, options =
179861
180467
  return response.text?.trim() || "";
179862
180468
  };
179863
180469
  }
179864
- const imageFiles = readdirSync43(join133(outputDir, "assets")).filter(
180470
+ const imageFiles = readdirSync43(join134(outputDir, "assets")).filter(
179865
180471
  (f) => /\.(png|jpg|jpeg|webp|gif)$/i.test(f)
179866
180472
  );
179867
180473
  const BATCH_SIZE = 20;
@@ -179889,13 +180495,13 @@ async function captionImagesWithGemini(outputDir, progress, warnings, options =
179889
180495
  const batch = imageFiles.slice(i, i + BATCH_SIZE);
179890
180496
  const results = await Promise.allSettled(
179891
180497
  batch.map(async (file) => {
179892
- const filePath = join133(outputDir, "assets", file);
180498
+ const filePath = join134(outputDir, "assets", file);
179893
180499
  const fd = openSync14(filePath, "r");
179894
180500
  let buffer;
179895
180501
  try {
179896
180502
  const stat = fstatSync9(fd);
179897
180503
  if (stat.size > 4e6) return { file, caption: "" };
179898
- buffer = readFileSync89(fd);
180504
+ buffer = readFileSync90(fd);
179899
180505
  } finally {
179900
180506
  closeSync15(fd);
179901
180507
  }
@@ -179928,11 +180534,11 @@ async function captionImagesWithGemini(outputDir, progress, warnings, options =
179928
180534
  `${Object.keys(geminiCaptions).length} images captioned with ${providerName}`
179929
180535
  );
179930
180536
  const svgFiles = [];
179931
- const assetsDir = join133(outputDir, "assets");
180537
+ const assetsDir = join134(outputDir, "assets");
179932
180538
  for (const f of readdirSync43(assetsDir)) {
179933
180539
  if (/\.svg$/i.test(f)) svgFiles.push({ file: f, relPath: f });
179934
180540
  }
179935
- const svgsSubdir = join133(assetsDir, "svgs");
180541
+ const svgsSubdir = join134(assetsDir, "svgs");
179936
180542
  if (existsSync117(svgsSubdir)) {
179937
180543
  for (const f of readdirSync43(svgsSubdir)) {
179938
180544
  if (/\.svg$/i.test(f)) svgFiles.push({ file: f, relPath: `svgs/${f}` });
@@ -179966,9 +180572,9 @@ async function captionImagesWithGemini(outputDir, progress, warnings, options =
179966
180572
  sharp5.concurrency(1);
179967
180573
  try {
179968
180574
  for (const { relPath } of batch) {
179969
- const filePath = join133(assetsDir, relPath);
180575
+ const filePath = join134(assetsDir, relPath);
179970
180576
  try {
179971
- const svgSource = readFileSync89(filePath, "utf-8");
180577
+ const svgSource = readFileSync90(filePath, "utf-8");
179972
180578
  const lightFillHits = (svgSource.match(/fill\s*=\s*["'](#fff(fff)?|white|#[ef][ef][ef]|#[ef]{6})["']/gi) || []).length;
179973
180579
  const darkFillHits = (svgSource.match(/fill\s*=\s*["'](#000(000)?|black|#[0-3]{6}|#[0-3]{3})["']/gi) || []).length;
179974
180580
  const bg = lightFillHits > darkFillHits ? { r: 32, g: 32, b: 32 } : { r: 255, g: 255, b: 255 };
@@ -180041,11 +180647,11 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
180041
180647
  const uncaptionedLines = [];
180042
180648
  const svgLines = [];
180043
180649
  const fontLines = [];
180044
- const assetsPath = join133(outputDir, "assets");
180650
+ const assetsPath = join134(outputDir, "assets");
180045
180651
  try {
180046
180652
  for (const file of readdirSync43(assetsPath)) {
180047
180653
  if (file === "svgs" || file === "fonts" || file === "lottie" || file === "videos") continue;
180048
- const filePath = join133(assetsPath, file);
180654
+ const filePath = join134(assetsPath, file);
180049
180655
  const stat = statSync45(filePath);
180050
180656
  if (!stat.isFile()) continue;
180051
180657
  const sizeKb = Math.round(stat.size / 1024);
@@ -180074,7 +180680,7 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
180074
180680
  } catch {
180075
180681
  }
180076
180682
  try {
180077
- const svgsPath = join133(assetsPath, "svgs");
180683
+ const svgsPath = join134(assetsPath, "svgs");
180078
180684
  for (const file of readdirSync43(svgsPath)) {
180079
180685
  if (!file.endsWith(".svg")) continue;
180080
180686
  const svgMatch = tokens.svgs.find(
@@ -180093,7 +180699,7 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
180093
180699
  } catch {
180094
180700
  }
180095
180701
  try {
180096
- const fontsPath = join133(assetsPath, "fonts");
180702
+ const fontsPath = join134(assetsPath, "fonts");
180097
180703
  for (const file of readdirSync43(fontsPath)) {
180098
180704
  fontLines.push(`fonts/${file} \u2014 font file`);
180099
180705
  }
@@ -180102,7 +180708,7 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
180102
180708
  const videoLines = [];
180103
180709
  try {
180104
180710
  const manifest = JSON.parse(
180105
- readFileSync89(join133(outputDir, "extracted", "video-manifest.json"), "utf-8")
180711
+ readFileSync90(join134(outputDir, "extracted", "video-manifest.json"), "utf-8")
180106
180712
  );
180107
180713
  for (const v2 of manifest) {
180108
180714
  if (!v2.localPath) continue;
@@ -180139,7 +180745,7 @@ __export(agentPromptGenerator_exports, {
180139
180745
  generateAgentPrompt: () => generateAgentPrompt
180140
180746
  });
180141
180747
  import { writeFileSync as writeFileSync55, readdirSync as readdirSync44, existsSync as existsSync118 } from "fs";
180142
- import { join as join134 } from "path";
180748
+ import { join as join135 } from "path";
180143
180749
  function inferColorRole(hex) {
180144
180750
  const r = parseInt(hex.slice(1, 3), 16) / 255;
180145
180751
  const g = parseInt(hex.slice(3, 5), 16) / 255;
@@ -180158,9 +180764,9 @@ function inferColorRole(hex) {
180158
180764
  }
180159
180765
  function generateAgentPrompt(outputDir, url, tokens, _animations, hasScreenshot, hasLottie, hasShaders, _catalogedAssets, _detectedLibraries) {
180160
180766
  const prompt = buildPrompt(outputDir, url, tokens, hasScreenshot, hasLottie, hasShaders);
180161
- writeFileSync55(join134(outputDir, "AGENTS.md"), prompt, "utf-8");
180162
- writeFileSync55(join134(outputDir, "CLAUDE.md"), prompt, "utf-8");
180163
- writeFileSync55(join134(outputDir, ".cursorrules"), prompt, "utf-8");
180767
+ writeFileSync55(join135(outputDir, "AGENTS.md"), prompt, "utf-8");
180768
+ writeFileSync55(join135(outputDir, "CLAUDE.md"), prompt, "utf-8");
180769
+ writeFileSync55(join135(outputDir, ".cursorrules"), prompt, "utf-8");
180164
180770
  }
180165
180771
  function buildPrompt(outputDir, url, tokens, hasScreenshot, hasLottie, hasShaders) {
180166
180772
  const title = tokens.title || new URL(url).hostname.replace(/^www\./, "");
@@ -180169,7 +180775,7 @@ function buildPrompt(outputDir, url, tokens, hasScreenshot, hasLottie, hasShader
180169
180775
  (f) => f.family + (f.variable && f.weightRange ? ` (${f.weightRange[0]}-${f.weightRange[1]} variable)` : f.weights.length > 0 ? ` (${f.weights.join(",")})` : "")
180170
180776
  ).join(", ") || "none detected";
180171
180777
  function contactSheetRows(dir, baseFile, label2) {
180172
- const fullDir = join134(outputDir, dir);
180778
+ const fullDir = join135(outputDir, dir);
180173
180779
  if (!existsSync118(fullDir)) return [];
180174
180780
  const baseName = baseFile.replace(/\.jpg$/, "");
180175
180781
  const escapedBase = baseName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -180202,7 +180808,7 @@ function buildPrompt(outputDir, url, tokens, hasScreenshot, hasLottie, hasShader
180202
180808
  tableRows.push(
180203
180809
  `| \`extracted/tokens.json\` | Design tokens: ${tokens.colors.length} colors, ${tokens.fonts.length} fonts, ${tokens.headings?.length ?? 0} headings, ${tokens.ctas?.length ?? 0} CTAs |`
180204
180810
  );
180205
- if (existsSync118(join134(outputDir, "extracted", "design-styles.json"))) {
180811
+ if (existsSync118(join135(outputDir, "extracted", "design-styles.json"))) {
180206
180812
  tableRows.push(
180207
180813
  "| `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. |"
180208
180814
  );
@@ -180273,15 +180879,15 @@ var init_agentPromptGenerator = __esm({
180273
180879
  });
180274
180880
 
180275
180881
  // src/capture/scaffolding.ts
180276
- import { existsSync as existsSync119, readFileSync as readFileSync90 } from "fs";
180277
- import { join as join135, resolve as resolve77 } from "path";
180882
+ import { existsSync as existsSync119, readFileSync as readFileSync91 } from "fs";
180883
+ import { join as join136, resolve as resolve77 } from "path";
180278
180884
  function loadEnvFile(startDir) {
180279
180885
  try {
180280
180886
  let dir = resolve77(startDir);
180281
180887
  for (let i = 0; i < 5; i++) {
180282
180888
  const envPath = resolve77(dir, ".env");
180283
180889
  try {
180284
- const envContent = readFileSync90(envPath, "utf-8");
180890
+ const envContent = readFileSync91(envPath, "utf-8");
180285
180891
  for (const line3 of envContent.split("\n")) {
180286
180892
  const trimmed = line3.trim();
180287
180893
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -180300,7 +180906,7 @@ function loadEnvFile(startDir) {
180300
180906
  }
180301
180907
  }
180302
180908
  async function generateProjectScaffold(outputDir, url, tokens, animationCatalog, hasScreenshots, hasLotties, hasShaders, catalogedAssets, progress, warnings, detectedLibraries) {
180303
- const metaPath = join135(outputDir, "meta.json");
180909
+ const metaPath = join136(outputDir, "meta.json");
180304
180910
  if (!existsSync119(metaPath)) {
180305
180911
  const hostname2 = new URL(url).hostname.replace(/^www\./, "");
180306
180912
  writeNewFileSync(
@@ -180354,10 +180960,10 @@ var init_pageBlockDetection = __esm({
180354
180960
 
180355
180961
  // src/capture/responseRecord.ts
180356
180962
  import { writeFileSync as writeFileSync56 } from "fs";
180357
- import { join as join136 } from "path";
180963
+ import { join as join137 } from "path";
180358
180964
  function writeResponseRecord(extractedDir, record2) {
180359
180965
  writeFileSync56(
180360
- join136(extractedDir, RESPONSE_RECORD_FILENAME),
180966
+ join137(extractedDir, RESPONSE_RECORD_FILENAME),
180361
180967
  JSON.stringify(record2, null, 2),
180362
180968
  "utf-8"
180363
180969
  );
@@ -180422,7 +181028,7 @@ __export(screenshotCapture_exports, {
180422
181028
  pngHeight: () => pngHeight
180423
181029
  });
180424
181030
  import { writeFileSync as writeFileSync57, mkdirSync as mkdirSync63 } from "fs";
180425
- import { join as join137 } from "path";
181031
+ import { join as join138 } from "path";
180426
181032
  function pngHeight(buf) {
180427
181033
  if (buf.length < 24) return null;
180428
181034
  if (buf[12] !== 73 || buf[13] !== 72 || buf[14] !== 68 || buf[15] !== 82) return null;
@@ -180447,7 +181053,7 @@ async function captureFullPagePlate(page, screenshotsDir, budget = {}) {
180447
181053
  const buffer = await page.screenshot({ type: "png", fullPage: true });
180448
181054
  const produced = pngHeight(buffer);
180449
181055
  if (produced != null && produced > MAX_PLATE_HEIGHT_PX) return null;
180450
- writeFileSync57(join137(screenshotsDir, "full-page.png"), buffer);
181056
+ writeFileSync57(join138(screenshotsDir, "full-page.png"), buffer);
180451
181057
  return "screenshots/full-page.png";
180452
181058
  } finally {
180453
181059
  try {
@@ -180462,7 +181068,7 @@ async function captureFullPagePlate(page, screenshotsDir, budget = {}) {
180462
181068
  }
180463
181069
  }
180464
181070
  async function captureScrollScreenshots(page, outputDir, budget = {}) {
180465
- const screenshotsDir = join137(outputDir, "screenshots");
181071
+ const screenshotsDir = join138(outputDir, "screenshots");
180466
181072
  mkdirSync63(screenshotsDir, { recursive: true });
180467
181073
  const MAX_SCREENSHOTS = 20;
180468
181074
  const filePaths = [];
@@ -180559,7 +181165,7 @@ async function captureScrollScreenshots(page, outputDir, budget = {}) {
180559
181165
  finalPositions[i] / Math.max(1, scrollHeight - viewportHeight) * 100
180560
181166
  );
180561
181167
  const filename = `scroll-${String(Math.min(pct, 100)).padStart(3, "0")}.png`;
180562
- const filePath = join137(screenshotsDir, filename);
181168
+ const filePath = join138(screenshotsDir, filename);
180563
181169
  if ((budget.remainingMs?.() ?? 1) <= 0) break;
180564
181170
  const buffer = await page.screenshot({ type: "png" });
180565
181171
  writeFileSync57(filePath, buffer);
@@ -180920,7 +181526,7 @@ __export(capture_exports, {
180920
181526
  captureWebsite: () => captureWebsite
180921
181527
  });
180922
181528
  import { mkdirSync as mkdirSync64, writeFileSync as writeFileSync58, existsSync as existsSync120 } from "fs";
180923
- import { join as join138 } from "path";
181529
+ import { join as join139 } from "path";
180924
181530
  async function captureWebsite(opts, onProgress) {
180925
181531
  const {
180926
181532
  url,
@@ -180962,9 +181568,9 @@ async function captureWebsite(opts, onProgress) {
180962
181568
  };
180963
181569
  phase("browser", "started");
180964
181570
  loadEnvFile(outputDir);
180965
- mkdirSync64(join138(outputDir, "extracted"), { recursive: true });
180966
- mkdirSync64(join138(outputDir, "screenshots"), { recursive: true });
180967
- mkdirSync64(join138(outputDir, "assets"), { recursive: true });
181571
+ mkdirSync64(join139(outputDir, "extracted"), { recursive: true });
181572
+ mkdirSync64(join139(outputDir, "screenshots"), { recursive: true });
181573
+ mkdirSync64(join139(outputDir, "assets"), { recursive: true });
180968
181574
  progress("browser", "Launching headless Chrome...");
180969
181575
  const { ensureBrowser: ensureBrowser2 } = await Promise.resolve().then(() => (init_manager2(), manager_exports2));
180970
181576
  const browser = await ensureBrowser2();
@@ -181075,7 +181681,7 @@ async function captureWebsite(opts, onProgress) {
181075
181681
  progress("warn", message);
181076
181682
  }
181077
181683
  const httpStatus = navigationResponse?.status() ?? null;
181078
- writeResponseRecord(join138(outputDir, "extracted"), { status: httpStatus });
181684
+ writeResponseRecord(join139(outputDir, "extracted"), { status: httpStatus });
181079
181685
  const blockedReason = detectBlockedPage({
181080
181686
  httpStatus,
181081
181687
  ...contentCheckTimedOut ? {
@@ -181139,7 +181745,7 @@ async function captureWebsite(opts, onProgress) {
181139
181745
  else discoveredLotties[existing] = found;
181140
181746
  }
181141
181747
  if (discoveredLotties.length > 0 && remainingMs() > 0) {
181142
- const lottieDir = join138(outputDir, "assets", "lottie");
181748
+ const lottieDir = join139(outputDir, "assets", "lottie");
181143
181749
  mkdirSync64(lottieDir, { recursive: true });
181144
181750
  const lottieBudget = { remainingMs, byteBudget: downloadByteBudget };
181145
181751
  const savedCount = await saveLottieAnimations(discoveredLotties, lottieDir, lottieBudget);
@@ -181160,7 +181766,7 @@ async function captureWebsite(opts, onProgress) {
181160
181766
  });
181161
181767
  capturedShaders = unique;
181162
181768
  writeFileSync58(
181163
- join138(outputDir, "extracted", "shaders.json"),
181769
+ join139(outputDir, "extracted", "shaders.json"),
181164
181770
  JSON.stringify(unique, null, 2),
181165
181771
  "utf-8"
181166
181772
  );
@@ -181175,7 +181781,7 @@ async function captureWebsite(opts, onProgress) {
181175
181781
  svgs: tokens.svgs.map(({ outerHTML: _, ...rest }) => rest)
181176
181782
  };
181177
181783
  writeFileSync58(
181178
- join138(outputDir, "extracted", "tokens.json"),
181784
+ join139(outputDir, "extracted", "tokens.json"),
181179
181785
  JSON.stringify(tokensForDisk, null, 2),
181180
181786
  "utf-8"
181181
181787
  );
@@ -181183,7 +181789,7 @@ async function captureWebsite(opts, onProgress) {
181183
181789
  try {
181184
181790
  const designStyles = await extractDesignStyles(page1);
181185
181791
  writeFileSync58(
181186
- join138(outputDir, "extracted", "design-styles.json"),
181792
+ join139(outputDir, "extracted", "design-styles.json"),
181187
181793
  JSON.stringify(designStyles, null, 2),
181188
181794
  "utf-8"
181189
181795
  );
@@ -181317,8 +181923,8 @@ ${err.stack}` : normalizeErrorMessage(err);
181317
181923
  );
181318
181924
  try {
181319
181925
  const fontsManifest = extractFontMetadata(
181320
- join138(outputDir, "assets", "fonts"),
181321
- join138(outputDir, "extracted", "fonts-manifest.json")
181926
+ join139(outputDir, "assets", "fonts"),
181927
+ join139(outputDir, "extracted", "fonts-manifest.json")
181322
181928
  );
181323
181929
  if (fontsManifest.families.length > 0) {
181324
181930
  const summary = fontsManifest.families.map((f) => `${f.family}${f.variable ? " (variable)" : ""} \xD7 ${f.fileCount}`).join(", ");
@@ -181345,7 +181951,7 @@ ${err.stack}` : normalizeErrorMessage(err);
181345
181951
  representativeAnimations: representativeAnims
181346
181952
  };
181347
181953
  writeFileSync58(
181348
- join138(outputDir, "extracted", "animations.json"),
181954
+ join139(outputDir, "extracted", "animations.json"),
181349
181955
  JSON.stringify(leanCatalog, null, 2),
181350
181956
  "utf-8"
181351
181957
  );
@@ -181362,7 +181968,7 @@ ${err.stack}` : normalizeErrorMessage(err);
181362
181968
  assets = assetPass.assets;
181363
181969
  assetDrops = assetPass.drops;
181364
181970
  writeFileSync58(
181365
- join138(outputDir, "extracted", "icons-manifest.json"),
181971
+ join139(outputDir, "extracted", "icons-manifest.json"),
181366
181972
  JSON.stringify(assetPass.icons, null, 2),
181367
181973
  "utf-8"
181368
181974
  );
@@ -181403,7 +182009,7 @@ ${err.stack}` : normalizeErrorMessage(err);
181403
182009
  svgs: tokens.svgs.map(({ outerHTML: _, ...rest }) => rest)
181404
182010
  };
181405
182011
  writeFileSync58(
181406
- join138(outputDir, "extracted", "tokens.json"),
182012
+ join139(outputDir, "extracted", "tokens.json"),
181407
182013
  JSON.stringify(tokensForDisk2, null, 2),
181408
182014
  "utf-8"
181409
182015
  );
@@ -181419,12 +182025,12 @@ ${extracted.bodyHtml}
181419
182025
  </body>
181420
182026
  </html>
181421
182027
  `;
181422
- writeFileSync58(join138(outputDir, "extracted", "page.html"), pageHtml, "utf-8");
182028
+ writeFileSync58(join139(outputDir, "extracted", "page.html"), pageHtml, "utf-8");
181423
182029
  } catch (err) {
181424
182030
  warnings.push(`page.html write failed: ${err}`);
181425
182031
  }
181426
182032
  if (visibleTextContent) {
181427
- writeFileSync58(join138(outputDir, "extracted", "visible-text.txt"), visibleTextContent, "utf-8");
182033
+ writeFileSync58(join139(outputDir, "extracted", "visible-text.txt"), visibleTextContent, "utf-8");
181428
182034
  }
181429
182035
  let geminiCaptions = {};
181430
182036
  if (skipVision) {
@@ -181461,7 +182067,7 @@ ${extracted.bodyHtml}
181461
182067
  const hasVisionKey = !!(!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));
181462
182068
  const header2 = hasVisionKey ? "# Asset Descriptions\n\nOne line per file. Read this instead of opening every image individually.\n\nTo find a specific brand or icon, **grep this file for the brand name in the description text** (e.g. `grep -i 'autodesk' asset-descriptions.md`). The Gemini Vision captions identify what's actually in each file \u2014 that's the agent's selector.\n\nThe `logo-<hash>.svg` filename prefix is a cheap structural hint (DOM said this SVG was inside a `<header>`, home-link `<a>`, or had an aria-label matching the page brand). It is NOT a content claim \u2014 many `logo-*` files are nav icons or decorative shapes. Trust the captions, not the filename prefix.\n\n" : "# Asset Descriptions\n\n\u26A0\uFE0F No vision credentials \u2014 descriptions below are catalog-derived (alt text, headings, section context, filename) instead of Vision-generated. To get richer Vision descriptions on the next capture, set GEMINI_API_KEY (or GOOGLE_API_KEY), or HYPERFRAMES_VERTEX_PROJECT_ID plus HYPERFRAMES_VERTEX_SERVICE_ACCOUNT for Vertex service-account auth, and re-run.\n\nThe `logo-<hash>.svg` filename prefix is a structural hint (DOM said this SVG was inside a `<header>`, home-link `<a>`, or had an aria-label matching the page brand). To pick the actual brand logo without Vision, open the `logo-*` candidates in a previewer or rasterize them with `sharp` before referencing \u2014 composing a fake logo ships off-brand in the final video.\n\n";
181463
182069
  writeFileSync58(
181464
- join138(outputDir, "extracted", "asset-descriptions.md"),
182070
+ join139(outputDir, "extracted", "asset-descriptions.md"),
181465
182071
  header2 + lines.map((l) => "- " + l).join("\n") + "\n",
181466
182072
  "utf-8"
181467
182073
  );
@@ -181479,8 +182085,8 @@ ${extracted.bodyHtml}
181479
182085
  const { createScrollContactSheet: createScrollContactSheet2, createAssetContactSheet: createAssetContactSheet2, createSvgContactSheet: createSvgContactSheet2 } = await Promise.resolve().then(() => (init_contactSheet(), contactSheet_exports));
181480
182086
  const contactSheetBudget = { remainingMs };
181481
182087
  const scrollSheets = await createScrollContactSheet2(
181482
- join138(outputDir, "screenshots"),
181483
- join138(outputDir, "screenshots", "contact-sheet.jpg"),
182088
+ join139(outputDir, "screenshots"),
182089
+ join139(outputDir, "screenshots", "contact-sheet.jpg"),
181484
182090
  contactSheetBudget
181485
182091
  );
181486
182092
  if (scrollSheets.length > 0)
@@ -181488,11 +182094,11 @@ ${extracted.bodyHtml}
181488
182094
  "design",
181489
182095
  `Screenshot contact sheet generated (${scrollSheets.length} page${scrollSheets.length > 1 ? "s" : ""})`
181490
182096
  );
181491
- const assetsImgDir = join138(outputDir, "assets");
182097
+ const assetsImgDir = join139(outputDir, "assets");
181492
182098
  if (existsSync120(assetsImgDir)) {
181493
182099
  const assetSheets = await createAssetContactSheet2(
181494
182100
  assetsImgDir,
181495
- join138(outputDir, "assets", "contact-sheet.jpg"),
182101
+ join139(outputDir, "assets", "contact-sheet.jpg"),
181496
182102
  contactSheetBudget
181497
182103
  );
181498
182104
  if (assetSheets.length > 0)
@@ -181501,9 +182107,9 @@ ${extracted.bodyHtml}
181501
182107
  `Asset contact sheet generated (${assetSheets.length} page${assetSheets.length > 1 ? "s" : ""})`
181502
182108
  );
181503
182109
  }
181504
- const svgsDir = join138(outputDir, "assets", "svgs");
181505
- const assetsRootDir = join138(outputDir, "assets");
181506
- const svgOutputPath = existsSync120(svgsDir) ? join138(outputDir, "assets", "svgs", "contact-sheet.jpg") : join138(outputDir, "assets", "contact-sheet-svgs.jpg");
182110
+ const svgsDir = join139(outputDir, "assets", "svgs");
182111
+ const assetsRootDir = join139(outputDir, "assets");
182112
+ const svgOutputPath = existsSync120(svgsDir) ? join139(outputDir, "assets", "svgs", "contact-sheet.jpg") : join139(outputDir, "assets", "contact-sheet-svgs.jpg");
181507
182113
  const svgSheets = await createSvgContactSheet2(
181508
182114
  svgsDir,
181509
182115
  svgOutputPath,
@@ -181536,7 +182142,7 @@ ${extracted.bodyHtml}
181536
182142
  animationCatalog,
181537
182143
  screenshots.length > 0,
181538
182144
  discoveredLotties.length > 0,
181539
- existsSync120(join138(outputDir, "extracted", "shaders.json")),
182145
+ existsSync120(join139(outputDir, "extracted", "shaders.json")),
181540
182146
  catalogedAssets,
181541
182147
  progress,
181542
182148
  warnings,
@@ -181900,10 +182506,10 @@ __export(state_exports, {
181900
182506
  stateFilePath: () => stateFilePath,
181901
182507
  writeStackOutputs: () => writeStackOutputs
181902
182508
  });
181903
- import { existsSync as existsSync121, mkdirSync as mkdirSync65, readdirSync as readdirSync45, readFileSync as readFileSync91, rmSync as rmSync40, writeFileSync as writeFileSync59 } from "fs";
181904
- import { dirname as dirname63, join as join139 } from "path";
182509
+ import { existsSync as existsSync121, mkdirSync as mkdirSync65, readdirSync as readdirSync45, readFileSync as readFileSync93, rmSync as rmSync40, writeFileSync as writeFileSync59 } from "fs";
182510
+ import { dirname as dirname63, join as join140 } from "path";
181905
182511
  function stateFilePath(stackName = DEFAULT_STACK_NAME, cwd = process.cwd()) {
181906
- return join139(cwd, STATE_DIR_NAME, `${STATE_FILE_PREFIX}${stackName}.json`);
182512
+ return join140(cwd, STATE_DIR_NAME, `${STATE_FILE_PREFIX}${stackName}.json`);
181907
182513
  }
181908
182514
  function writeStackOutputs(outputs, cwd = process.cwd()) {
181909
182515
  const path = stateFilePath(outputs.stackName, cwd);
@@ -181915,7 +182521,7 @@ function readStackOutputs(stackName = DEFAULT_STACK_NAME, cwd = process.cwd()) {
181915
182521
  const path = stateFilePath(stackName, cwd);
181916
182522
  if (!existsSync121(path)) return null;
181917
182523
  try {
181918
- return JSON.parse(readFileSync91(path, "utf-8"));
182524
+ return JSON.parse(readFileSync93(path, "utf-8"));
181919
182525
  } catch {
181920
182526
  return null;
181921
182527
  }
@@ -181925,7 +182531,7 @@ function deleteStackOutputs(stackName = DEFAULT_STACK_NAME, cwd = process.cwd())
181925
182531
  if (existsSync121(path)) rmSync40(path);
181926
182532
  }
181927
182533
  function listStackNames(cwd = process.cwd()) {
181928
- const dir = join139(cwd, STATE_DIR_NAME);
182534
+ const dir = join140(cwd, STATE_DIR_NAME);
181929
182535
  if (!existsSync121(dir)) return [];
181930
182536
  return readdirSync45(dir).filter((f) => f.startsWith(STATE_FILE_PREFIX) && f.endsWith(".json")).map((f) => f.slice(STATE_FILE_PREFIX.length, -".json".length));
181931
182537
  }
@@ -181958,7 +182564,7 @@ var init_state = __esm({
181958
182564
  // src/commands/lambda/sam.ts
181959
182565
  import { execFileSync as execFileSync19, spawnSync as spawnSync4 } from "child_process";
181960
182566
  import { existsSync as existsSync122 } from "fs";
181961
- import { join as join140 } from "path";
182567
+ import { join as join141 } from "path";
181962
182568
  function assertSamAvailable() {
181963
182569
  try {
181964
182570
  execFileSync19("sam", ["--version"], { stdio: "ignore" });
@@ -181978,7 +182584,7 @@ function assertAwsCliAvailable() {
181978
182584
  }
181979
182585
  }
181980
182586
  function locateSamTemplate(repoRoot2) {
181981
- const candidate = join140(repoRoot2, "examples", "aws-lambda", "template.yaml");
182587
+ const candidate = join141(repoRoot2, "examples", "aws-lambda", "template.yaml");
181982
182588
  if (!existsSync122(candidate)) {
181983
182589
  throw new Error(
181984
182590
  `[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.`
@@ -182012,7 +182618,7 @@ function samDeploy(opts) {
182012
182618
  if (opts.awsProfile) {
182013
182619
  args.push("--profile", opts.awsProfile);
182014
182620
  }
182015
- const samDir = join140(opts.repoRoot, "examples", "aws-lambda");
182621
+ const samDir = join141(opts.repoRoot, "examples", "aws-lambda");
182016
182622
  const result = spawnSync4("sam", args, { cwd: samDir, stdio: opts.stdio ?? "inherit" });
182017
182623
  if (result.status !== 0) {
182018
182624
  throw new Error(
@@ -182030,7 +182636,7 @@ function samDelete(opts) {
182030
182636
  if (opts.awsProfile) {
182031
182637
  args.push("--profile", opts.awsProfile);
182032
182638
  }
182033
- const samDir = join140(opts.repoRoot, "examples", "aws-lambda");
182639
+ const samDir = join141(opts.repoRoot, "examples", "aws-lambda");
182034
182640
  const result = spawnSync4("sam", args, { cwd: samDir, stdio: opts.stdio ?? "inherit" });
182035
182641
  if (result.status !== 0) {
182036
182642
  throw new Error(`[lambda] sam delete exited with code ${result.status ?? "unknown"}`);
@@ -182114,7 +182720,7 @@ __export(deploy_exports, {
182114
182720
  });
182115
182721
  import { spawnSync as spawnSync5 } from "child_process";
182116
182722
  import { existsSync as existsSync124 } from "fs";
182117
- import { join as join141, resolve as resolve80 } from "path";
182723
+ import { join as join143, resolve as resolve80 } from "path";
182118
182724
  async function runDeploy(args = {}) {
182119
182725
  const resolved2 = {
182120
182726
  stackName: args.stackName ?? DEFAULT_STACK_NAME,
@@ -182131,7 +182737,7 @@ async function runDeploy(args = {}) {
182131
182737
  console.log(c.dim("\u2192 Building handler ZIP"));
182132
182738
  buildHandlerZip(root);
182133
182739
  } else {
182134
- const zip = join141(root, "packages", "aws-lambda", "dist", "handler.zip");
182740
+ const zip = join143(root, "packages", "aws-lambda", "dist", "handler.zip");
182135
182741
  if (!existsSync124(zip)) {
182136
182742
  throw new Error(
182137
182743
  `--skip-build set but ${zip} does not exist. Run \`bun run --cwd packages/aws-lambda build:zip\` first or drop --skip-build.`
@@ -182175,7 +182781,7 @@ async function runDeploy(args = {}) {
182175
182781
  function buildHandlerZip(root) {
182176
182782
  const result = spawnSync5(
182177
182783
  "bun",
182178
- ["run", "--cwd", join141(root, "packages", "aws-lambda"), "build:zip"],
182784
+ ["run", "--cwd", join143(root, "packages", "aws-lambda"), "build:zip"],
182179
182785
  { stdio: "inherit" }
182180
182786
  );
182181
182787
  if (result.status !== 0) {
@@ -182244,14 +182850,14 @@ var init_sites = __esm({
182244
182850
  });
182245
182851
 
182246
182852
  // src/commands/lambda/_dimensions.ts
182247
- import { readFileSync as readFileSync93 } from "fs";
182248
- import { join as join143 } from "path";
182853
+ import { readFileSync as readFileSync94 } from "fs";
182854
+ import { join as join144 } from "path";
182249
182855
  function warnOnDimensionMismatch(args) {
182250
182856
  if (args.quiet) return;
182251
182857
  if (args.outputResolution) return;
182252
182858
  let html;
182253
182859
  try {
182254
- html = readFileSync93(join143(args.projectDir, "index.html"), "utf-8");
182860
+ html = readFileSync94(join144(args.projectDir, "index.html"), "utf-8");
182255
182861
  } catch {
182256
182862
  return;
182257
182863
  }
@@ -182281,7 +182887,7 @@ __export(render_exports2, {
182281
182887
  runRender: () => runRender
182282
182888
  });
182283
182889
  import { existsSync as existsSync125 } from "fs";
182284
- import { join as join144, resolve as resolvePath2 } from "path";
182890
+ import { join as join145, resolve as resolvePath2 } from "path";
182285
182891
  async function loadSDK2() {
182286
182892
  return import("@hyperframes/aws-lambda/sdk");
182287
182893
  }
@@ -182297,7 +182903,7 @@ async function runRender(args) {
182297
182903
  });
182298
182904
  const variables = resolveVariablesArg(args.variables, args.variablesFile);
182299
182905
  if (variables && Object.keys(variables).length > 0) {
182300
- const indexPath2 = join144(projectDir, "index.html");
182906
+ const indexPath2 = join145(projectDir, "index.html");
182301
182907
  if (existsSync125(indexPath2)) {
182302
182908
  const issues = validateVariablesAgainstProject(indexPath2, variables);
182303
182909
  reportVariableIssues(issues, { strict: args.strictVariables ?? false, quiet: args.json });
@@ -182431,8 +183037,8 @@ __export(render_batch_exports, {
182431
183037
  runRenderBatch: () => runRenderBatch,
182432
183038
  runWithConcurrencyLimit: () => runWithConcurrencyLimit
182433
183039
  });
182434
- import { existsSync as existsSync126, readFileSync as readFileSync94 } from "fs";
182435
- import { join as join145, resolve as resolvePath3 } from "path";
183040
+ import { existsSync as existsSync126, readFileSync as readFileSync95 } from "fs";
183041
+ import { join as join146, resolve as resolvePath3 } from "path";
182436
183042
  async function loadSDK3() {
182437
183043
  return import("@hyperframes/aws-lambda/sdk");
182438
183044
  }
@@ -182456,7 +183062,7 @@ async function runRenderBatch(args) {
182456
183062
  outputResolution: args.outputResolution,
182457
183063
  quiet: args.json
182458
183064
  });
182459
- const schema = loadProjectVariableSchema(join145(projectDir, "index.html"));
183065
+ const schema = loadProjectVariableSchema(join146(projectDir, "index.html"));
182460
183066
  const strict = args.strictVariables ?? false;
182461
183067
  let hadStrictIssue = false;
182462
183068
  for (const { entry, lineNumber } of entries2) {
@@ -182591,7 +183197,7 @@ function buildLambdaBatchRenderConfig(args) {
182591
183197
  return config;
182592
183198
  }
182593
183199
  function parseBatchFile(path) {
182594
- const raw = readFileSync94(path, "utf8");
183200
+ const raw = readFileSync95(path, "utf8");
182595
183201
  const lines = raw.split(/\r?\n/);
182596
183202
  const out = [];
182597
183203
  for (let i = 0; i < lines.length; i++) {
@@ -182785,7 +183391,7 @@ __export(policies_exports, {
182785
183391
  runPolicies: () => runPolicies,
182786
183392
  validatePolicy: () => validatePolicy
182787
183393
  });
182788
- import { readFileSync as readFileSync95 } from "fs";
183394
+ import { readFileSync as readFileSync96 } from "fs";
182789
183395
  function allRequiredActions() {
182790
183396
  const set = /* @__PURE__ */ new Set();
182791
183397
  for (const group of Object.values(REQUIRED_ACTIONS)) {
@@ -182891,7 +183497,7 @@ async function runPolicies(args) {
182891
183497
  }
182892
183498
  }
182893
183499
  function validatePolicy(policyPath) {
182894
- const raw = readFileSync95(policyPath, "utf-8");
183500
+ const raw = readFileSync96(policyPath, "utf-8");
182895
183501
  const parsed = JSON.parse(raw);
182896
183502
  const statements = Array.isArray(parsed.Statement) ? parsed.Statement : parsed.Statement ? [
182897
183503
  parsed.Statement
@@ -183502,9 +184108,9 @@ __export(cloudrun_exports, {
183502
184108
  missingCloudRunAdapterMessage: () => missingCloudRunAdapterMessage
183503
184109
  });
183504
184110
  import { spawnSync as spawnSync6 } from "child_process";
183505
- import { existsSync as existsSync127, mkdirSync as mkdirSync66, readFileSync as readFileSync96, writeFileSync as writeFileSync60 } from "fs";
184111
+ import { existsSync as existsSync127, mkdirSync as mkdirSync66, readFileSync as readFileSync97, writeFileSync as writeFileSync60 } from "fs";
183506
184112
  import { homedir as homedir24 } from "os";
183507
- import { join as join146, resolve as resolve81 } from "path";
184113
+ import { join as join147, resolve as resolve81 } from "path";
183508
184114
  function loadCloudRunAdapter() {
183509
184115
  cloudRunAdapterPromise ??= Promise.all([
183510
184116
  import("@hyperframes/gcp-cloud-run/sdk"),
@@ -183523,10 +184129,10 @@ Or, for an opt-in project setup:
183523
184129
  ${c.accent("npm install @hyperframes/gcp-cloud-run")}`;
183524
184130
  }
183525
184131
  function stateDir() {
183526
- return join146(homedir24(), ".hyperframes");
184132
+ return join147(homedir24(), ".hyperframes");
183527
184133
  }
183528
184134
  function statePath() {
183529
- return join146(stateDir(), "cloudrun-state.json");
184135
+ return join147(stateDir(), "cloudrun-state.json");
183530
184136
  }
183531
184137
  function writeState2(state) {
183532
184138
  mkdirSync66(stateDir(), { recursive: true });
@@ -183540,7 +184146,7 @@ function readState2(args) {
183540
184146
  let base2 = {};
183541
184147
  if (existsSync127(statePath())) {
183542
184148
  try {
183543
- base2 = JSON.parse(readFileSync96(statePath(), "utf8"));
184149
+ base2 = JSON.parse(readFileSync97(statePath(), "utf8"));
183544
184150
  } catch {
183545
184151
  }
183546
184152
  }
@@ -183683,11 +184289,11 @@ function machineVars(args, project, region, image) {
183683
184289
  }
183684
184290
  function findRepoRoot(tfDir) {
183685
184291
  const candidate = resolve81(tfDir, "..", "..", "..");
183686
- if (existsSync127(join146(candidate, "packages", "gcp-cloud-run", "Dockerfile"))) return candidate;
184292
+ if (existsSync127(join147(candidate, "packages", "gcp-cloud-run", "Dockerfile"))) return candidate;
183687
184293
  return null;
183688
184294
  }
183689
184295
  function writeCloudBuildConfig(image) {
183690
- const cfgPath = join146(stateDir(), "cloudrun-cloudbuild.yaml");
184296
+ const cfgPath = join147(stateDir(), "cloudrun-cloudbuild.yaml");
183691
184297
  mkdirSync66(stateDir(), { recursive: true });
183692
184298
  writeFileSync60(
183693
184299
  cfgPath,
@@ -183899,7 +184505,7 @@ async function runRenderBatch2(args) {
183899
184505
  if (failed.length > 0) failCommand();
183900
184506
  }
183901
184507
  function parseBatchFile2(path) {
183902
- const lines = readFileSync96(path, "utf8").split(/\r?\n/);
184508
+ const lines = readFileSync97(path, "utf8").split(/\r?\n/);
183903
184509
  const entries2 = [];
183904
184510
  lines.forEach((line3, idx) => {
183905
184511
  const trimmed = line3.trim();
@@ -183924,7 +184530,7 @@ function parseBatchFile2(path) {
183924
184530
  async function runDestroy2(args) {
183925
184531
  const { getTerraformModuleDir } = await loadCloudRunAdapter();
183926
184532
  const tfDir = getTerraformModuleDir();
183927
- const state = existsSync127(statePath()) ? JSON.parse(readFileSync96(statePath(), "utf8")) : {};
184533
+ const state = existsSync127(statePath()) ? JSON.parse(readFileSync97(statePath(), "utf8")) : {};
183928
184534
  const project = args.project ?? state.projectId;
183929
184535
  const region = args.region ?? state.region ?? "us-central1";
183930
184536
  const image = args.image ?? "unused:latest";
@@ -183979,7 +184585,7 @@ function resolveAndValidateVariables(args, projectDir) {
183979
184585
  args["variables-file"]
183980
184586
  );
183981
184587
  if (variables && Object.keys(variables).length > 0) {
183982
- const indexPath2 = join146(projectDir, "index.html");
184588
+ const indexPath2 = join147(projectDir, "index.html");
183983
184589
  if (existsSync127(indexPath2)) {
183984
184590
  const issues = validateVariablesAgainstProject(indexPath2, variables);
183985
184591
  reportVariableIssues(issues, {
@@ -184226,7 +184832,7 @@ ${HELP2}`);
184226
184832
  });
184227
184833
 
184228
184834
  // src/cloud/detectAspectRatio.ts
184229
- import { readFileSync as readFileSync97 } from "fs";
184835
+ import { readFileSync as readFileSync98 } from "fs";
184230
184836
  function extractAttributeNumber(tag, re2) {
184231
184837
  const match = tag.match(re2);
184232
184838
  if (!match) return null;
@@ -184238,7 +184844,7 @@ function extractAttributeNumber(tag, re2) {
184238
184844
  function detectAspectRatioFromHtml(entryHtmlPath) {
184239
184845
  let html;
184240
184846
  try {
184241
- html = readFileSync97(entryHtmlPath, "utf-8");
184847
+ html = readFileSync98(entryHtmlPath, "utf-8");
184242
184848
  } catch (err) {
184243
184849
  return { kind: "read-error", error: normalizeErrorMessage(err) };
184244
184850
  }
@@ -184352,7 +184958,7 @@ import {
184352
184958
  rmSync as rmSync41,
184353
184959
  statSync as statSync46
184354
184960
  } from "fs";
184355
- import { dirname as dirname65, join as join147, resolve as resolve82 } from "path";
184961
+ import { dirname as dirname65, join as join148, resolve as resolve82 } from "path";
184356
184962
  import { pipeline as pipeline3 } from "stream/promises";
184357
184963
  async function downloadToFile(url, destPath, options = {}) {
184358
184964
  const fetchImpl = options.fetchImpl ?? fetch;
@@ -184369,8 +184975,8 @@ async function downloadToFile(url, destPath, options = {}) {
184369
184975
  const totalOpt = total !== void 0 && Number.isFinite(total) ? total : void 0;
184370
184976
  const destination = resolveDownloadDestination(destPath);
184371
184977
  const previous = statSync46(destination, { throwIfNoEntry: false });
184372
- const stage = mkdtempSync27(join147(dirname65(destination), ".hf-download-"));
184373
- const stagedFile = join147(stage, "download");
184978
+ const stage = mkdtempSync27(join148(dirname65(destination), ".hf-download-"));
184979
+ const stagedFile = join148(stage, "download");
184374
184980
  let bytes = 0;
184375
184981
  try {
184376
184982
  await pipeline3(
@@ -187036,12 +187642,12 @@ var init_freeze = __esm({
187036
187642
  });
187037
187643
 
187038
187644
  // ../core/dist/figma/jsonl.js
187039
- import { existsSync as existsSync129, readFileSync as readFileSync98 } from "fs";
187645
+ import { existsSync as existsSync129, readFileSync as readFileSync99 } from "fs";
187040
187646
  function readJsonlValues(path) {
187041
187647
  if (!existsSync129(path))
187042
187648
  return [];
187043
187649
  const out = [];
187044
- for (const line3 of readFileSync98(path, "utf8").split(/\r?\n/)) {
187650
+ for (const line3 of readFileSync99(path, "utf8").split(/\r?\n/)) {
187045
187651
  const trimmed = line3.trim();
187046
187652
  if (trimmed.length === 0)
187047
187653
  continue;
@@ -187060,16 +187666,16 @@ var init_jsonl = __esm({
187060
187666
  });
187061
187667
 
187062
187668
  // ../core/dist/figma/manifest.js
187063
- import { appendFileSync as appendFileSync2, existsSync as existsSync130, mkdirSync as mkdirSync69, readFileSync as readFileSync99, writeFileSync as writeFileSync63 } from "fs";
187064
- import { join as join148 } from "path";
187669
+ import { appendFileSync as appendFileSync2, existsSync as existsSync130, mkdirSync as mkdirSync69, readFileSync as readFileSync100, writeFileSync as writeFileSync63 } from "fs";
187670
+ import { join as join149 } from "path";
187065
187671
  function mediaDir(projectDir) {
187066
- return join148(projectDir, ".media");
187672
+ return join149(projectDir, ".media");
187067
187673
  }
187068
187674
  function manifestPath2(projectDir) {
187069
- return join148(mediaDir(projectDir), MANIFEST_FILE2);
187675
+ return join149(mediaDir(projectDir), MANIFEST_FILE2);
187070
187676
  }
187071
187677
  function typeDirPath(projectDir, type) {
187072
- return join148(mediaDir(projectDir), TYPE_DIRS[type]);
187678
+ return join149(mediaDir(projectDir), TYPE_DIRS[type]);
187073
187679
  }
187074
187680
  function isFigmaManifestRecord(value) {
187075
187681
  if (typeof value !== "object" || value === null)
@@ -187101,7 +187707,7 @@ function findAllByFigmaNode(projectDir, fileKey2, nodeId) {
187101
187707
  }
187102
187708
  function updateRecord(projectDir, record2) {
187103
187709
  const p2 = manifestPath2(projectDir);
187104
- const lines = readFileSync99(p2, "utf8").split(/\r?\n/);
187710
+ const lines = readFileSync100(p2, "utf8").split(/\r?\n/);
187105
187711
  const out = lines.map((line3) => {
187106
187712
  const trimmed = line3.trim();
187107
187713
  if (trimmed.length === 0)
@@ -187122,7 +187728,7 @@ function nextId(projectDir, type) {
187122
187728
  const p2 = manifestPath2(projectDir);
187123
187729
  if (!existsSync130(p2))
187124
187730
  return `${type}_001`;
187125
- for (const line3 of readFileSync99(p2, "utf8").split(/\r?\n/)) {
187731
+ for (const line3 of readFileSync100(p2, "utf8").split(/\r?\n/)) {
187126
187732
  const trimmed = line3.trim();
187127
187733
  if (trimmed.length === 0)
187128
187734
  continue;
@@ -187156,12 +187762,12 @@ var init_manifest = __esm({
187156
187762
 
187157
187763
  // ../core/dist/figma/mediaIndex.js
187158
187764
  import { mkdirSync as mkdirSync70, writeFileSync as writeFileSync64 } from "fs";
187159
- import { dirname as dirname67, join as join149 } from "path";
187765
+ import { dirname as dirname67, join as join150 } from "path";
187160
187766
  function isRow(value) {
187161
187767
  return typeof value === "object" && value !== null;
187162
187768
  }
187163
187769
  function indexPath(projectDir) {
187164
- return join149(mediaDir(projectDir), "index.md");
187770
+ return join150(mediaDir(projectDir), "index.md");
187165
187771
  }
187166
187772
  function pad(str, len2) {
187167
187773
  return String(str ?? "").padEnd(len2);
@@ -187273,9 +187879,9 @@ var init_sanitizeSvg = __esm({
187273
187879
 
187274
187880
  // ../core/dist/figma/bindings.js
187275
187881
  import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync71, writeFileSync as writeFileSync65 } from "fs";
187276
- import { join as join150 } from "path";
187882
+ import { join as join151 } from "path";
187277
187883
  function bindingsPath(projectDir) {
187278
- return join150(mediaDir(projectDir), BINDINGS_FILE);
187884
+ return join151(mediaDir(projectDir), BINDINGS_FILE);
187279
187885
  }
187280
187886
  function isRecord16(value) {
187281
187887
  return typeof value === "object" && value !== null;
@@ -187876,7 +188482,7 @@ __export(asset_exports, {
187876
188482
  runAssetImportMany: () => runAssetImportMany
187877
188483
  });
187878
188484
  import { existsSync as existsSync131 } from "fs";
187879
- import { join as join151, relative as relative36 } from "path";
188485
+ import { join as join152, relative as relative36 } from "path";
187880
188486
  function gatherAssetRefs(positionals) {
187881
188487
  return positionals.flatMap((r) => /^https?:/i.test(r.trim()) ? [r] : r.split(",")).map((r) => r.trim()).filter((r) => r.length > 0);
187882
188488
  }
@@ -187890,7 +188496,7 @@ function requireNodeRef(refInput) {
187890
188496
  }
187891
188497
  function reuseExisting(fileKey2, nodeId, opts, version2, deps, description, entity) {
187892
188498
  const existing = findAllByFigmaNode(deps.projectDir, fileKey2, nodeId).find(
187893
- (r) => r.provenance.format === opts.format && (r.provenance.scale ?? 1) === (opts.scale ?? 1) && r.provenance.version === version2 && existsSync131(join151(deps.projectDir, r.path))
188499
+ (r) => r.provenance.format === opts.format && (r.provenance.scale ?? 1) === (opts.scale ?? 1) && r.provenance.version === version2 && existsSync131(join152(deps.projectDir, r.path))
187894
188500
  );
187895
188501
  if (!existing) return null;
187896
188502
  let record2 = existing;
@@ -187913,7 +188519,7 @@ async function freezeAndRecord(fileKey2, nodeId, url, ext, opts, version2, deps,
187913
188519
  bytes = new TextEncoder().encode(sanitizeSvg(new TextDecoder().decode(bytes)));
187914
188520
  }
187915
188521
  const id = nextId(deps.projectDir, "image");
187916
- const destAbs = join151(typeDirPath(deps.projectDir, "image"), `${id}.${ext}`);
188522
+ const destAbs = join152(typeDirPath(deps.projectDir, "image"), `${id}.${ext}`);
187917
188523
  freezeBytes(bytes, destAbs);
187918
188524
  const record2 = {
187919
188525
  id,
@@ -188083,11 +188689,11 @@ __export(tokens_exports, {
188083
188689
  runTokensImport: () => runTokensImport
188084
188690
  });
188085
188691
  import { writeFileSync as writeFileSync66 } from "fs";
188086
- import { join as join152 } from "path";
188692
+ import { join as join153 } from "path";
188087
188693
  async function runTokensImport(refInput, deps) {
188088
188694
  const { fileKey: fileKey2 } = parseFigmaRef(refInput);
188089
188695
  const { version: version2 } = await deps.client.fileVersion(fileKey2);
188090
- const sidecarPath = join152(deps.projectDir, "figma-tokens.json");
188696
+ const sidecarPath = join153(deps.projectDir, "figma-tokens.json");
188091
188697
  let vars = null;
188092
188698
  try {
188093
188699
  vars = await deps.client.variables(fileKey2);
@@ -188162,7 +188768,7 @@ __export(component_exports, {
188162
188768
  runComponentImport: () => runComponentImport
188163
188769
  });
188164
188770
  import { existsSync as existsSync132, mkdirSync as mkdirSync72, writeFileSync as writeFileSync67 } from "fs";
188165
- import { join as join153, relative as relative37 } from "path";
188771
+ import { join as join154, relative as relative37 } from "path";
188166
188772
  function escapeAttr2(value) {
188167
188773
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
188168
188774
  }
@@ -188173,7 +188779,7 @@ async function runComponentImport(refInput, deps) {
188173
188779
  const bindings = resolveBindings(tree, readBindings(deps.projectDir));
188174
188780
  const mapped = nodeToHtml(tree, bindings, { rootName: deps.name });
188175
188781
  const name = slugify(deps.name ?? tree.name);
188176
- const componentDir = join153(deps.projectDir, "compositions", "components", name);
188782
+ const componentDir = join154(deps.projectDir, "compositions", "components", name);
188177
188783
  if (existsSync132(componentDir))
188178
188784
  console.warn(
188179
188785
  `component dir compositions/components/${name} already exists \u2014 overwriting (rename the figma frame for a separate import)`
@@ -188185,7 +188791,7 @@ async function runComponentImport(refInput, deps) {
188185
188791
  componentDir,
188186
188792
  deps
188187
188793
  );
188188
- const htmlFile = join153(componentDir, `${name}.html`);
188794
+ const htmlFile = join154(componentDir, `${name}.html`);
188189
188795
  writeFileSync67(htmlFile, html + "\n");
188190
188796
  const registryItem = {
188191
188797
  name,
@@ -188207,7 +188813,7 @@ async function runComponentImport(refInput, deps) {
188207
188813
  ]
188208
188814
  };
188209
188815
  writeFileSync67(
188210
- join153(componentDir, "registry-item.json"),
188816
+ join154(componentDir, "registry-item.json"),
188211
188817
  JSON.stringify(registryItem, null, 2) + "\n"
188212
188818
  );
188213
188819
  return {
@@ -188232,7 +188838,7 @@ async function rasterizeFallback(mapped, fileKey2, componentDir, deps) {
188232
188838
  continue;
188233
188839
  }
188234
188840
  frozenAssets.push(asset.record.path);
188235
- const srcRel = relative37(componentDir, join153(deps.projectDir, asset.record.path)).replaceAll(
188841
+ const srcRel = relative37(componentDir, join154(deps.projectDir, asset.record.path)).replaceAll(
188236
188842
  "\\",
188237
188843
  "/"
188238
188844
  );
@@ -188381,7 +188987,7 @@ __export(autoUpdate_exports, {
188381
188987
  import { spawn as spawn19 } from "child_process";
188382
188988
  import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync73, openSync as openSync15 } from "fs";
188383
188989
  import { homedir as homedir25 } from "os";
188384
- import { join as join154 } from "path";
188990
+ import { join as join155 } from "path";
188385
188991
  import { compareVersions as compareVersions3 } from "compare-versions";
188386
188992
  function isAutoInstallDisabled() {
188387
188993
  if (isDevMode()) return true;
@@ -188404,7 +189010,7 @@ function log(line3) {
188404
189010
  }
188405
189011
  function launchDetachedInstall(invocation, displayCommand, version2) {
188406
189012
  mkdirSync73(CONFIG_DIR3, { recursive: true, mode: 448 });
188407
- const configFile = join154(CONFIG_DIR3, "config.json");
189013
+ const configFile = join155(CONFIG_DIR3, "config.json");
188408
189014
  const nodeScript = `
188409
189015
  const { execFile } = require("node:child_process");
188410
189016
  const { readFileSync, renameSync, writeFileSync } = require("node:fs");
@@ -188525,8 +189131,8 @@ var init_autoUpdate = __esm({
188525
189131
  init_config();
188526
189132
  init_env();
188527
189133
  init_installerDetection();
188528
- CONFIG_DIR3 = join154(homedir25(), ".hyperframes");
188529
- LOG_FILE = join154(CONFIG_DIR3, "auto-update.log");
189134
+ CONFIG_DIR3 = join155(homedir25(), ".hyperframes");
189135
+ LOG_FILE = join155(CONFIG_DIR3, "auto-update.log");
188530
189136
  PENDING_TIMEOUT_MS = 10 * 60 * 1e3;
188531
189137
  }
188532
189138
  });
@@ -188722,7 +189328,7 @@ var init_help = __esm({
188722
189328
  init_version();
188723
189329
  init_dist();
188724
189330
  init_runId();
188725
- import { dirname as dirname68, join as join155, resolve as resolve83 } from "path";
189331
+ import { dirname as dirname68, join as join156, resolve as resolve83 } from "path";
188726
189332
  import { fileURLToPath as fileURLToPath17 } from "url";
188727
189333
  import { existsSync as existsSync133 } from "fs";
188728
189334
 
@@ -188842,7 +189448,7 @@ for (const stream of [process.stdout, process.stderr]) {
188842
189448
  }
188843
189449
  (() => {
188844
189450
  const here = dirname68(fileURLToPath17(import.meta.url));
188845
- const shader = join155(here, "shaderTransitionWorker.js");
189451
+ const shader = join156(here, "shaderTransitionWorker.js");
188846
189452
  if (!process.env.HF_SHADER_WORKER_ENTRY && existsSync133(shader)) {
188847
189453
  process.env.HF_SHADER_WORKER_ENTRY = shader;
188848
189454
  }
@@ -188855,10 +189461,10 @@ if (rootVersionRequested) {
188855
189461
  process.exit(0);
188856
189462
  }
188857
189463
  try {
188858
- const { readFileSync: readFileSync100 } = await import("fs");
189464
+ const { readFileSync: readFileSync101 } = await import("fs");
188859
189465
  const { resolve: resolve84 } = await import("path");
188860
189466
  const envPath = resolve84(process.cwd(), ".env");
188861
- const envContent = readFileSync100(envPath, "utf-8");
189467
+ const envContent = readFileSync101(envPath, "utf-8");
188862
189468
  for (const rawLine of envContent.split("\n")) {
188863
189469
  let line3 = rawLine.trim();
188864
189470
  if (!line3 || line3.startsWith("#")) continue;