hyperframes 0.8.56 → 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.56" : "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 }
@@ -173591,12 +174197,12 @@ __export(snapshot_exports, {
173591
174197
  requireSnapshotFfmpeg: () => requireSnapshotFfmpeg,
173592
174198
  resolveSnapshotVideoClipStart: () => resolveSnapshotVideoClipStart,
173593
174199
  resolveSnapshotVideoFrameTime: () => resolveSnapshotVideoFrameTime,
173594
- resolveSnapshotVideoPlaybackRate: () => resolveSnapshotVideoPlaybackRate,
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]")
@@ -173635,10 +174241,13 @@ function resolveSnapshotVideoFrameTime(input2) {
173635
174241
  function resolveSnapshotVideoClipStart(input2) {
173636
174242
  return input2.runtimeResolvedStart ?? input2.authoredStart;
173637
174243
  }
173638
- function resolveSnapshotVideoPlaybackRate(input2) {
174244
+ function resolveSnapshotVideoRateSpec(input2) {
173639
174245
  const authoredRate = Number.parseFloat(input2.authoredRate ?? "");
173640
- const rawRate = Number.isFinite(authoredRate) && authoredRate > 0 ? authoredRate : input2.defaultRate;
173641
- return Number.isFinite(rawRate) && rawRate > 0 ? Math.max(0.1, Math.min(5, rawRate)) : 1;
174246
+ const attrs2 = {
174247
+ "data-playback-rate": Number.isFinite(authoredRate) && authoredRate > 0 ? input2.authoredRate : String(input2.defaultRate),
174248
+ "data-automation": input2.authoredAutomation
174249
+ };
174250
+ return readElementRateSpec({ getAttribute: (name) => attrs2[name] ?? null });
173642
174251
  }
173643
174252
  function requireSnapshotFfmpeg(ffmpegPath) {
173644
174253
  if (ffmpegPath) return ffmpegPath;
@@ -173647,8 +174256,8 @@ function requireSnapshotFfmpeg(ffmpegPath) {
173647
174256
  );
173648
174257
  }
173649
174258
  async function extractVideoFrameToBuffer(videoPath, timeSeconds, useVp9AlphaDecoder = false, accurateSeek = false) {
173650
- const tmp = mkdtempSync24(join124(tmpdir16(), "hf-snapshot-frame-"));
173651
- const outPath = join124(tmp, "frame.png");
174259
+ const tmp = mkdtempSync24(join125(tmpdir16(), "hf-snapshot-frame-"));
174260
+ const outPath = join125(tmp, "frame.png");
173652
174261
  try {
173653
174262
  const ffmpegPath = requireSnapshotFfmpeg(findFFmpeg());
173654
174263
  const args = ["-hide_banner", "-loglevel", "error"];
@@ -173667,7 +174276,7 @@ async function extractVideoFrameToBuffer(videoPath, timeSeconds, useVp9AlphaDeco
173667
174276
  );
173668
174277
  const result = await runFfmpegOnce(ffmpegPath, args, FFMPEG_EXTRACT_TIMEOUT_MS);
173669
174278
  if (result.code !== 0 || result.timedOut || !existsSync111(outPath)) return null;
173670
- return readFileSync83(outPath);
174279
+ return readFileSync84(outPath);
173671
174280
  } finally {
173672
174281
  try {
173673
174282
  rmSync37(tmp, { recursive: true, force: true });
@@ -173759,13 +174368,13 @@ async function captureSnapshots(projectDir, opts) {
173759
174368
  );
173760
174369
  }
173761
174370
  const cameraExpr = opts.angle && (opts.angle.yaw !== 0 || opts.angle.pitch !== 0) ? `(${orbitStageSource()})(${JSON.stringify(opts.angle)})` : null;
173762
- const snapshotDir = opts.outputDir ?? join124(projectDir, "snapshots");
174371
+ const snapshotDir = opts.outputDir ?? join125(projectDir, "snapshots");
173763
174372
  mkdirSync56(snapshotDir, { recursive: true });
173764
174373
  try {
173765
174374
  const { readdirSync: readdirSync46 } = await import("fs");
173766
174375
  for (const file of readdirSync46(snapshotDir)) {
173767
174376
  if (/\.(png|jpg|jpeg)$/i.test(file)) {
173768
- rmSync37(join124(snapshotDir, file), { force: true });
174377
+ rmSync37(join125(snapshotDir, file), { force: true });
173769
174378
  }
173770
174379
  }
173771
174380
  } catch {
@@ -173815,6 +174424,7 @@ async function captureSnapshots(projectDir, opts) {
173815
174424
  src: v2.currentSrc || v2.src,
173816
174425
  authoredStart,
173817
174426
  authoredRate: v2.dataset.playbackRate,
174427
+ authoredAutomation: v2.dataset.automation,
173818
174428
  defaultRate: v2.defaultPlaybackRate,
173819
174429
  runtimeResolvedStart: runtimeResolvedStart !== void 0 && Number.isFinite(runtimeResolvedStart) ? runtimeResolvedStart : null,
173820
174430
  authoredDuration: Number.isFinite(rawDuration) && rawDuration > 0 ? rawDuration : null,
@@ -173826,9 +174436,12 @@ async function captureSnapshots(projectDir, opts) {
173826
174436
  });
173827
174437
  const active = candidates.flatMap((candidate) => {
173828
174438
  const start = resolveSnapshotVideoClipStart(candidate);
173829
- const playbackRate = resolveSnapshotVideoPlaybackRate(candidate);
173830
- const duration2 = candidate.authoredDuration ?? (candidate.srcDuration > 0 ? Math.max(0, (candidate.srcDuration - candidate.mediaStart) / playbackRate) : Number.POSITIVE_INFINITY);
173831
- let relTime = (time - start) * playbackRate + candidate.mediaStart;
174439
+ const playbackRate = resolveSnapshotVideoRateSpec(candidate);
174440
+ const duration2 = candidate.authoredDuration ?? (candidate.srcDuration > 0 ? Math.max(
174441
+ 0,
174442
+ timeAtSourceTime(playbackRate, candidate.srcDuration - candidate.mediaStart)
174443
+ ) : Number.POSITIVE_INFINITY);
174444
+ let relTime = sourceTimeAt(playbackRate, time - start) + candidate.mediaStart;
173832
174445
  if (candidate.loop && candidate.srcDuration > candidate.mediaStart && relTime >= candidate.srcDuration) {
173833
174446
  relTime = candidate.mediaStart + (relTime - candidate.mediaStart) % (candidate.srcDuration - candidate.mediaStart);
173834
174447
  }
@@ -173895,7 +174508,7 @@ async function captureSnapshots(projectDir, opts) {
173895
174508
  const timeLabel = formatSnapshotTimestamp(time);
173896
174509
  const index = String(i).padStart(2, "0");
173897
174510
  const filename = `frame-${index}-at-${timeLabel}.png`;
173898
- const framePath = join124(snapshotDir, filename);
174511
+ const framePath = join125(snapshotDir, filename);
173899
174512
  if (opts.zoom) {
173900
174513
  const canvas = await page.evaluate(() => ({
173901
174514
  width: window.innerWidth,
@@ -173924,9 +174537,9 @@ async function captureSnapshots(projectDir, opts) {
173924
174537
  ` ${c.warn("\u26A0")} --against has no frame at ${timeLabel} \u2014 reference pair skipped`
173925
174538
  );
173926
174539
  } else {
173927
- const refPath = join124(snapshotDir, `ref-${index}-at-${timeLabel}.png`);
174540
+ const refPath = join125(snapshotDir, `ref-${index}-at-${timeLabel}.png`);
173928
174541
  writeFileSync48(refPath, refPng);
173929
- const pairPath = join124(snapshotDir, `pair-${index}-at-${timeLabel}.jpg`);
174542
+ const pairPath = join125(snapshotDir, `pair-${index}-at-${timeLabel}.jpg`);
173930
174543
  const { createContactSheet: createContactSheet2 } = await Promise.resolve().then(() => (init_contactSheet(), contactSheet_exports));
173931
174544
  await createContactSheet2([framePath, refPath], pairPath, {
173932
174545
  cols: 2,
@@ -173955,6 +174568,7 @@ var init_snapshot = __esm({
173955
174568
  init_commandResult();
173956
174569
  init_dist();
173957
174570
  init_captureCompositionFrame();
174571
+ init_dist3();
173958
174572
  init_project();
173959
174573
  init_lintProject();
173960
174574
  init_lintFormat();
@@ -174092,7 +174706,7 @@ var init_snapshot = __esm({
174092
174706
  const angleLabel = camera && (camera.yaw !== 0 || camera.pitch !== 0) ? ` ${c.dim(`(angle yaw ${camera.yaw}\xB0 pitch ${camera.pitch}\xB0)`)}` : "";
174093
174707
  console.log(`${c.accent("\u25C6")} Capturing ${label2} from ${c.accent(project.name)}${angleLabel}`);
174094
174708
  try {
174095
- 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");
174096
174710
  const paths = await captureSnapshots(project.dir, {
174097
174711
  frames,
174098
174712
  timeout,
@@ -174129,7 +174743,7 @@ ${c.success("\u25C7")} ${paths.length} snapshots saved to ${args.output ? snaps
174129
174743
  const { createSnapshotContactSheet: createSnapshotContactSheet2 } = await Promise.resolve().then(() => (init_contactSheet(), contactSheet_exports));
174130
174744
  const sheets = await createSnapshotContactSheet2(
174131
174745
  snapshotDir,
174132
- join124(snapshotDir, "contact-sheet.jpg")
174746
+ join125(snapshotDir, "contact-sheet.jpg")
174133
174747
  );
174134
174748
  if (sheets.length > 0) {
174135
174749
  const label3 = sheets.length === 1 ? "contact-sheet.jpg" : `contact-sheet-1..${sheets.length}.jpg`;
@@ -174165,9 +174779,9 @@ ${c.success("\u25C7")} ${paths.length} snapshots saved to ${args.output ? snaps
174165
174779
  const results = await Promise.allSettled(
174166
174780
  paths.map(async (p2) => {
174167
174781
  const filename = basename28(p2);
174168
- const filePath = join124(snapshotDir, filename);
174782
+ const filePath = join125(snapshotDir, filename);
174169
174783
  if (!existsSync111(filePath)) return { filename, desc: "file not found" };
174170
- const raw = readFileSync83(filePath);
174784
+ const raw = readFileSync84(filePath);
174171
174785
  let imageData;
174172
174786
  let mimeType = "image/png";
174173
174787
  if (sharpFn) {
@@ -174203,7 +174817,7 @@ ${c.success("\u25C7")} ${paths.length} snapshots saved to ${args.output ? snaps
174203
174817
  descriptions.push(`## (error)`, `Gemini call failed: ${errMsg.slice(0, 120)}`, ``);
174204
174818
  }
174205
174819
  }
174206
- const descPath = join124(snapshotDir, "descriptions.md");
174820
+ const descPath = join125(snapshotDir, "descriptions.md");
174207
174821
  writeFileSync48(descPath, descriptions.join("\n"));
174208
174822
  console.log(` ${c.dim("descriptions.md")} (Gemini frame analysis)`);
174209
174823
  }
@@ -174536,8 +175150,8 @@ __export(media_treatment_exports, {
174536
175150
  mediaTreatmentCommand: () => mediaTreatmentCommand,
174537
175151
  resolveMediaTreatmentSource: () => resolveMediaTreatmentSource
174538
175152
  });
174539
- import { existsSync as existsSync112, readFileSync as readFileSync84, writeFileSync as writeFileSync49 } from "fs";
174540
- 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";
174541
175155
  function getMediaTreatmentCapabilityOverview() {
174542
175156
  const capabilities = getHfColorGradingCapabilities();
174543
175157
  const family = (id, label2, description) => ({
@@ -174915,7 +175529,7 @@ function resolveMediaTreatmentSource(projectDir, compositionFile, source) {
174915
175529
  }
174916
175530
  const cleanSource = cleanAssetUrl(sourceUrl);
174917
175531
  if (!cleanSource) throw new Error("Selected media has no analyzable local src");
174918
- 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)));
174919
175533
  const asset = resolveExistingLocalAsset(projectDir, projectRelative);
174920
175534
  if (!asset) throw new Error(`Media file not found: ${source}`);
174921
175535
  return asset.resolved;
@@ -174980,7 +175594,7 @@ function analyzeTarget(args) {
174980
175594
  }
174981
175595
  const selectorIndex = parseSelectorIndex(readOptionalString(args["selector-index"]));
174982
175596
  const { element, tag } = selectMediaElement(
174983
- readFileSync84(filePath, "utf8"),
175597
+ readFileSync85(filePath, "utf8"),
174984
175598
  selector,
174985
175599
  selectorIndex
174986
175600
  );
@@ -174998,14 +175612,14 @@ function analyzeTarget(args) {
174998
175612
  ...analyzeMediaTreatment(mediaPath)
174999
175613
  };
175000
175614
  }
175001
- function prepareMutation(args) {
175615
+ function prepareMutation2(args) {
175002
175616
  const { project, filePath } = resolveMutationFile(args);
175003
175617
  const selector = readOptionalString(args.selector);
175004
175618
  if (!selector) throw new Error("--selector is required");
175005
175619
  const clear = args.clear === true;
175006
175620
  const apply = args.apply === true;
175007
175621
  const selectorIndex = parseSelectorIndex(readOptionalString(args["selector-index"]));
175008
- const result = applyMediaTreatmentToHtml(readFileSync84(filePath, "utf8"), {
175622
+ const result = applyMediaTreatmentToHtml(readFileSync85(filePath, "utf8"), {
175009
175623
  selector,
175010
175624
  selectorIndex,
175011
175625
  grading: parseGrading(readOptionalString(args.grading), apply, clear),
@@ -175047,7 +175661,7 @@ function printAnalysis(args) {
175047
175661
  console.log(` suggested patch: ${JSON.stringify(result.suggestedPatch)}`);
175048
175662
  }
175049
175663
  function printMutation(args) {
175050
- const { action, result, selector, payload } = prepareMutation(args);
175664
+ const { action, result, selector, payload } = prepareMutation2(args);
175051
175665
  if (args.json === true) {
175052
175666
  console.log(JSON.stringify(withMeta(payload), null, 2));
175053
175667
  return;
@@ -175194,12 +175808,12 @@ import {
175194
175808
  existsSync as existsSync113,
175195
175809
  mkdirSync as mkdirSync57,
175196
175810
  mkdtempSync as mkdtempSync25,
175197
- readFileSync as readFileSync85,
175811
+ readFileSync as readFileSync86,
175198
175812
  rmSync as rmSync38,
175199
175813
  writeFileSync as writeFileSync50
175200
175814
  } from "fs";
175201
175815
  import { tmpdir as tmpdir17 } from "os";
175202
- 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";
175203
175817
  import sharp2 from "sharp";
175204
175818
  function isRecord14(value) {
175205
175819
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -175290,7 +175904,7 @@ function parseGradesFile(filePath) {
175290
175904
  }
175291
175905
  let parsed;
175292
175906
  try {
175293
- parsed = JSON.parse(readFileSync85(filePath, "utf-8"));
175907
+ parsed = JSON.parse(readFileSync86(filePath, "utf-8"));
175294
175908
  } catch (err) {
175295
175909
  throw new Error(`Could not parse grades JSON: ${normalizeErrorMessage(err)}`);
175296
175910
  }
@@ -175455,10 +176069,10 @@ function frameFileNameForPath(framePath) {
175455
176069
  return "frame.png";
175456
176070
  }
175457
176071
  async function prepareGradeCompareTempProject(opts) {
175458
- const tempDir = mkdtempSync25(join126(tmpdir17(), "hf-grade-compare-"));
176072
+ const tempDir = mkdtempSync25(join127(tmpdir17(), "hf-grade-compare-"));
175459
176073
  try {
175460
176074
  const frameFileName2 = opts.frameFileName ?? frameFileNameForPath(opts.framePath);
175461
- writeFileSync50(join126(tempDir, frameFileName2), opts.frameBuffer);
176075
+ writeFileSync50(join127(tempDir, frameFileName2), opts.frameBuffer);
175462
176076
  let lutIndex = 0;
175463
176077
  const stagedCells = opts.cells.map((cell) => {
175464
176078
  const lutSrc = lutSrcFromGrading(cell.grading);
@@ -175467,7 +176081,7 @@ async function prepareGradeCompareTempProject(opts) {
175467
176081
  if (!existsSync113(sourcePath)) {
175468
176082
  throw new Error(`LUT file not found for "${cell.label}": ${sourcePath}`);
175469
176083
  }
175470
- const lutText = readFileSync85(sourcePath, "utf-8");
176084
+ const lutText = readFileSync86(sourcePath, "utf-8");
175471
176085
  try {
175472
176086
  parseCubeLut(lutText, { maxSize: 64 });
175473
176087
  } catch (err) {
@@ -175478,7 +176092,7 @@ async function prepareGradeCompareTempProject(opts) {
175478
176092
  const lutExt = extname21(sourcePath) || ".cube";
175479
176093
  const stagedName = `lut-${lutIndex}${lutExt}`;
175480
176094
  lutIndex += 1;
175481
- copyFileSync10(sourcePath, join126(tempDir, stagedName));
176095
+ copyFileSync10(sourcePath, join127(tempDir, stagedName));
175482
176096
  return {
175483
176097
  label: cell.label,
175484
176098
  grading: rewriteGradingLutSrc(cell.grading, stagedName)
@@ -175490,7 +176104,7 @@ async function prepareGradeCompareTempProject(opts) {
175490
176104
  frameWidth: opts.frameWidth,
175491
176105
  frameHeight: opts.frameHeight
175492
176106
  });
175493
- writeFileSync50(join126(tempDir, "index.html"), html);
176107
+ writeFileSync50(join127(tempDir, "index.html"), html);
175494
176108
  return { tempDir, html, cells: stagedCells };
175495
176109
  } catch (err) {
175496
176110
  rmSync38(tempDir, { recursive: true, force: true });
@@ -175502,8 +176116,8 @@ function isVideoPath2(filePath) {
175502
176116
  return [".mp4", ".mov", ".m4v", ".webm", ".mkv", ".avi", ".mpeg", ".mpg", ".ogv"].includes(ext);
175503
176117
  }
175504
176118
  async function extractVideoFrameToBuffer2(videoPath) {
175505
- const tmp = mkdtempSync25(join126(tmpdir17(), "hf-grade-compare-frame-"));
175506
- const outPath = join126(tmp, "frame.png");
176119
+ const tmp = mkdtempSync25(join127(tmpdir17(), "hf-grade-compare-frame-"));
176120
+ const outPath = join127(tmp, "frame.png");
175507
176121
  try {
175508
176122
  const ffmpegPath = findFFmpeg();
175509
176123
  if (!ffmpegPath) return null;
@@ -175530,7 +176144,7 @@ async function extractVideoFrameToBuffer2(videoPath) {
175530
176144
  const detail = result.stderr.trim() ? `: ${result.stderr.trim()}` : "";
175531
176145
  throw new Error(`ffmpeg could not extract first frame from ${videoPath}${detail}`);
175532
176146
  }
175533
- return readFileSync85(outPath);
176147
+ return readFileSync86(outPath);
175534
176148
  } finally {
175535
176149
  rmSync38(tmp, { recursive: true, force: true });
175536
176150
  }
@@ -175539,7 +176153,7 @@ async function loadReferenceFrame(framePath) {
175539
176153
  if (!existsSync113(framePath)) {
175540
176154
  throw new Error(`Reference frame not found: ${framePath}`);
175541
176155
  }
175542
- const buffer = isVideoPath2(framePath) ? await extractVideoFrameToBuffer2(framePath) : readFileSync85(framePath);
176156
+ const buffer = isVideoPath2(framePath) ? await extractVideoFrameToBuffer2(framePath) : readFileSync86(framePath);
175543
176157
  if (!buffer) {
175544
176158
  throw new Error(`Could not extract a frame from video: ${framePath}`);
175545
176159
  }
@@ -175558,7 +176172,7 @@ async function captureGradeCompareSheet(projectDir, timeoutMs) {
175558
176172
  const { bundleToSingleHtml: bundleToSingleHtml2 } = await Promise.resolve().then(() => (init_compiler(), compiler_exports));
175559
176173
  const html = await bundleToSingleHtml2(projectDir);
175560
176174
  const server = await serveStaticProjectHtml(projectDir, html);
175561
- const sheetPath = join126(projectDir, "grade-compare.png");
176175
+ const sheetPath = join127(projectDir, "grade-compare.png");
175562
176176
  try {
175563
176177
  const {
175564
176178
  browser: chromeBrowser,
@@ -175746,7 +176360,7 @@ __export(compare_exports, {
175746
176360
  });
175747
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";
175748
176362
  import { tmpdir as tmpdir18 } from "os";
175749
- 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";
175750
176364
  function defaultLabelForPath(input2) {
175751
176365
  const name = basename31(input2);
175752
176366
  return extname23(name).toLowerCase() === ".html" ? basename31(name, extname23(name)) : name;
@@ -175845,7 +176459,7 @@ function inputError(variant) {
175845
176459
  );
175846
176460
  }
175847
176461
  function stageHtmlVariant(variant) {
175848
- const stagedDir = mkdtempSync26(join127(tmpdir18(), "hf-compare-variant-"));
176462
+ const stagedDir = mkdtempSync26(join128(tmpdir18(), "hf-compare-variant-"));
175849
176463
  try {
175850
176464
  cpSync6(dirname62(variant.inputPath), stagedDir, {
175851
176465
  recursive: true,
@@ -175856,7 +176470,7 @@ function stageHtmlVariant(variant) {
175856
176470
  });
175857
176471
  const sourceName = basename31(variant.inputPath);
175858
176472
  if (sourceName !== "index.html") {
175859
- renameSync21(join127(stagedDir, sourceName), join127(stagedDir, "index.html"));
176473
+ renameSync21(join128(stagedDir, sourceName), join128(stagedDir, "index.html"));
175860
176474
  }
175861
176475
  return {
175862
176476
  ...variant,
@@ -175876,7 +176490,7 @@ function prepareCompareVariantProjects(variants) {
175876
176490
  throw inputError(variant);
175877
176491
  }
175878
176492
  const stat = statSync43(variant.inputPath);
175879
- if (stat.isDirectory() && existsSync114(join127(variant.inputPath, "index.html"))) {
176493
+ if (stat.isDirectory() && existsSync114(join128(variant.inputPath, "index.html"))) {
175880
176494
  prepared.push({
175881
176495
  ...variant,
175882
176496
  projectDir: variant.inputPath
@@ -175942,13 +176556,13 @@ async function renderCompareSheet(parsed) {
175942
176556
  const capResult = capCompareVariants(parsed.variants);
175943
176557
  const variants = capResult.variants;
175944
176558
  const prepared = prepareCompareVariantProjects(variants);
175945
- const frameDir = mkdtempSync26(join127(tmpdir18(), "hf-compare-frames-"));
176559
+ const frameDir = mkdtempSync26(join128(tmpdir18(), "hf-compare-frames-"));
175946
176560
  const framePaths = [];
175947
176561
  try {
175948
176562
  let renderReadyTimedOut = false;
175949
176563
  for (let i = 0; i < prepared.length; i++) {
175950
176564
  const variant = prepared[i];
175951
- 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`);
175952
176566
  const rendered = await renderCompareVariant(variant, {
175953
176567
  atSeconds: parsed.atSeconds,
175954
176568
  framePath,
@@ -176459,7 +177073,7 @@ var init_captureImageValidation = __esm({
176459
177073
 
176460
177074
  // src/capture/assetDownloader.ts
176461
177075
  import { writeFileSync as writeFileSync51, mkdirSync as mkdirSync59 } from "fs";
176462
- import { join as join128, extname as extname24 } from "path";
177076
+ import { join as join129, extname as extname24 } from "path";
176463
177077
  import { createHash as createHash27 } from "crypto";
176464
177078
  function noDrops() {
176465
177079
  return { "size-floor": 0, "budget-exhausted": 0, "cap-reached": 0, unavailable: 0 };
@@ -176521,7 +177135,7 @@ async function fetchAndInspectIcon(icon, rank, stem, outputDir, timeoutMs, byteB
176521
177135
  const ext = await captureImageExtension(buffer);
176522
177136
  if (!ext) return null;
176523
177137
  const file = `assets/${stem}${ext}`;
176524
- writeFileSync51(join128(outputDir, file), buffer);
177138
+ writeFileSync51(join129(outputDir, file), buffer);
176525
177139
  const verdict = await classifyIcon(buffer, ext);
176526
177140
  return {
176527
177141
  buffer,
@@ -176543,7 +177157,7 @@ function promoteHeadline(manifest, bytesByFile, outputDir) {
176543
177157
  )[0];
176544
177158
  if (!chosen) return null;
176545
177159
  const file = `assets/favicon${extname24(chosen.file)}`;
176546
- writeFileSync51(join128(outputDir, file), bytesByFile.get(chosen.file));
177160
+ writeFileSync51(join129(outputDir, file), bytesByFile.get(chosen.file));
176547
177161
  manifest.headline = {
176548
177162
  file,
176549
177163
  source: chosen.file,
@@ -176598,13 +177212,13 @@ async function downloadDeclaredIcons(faviconLinks, outputDir, drops, options) {
176598
177212
  }
176599
177213
  async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks, options = {}) {
176600
177214
  options = { ...options, byteBudget: options.byteBudget ?? createCaptureDownloadBudget() };
176601
- const assetsDir = join128(outputDir, "assets");
177215
+ const assetsDir = join129(outputDir, "assets");
176602
177216
  mkdirSync59(assetsDir, { recursive: true });
176603
177217
  const assets = [];
176604
177218
  const drops = noDrops();
176605
177219
  const downloadedUrls = /* @__PURE__ */ new Set();
176606
177220
  let icons = emptyIconManifest();
176607
- mkdirSync59(join128(outputDir, "assets", "svgs"), { recursive: true });
177221
+ mkdirSync59(join129(outputDir, "assets", "svgs"), { recursive: true });
176608
177222
  const usedSvgNames = /* @__PURE__ */ new Set();
176609
177223
  const MAX_INLINE_SVGS = 30;
176610
177224
  drops["cap-reached"] += Math.max(0, tokens.svgs.length - MAX_INLINE_SVGS);
@@ -176626,7 +177240,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks,
176626
177240
  const name = `${finalSlug}.svg`;
176627
177241
  const localPath = `assets/svgs/${name}`;
176628
177242
  try {
176629
- writeFileSync51(join128(outputDir, localPath), svgFile, "utf-8");
177243
+ writeFileSync51(join129(outputDir, localPath), svgFile, "utf-8");
176630
177244
  assets.push({ url: "", localPath, type: "svg" });
176631
177245
  } catch {
176632
177246
  drops.unavailable++;
@@ -176716,7 +177330,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks,
176716
177330
  const name = `${slug}${ext}`;
176717
177331
  usedNames.add(slug);
176718
177332
  const localPath = `assets/${name}`;
176719
- writeFileSync51(join128(outputDir, localPath), buffer);
177333
+ writeFileSync51(join129(outputDir, localPath), buffer);
176720
177334
  assets.push({ url, localPath, type: "image" });
176721
177335
  imgIdx++;
176722
177336
  } catch {
@@ -176743,7 +177357,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks,
176743
177357
  drops["size-floor"]++;
176744
177358
  } else {
176745
177359
  const localPath = `assets/og-image${ext}`;
176746
- writeFileSync51(join128(outputDir, localPath), buffer);
177360
+ writeFileSync51(join129(outputDir, localPath), buffer);
176747
177361
  assets.push({ url: tokens.ogImage, localPath, type: "image" });
176748
177362
  }
176749
177363
  }
@@ -176769,7 +177383,7 @@ function normalizeUrl(u) {
176769
177383
  }
176770
177384
  async function downloadAndRewriteFonts(css, outputDir, options = {}) {
176771
177385
  options = { ...options, byteBudget: options.byteBudget ?? createCaptureDownloadBudget() };
176772
- const assetsDir = join128(outputDir, "assets", "fonts");
177386
+ const assetsDir = join129(outputDir, "assets", "fonts");
176773
177387
  mkdirSync59(assetsDir, { recursive: true });
176774
177388
  const drops = noDrops();
176775
177389
  const fontUrlRegex = /url\(['"]?(https?:\/\/[^'")\s]+\.(?:woff2?|ttf|otf)[^'")\s]*?)['"]?\)/g;
@@ -176827,7 +177441,7 @@ async function downloadAndRewriteFonts(css, outputDir, options = {}) {
176827
177441
  const extension = buffer && captureFontExtension(buffer);
176828
177442
  if (buffer && extension) {
176829
177443
  const filename = captureFontFilename(fontUrl, extension, usedFontNames);
176830
- const localPath = join128(assetsDir, filename);
177444
+ const localPath = join129(assetsDir, filename);
176831
177445
  const relativePath = `assets/fonts/${filename}`;
176832
177446
  writeFileSync51(localPath, buffer);
176833
177447
  rewritten = rewritten.split(fontUrl).join(relativePath);
@@ -176949,8 +177563,8 @@ __export(video_exports, {
176949
177563
  runVideoMode: () => runVideoMode,
176950
177564
  safeFilename: () => safeFilename
176951
177565
  });
176952
- import { createWriteStream as createWriteStream3, existsSync as existsSync115, mkdirSync as mkdirSync60, readFileSync as readFileSync86, unlinkSync as unlinkSync11 } from "fs";
176953
- 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";
176954
177568
  async function streamToFile(url, destPath) {
176955
177569
  const r = await safeFetch(url, {
176956
177570
  signal: AbortSignal.timeout(12e4),
@@ -177069,8 +177683,8 @@ function pickManifestEntry(manifest, args) {
177069
177683
  }
177070
177684
  async function runVideoMode(args) {
177071
177685
  const projectDir = resolve76(args.project);
177072
- const directPath = join129(projectDir, "extracted", "video-manifest.json");
177073
- 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");
177074
177688
  const manifestPath3 = existsSync115(directPath) ? directPath : w2hPath;
177075
177689
  const isW2hLayout = manifestPath3 === w2hPath;
177076
177690
  if (!existsSync115(manifestPath3)) {
@@ -177083,7 +177697,7 @@ async function runVideoMode(args) {
177083
177697
  }
177084
177698
  let manifest;
177085
177699
  try {
177086
- manifest = JSON.parse(readFileSync86(manifestPath3, "utf-8"));
177700
+ manifest = JSON.parse(readFileSync87(manifestPath3, "utf-8"));
177087
177701
  } catch (e2) {
177088
177702
  console.error(`${c.error("\u2717")} video-manifest.json is malformed: ${e2.message}`);
177089
177703
  setCommandExitCode(1);
@@ -177124,10 +177738,10 @@ async function runVideoMode(args) {
177124
177738
  setCommandExitCode(1);
177125
177739
  return;
177126
177740
  }
177127
- 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");
177128
177742
  mkdirSync60(outDir, { recursive: true });
177129
177743
  const fname = safeFilename(entry.filename || basename32(entry.url));
177130
- const outPath = join129(outDir, fname);
177744
+ const outPath = join130(outDir, fname);
177131
177745
  const relPath = isW2hLayout ? `capture/assets/videos/${fname}` : `assets/videos/${fname}`;
177132
177746
  console.log(
177133
177747
  `${c.accent("\u25B8")} downloading [${entry.index}] ${entry.filename} (${entry.sourceWidth || entry.width}\xD7${entry.sourceHeight || entry.height})`
@@ -178624,8 +179238,8 @@ var init_designStyleExtractor = __esm({
178624
179238
  });
178625
179239
 
178626
179240
  // src/capture/fontMetadataExtractor.ts
178627
- import { readdirSync as readdirSync40, readFileSync as readFileSync87, writeFileSync as writeFileSync53, existsSync as existsSync116 } from "fs";
178628
- 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";
178629
179243
  import * as fontkit from "fontkit";
178630
179244
  function isFontCollection(value) {
178631
179245
  return value.type === "TTC" || value.type === "DFont";
@@ -178636,7 +179250,7 @@ function extractFontMetadata(fontsDir, outputPath) {
178636
179250
  if (existsSync116(fontsDir)) {
178637
179251
  const fontFiles = readdirSync40(fontsDir).filter((f) => /\.(woff2?|ttf|otf)$/i.test(f));
178638
179252
  for (const filename of fontFiles) {
178639
- const fullPath = join130(fontsDir, filename);
179253
+ const fullPath = join131(fontsDir, filename);
178640
179254
  const meta = readSingleFont(fullPath, filename);
178641
179255
  if (meta.identified) {
178642
179256
  files.push(meta);
@@ -178675,7 +179289,7 @@ function readSingleFont(fullPath, filename) {
178675
179289
  isIcon: false
178676
179290
  };
178677
179291
  try {
178678
- const buf = readFileSync87(fullPath);
179292
+ const buf = readFileSync88(fullPath);
178679
179293
  const created = fontkit.create(buf);
178680
179294
  const font = isFontCollection(created) ? created.fonts[0] : created;
178681
179295
  if (!font) return empty2;
@@ -179191,8 +179805,8 @@ var init_lottiePreviewRequests = __esm({
179191
179805
  });
179192
179806
 
179193
179807
  // src/capture/mediaCapture.ts
179194
- import { mkdirSync as mkdirSync61, writeFileSync as writeFileSync54, readdirSync as readdirSync41, readFileSync as readFileSync88, statSync as statSync44 } from "fs";
179195
- 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";
179196
179810
  function liveRemainingMs(budget, fallbackMs) {
179197
179811
  return budget.remainingMs?.() ?? fallbackMs;
179198
179812
  }
@@ -179230,7 +179844,7 @@ async function saveLottieAnimations(discoveredLotties, lottieDir, budget = {}) {
179230
179844
  if (savedHashes.has(hash2)) continue;
179231
179845
  savedHashes.add(hash2);
179232
179846
  if (!validLottieJson(jsonData)) continue;
179233
- writeFileSync54(join131(lottieDir, `animation-${savedCount}.json`), jsonData, "utf-8");
179847
+ writeFileSync54(join133(lottieDir, `animation-${savedCount}.json`), jsonData, "utf-8");
179234
179848
  savedCount++;
179235
179849
  }
179236
179850
  } catch {
@@ -179240,18 +179854,18 @@ async function saveLottieAnimations(discoveredLotties, lottieDir, budget = {}) {
179240
179854
  }
179241
179855
  async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir, budget = {}) {
179242
179856
  const manifest = [];
179243
- const previewDir = join131(lottieDir, "previews");
179857
+ const previewDir = join133(lottieDir, "previews");
179244
179858
  mkdirSync61(previewDir, { recursive: true });
179245
179859
  for (const file of readdirSync41(lottieDir)) {
179246
179860
  if (!file.endsWith(".json")) continue;
179247
179861
  if (liveRemainingMs(budget, 1) <= 0) break;
179248
179862
  try {
179249
- const raw = JSON.parse(readFileSync88(join131(lottieDir, file), "utf-8"));
179863
+ const raw = JSON.parse(readFileSync89(join133(lottieDir, file), "utf-8"));
179250
179864
  const fr = raw.fr || 30;
179251
179865
  const dur = ((raw.op || 0) - (raw.ip || 0)) / fr;
179252
179866
  const previewName = file.replace(".json", "-preview.png");
179253
179867
  let preview;
179254
- const fileSize = statSync44(join131(lottieDir, file)).size;
179868
+ const fileSize = statSync44(join133(lottieDir, file)).size;
179255
179869
  if (fileSize > 2e6) continue;
179256
179870
  let previewPage;
179257
179871
  try {
@@ -179260,7 +179874,7 @@ async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir, budget
179260
179874
  if (liveRemainingMs(budget, 1) <= 0) break;
179261
179875
  await guardLottiePreviewRequests(previewPage);
179262
179876
  await previewPage.setViewport({ width: 400, height: 400 });
179263
- const animData = JSON.parse(readFileSync88(join131(lottieDir, file), "utf-8"));
179877
+ const animData = JSON.parse(readFileSync89(join133(lottieDir, file), "utf-8"));
179264
179878
  const midFrame = Math.floor(((raw.op || 0) - (raw.ip || 0)) * 0.3);
179265
179879
  await previewPage.setContent(
179266
179880
  `<!DOCTYPE html>
@@ -179291,7 +179905,7 @@ async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir, budget
179291
179905
  });
179292
179906
  if (liveRemainingMs(budget, 1) > 0) {
179293
179907
  await previewPage.screenshot({
179294
- path: join131(previewDir, previewName),
179908
+ path: join133(previewDir, previewName),
179295
179909
  type: "png",
179296
179910
  omitBackground: true
179297
179911
  });
@@ -179317,7 +179931,7 @@ async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir, budget
179317
179931
  }
179318
179932
  if (manifest.length > 0) {
179319
179933
  writeFileSync54(
179320
- join131(outputDir, "extracted", "lottie-manifest.json"),
179934
+ join133(outputDir, "extracted", "lottie-manifest.json"),
179321
179935
  JSON.stringify(manifest, null, 2),
179322
179936
  "utf-8"
179323
179937
  );
@@ -179355,7 +179969,7 @@ async function downloadVideoBody(srcUrl, filename, videosDir, timeoutMs) {
179355
179969
  }
179356
179970
  if (total < 1024) return null;
179357
179971
  const safe = /\.[a-z0-9]+$/i.test(filename) ? filename.replace(/[^\w.-]/g, "_") : `video${ext}`;
179358
- writeFileSync54(join131(videosDir, safe), Buffer.concat(chunks));
179972
+ writeFileSync54(join133(videosDir, safe), Buffer.concat(chunks));
179359
179973
  return `assets/videos/${safe}`;
179360
179974
  } catch {
179361
179975
  return null;
@@ -179422,9 +180036,9 @@ async function captureVideoManifest(page, outputDir, progress, opts) {
179422
180036
  }
179423
180037
  const merged = [...byKey.values()];
179424
180038
  if (merged.length === 0) return;
179425
- const videoManifestDir = join131(outputDir, "assets", "videos");
180039
+ const videoManifestDir = join133(outputDir, "assets", "videos");
179426
180040
  mkdirSync61(videoManifestDir, { recursive: true });
179427
- const previewDir = join131(videoManifestDir, "previews");
180041
+ const previewDir = join133(videoManifestDir, "previews");
179428
180042
  mkdirSync61(previewDir, { recursive: true });
179429
180043
  const videoManifest = [];
179430
180044
  const dlStart = Date.now();
@@ -179453,7 +180067,7 @@ async function captureVideoManifest(page, outputDir, progress, opts) {
179453
180067
  await new Promise((r) => setTimeout(r, 200));
179454
180068
  if (liveRemainingMs(opts ?? {}, 1) > 0) {
179455
180069
  await page.screenshot({
179456
- path: join131(previewDir, previewName),
180070
+ path: join133(previewDir, previewName),
179457
180071
  clip: {
179458
180072
  x: Math.max(0, rect.x),
179459
180073
  y: Math.max(0, rect.y),
@@ -179490,7 +180104,7 @@ async function captureVideoManifest(page, outputDir, progress, opts) {
179490
180104
  }
179491
180105
  if (videoManifest.length > 0) {
179492
180106
  writeFileSync54(
179493
- join131(outputDir, "extracted", "video-manifest.json"),
180107
+ join133(outputDir, "extracted", "video-manifest.json"),
179494
180108
  JSON.stringify(videoManifest, null, 2),
179495
180109
  "utf-8"
179496
180110
  );
@@ -179567,12 +180181,12 @@ import {
179567
180181
  existsSync as existsSync117,
179568
180182
  readdirSync as readdirSync43,
179569
180183
  statSync as statSync45,
179570
- readFileSync as readFileSync89,
180184
+ readFileSync as readFileSync90,
179571
180185
  openSync as openSync14,
179572
180186
  fstatSync as fstatSync9,
179573
180187
  closeSync as closeSync15
179574
180188
  } from "fs";
179575
- import { basename as basename33, join as join133 } from "path";
180189
+ import { basename as basename33, join as join134 } from "path";
179576
180190
  function resolveVisionPhaseCompletion(outcome, remainingMs) {
179577
180191
  if (outcome.budgetExhausted || remainingMs <= 0) {
179578
180192
  return { status: "degraded", reason: "budget-exhausted" };
@@ -179853,7 +180467,7 @@ async function captionImagesWithGemini(outputDir, progress, warnings, options =
179853
180467
  return response.text?.trim() || "";
179854
180468
  };
179855
180469
  }
179856
- const imageFiles = readdirSync43(join133(outputDir, "assets")).filter(
180470
+ const imageFiles = readdirSync43(join134(outputDir, "assets")).filter(
179857
180471
  (f) => /\.(png|jpg|jpeg|webp|gif)$/i.test(f)
179858
180472
  );
179859
180473
  const BATCH_SIZE = 20;
@@ -179881,13 +180495,13 @@ async function captionImagesWithGemini(outputDir, progress, warnings, options =
179881
180495
  const batch = imageFiles.slice(i, i + BATCH_SIZE);
179882
180496
  const results = await Promise.allSettled(
179883
180497
  batch.map(async (file) => {
179884
- const filePath = join133(outputDir, "assets", file);
180498
+ const filePath = join134(outputDir, "assets", file);
179885
180499
  const fd = openSync14(filePath, "r");
179886
180500
  let buffer;
179887
180501
  try {
179888
180502
  const stat = fstatSync9(fd);
179889
180503
  if (stat.size > 4e6) return { file, caption: "" };
179890
- buffer = readFileSync89(fd);
180504
+ buffer = readFileSync90(fd);
179891
180505
  } finally {
179892
180506
  closeSync15(fd);
179893
180507
  }
@@ -179920,11 +180534,11 @@ async function captionImagesWithGemini(outputDir, progress, warnings, options =
179920
180534
  `${Object.keys(geminiCaptions).length} images captioned with ${providerName}`
179921
180535
  );
179922
180536
  const svgFiles = [];
179923
- const assetsDir = join133(outputDir, "assets");
180537
+ const assetsDir = join134(outputDir, "assets");
179924
180538
  for (const f of readdirSync43(assetsDir)) {
179925
180539
  if (/\.svg$/i.test(f)) svgFiles.push({ file: f, relPath: f });
179926
180540
  }
179927
- const svgsSubdir = join133(assetsDir, "svgs");
180541
+ const svgsSubdir = join134(assetsDir, "svgs");
179928
180542
  if (existsSync117(svgsSubdir)) {
179929
180543
  for (const f of readdirSync43(svgsSubdir)) {
179930
180544
  if (/\.svg$/i.test(f)) svgFiles.push({ file: f, relPath: `svgs/${f}` });
@@ -179958,9 +180572,9 @@ async function captionImagesWithGemini(outputDir, progress, warnings, options =
179958
180572
  sharp5.concurrency(1);
179959
180573
  try {
179960
180574
  for (const { relPath } of batch) {
179961
- const filePath = join133(assetsDir, relPath);
180575
+ const filePath = join134(assetsDir, relPath);
179962
180576
  try {
179963
- const svgSource = readFileSync89(filePath, "utf-8");
180577
+ const svgSource = readFileSync90(filePath, "utf-8");
179964
180578
  const lightFillHits = (svgSource.match(/fill\s*=\s*["'](#fff(fff)?|white|#[ef][ef][ef]|#[ef]{6})["']/gi) || []).length;
179965
180579
  const darkFillHits = (svgSource.match(/fill\s*=\s*["'](#000(000)?|black|#[0-3]{6}|#[0-3]{3})["']/gi) || []).length;
179966
180580
  const bg = lightFillHits > darkFillHits ? { r: 32, g: 32, b: 32 } : { r: 255, g: 255, b: 255 };
@@ -180033,11 +180647,11 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
180033
180647
  const uncaptionedLines = [];
180034
180648
  const svgLines = [];
180035
180649
  const fontLines = [];
180036
- const assetsPath = join133(outputDir, "assets");
180650
+ const assetsPath = join134(outputDir, "assets");
180037
180651
  try {
180038
180652
  for (const file of readdirSync43(assetsPath)) {
180039
180653
  if (file === "svgs" || file === "fonts" || file === "lottie" || file === "videos") continue;
180040
- const filePath = join133(assetsPath, file);
180654
+ const filePath = join134(assetsPath, file);
180041
180655
  const stat = statSync45(filePath);
180042
180656
  if (!stat.isFile()) continue;
180043
180657
  const sizeKb = Math.round(stat.size / 1024);
@@ -180066,7 +180680,7 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
180066
180680
  } catch {
180067
180681
  }
180068
180682
  try {
180069
- const svgsPath = join133(assetsPath, "svgs");
180683
+ const svgsPath = join134(assetsPath, "svgs");
180070
180684
  for (const file of readdirSync43(svgsPath)) {
180071
180685
  if (!file.endsWith(".svg")) continue;
180072
180686
  const svgMatch = tokens.svgs.find(
@@ -180085,7 +180699,7 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
180085
180699
  } catch {
180086
180700
  }
180087
180701
  try {
180088
- const fontsPath = join133(assetsPath, "fonts");
180702
+ const fontsPath = join134(assetsPath, "fonts");
180089
180703
  for (const file of readdirSync43(fontsPath)) {
180090
180704
  fontLines.push(`fonts/${file} \u2014 font file`);
180091
180705
  }
@@ -180094,7 +180708,7 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
180094
180708
  const videoLines = [];
180095
180709
  try {
180096
180710
  const manifest = JSON.parse(
180097
- readFileSync89(join133(outputDir, "extracted", "video-manifest.json"), "utf-8")
180711
+ readFileSync90(join134(outputDir, "extracted", "video-manifest.json"), "utf-8")
180098
180712
  );
180099
180713
  for (const v2 of manifest) {
180100
180714
  if (!v2.localPath) continue;
@@ -180131,7 +180745,7 @@ __export(agentPromptGenerator_exports, {
180131
180745
  generateAgentPrompt: () => generateAgentPrompt
180132
180746
  });
180133
180747
  import { writeFileSync as writeFileSync55, readdirSync as readdirSync44, existsSync as existsSync118 } from "fs";
180134
- import { join as join134 } from "path";
180748
+ import { join as join135 } from "path";
180135
180749
  function inferColorRole(hex) {
180136
180750
  const r = parseInt(hex.slice(1, 3), 16) / 255;
180137
180751
  const g = parseInt(hex.slice(3, 5), 16) / 255;
@@ -180150,9 +180764,9 @@ function inferColorRole(hex) {
180150
180764
  }
180151
180765
  function generateAgentPrompt(outputDir, url, tokens, _animations, hasScreenshot, hasLottie, hasShaders, _catalogedAssets, _detectedLibraries) {
180152
180766
  const prompt = buildPrompt(outputDir, url, tokens, hasScreenshot, hasLottie, hasShaders);
180153
- writeFileSync55(join134(outputDir, "AGENTS.md"), prompt, "utf-8");
180154
- writeFileSync55(join134(outputDir, "CLAUDE.md"), prompt, "utf-8");
180155
- 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");
180156
180770
  }
180157
180771
  function buildPrompt(outputDir, url, tokens, hasScreenshot, hasLottie, hasShaders) {
180158
180772
  const title = tokens.title || new URL(url).hostname.replace(/^www\./, "");
@@ -180161,7 +180775,7 @@ function buildPrompt(outputDir, url, tokens, hasScreenshot, hasLottie, hasShader
180161
180775
  (f) => f.family + (f.variable && f.weightRange ? ` (${f.weightRange[0]}-${f.weightRange[1]} variable)` : f.weights.length > 0 ? ` (${f.weights.join(",")})` : "")
180162
180776
  ).join(", ") || "none detected";
180163
180777
  function contactSheetRows(dir, baseFile, label2) {
180164
- const fullDir = join134(outputDir, dir);
180778
+ const fullDir = join135(outputDir, dir);
180165
180779
  if (!existsSync118(fullDir)) return [];
180166
180780
  const baseName = baseFile.replace(/\.jpg$/, "");
180167
180781
  const escapedBase = baseName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -180194,7 +180808,7 @@ function buildPrompt(outputDir, url, tokens, hasScreenshot, hasLottie, hasShader
180194
180808
  tableRows.push(
180195
180809
  `| \`extracted/tokens.json\` | Design tokens: ${tokens.colors.length} colors, ${tokens.fonts.length} fonts, ${tokens.headings?.length ?? 0} headings, ${tokens.ctas?.length ?? 0} CTAs |`
180196
180810
  );
180197
- if (existsSync118(join134(outputDir, "extracted", "design-styles.json"))) {
180811
+ if (existsSync118(join135(outputDir, "extracted", "design-styles.json"))) {
180198
180812
  tableRows.push(
180199
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. |"
180200
180814
  );
@@ -180265,15 +180879,15 @@ var init_agentPromptGenerator = __esm({
180265
180879
  });
180266
180880
 
180267
180881
  // src/capture/scaffolding.ts
180268
- import { existsSync as existsSync119, readFileSync as readFileSync90 } from "fs";
180269
- 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";
180270
180884
  function loadEnvFile(startDir) {
180271
180885
  try {
180272
180886
  let dir = resolve77(startDir);
180273
180887
  for (let i = 0; i < 5; i++) {
180274
180888
  const envPath = resolve77(dir, ".env");
180275
180889
  try {
180276
- const envContent = readFileSync90(envPath, "utf-8");
180890
+ const envContent = readFileSync91(envPath, "utf-8");
180277
180891
  for (const line3 of envContent.split("\n")) {
180278
180892
  const trimmed = line3.trim();
180279
180893
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -180292,7 +180906,7 @@ function loadEnvFile(startDir) {
180292
180906
  }
180293
180907
  }
180294
180908
  async function generateProjectScaffold(outputDir, url, tokens, animationCatalog, hasScreenshots, hasLotties, hasShaders, catalogedAssets, progress, warnings, detectedLibraries) {
180295
- const metaPath = join135(outputDir, "meta.json");
180909
+ const metaPath = join136(outputDir, "meta.json");
180296
180910
  if (!existsSync119(metaPath)) {
180297
180911
  const hostname2 = new URL(url).hostname.replace(/^www\./, "");
180298
180912
  writeNewFileSync(
@@ -180346,10 +180960,10 @@ var init_pageBlockDetection = __esm({
180346
180960
 
180347
180961
  // src/capture/responseRecord.ts
180348
180962
  import { writeFileSync as writeFileSync56 } from "fs";
180349
- import { join as join136 } from "path";
180963
+ import { join as join137 } from "path";
180350
180964
  function writeResponseRecord(extractedDir, record2) {
180351
180965
  writeFileSync56(
180352
- join136(extractedDir, RESPONSE_RECORD_FILENAME),
180966
+ join137(extractedDir, RESPONSE_RECORD_FILENAME),
180353
180967
  JSON.stringify(record2, null, 2),
180354
180968
  "utf-8"
180355
180969
  );
@@ -180414,7 +181028,7 @@ __export(screenshotCapture_exports, {
180414
181028
  pngHeight: () => pngHeight
180415
181029
  });
180416
181030
  import { writeFileSync as writeFileSync57, mkdirSync as mkdirSync63 } from "fs";
180417
- import { join as join137 } from "path";
181031
+ import { join as join138 } from "path";
180418
181032
  function pngHeight(buf) {
180419
181033
  if (buf.length < 24) return null;
180420
181034
  if (buf[12] !== 73 || buf[13] !== 72 || buf[14] !== 68 || buf[15] !== 82) return null;
@@ -180439,7 +181053,7 @@ async function captureFullPagePlate(page, screenshotsDir, budget = {}) {
180439
181053
  const buffer = await page.screenshot({ type: "png", fullPage: true });
180440
181054
  const produced = pngHeight(buffer);
180441
181055
  if (produced != null && produced > MAX_PLATE_HEIGHT_PX) return null;
180442
- writeFileSync57(join137(screenshotsDir, "full-page.png"), buffer);
181056
+ writeFileSync57(join138(screenshotsDir, "full-page.png"), buffer);
180443
181057
  return "screenshots/full-page.png";
180444
181058
  } finally {
180445
181059
  try {
@@ -180454,7 +181068,7 @@ async function captureFullPagePlate(page, screenshotsDir, budget = {}) {
180454
181068
  }
180455
181069
  }
180456
181070
  async function captureScrollScreenshots(page, outputDir, budget = {}) {
180457
- const screenshotsDir = join137(outputDir, "screenshots");
181071
+ const screenshotsDir = join138(outputDir, "screenshots");
180458
181072
  mkdirSync63(screenshotsDir, { recursive: true });
180459
181073
  const MAX_SCREENSHOTS = 20;
180460
181074
  const filePaths = [];
@@ -180551,7 +181165,7 @@ async function captureScrollScreenshots(page, outputDir, budget = {}) {
180551
181165
  finalPositions[i] / Math.max(1, scrollHeight - viewportHeight) * 100
180552
181166
  );
180553
181167
  const filename = `scroll-${String(Math.min(pct, 100)).padStart(3, "0")}.png`;
180554
- const filePath = join137(screenshotsDir, filename);
181168
+ const filePath = join138(screenshotsDir, filename);
180555
181169
  if ((budget.remainingMs?.() ?? 1) <= 0) break;
180556
181170
  const buffer = await page.screenshot({ type: "png" });
180557
181171
  writeFileSync57(filePath, buffer);
@@ -180912,7 +181526,7 @@ __export(capture_exports, {
180912
181526
  captureWebsite: () => captureWebsite
180913
181527
  });
180914
181528
  import { mkdirSync as mkdirSync64, writeFileSync as writeFileSync58, existsSync as existsSync120 } from "fs";
180915
- import { join as join138 } from "path";
181529
+ import { join as join139 } from "path";
180916
181530
  async function captureWebsite(opts, onProgress) {
180917
181531
  const {
180918
181532
  url,
@@ -180954,9 +181568,9 @@ async function captureWebsite(opts, onProgress) {
180954
181568
  };
180955
181569
  phase("browser", "started");
180956
181570
  loadEnvFile(outputDir);
180957
- mkdirSync64(join138(outputDir, "extracted"), { recursive: true });
180958
- mkdirSync64(join138(outputDir, "screenshots"), { recursive: true });
180959
- 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 });
180960
181574
  progress("browser", "Launching headless Chrome...");
180961
181575
  const { ensureBrowser: ensureBrowser2 } = await Promise.resolve().then(() => (init_manager2(), manager_exports2));
180962
181576
  const browser = await ensureBrowser2();
@@ -181067,7 +181681,7 @@ async function captureWebsite(opts, onProgress) {
181067
181681
  progress("warn", message);
181068
181682
  }
181069
181683
  const httpStatus = navigationResponse?.status() ?? null;
181070
- writeResponseRecord(join138(outputDir, "extracted"), { status: httpStatus });
181684
+ writeResponseRecord(join139(outputDir, "extracted"), { status: httpStatus });
181071
181685
  const blockedReason = detectBlockedPage({
181072
181686
  httpStatus,
181073
181687
  ...contentCheckTimedOut ? {
@@ -181131,7 +181745,7 @@ async function captureWebsite(opts, onProgress) {
181131
181745
  else discoveredLotties[existing] = found;
181132
181746
  }
181133
181747
  if (discoveredLotties.length > 0 && remainingMs() > 0) {
181134
- const lottieDir = join138(outputDir, "assets", "lottie");
181748
+ const lottieDir = join139(outputDir, "assets", "lottie");
181135
181749
  mkdirSync64(lottieDir, { recursive: true });
181136
181750
  const lottieBudget = { remainingMs, byteBudget: downloadByteBudget };
181137
181751
  const savedCount = await saveLottieAnimations(discoveredLotties, lottieDir, lottieBudget);
@@ -181152,7 +181766,7 @@ async function captureWebsite(opts, onProgress) {
181152
181766
  });
181153
181767
  capturedShaders = unique;
181154
181768
  writeFileSync58(
181155
- join138(outputDir, "extracted", "shaders.json"),
181769
+ join139(outputDir, "extracted", "shaders.json"),
181156
181770
  JSON.stringify(unique, null, 2),
181157
181771
  "utf-8"
181158
181772
  );
@@ -181167,7 +181781,7 @@ async function captureWebsite(opts, onProgress) {
181167
181781
  svgs: tokens.svgs.map(({ outerHTML: _, ...rest }) => rest)
181168
181782
  };
181169
181783
  writeFileSync58(
181170
- join138(outputDir, "extracted", "tokens.json"),
181784
+ join139(outputDir, "extracted", "tokens.json"),
181171
181785
  JSON.stringify(tokensForDisk, null, 2),
181172
181786
  "utf-8"
181173
181787
  );
@@ -181175,7 +181789,7 @@ async function captureWebsite(opts, onProgress) {
181175
181789
  try {
181176
181790
  const designStyles = await extractDesignStyles(page1);
181177
181791
  writeFileSync58(
181178
- join138(outputDir, "extracted", "design-styles.json"),
181792
+ join139(outputDir, "extracted", "design-styles.json"),
181179
181793
  JSON.stringify(designStyles, null, 2),
181180
181794
  "utf-8"
181181
181795
  );
@@ -181309,8 +181923,8 @@ ${err.stack}` : normalizeErrorMessage(err);
181309
181923
  );
181310
181924
  try {
181311
181925
  const fontsManifest = extractFontMetadata(
181312
- join138(outputDir, "assets", "fonts"),
181313
- join138(outputDir, "extracted", "fonts-manifest.json")
181926
+ join139(outputDir, "assets", "fonts"),
181927
+ join139(outputDir, "extracted", "fonts-manifest.json")
181314
181928
  );
181315
181929
  if (fontsManifest.families.length > 0) {
181316
181930
  const summary = fontsManifest.families.map((f) => `${f.family}${f.variable ? " (variable)" : ""} \xD7 ${f.fileCount}`).join(", ");
@@ -181337,7 +181951,7 @@ ${err.stack}` : normalizeErrorMessage(err);
181337
181951
  representativeAnimations: representativeAnims
181338
181952
  };
181339
181953
  writeFileSync58(
181340
- join138(outputDir, "extracted", "animations.json"),
181954
+ join139(outputDir, "extracted", "animations.json"),
181341
181955
  JSON.stringify(leanCatalog, null, 2),
181342
181956
  "utf-8"
181343
181957
  );
@@ -181354,7 +181968,7 @@ ${err.stack}` : normalizeErrorMessage(err);
181354
181968
  assets = assetPass.assets;
181355
181969
  assetDrops = assetPass.drops;
181356
181970
  writeFileSync58(
181357
- join138(outputDir, "extracted", "icons-manifest.json"),
181971
+ join139(outputDir, "extracted", "icons-manifest.json"),
181358
181972
  JSON.stringify(assetPass.icons, null, 2),
181359
181973
  "utf-8"
181360
181974
  );
@@ -181395,7 +182009,7 @@ ${err.stack}` : normalizeErrorMessage(err);
181395
182009
  svgs: tokens.svgs.map(({ outerHTML: _, ...rest }) => rest)
181396
182010
  };
181397
182011
  writeFileSync58(
181398
- join138(outputDir, "extracted", "tokens.json"),
182012
+ join139(outputDir, "extracted", "tokens.json"),
181399
182013
  JSON.stringify(tokensForDisk2, null, 2),
181400
182014
  "utf-8"
181401
182015
  );
@@ -181411,12 +182025,12 @@ ${extracted.bodyHtml}
181411
182025
  </body>
181412
182026
  </html>
181413
182027
  `;
181414
- writeFileSync58(join138(outputDir, "extracted", "page.html"), pageHtml, "utf-8");
182028
+ writeFileSync58(join139(outputDir, "extracted", "page.html"), pageHtml, "utf-8");
181415
182029
  } catch (err) {
181416
182030
  warnings.push(`page.html write failed: ${err}`);
181417
182031
  }
181418
182032
  if (visibleTextContent) {
181419
- writeFileSync58(join138(outputDir, "extracted", "visible-text.txt"), visibleTextContent, "utf-8");
182033
+ writeFileSync58(join139(outputDir, "extracted", "visible-text.txt"), visibleTextContent, "utf-8");
181420
182034
  }
181421
182035
  let geminiCaptions = {};
181422
182036
  if (skipVision) {
@@ -181453,7 +182067,7 @@ ${extracted.bodyHtml}
181453
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));
181454
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";
181455
182069
  writeFileSync58(
181456
- join138(outputDir, "extracted", "asset-descriptions.md"),
182070
+ join139(outputDir, "extracted", "asset-descriptions.md"),
181457
182071
  header2 + lines.map((l) => "- " + l).join("\n") + "\n",
181458
182072
  "utf-8"
181459
182073
  );
@@ -181471,8 +182085,8 @@ ${extracted.bodyHtml}
181471
182085
  const { createScrollContactSheet: createScrollContactSheet2, createAssetContactSheet: createAssetContactSheet2, createSvgContactSheet: createSvgContactSheet2 } = await Promise.resolve().then(() => (init_contactSheet(), contactSheet_exports));
181472
182086
  const contactSheetBudget = { remainingMs };
181473
182087
  const scrollSheets = await createScrollContactSheet2(
181474
- join138(outputDir, "screenshots"),
181475
- join138(outputDir, "screenshots", "contact-sheet.jpg"),
182088
+ join139(outputDir, "screenshots"),
182089
+ join139(outputDir, "screenshots", "contact-sheet.jpg"),
181476
182090
  contactSheetBudget
181477
182091
  );
181478
182092
  if (scrollSheets.length > 0)
@@ -181480,11 +182094,11 @@ ${extracted.bodyHtml}
181480
182094
  "design",
181481
182095
  `Screenshot contact sheet generated (${scrollSheets.length} page${scrollSheets.length > 1 ? "s" : ""})`
181482
182096
  );
181483
- const assetsImgDir = join138(outputDir, "assets");
182097
+ const assetsImgDir = join139(outputDir, "assets");
181484
182098
  if (existsSync120(assetsImgDir)) {
181485
182099
  const assetSheets = await createAssetContactSheet2(
181486
182100
  assetsImgDir,
181487
- join138(outputDir, "assets", "contact-sheet.jpg"),
182101
+ join139(outputDir, "assets", "contact-sheet.jpg"),
181488
182102
  contactSheetBudget
181489
182103
  );
181490
182104
  if (assetSheets.length > 0)
@@ -181493,9 +182107,9 @@ ${extracted.bodyHtml}
181493
182107
  `Asset contact sheet generated (${assetSheets.length} page${assetSheets.length > 1 ? "s" : ""})`
181494
182108
  );
181495
182109
  }
181496
- const svgsDir = join138(outputDir, "assets", "svgs");
181497
- const assetsRootDir = join138(outputDir, "assets");
181498
- 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");
181499
182113
  const svgSheets = await createSvgContactSheet2(
181500
182114
  svgsDir,
181501
182115
  svgOutputPath,
@@ -181528,7 +182142,7 @@ ${extracted.bodyHtml}
181528
182142
  animationCatalog,
181529
182143
  screenshots.length > 0,
181530
182144
  discoveredLotties.length > 0,
181531
- existsSync120(join138(outputDir, "extracted", "shaders.json")),
182145
+ existsSync120(join139(outputDir, "extracted", "shaders.json")),
181532
182146
  catalogedAssets,
181533
182147
  progress,
181534
182148
  warnings,
@@ -181892,10 +182506,10 @@ __export(state_exports, {
181892
182506
  stateFilePath: () => stateFilePath,
181893
182507
  writeStackOutputs: () => writeStackOutputs
181894
182508
  });
181895
- import { existsSync as existsSync121, mkdirSync as mkdirSync65, readdirSync as readdirSync45, readFileSync as readFileSync91, rmSync as rmSync40, writeFileSync as writeFileSync59 } from "fs";
181896
- 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";
181897
182511
  function stateFilePath(stackName = DEFAULT_STACK_NAME, cwd = process.cwd()) {
181898
- return join139(cwd, STATE_DIR_NAME, `${STATE_FILE_PREFIX}${stackName}.json`);
182512
+ return join140(cwd, STATE_DIR_NAME, `${STATE_FILE_PREFIX}${stackName}.json`);
181899
182513
  }
181900
182514
  function writeStackOutputs(outputs, cwd = process.cwd()) {
181901
182515
  const path = stateFilePath(outputs.stackName, cwd);
@@ -181907,7 +182521,7 @@ function readStackOutputs(stackName = DEFAULT_STACK_NAME, cwd = process.cwd()) {
181907
182521
  const path = stateFilePath(stackName, cwd);
181908
182522
  if (!existsSync121(path)) return null;
181909
182523
  try {
181910
- return JSON.parse(readFileSync91(path, "utf-8"));
182524
+ return JSON.parse(readFileSync93(path, "utf-8"));
181911
182525
  } catch {
181912
182526
  return null;
181913
182527
  }
@@ -181917,7 +182531,7 @@ function deleteStackOutputs(stackName = DEFAULT_STACK_NAME, cwd = process.cwd())
181917
182531
  if (existsSync121(path)) rmSync40(path);
181918
182532
  }
181919
182533
  function listStackNames(cwd = process.cwd()) {
181920
- const dir = join139(cwd, STATE_DIR_NAME);
182534
+ const dir = join140(cwd, STATE_DIR_NAME);
181921
182535
  if (!existsSync121(dir)) return [];
181922
182536
  return readdirSync45(dir).filter((f) => f.startsWith(STATE_FILE_PREFIX) && f.endsWith(".json")).map((f) => f.slice(STATE_FILE_PREFIX.length, -".json".length));
181923
182537
  }
@@ -181950,7 +182564,7 @@ var init_state = __esm({
181950
182564
  // src/commands/lambda/sam.ts
181951
182565
  import { execFileSync as execFileSync19, spawnSync as spawnSync4 } from "child_process";
181952
182566
  import { existsSync as existsSync122 } from "fs";
181953
- import { join as join140 } from "path";
182567
+ import { join as join141 } from "path";
181954
182568
  function assertSamAvailable() {
181955
182569
  try {
181956
182570
  execFileSync19("sam", ["--version"], { stdio: "ignore" });
@@ -181970,7 +182584,7 @@ function assertAwsCliAvailable() {
181970
182584
  }
181971
182585
  }
181972
182586
  function locateSamTemplate(repoRoot2) {
181973
- const candidate = join140(repoRoot2, "examples", "aws-lambda", "template.yaml");
182587
+ const candidate = join141(repoRoot2, "examples", "aws-lambda", "template.yaml");
181974
182588
  if (!existsSync122(candidate)) {
181975
182589
  throw new Error(
181976
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.`
@@ -182004,7 +182618,7 @@ function samDeploy(opts) {
182004
182618
  if (opts.awsProfile) {
182005
182619
  args.push("--profile", opts.awsProfile);
182006
182620
  }
182007
- const samDir = join140(opts.repoRoot, "examples", "aws-lambda");
182621
+ const samDir = join141(opts.repoRoot, "examples", "aws-lambda");
182008
182622
  const result = spawnSync4("sam", args, { cwd: samDir, stdio: opts.stdio ?? "inherit" });
182009
182623
  if (result.status !== 0) {
182010
182624
  throw new Error(
@@ -182022,7 +182636,7 @@ function samDelete(opts) {
182022
182636
  if (opts.awsProfile) {
182023
182637
  args.push("--profile", opts.awsProfile);
182024
182638
  }
182025
- const samDir = join140(opts.repoRoot, "examples", "aws-lambda");
182639
+ const samDir = join141(opts.repoRoot, "examples", "aws-lambda");
182026
182640
  const result = spawnSync4("sam", args, { cwd: samDir, stdio: opts.stdio ?? "inherit" });
182027
182641
  if (result.status !== 0) {
182028
182642
  throw new Error(`[lambda] sam delete exited with code ${result.status ?? "unknown"}`);
@@ -182106,7 +182720,7 @@ __export(deploy_exports, {
182106
182720
  });
182107
182721
  import { spawnSync as spawnSync5 } from "child_process";
182108
182722
  import { existsSync as existsSync124 } from "fs";
182109
- import { join as join141, resolve as resolve80 } from "path";
182723
+ import { join as join143, resolve as resolve80 } from "path";
182110
182724
  async function runDeploy(args = {}) {
182111
182725
  const resolved2 = {
182112
182726
  stackName: args.stackName ?? DEFAULT_STACK_NAME,
@@ -182123,7 +182737,7 @@ async function runDeploy(args = {}) {
182123
182737
  console.log(c.dim("\u2192 Building handler ZIP"));
182124
182738
  buildHandlerZip(root);
182125
182739
  } else {
182126
- const zip = join141(root, "packages", "aws-lambda", "dist", "handler.zip");
182740
+ const zip = join143(root, "packages", "aws-lambda", "dist", "handler.zip");
182127
182741
  if (!existsSync124(zip)) {
182128
182742
  throw new Error(
182129
182743
  `--skip-build set but ${zip} does not exist. Run \`bun run --cwd packages/aws-lambda build:zip\` first or drop --skip-build.`
@@ -182167,7 +182781,7 @@ async function runDeploy(args = {}) {
182167
182781
  function buildHandlerZip(root) {
182168
182782
  const result = spawnSync5(
182169
182783
  "bun",
182170
- ["run", "--cwd", join141(root, "packages", "aws-lambda"), "build:zip"],
182784
+ ["run", "--cwd", join143(root, "packages", "aws-lambda"), "build:zip"],
182171
182785
  { stdio: "inherit" }
182172
182786
  );
182173
182787
  if (result.status !== 0) {
@@ -182236,14 +182850,14 @@ var init_sites = __esm({
182236
182850
  });
182237
182851
 
182238
182852
  // src/commands/lambda/_dimensions.ts
182239
- import { readFileSync as readFileSync93 } from "fs";
182240
- import { join as join143 } from "path";
182853
+ import { readFileSync as readFileSync94 } from "fs";
182854
+ import { join as join144 } from "path";
182241
182855
  function warnOnDimensionMismatch(args) {
182242
182856
  if (args.quiet) return;
182243
182857
  if (args.outputResolution) return;
182244
182858
  let html;
182245
182859
  try {
182246
- html = readFileSync93(join143(args.projectDir, "index.html"), "utf-8");
182860
+ html = readFileSync94(join144(args.projectDir, "index.html"), "utf-8");
182247
182861
  } catch {
182248
182862
  return;
182249
182863
  }
@@ -182273,7 +182887,7 @@ __export(render_exports2, {
182273
182887
  runRender: () => runRender
182274
182888
  });
182275
182889
  import { existsSync as existsSync125 } from "fs";
182276
- import { join as join144, resolve as resolvePath2 } from "path";
182890
+ import { join as join145, resolve as resolvePath2 } from "path";
182277
182891
  async function loadSDK2() {
182278
182892
  return import("@hyperframes/aws-lambda/sdk");
182279
182893
  }
@@ -182289,7 +182903,7 @@ async function runRender(args) {
182289
182903
  });
182290
182904
  const variables = resolveVariablesArg(args.variables, args.variablesFile);
182291
182905
  if (variables && Object.keys(variables).length > 0) {
182292
- const indexPath2 = join144(projectDir, "index.html");
182906
+ const indexPath2 = join145(projectDir, "index.html");
182293
182907
  if (existsSync125(indexPath2)) {
182294
182908
  const issues = validateVariablesAgainstProject(indexPath2, variables);
182295
182909
  reportVariableIssues(issues, { strict: args.strictVariables ?? false, quiet: args.json });
@@ -182423,8 +183037,8 @@ __export(render_batch_exports, {
182423
183037
  runRenderBatch: () => runRenderBatch,
182424
183038
  runWithConcurrencyLimit: () => runWithConcurrencyLimit
182425
183039
  });
182426
- import { existsSync as existsSync126, readFileSync as readFileSync94 } from "fs";
182427
- 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";
182428
183042
  async function loadSDK3() {
182429
183043
  return import("@hyperframes/aws-lambda/sdk");
182430
183044
  }
@@ -182448,7 +183062,7 @@ async function runRenderBatch(args) {
182448
183062
  outputResolution: args.outputResolution,
182449
183063
  quiet: args.json
182450
183064
  });
182451
- const schema = loadProjectVariableSchema(join145(projectDir, "index.html"));
183065
+ const schema = loadProjectVariableSchema(join146(projectDir, "index.html"));
182452
183066
  const strict = args.strictVariables ?? false;
182453
183067
  let hadStrictIssue = false;
182454
183068
  for (const { entry, lineNumber } of entries2) {
@@ -182583,7 +183197,7 @@ function buildLambdaBatchRenderConfig(args) {
182583
183197
  return config;
182584
183198
  }
182585
183199
  function parseBatchFile(path) {
182586
- const raw = readFileSync94(path, "utf8");
183200
+ const raw = readFileSync95(path, "utf8");
182587
183201
  const lines = raw.split(/\r?\n/);
182588
183202
  const out = [];
182589
183203
  for (let i = 0; i < lines.length; i++) {
@@ -182777,7 +183391,7 @@ __export(policies_exports, {
182777
183391
  runPolicies: () => runPolicies,
182778
183392
  validatePolicy: () => validatePolicy
182779
183393
  });
182780
- import { readFileSync as readFileSync95 } from "fs";
183394
+ import { readFileSync as readFileSync96 } from "fs";
182781
183395
  function allRequiredActions() {
182782
183396
  const set = /* @__PURE__ */ new Set();
182783
183397
  for (const group of Object.values(REQUIRED_ACTIONS)) {
@@ -182883,7 +183497,7 @@ async function runPolicies(args) {
182883
183497
  }
182884
183498
  }
182885
183499
  function validatePolicy(policyPath) {
182886
- const raw = readFileSync95(policyPath, "utf-8");
183500
+ const raw = readFileSync96(policyPath, "utf-8");
182887
183501
  const parsed = JSON.parse(raw);
182888
183502
  const statements = Array.isArray(parsed.Statement) ? parsed.Statement : parsed.Statement ? [
182889
183503
  parsed.Statement
@@ -183494,9 +184108,9 @@ __export(cloudrun_exports, {
183494
184108
  missingCloudRunAdapterMessage: () => missingCloudRunAdapterMessage
183495
184109
  });
183496
184110
  import { spawnSync as spawnSync6 } from "child_process";
183497
- 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";
183498
184112
  import { homedir as homedir24 } from "os";
183499
- import { join as join146, resolve as resolve81 } from "path";
184113
+ import { join as join147, resolve as resolve81 } from "path";
183500
184114
  function loadCloudRunAdapter() {
183501
184115
  cloudRunAdapterPromise ??= Promise.all([
183502
184116
  import("@hyperframes/gcp-cloud-run/sdk"),
@@ -183515,10 +184129,10 @@ Or, for an opt-in project setup:
183515
184129
  ${c.accent("npm install @hyperframes/gcp-cloud-run")}`;
183516
184130
  }
183517
184131
  function stateDir() {
183518
- return join146(homedir24(), ".hyperframes");
184132
+ return join147(homedir24(), ".hyperframes");
183519
184133
  }
183520
184134
  function statePath() {
183521
- return join146(stateDir(), "cloudrun-state.json");
184135
+ return join147(stateDir(), "cloudrun-state.json");
183522
184136
  }
183523
184137
  function writeState2(state) {
183524
184138
  mkdirSync66(stateDir(), { recursive: true });
@@ -183532,7 +184146,7 @@ function readState2(args) {
183532
184146
  let base2 = {};
183533
184147
  if (existsSync127(statePath())) {
183534
184148
  try {
183535
- base2 = JSON.parse(readFileSync96(statePath(), "utf8"));
184149
+ base2 = JSON.parse(readFileSync97(statePath(), "utf8"));
183536
184150
  } catch {
183537
184151
  }
183538
184152
  }
@@ -183675,11 +184289,11 @@ function machineVars(args, project, region, image) {
183675
184289
  }
183676
184290
  function findRepoRoot(tfDir) {
183677
184291
  const candidate = resolve81(tfDir, "..", "..", "..");
183678
- if (existsSync127(join146(candidate, "packages", "gcp-cloud-run", "Dockerfile"))) return candidate;
184292
+ if (existsSync127(join147(candidate, "packages", "gcp-cloud-run", "Dockerfile"))) return candidate;
183679
184293
  return null;
183680
184294
  }
183681
184295
  function writeCloudBuildConfig(image) {
183682
- const cfgPath = join146(stateDir(), "cloudrun-cloudbuild.yaml");
184296
+ const cfgPath = join147(stateDir(), "cloudrun-cloudbuild.yaml");
183683
184297
  mkdirSync66(stateDir(), { recursive: true });
183684
184298
  writeFileSync60(
183685
184299
  cfgPath,
@@ -183891,7 +184505,7 @@ async function runRenderBatch2(args) {
183891
184505
  if (failed.length > 0) failCommand();
183892
184506
  }
183893
184507
  function parseBatchFile2(path) {
183894
- const lines = readFileSync96(path, "utf8").split(/\r?\n/);
184508
+ const lines = readFileSync97(path, "utf8").split(/\r?\n/);
183895
184509
  const entries2 = [];
183896
184510
  lines.forEach((line3, idx) => {
183897
184511
  const trimmed = line3.trim();
@@ -183916,7 +184530,7 @@ function parseBatchFile2(path) {
183916
184530
  async function runDestroy2(args) {
183917
184531
  const { getTerraformModuleDir } = await loadCloudRunAdapter();
183918
184532
  const tfDir = getTerraformModuleDir();
183919
- const state = existsSync127(statePath()) ? JSON.parse(readFileSync96(statePath(), "utf8")) : {};
184533
+ const state = existsSync127(statePath()) ? JSON.parse(readFileSync97(statePath(), "utf8")) : {};
183920
184534
  const project = args.project ?? state.projectId;
183921
184535
  const region = args.region ?? state.region ?? "us-central1";
183922
184536
  const image = args.image ?? "unused:latest";
@@ -183971,7 +184585,7 @@ function resolveAndValidateVariables(args, projectDir) {
183971
184585
  args["variables-file"]
183972
184586
  );
183973
184587
  if (variables && Object.keys(variables).length > 0) {
183974
- const indexPath2 = join146(projectDir, "index.html");
184588
+ const indexPath2 = join147(projectDir, "index.html");
183975
184589
  if (existsSync127(indexPath2)) {
183976
184590
  const issues = validateVariablesAgainstProject(indexPath2, variables);
183977
184591
  reportVariableIssues(issues, {
@@ -184218,7 +184832,7 @@ ${HELP2}`);
184218
184832
  });
184219
184833
 
184220
184834
  // src/cloud/detectAspectRatio.ts
184221
- import { readFileSync as readFileSync97 } from "fs";
184835
+ import { readFileSync as readFileSync98 } from "fs";
184222
184836
  function extractAttributeNumber(tag, re2) {
184223
184837
  const match = tag.match(re2);
184224
184838
  if (!match) return null;
@@ -184230,7 +184844,7 @@ function extractAttributeNumber(tag, re2) {
184230
184844
  function detectAspectRatioFromHtml(entryHtmlPath) {
184231
184845
  let html;
184232
184846
  try {
184233
- html = readFileSync97(entryHtmlPath, "utf-8");
184847
+ html = readFileSync98(entryHtmlPath, "utf-8");
184234
184848
  } catch (err) {
184235
184849
  return { kind: "read-error", error: normalizeErrorMessage(err) };
184236
184850
  }
@@ -184344,7 +184958,7 @@ import {
184344
184958
  rmSync as rmSync41,
184345
184959
  statSync as statSync46
184346
184960
  } from "fs";
184347
- 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";
184348
184962
  import { pipeline as pipeline3 } from "stream/promises";
184349
184963
  async function downloadToFile(url, destPath, options = {}) {
184350
184964
  const fetchImpl = options.fetchImpl ?? fetch;
@@ -184361,8 +184975,8 @@ async function downloadToFile(url, destPath, options = {}) {
184361
184975
  const totalOpt = total !== void 0 && Number.isFinite(total) ? total : void 0;
184362
184976
  const destination = resolveDownloadDestination(destPath);
184363
184977
  const previous = statSync46(destination, { throwIfNoEntry: false });
184364
- const stage = mkdtempSync27(join147(dirname65(destination), ".hf-download-"));
184365
- const stagedFile = join147(stage, "download");
184978
+ const stage = mkdtempSync27(join148(dirname65(destination), ".hf-download-"));
184979
+ const stagedFile = join148(stage, "download");
184366
184980
  let bytes = 0;
184367
184981
  try {
184368
184982
  await pipeline3(
@@ -187028,12 +187642,12 @@ var init_freeze = __esm({
187028
187642
  });
187029
187643
 
187030
187644
  // ../core/dist/figma/jsonl.js
187031
- import { existsSync as existsSync129, readFileSync as readFileSync98 } from "fs";
187645
+ import { existsSync as existsSync129, readFileSync as readFileSync99 } from "fs";
187032
187646
  function readJsonlValues(path) {
187033
187647
  if (!existsSync129(path))
187034
187648
  return [];
187035
187649
  const out = [];
187036
- for (const line3 of readFileSync98(path, "utf8").split(/\r?\n/)) {
187650
+ for (const line3 of readFileSync99(path, "utf8").split(/\r?\n/)) {
187037
187651
  const trimmed = line3.trim();
187038
187652
  if (trimmed.length === 0)
187039
187653
  continue;
@@ -187052,16 +187666,16 @@ var init_jsonl = __esm({
187052
187666
  });
187053
187667
 
187054
187668
  // ../core/dist/figma/manifest.js
187055
- import { appendFileSync as appendFileSync2, existsSync as existsSync130, mkdirSync as mkdirSync69, readFileSync as readFileSync99, writeFileSync as writeFileSync63 } from "fs";
187056
- 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";
187057
187671
  function mediaDir(projectDir) {
187058
- return join148(projectDir, ".media");
187672
+ return join149(projectDir, ".media");
187059
187673
  }
187060
187674
  function manifestPath2(projectDir) {
187061
- return join148(mediaDir(projectDir), MANIFEST_FILE2);
187675
+ return join149(mediaDir(projectDir), MANIFEST_FILE2);
187062
187676
  }
187063
187677
  function typeDirPath(projectDir, type) {
187064
- return join148(mediaDir(projectDir), TYPE_DIRS[type]);
187678
+ return join149(mediaDir(projectDir), TYPE_DIRS[type]);
187065
187679
  }
187066
187680
  function isFigmaManifestRecord(value) {
187067
187681
  if (typeof value !== "object" || value === null)
@@ -187093,7 +187707,7 @@ function findAllByFigmaNode(projectDir, fileKey2, nodeId) {
187093
187707
  }
187094
187708
  function updateRecord(projectDir, record2) {
187095
187709
  const p2 = manifestPath2(projectDir);
187096
- const lines = readFileSync99(p2, "utf8").split(/\r?\n/);
187710
+ const lines = readFileSync100(p2, "utf8").split(/\r?\n/);
187097
187711
  const out = lines.map((line3) => {
187098
187712
  const trimmed = line3.trim();
187099
187713
  if (trimmed.length === 0)
@@ -187114,7 +187728,7 @@ function nextId(projectDir, type) {
187114
187728
  const p2 = manifestPath2(projectDir);
187115
187729
  if (!existsSync130(p2))
187116
187730
  return `${type}_001`;
187117
- for (const line3 of readFileSync99(p2, "utf8").split(/\r?\n/)) {
187731
+ for (const line3 of readFileSync100(p2, "utf8").split(/\r?\n/)) {
187118
187732
  const trimmed = line3.trim();
187119
187733
  if (trimmed.length === 0)
187120
187734
  continue;
@@ -187148,12 +187762,12 @@ var init_manifest = __esm({
187148
187762
 
187149
187763
  // ../core/dist/figma/mediaIndex.js
187150
187764
  import { mkdirSync as mkdirSync70, writeFileSync as writeFileSync64 } from "fs";
187151
- import { dirname as dirname67, join as join149 } from "path";
187765
+ import { dirname as dirname67, join as join150 } from "path";
187152
187766
  function isRow(value) {
187153
187767
  return typeof value === "object" && value !== null;
187154
187768
  }
187155
187769
  function indexPath(projectDir) {
187156
- return join149(mediaDir(projectDir), "index.md");
187770
+ return join150(mediaDir(projectDir), "index.md");
187157
187771
  }
187158
187772
  function pad(str, len2) {
187159
187773
  return String(str ?? "").padEnd(len2);
@@ -187265,9 +187879,9 @@ var init_sanitizeSvg = __esm({
187265
187879
 
187266
187880
  // ../core/dist/figma/bindings.js
187267
187881
  import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync71, writeFileSync as writeFileSync65 } from "fs";
187268
- import { join as join150 } from "path";
187882
+ import { join as join151 } from "path";
187269
187883
  function bindingsPath(projectDir) {
187270
- return join150(mediaDir(projectDir), BINDINGS_FILE);
187884
+ return join151(mediaDir(projectDir), BINDINGS_FILE);
187271
187885
  }
187272
187886
  function isRecord16(value) {
187273
187887
  return typeof value === "object" && value !== null;
@@ -187868,7 +188482,7 @@ __export(asset_exports, {
187868
188482
  runAssetImportMany: () => runAssetImportMany
187869
188483
  });
187870
188484
  import { existsSync as existsSync131 } from "fs";
187871
- import { join as join151, relative as relative36 } from "path";
188485
+ import { join as join152, relative as relative36 } from "path";
187872
188486
  function gatherAssetRefs(positionals) {
187873
188487
  return positionals.flatMap((r) => /^https?:/i.test(r.trim()) ? [r] : r.split(",")).map((r) => r.trim()).filter((r) => r.length > 0);
187874
188488
  }
@@ -187882,7 +188496,7 @@ function requireNodeRef(refInput) {
187882
188496
  }
187883
188497
  function reuseExisting(fileKey2, nodeId, opts, version2, deps, description, entity) {
187884
188498
  const existing = findAllByFigmaNode(deps.projectDir, fileKey2, nodeId).find(
187885
- (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))
187886
188500
  );
187887
188501
  if (!existing) return null;
187888
188502
  let record2 = existing;
@@ -187905,7 +188519,7 @@ async function freezeAndRecord(fileKey2, nodeId, url, ext, opts, version2, deps,
187905
188519
  bytes = new TextEncoder().encode(sanitizeSvg(new TextDecoder().decode(bytes)));
187906
188520
  }
187907
188521
  const id = nextId(deps.projectDir, "image");
187908
- const destAbs = join151(typeDirPath(deps.projectDir, "image"), `${id}.${ext}`);
188522
+ const destAbs = join152(typeDirPath(deps.projectDir, "image"), `${id}.${ext}`);
187909
188523
  freezeBytes(bytes, destAbs);
187910
188524
  const record2 = {
187911
188525
  id,
@@ -188075,11 +188689,11 @@ __export(tokens_exports, {
188075
188689
  runTokensImport: () => runTokensImport
188076
188690
  });
188077
188691
  import { writeFileSync as writeFileSync66 } from "fs";
188078
- import { join as join152 } from "path";
188692
+ import { join as join153 } from "path";
188079
188693
  async function runTokensImport(refInput, deps) {
188080
188694
  const { fileKey: fileKey2 } = parseFigmaRef(refInput);
188081
188695
  const { version: version2 } = await deps.client.fileVersion(fileKey2);
188082
- const sidecarPath = join152(deps.projectDir, "figma-tokens.json");
188696
+ const sidecarPath = join153(deps.projectDir, "figma-tokens.json");
188083
188697
  let vars = null;
188084
188698
  try {
188085
188699
  vars = await deps.client.variables(fileKey2);
@@ -188154,7 +188768,7 @@ __export(component_exports, {
188154
188768
  runComponentImport: () => runComponentImport
188155
188769
  });
188156
188770
  import { existsSync as existsSync132, mkdirSync as mkdirSync72, writeFileSync as writeFileSync67 } from "fs";
188157
- import { join as join153, relative as relative37 } from "path";
188771
+ import { join as join154, relative as relative37 } from "path";
188158
188772
  function escapeAttr2(value) {
188159
188773
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
188160
188774
  }
@@ -188165,7 +188779,7 @@ async function runComponentImport(refInput, deps) {
188165
188779
  const bindings = resolveBindings(tree, readBindings(deps.projectDir));
188166
188780
  const mapped = nodeToHtml(tree, bindings, { rootName: deps.name });
188167
188781
  const name = slugify(deps.name ?? tree.name);
188168
- const componentDir = join153(deps.projectDir, "compositions", "components", name);
188782
+ const componentDir = join154(deps.projectDir, "compositions", "components", name);
188169
188783
  if (existsSync132(componentDir))
188170
188784
  console.warn(
188171
188785
  `component dir compositions/components/${name} already exists \u2014 overwriting (rename the figma frame for a separate import)`
@@ -188177,7 +188791,7 @@ async function runComponentImport(refInput, deps) {
188177
188791
  componentDir,
188178
188792
  deps
188179
188793
  );
188180
- const htmlFile = join153(componentDir, `${name}.html`);
188794
+ const htmlFile = join154(componentDir, `${name}.html`);
188181
188795
  writeFileSync67(htmlFile, html + "\n");
188182
188796
  const registryItem = {
188183
188797
  name,
@@ -188199,7 +188813,7 @@ async function runComponentImport(refInput, deps) {
188199
188813
  ]
188200
188814
  };
188201
188815
  writeFileSync67(
188202
- join153(componentDir, "registry-item.json"),
188816
+ join154(componentDir, "registry-item.json"),
188203
188817
  JSON.stringify(registryItem, null, 2) + "\n"
188204
188818
  );
188205
188819
  return {
@@ -188224,7 +188838,7 @@ async function rasterizeFallback(mapped, fileKey2, componentDir, deps) {
188224
188838
  continue;
188225
188839
  }
188226
188840
  frozenAssets.push(asset.record.path);
188227
- const srcRel = relative37(componentDir, join153(deps.projectDir, asset.record.path)).replaceAll(
188841
+ const srcRel = relative37(componentDir, join154(deps.projectDir, asset.record.path)).replaceAll(
188228
188842
  "\\",
188229
188843
  "/"
188230
188844
  );
@@ -188373,7 +188987,7 @@ __export(autoUpdate_exports, {
188373
188987
  import { spawn as spawn19 } from "child_process";
188374
188988
  import { appendFileSync as appendFileSync4, mkdirSync as mkdirSync73, openSync as openSync15 } from "fs";
188375
188989
  import { homedir as homedir25 } from "os";
188376
- import { join as join154 } from "path";
188990
+ import { join as join155 } from "path";
188377
188991
  import { compareVersions as compareVersions3 } from "compare-versions";
188378
188992
  function isAutoInstallDisabled() {
188379
188993
  if (isDevMode()) return true;
@@ -188396,7 +189010,7 @@ function log(line3) {
188396
189010
  }
188397
189011
  function launchDetachedInstall(invocation, displayCommand, version2) {
188398
189012
  mkdirSync73(CONFIG_DIR3, { recursive: true, mode: 448 });
188399
- const configFile = join154(CONFIG_DIR3, "config.json");
189013
+ const configFile = join155(CONFIG_DIR3, "config.json");
188400
189014
  const nodeScript = `
188401
189015
  const { execFile } = require("node:child_process");
188402
189016
  const { readFileSync, renameSync, writeFileSync } = require("node:fs");
@@ -188517,8 +189131,8 @@ var init_autoUpdate = __esm({
188517
189131
  init_config();
188518
189132
  init_env();
188519
189133
  init_installerDetection();
188520
- CONFIG_DIR3 = join154(homedir25(), ".hyperframes");
188521
- LOG_FILE = join154(CONFIG_DIR3, "auto-update.log");
189134
+ CONFIG_DIR3 = join155(homedir25(), ".hyperframes");
189135
+ LOG_FILE = join155(CONFIG_DIR3, "auto-update.log");
188522
189136
  PENDING_TIMEOUT_MS = 10 * 60 * 1e3;
188523
189137
  }
188524
189138
  });
@@ -188714,7 +189328,7 @@ var init_help = __esm({
188714
189328
  init_version();
188715
189329
  init_dist();
188716
189330
  init_runId();
188717
- 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";
188718
189332
  import { fileURLToPath as fileURLToPath17 } from "url";
188719
189333
  import { existsSync as existsSync133 } from "fs";
188720
189334
 
@@ -188834,7 +189448,7 @@ for (const stream of [process.stdout, process.stderr]) {
188834
189448
  }
188835
189449
  (() => {
188836
189450
  const here = dirname68(fileURLToPath17(import.meta.url));
188837
- const shader = join155(here, "shaderTransitionWorker.js");
189451
+ const shader = join156(here, "shaderTransitionWorker.js");
188838
189452
  if (!process.env.HF_SHADER_WORKER_ENTRY && existsSync133(shader)) {
188839
189453
  process.env.HF_SHADER_WORKER_ENTRY = shader;
188840
189454
  }
@@ -188847,10 +189461,10 @@ if (rootVersionRequested) {
188847
189461
  process.exit(0);
188848
189462
  }
188849
189463
  try {
188850
- const { readFileSync: readFileSync100 } = await import("fs");
189464
+ const { readFileSync: readFileSync101 } = await import("fs");
188851
189465
  const { resolve: resolve84 } = await import("path");
188852
189466
  const envPath = resolve84(process.cwd(), ".env");
188853
- const envContent = readFileSync100(envPath, "utf-8");
189467
+ const envContent = readFileSync101(envPath, "utf-8");
188854
189468
  for (const rawLine of envContent.split("\n")) {
188855
189469
  let line3 = rawLine.trim();
188856
189470
  if (!line3 || line3.startsWith("#")) continue;