pixelkiln 0.81.0 → 0.82.0

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
@@ -68,7 +68,7 @@ function exitCodeFor(err) {
68
68
 
69
69
  // src/cli/main.ts
70
70
  import { existsSync as existsSync37 } from "fs";
71
- import { readFile as readFile32 } from "fs/promises";
71
+ import { readFile as readFile33 } from "fs/promises";
72
72
 
73
73
  // src/cli/args.ts
74
74
  import path from "path";
@@ -14628,10 +14628,11 @@ import { existsSync as existsSync26 } from "fs";
14628
14628
  // src/gallery/snapshot.ts
14629
14629
  import { createHash as createHash3 } from "crypto";
14630
14630
  import { existsSync as existsSync21 } from "fs";
14631
- import { readFile as readFile18, stat as stat2 } from "fs/promises";
14631
+ import { readFile as readFile19, stat as stat2 } from "fs/promises";
14632
14632
  import path30 from "path";
14633
14633
 
14634
14634
  // src/gallery/edit.ts
14635
+ import { readFile as readFile18, rename as rename9, writeFile as writeFile14 } from "fs/promises";
14635
14636
  import path28 from "path";
14636
14637
  import { z as z10 } from "zod";
14637
14638
 
@@ -14876,9 +14877,62 @@ var HandEditRequestSchema = z10.discriminatedUnion("action", [
14876
14877
  protocol: z10.number().int().positive()
14877
14878
  }).strict()
14878
14879
  ]);
14880
+ var UNDO_DEPTH = 20;
14881
+ var UndoRequestSchema = z10.object({
14882
+ action: z10.literal("undo-edit"),
14883
+ project: z10.string().min(1).optional(),
14884
+ /** The manifest as the page last saw it; undo refuses a manifest someone else has changed since. */
14885
+ expectedSha256: z10.string().regex(/^[0-9a-f]{64}$/)
14886
+ }).strict();
14879
14887
  function createGalleryEditHandler(opts) {
14880
14888
  const log2 = opts.onProgress ?? (() => {
14881
14889
  });
14890
+ const apply = createEditApplier(opts, log2);
14891
+ const history = /* @__PURE__ */ new Map();
14892
+ const manifestOf = async (project) => {
14893
+ try {
14894
+ return path28.resolve(await opts.manifestFor(typeof project === "string" ? project : void 0));
14895
+ } catch {
14896
+ return null;
14897
+ }
14898
+ };
14899
+ return async (body) => {
14900
+ const undo = UndoRequestSchema.safeParse(body);
14901
+ if (undo.success) {
14902
+ const manifestPath2 = await manifestOf(undo.data.project);
14903
+ if (!manifestPath2) throw new ManifestEditError("no manifest to undo an edit in");
14904
+ const current = await readFile18(manifestPath2, "utf8");
14905
+ if (sha256(current) !== undo.data.expectedSha256) throw new ManifestDriftError(manifestPath2);
14906
+ const stack = history.get(manifestPath2) ?? [];
14907
+ const last = stack[stack.length - 1];
14908
+ if (!last) throw new ManifestEditError("nothing to undo: this gallery has not changed the manifest");
14909
+ if (last.afterSha !== sha256(current)) {
14910
+ history.delete(manifestPath2);
14911
+ throw Object.assign(new Error("the manifest changed outside the gallery since its last edit; undo would discard that change"), { status: 409 });
14912
+ }
14913
+ const tmp = manifestPath2 + ".pixelkiln-undo";
14914
+ await writeFile14(tmp, last.before);
14915
+ await rename9(tmp, manifestPath2);
14916
+ stack.pop();
14917
+ log2(` manifest edit undone in ${path28.relative(process.cwd(), manifestPath2) || manifestPath2}`);
14918
+ return opts.reload();
14919
+ }
14920
+ const manifestPath = await manifestOf(body?.project);
14921
+ const before = manifestPath ? await readFile18(manifestPath, "utf8").catch(() => null) : null;
14922
+ const build = await apply(body);
14923
+ if (manifestPath && before !== null) {
14924
+ const after = await readFile18(manifestPath, "utf8").catch(() => null);
14925
+ if (after !== null && after !== before) {
14926
+ const stack = history.get(manifestPath) ?? [];
14927
+ stack.push({ before, afterSha: sha256(after) });
14928
+ if (stack.length > UNDO_DEPTH) stack.shift();
14929
+ history.set(manifestPath, stack);
14930
+ }
14931
+ }
14932
+ return build;
14933
+ };
14934
+ }
14935
+ function createEditApplier(opts, log2) {
14882
14936
  return async (body) => {
14883
14937
  const handEdit = HandEditRequestSchema.safeParse(body);
14884
14938
  if (handEdit.success) {
@@ -15220,7 +15274,7 @@ function describeHistory(media, entry, cacheDir) {
15220
15274
  var MAX_COMPARED_BYTES = 8 * 1024 * 1024;
15221
15275
  async function samePixels(a, b) {
15222
15276
  try {
15223
- const [left, right] = await Promise.all([readFile18(a), readFile18(b)]);
15277
+ const [left, right] = await Promise.all([readFile19(a), readFile19(b)]);
15224
15278
  if (left.length > MAX_COMPARED_BYTES || right.length > MAX_COMPARED_BYTES) return false;
15225
15279
  const x = decodePng(left);
15226
15280
  const y = decodePng(right);
@@ -15378,7 +15432,7 @@ async function buildGallerySnapshot(opts) {
15378
15432
  const media = /* @__PURE__ */ new Map();
15379
15433
  const plan = await buildPlan(specs, lock);
15380
15434
  const items = [];
15381
- const manifestText = await readFile18(loaded.path, "utf8");
15435
+ const manifestText = await readFile19(loaded.path, "utf8");
15382
15436
  const manifestSha256 = sha256(manifestText);
15383
15437
  const cacheDir = path30.resolve(path30.dirname(opts.lockPath), ".pixelkiln", "cache");
15384
15438
  let rawStyles = {};
@@ -15730,7 +15784,7 @@ async function buildWorkspaceGallerySnapshot(opts) {
15730
15784
  root: loaded.root,
15731
15785
  provider: loaded.manifest.provider,
15732
15786
  account: project.account ?? null,
15733
- manifestSha256: manifestSha256 ?? sha256(await readFile18(manifestPath, "utf8")),
15787
+ manifestSha256: manifestSha256 ?? sha256(await readFile19(manifestPath, "utf8")),
15734
15788
  historyLimit: historyLimit(loaded.manifest),
15735
15789
  entries: Object.keys(lock.entries).length,
15736
15790
  items: projectItems.length,
@@ -15789,7 +15843,7 @@ async function buildWorkspaceGallerySnapshot(opts) {
15789
15843
  // src/gallery/server.ts
15790
15844
  import { randomBytes } from "crypto";
15791
15845
  import { createServer as createServer2 } from "http";
15792
- import { readFile as readFile19 } from "fs/promises";
15846
+ import { readFile as readFile20 } from "fs/promises";
15793
15847
 
15794
15848
  // src/gallery/page.ts
15795
15849
  import { existsSync as existsSync22, readFileSync as readFileSync4 } from "fs";
@@ -15900,6 +15954,7 @@ ${css}</style>
15900
15954
  </header>
15901
15955
  <main id="root"></main>
15902
15956
  <div id="selbar" class="selbar" hidden></div>
15957
+ <div id="toast" class="toast" role="status" aria-live="polite" hidden></div>
15903
15958
  <footer>
15904
15959
  Click a sprite for its full record; press <kbd>?</kbd> for every keyboard shortcut. This page reads the manifest, lockfile,
15905
15960
  and disk only. It never contacts a provider<span id="foot-edit"> and never writes anything</span><span id="foot-editing" hidden>.
@@ -16103,7 +16158,7 @@ async function serveGallery(opts) {
16103
16158
  const asset = reviewAssets.get(jobId)?.get(url2.pathname);
16104
16159
  if (!asset) return fail(404, "no such review asset");
16105
16160
  try {
16106
- const bytes = await readFile19(asset.path);
16161
+ const bytes = await readFile20(asset.path);
16107
16162
  res.writeHead(200, {
16108
16163
  "Content-Type": asset.contentType,
16109
16164
  "Cache-Control": "no-store",
@@ -16186,7 +16241,7 @@ async function serveGallery(opts) {
16186
16241
  if (!asset) return fail(404, "no such gallery media");
16187
16242
  let bytes;
16188
16243
  try {
16189
- bytes = await readFile19(asset.path);
16244
+ bytes = await readFile20(asset.path);
16190
16245
  } catch {
16191
16246
  return fail(404, "gallery media is no longer on disk; refresh the page");
16192
16247
  }
@@ -16642,10 +16697,10 @@ function ownedReferenceImageId(spec, lock, providerId) {
16642
16697
 
16643
16698
  // src/pick/server.ts
16644
16699
  import { existsSync as existsSync24 } from "fs";
16645
- import { readFile as readFile21 } from "fs/promises";
16700
+ import { readFile as readFile22 } from "fs/promises";
16646
16701
 
16647
16702
  // src/pipeline/init.ts
16648
- import { readdir, readFile as readFile20, writeFile as writeFile14 } from "fs/promises";
16703
+ import { readdir, readFile as readFile21, writeFile as writeFile15 } from "fs/promises";
16649
16704
  import { existsSync as existsSync23 } from "fs";
16650
16705
  import path31 from "path";
16651
16706
  function pngSize(buf) {
@@ -16675,7 +16730,7 @@ async function scanAssets(root, opts = {}) {
16675
16730
  const skipped = [];
16676
16731
  const seen = /* @__PURE__ */ new Map();
16677
16732
  for (const file of files.sort()) {
16678
- const size = pngSize(await readFile20(file));
16733
+ const size = pngSize(await readFile21(file));
16679
16734
  if (!size) {
16680
16735
  skipped.push(`${file} (not a readable PNG)`);
16681
16736
  continue;
@@ -16743,7 +16798,7 @@ async function writeManifestFile(target, manifest) {
16743
16798
  if (existsSync23(target)) {
16744
16799
  throw new Error(`${target} already exists; refusing to overwrite an existing manifest.`);
16745
16800
  }
16746
- await writeFile14(target, JSON.stringify(manifest, null, 2) + "\n");
16801
+ await writeFile15(target, JSON.stringify(manifest, null, 2) + "\n");
16747
16802
  }
16748
16803
 
16749
16804
  // src/pick/sheet.ts
@@ -17162,7 +17217,7 @@ async function currentArt(entry, spec) {
17162
17217
  let width = entry.width;
17163
17218
  let height = entry.height;
17164
17219
  if (contentType === "image/png") {
17165
- const size = pngSize(await readFile21(file));
17220
+ const size = pngSize(await readFile22(file));
17166
17221
  if (size) ({ width, height } = size);
17167
17222
  }
17168
17223
  return { path: file, contentType, width, height };
@@ -17695,13 +17750,13 @@ function createGenerateHandlers(opts) {
17695
17750
  }
17696
17751
 
17697
17752
  // src/gallery/editor.ts
17698
- import { readFile as readFile23 } from "fs/promises";
17753
+ import { readFile as readFile24 } from "fs/promises";
17699
17754
  import path33 from "path";
17700
17755
 
17701
17756
  // src/editor/install.ts
17702
17757
  import { createHash as createHash4 } from "crypto";
17703
17758
  import { existsSync as existsSync25 } from "fs";
17704
- import { mkdir as mkdir12, readFile as readFile22, rename as rename9, rm as rm10, stat as stat3, writeFile as writeFile15 } from "fs/promises";
17759
+ import { mkdir as mkdir12, readFile as readFile23, rename as rename10, rm as rm10, stat as stat3, writeFile as writeFile16 } from "fs/promises";
17705
17760
  import os2 from "os";
17706
17761
  import path32 from "path";
17707
17762
 
@@ -17785,7 +17840,7 @@ function editorBaseUrl(pin = EDITOR_PIN) {
17785
17840
  }
17786
17841
  async function sha256Of(file) {
17787
17842
  try {
17788
- return createHash4("sha256").update(await readFile22(file)).digest("hex");
17843
+ return createHash4("sha256").update(await readFile23(file)).digest("hex");
17789
17844
  } catch {
17790
17845
  return null;
17791
17846
  }
@@ -17859,8 +17914,8 @@ async function installEditor(opts = {}) {
17859
17914
  const final = path32.join(dir, name);
17860
17915
  const tmp = `${final}.${process.pid}.part`;
17861
17916
  try {
17862
- await writeFile15(tmp, bytes);
17863
- await rename9(tmp, final);
17917
+ await writeFile16(tmp, bytes);
17918
+ await rename10(tmp, final);
17864
17919
  } finally {
17865
17920
  await rm10(tmp, { force: true });
17866
17921
  }
@@ -17978,7 +18033,7 @@ function createGalleryEditorHandlers(opts = {}) {
17978
18033
  if (!status.installed) return null;
17979
18034
  let bytes;
17980
18035
  try {
17981
- bytes = await readFile23(path33.join(dir, name));
18036
+ bytes = await readFile24(path33.join(dir, name));
17982
18037
  } catch {
17983
18038
  verified = null;
17984
18039
  return null;
@@ -18445,7 +18500,7 @@ import { existsSync as existsSync28 } from "fs";
18445
18500
 
18446
18501
  // src/pipeline/cache-health.ts
18447
18502
  import { existsSync as existsSync27 } from "fs";
18448
- import { readFile as readFile24, readdir as readdir2, rm as rm11 } from "fs/promises";
18503
+ import { readFile as readFile25, readdir as readdir2, rm as rm11 } from "fs/promises";
18449
18504
  import path35 from "path";
18450
18505
  async function inspectCaches(lock, lockPath, options = {}) {
18451
18506
  if (options.prune && !existsSync27(lockPath)) {
@@ -18473,7 +18528,7 @@ async function inspectCaches(lock, lockPath, options = {}) {
18473
18528
  await saveCache(remotePath, { version: 1, hashes: {} });
18474
18529
  removed.resetRemoteHashCache = true;
18475
18530
  } else if (remoteHashes.invalidIds.length) {
18476
- const cache = parseCache(JSON.parse(await readFile24(remotePath, "utf8")));
18531
+ const cache = parseCache(JSON.parse(await readFile25(remotePath, "utf8")));
18477
18532
  for (const id of remoteHashes.invalidIds) delete cache.hashes[id];
18478
18533
  removed.remoteHashEntries = remoteHashes.invalidIds.length;
18479
18534
  await saveCache(remotePath, cache);
@@ -18517,7 +18572,7 @@ async function inspectContentCache(contentDir, referenced) {
18517
18572
  const expected = mediaType ? entry.name.slice(0, -4) : "";
18518
18573
  let bytes;
18519
18574
  try {
18520
- bytes = await readFile24(file);
18575
+ bytes = await readFile25(file);
18521
18576
  report.bytes += bytes.length;
18522
18577
  } catch (err) {
18523
18578
  report.invalid.push({
@@ -18564,7 +18619,7 @@ async function inspectRemoteHashCache(remotePath) {
18564
18619
  if (!report.exists) return report;
18565
18620
  let cache;
18566
18621
  try {
18567
- cache = parseCache(JSON.parse(await readFile24(remotePath, "utf8")));
18622
+ cache = parseCache(JSON.parse(await readFile25(remotePath, "utf8")));
18568
18623
  } catch (err) {
18569
18624
  report.error = err instanceof Error ? err.message : String(err);
18570
18625
  return report;
@@ -18891,7 +18946,7 @@ async function nearestExistingDirectory(start) {
18891
18946
  }
18892
18947
 
18893
18948
  // src/pipeline/audit.ts
18894
- import { readFile as readFile25 } from "fs/promises";
18949
+ import { readFile as readFile26 } from "fs/promises";
18895
18950
  import { existsSync as existsSync31 } from "fs";
18896
18951
  import path38 from "path";
18897
18952
  function paletteDistance(asset, reference) {
@@ -18934,7 +18989,7 @@ async function auditStyle(loaded, specs, styleId, lock) {
18934
18989
  continue;
18935
18990
  }
18936
18991
  try {
18937
- const png = decodePng(await readFile25(output.absolutePath));
18992
+ const png = decodePng(await readFile26(output.absolutePath));
18938
18993
  const palette = extractPalette(png, 12);
18939
18994
  assets.push({
18940
18995
  assetId: spec.assetId,
@@ -18961,7 +19016,7 @@ async function auditStyle(loaded, specs, styleId, lock) {
18961
19016
  const abs = path38.resolve(loaded.root, img.path);
18962
19017
  if (!existsSync31(abs)) continue;
18963
19018
  try {
18964
- refPalettes.push(extractPalette(decodePng(await readFile25(abs)), 12));
19019
+ refPalettes.push(extractPalette(decodePng(await readFile26(abs)), 12));
18965
19020
  } catch {
18966
19021
  }
18967
19022
  }
@@ -19190,7 +19245,7 @@ async function runAudit(args) {
19190
19245
 
19191
19246
  // src/cli/commands/pack.ts
19192
19247
  import path40 from "path";
19193
- import { readFile as readFile26 } from "fs/promises";
19248
+ import { readFile as readFile27 } from "fs/promises";
19194
19249
 
19195
19250
  // src/pipeline/pack.ts
19196
19251
  import { readFileSync as readFileSync5 } from "fs";
@@ -19903,7 +19958,7 @@ async function runPack(args) {
19903
19958
  if (args.primaryOnly || args.outputRoles.length) {
19904
19959
  throw new Error("--primary-only and --output-role require manifest-driven pack");
19905
19960
  }
19906
- const raw = JSON.parse(await readFile26(path40.resolve(args.inputs), "utf8"));
19961
+ const raw = JSON.parse(await readFile27(path40.resolve(args.inputs), "utf8"));
19907
19962
  const inputs = resolvePackInputs(raw, args.inputs);
19908
19963
  const { png, atlas, skipped, sources } = packSprites(inputs, { columns: args.columns });
19909
19964
  const format = sheetFormat(args);
@@ -20132,7 +20187,7 @@ import path42 from "path";
20132
20187
 
20133
20188
  // src/pixellab-utils.ts
20134
20189
  import { existsSync as existsSync32 } from "fs";
20135
- import { mkdir as mkdir13, readFile as readFile27, writeFile as writeFile16 } from "fs/promises";
20190
+ import { mkdir as mkdir13, readFile as readFile28, writeFile as writeFile17 } from "fs/promises";
20136
20191
  import path41 from "path";
20137
20192
  var UNZOOM_MIN_SIDE = 256;
20138
20193
  var UNZOOM_MAX_AREA = 2048 * 2048;
@@ -20144,7 +20199,7 @@ async function unzoomFile(client2, input, opts = {}) {
20144
20199
  const out = path41.resolve(opts.out ?? defaultUnzoomOut(input));
20145
20200
  if (path41.resolve(input) === out) throw new Error("unzoom will not overwrite its own input; pass a different --out");
20146
20201
  if (existsSync32(out) && !opts.force) throw new Error(`${out} already exists; pass --force to replace it`);
20147
- const bytes = await readFile27(input);
20202
+ const bytes = await readFile28(input);
20148
20203
  const metadata = imageMetadata(bytes);
20149
20204
  if (!metadata) throw new Error(`${input} is not a readable PNG or JPEG`);
20150
20205
  const { width, height, format } = metadata;
@@ -20158,7 +20213,7 @@ async function unzoomFile(client2, input, opts = {}) {
20158
20213
  }
20159
20214
  const res = await client2.unzoom({ image: { base64: bytes.toString("base64"), format }, quantize: opts.quantize });
20160
20215
  await mkdir13(path41.dirname(out), { recursive: true });
20161
- await writeFile16(out, res.png);
20216
+ await writeFile17(out, res.png);
20162
20217
  return { out, original: res.originalSize, unzoomed: res.unzoomedSize, zoomFactor: res.zoomFactor, usage: res.usage };
20163
20218
  }
20164
20219
  var FONT_WEIGHTS = ["Bold", "Regular"];
@@ -20200,8 +20255,8 @@ async function generateFont(client2, opts) {
20200
20255
  client2.download(job.download_atlas_url)
20201
20256
  ]);
20202
20257
  await mkdir13(path41.dirname(ttf), { recursive: true });
20203
- await writeFile16(ttf, ttfBytes);
20204
- await writeFile16(atlas, atlasBytes);
20258
+ await writeFile17(ttf, ttfBytes);
20259
+ await writeFile17(atlas, atlasBytes);
20205
20260
  return { jobId, ttf, atlas, glyphPx: job.glyph_px ?? null, usage: job.usage };
20206
20261
  }
20207
20262
  if (Date.now() - started > timeout) {
@@ -20282,12 +20337,12 @@ async function runFont(args) {
20282
20337
 
20283
20338
  // src/cli/commands/quality.ts
20284
20339
  import path44 from "path";
20285
- import { readFile as readFile29 } from "fs/promises";
20340
+ import { readFile as readFile30 } from "fs/promises";
20286
20341
 
20287
20342
  // src/pipeline/quality-regression.ts
20288
20343
  import path43 from "path";
20289
20344
  import { existsSync as existsSync33 } from "fs";
20290
- import { readFile as readFile28 } from "fs/promises";
20345
+ import { readFile as readFile29 } from "fs/promises";
20291
20346
  import { z as z12 } from "zod";
20292
20347
  var SHA256_RE = /^[0-9a-f]{64}$/;
20293
20348
  var HEX_RE = /^#[0-9a-f]{6}$/;
@@ -20443,7 +20498,7 @@ async function measureImageQuality(file) {
20443
20498
  const absolute = path43.resolve(file);
20444
20499
  let bytes;
20445
20500
  try {
20446
- bytes = await readFile28(absolute);
20501
+ bytes = await readFile29(absolute);
20447
20502
  } catch (error) {
20448
20503
  throw new Error(`Cannot read quality image ${absolute}: ${message4(error)}`, { cause: error });
20449
20504
  }
@@ -20530,7 +20585,7 @@ async function snapshotQualityBaseline(inputs, baselinePath, options = {}) {
20530
20585
  });
20531
20586
  const data = Buffer.from(JSON.stringify(baseline, null, 2) + "\n");
20532
20587
  if (existsSync33(absolute) && !options.force) {
20533
- const current = await readFile28(absolute);
20588
+ const current = await readFile29(absolute);
20534
20589
  if (!current.equals(data)) {
20535
20590
  throw new OverwriteRefusedError(
20536
20591
  `Quality baseline already exists with different content: ${absolute}. Pass --force to replace it.`,
@@ -20545,7 +20600,7 @@ async function readQualityBaseline(baselinePath) {
20545
20600
  const absolute = path43.resolve(baselinePath);
20546
20601
  let raw;
20547
20602
  try {
20548
- raw = JSON.parse(await readFile28(absolute, "utf8"));
20603
+ raw = JSON.parse(await readFile29(absolute, "utf8"));
20549
20604
  } catch (error) {
20550
20605
  throw new Error(`Could not read quality baseline ${absolute}: ${message4(error)}`, { cause: error });
20551
20606
  }
@@ -20675,7 +20730,7 @@ async function runQuality(args) {
20675
20730
  if (!args.out) throw new Error("quality snapshot needs --out <pixelkiln.quality.json>.");
20676
20731
  let raw;
20677
20732
  try {
20678
- raw = JSON.parse(await readFile29(path44.resolve(args.inputs), "utf8"));
20733
+ raw = JSON.parse(await readFile30(path44.resolve(args.inputs), "utf8"));
20679
20734
  } catch (error) {
20680
20735
  throw new Error(
20681
20736
  `Could not read quality inputs ${path44.resolve(args.inputs)}: ${error instanceof Error ? error.message : String(error)}`,
@@ -20727,7 +20782,7 @@ import path46 from "path";
20727
20782
  // src/recipes.ts
20728
20783
  import path45 from "path";
20729
20784
  import { existsSync as existsSync34 } from "fs";
20730
- import { readdir as readdir3, readFile as readFile30 } from "fs/promises";
20785
+ import { readdir as readdir3, readFile as readFile31 } from "fs/promises";
20731
20786
  import { fileURLToPath as fileURLToPath2 } from "url";
20732
20787
  import { z as z13 } from "zod";
20733
20788
  var SHA256_RE2 = /^[0-9a-f]{64}$/;
@@ -20915,7 +20970,7 @@ async function readRecipeFile(recipePath, bundled) {
20915
20970
  const absolute = path45.resolve(recipePath);
20916
20971
  let raw;
20917
20972
  try {
20918
- raw = JSON.parse(await readFile30(absolute, "utf8"));
20973
+ raw = JSON.parse(await readFile31(absolute, "utf8"));
20919
20974
  } catch (error) {
20920
20975
  throw new Error(`Could not read recipe ${absolute}: ${message5(error)}`, { cause: error });
20921
20976
  }
@@ -21057,12 +21112,12 @@ async function installRecipe(target, options = {}) {
21057
21112
  ];
21058
21113
  const files = await Promise.all(sources.map(async (file) => ({
21059
21114
  path: path45.join(destination, ...file.path.split("/")),
21060
- data: await readFile30(file.source)
21115
+ data: await readFile31(file.source)
21061
21116
  })));
21062
21117
  if (!options.force) {
21063
21118
  for (const file of files) {
21064
21119
  if (!existsSync34(file.path)) continue;
21065
- const current = await readFile30(file.path);
21120
+ const current = await readFile31(file.path);
21066
21121
  if (!current.equals(file.data)) {
21067
21122
  throw new OverwriteRefusedError(
21068
21123
  `Recipe destination has local changes: ${file.path}. Choose another --out or pass --force to replace declared recipe files.`,
@@ -21302,7 +21357,7 @@ async function runRefine(args) {
21302
21357
  // src/cli/commands/skeleton.ts
21303
21358
  import path48 from "path";
21304
21359
  import { existsSync as existsSync35 } from "fs";
21305
- import { readFile as readFile31, writeFile as writeFile17 } from "fs/promises";
21360
+ import { readFile as readFile32, writeFile as writeFile18 } from "fs/promises";
21306
21361
 
21307
21362
  // src/skeleton-preview.ts
21308
21363
  var RIGHT = [255, 140, 40, 255];
@@ -21418,7 +21473,7 @@ async function runEstimateSkeleton(args) {
21418
21473
  }
21419
21474
  loadEnvFiles(path48.dirname(path48.resolve(args.manifest)));
21420
21475
  loadEnvFiles(process.cwd());
21421
- const bytes = await readFile31(path48.resolve(image));
21476
+ const bytes = await readFile32(path48.resolve(image));
21422
21477
  const metadata = imageMetadata(bytes);
21423
21478
  if (!metadata) throw new UsageError(`${image} is not a readable PNG or JPEG`);
21424
21479
  const sizeProblem = estimateSkeletonSizeProblem(metadata.width, metadata.height);
@@ -21430,7 +21485,7 @@ async function runEstimateSkeleton(args) {
21430
21485
  const frames = args.frames ?? DEFAULT_SCAFFOLD_FRAMES;
21431
21486
  const json = JSON.stringify(scaffoldSkeletonSet(res.keypoints, frames), null, 2);
21432
21487
  if (args.out) {
21433
- await writeFile17(path48.resolve(args.out), json + "\n");
21488
+ await writeFile18(path48.resolve(args.out), json + "\n");
21434
21489
  log(` wrote ${args.out}: the estimated pose and ${frames} frames to edit`);
21435
21490
  log(` preview it with: pixelkiln skeleton-preview ${args.out} --from ${image}`);
21436
21491
  } else {
@@ -21462,7 +21517,7 @@ async function runSkeletonPreview(args) {
21462
21517
  if (!existsSync35(keypointsFile)) throw new UsageError(`Keypoints file does not exist yet: ${keypointsFile}`);
21463
21518
  let json;
21464
21519
  try {
21465
- json = JSON.parse(await readFile31(keypointsFile, "utf8"));
21520
+ json = JSON.parse(await readFile32(keypointsFile, "utf8"));
21466
21521
  } catch (error) {
21467
21522
  throw new UsageError(`${keypointsFile} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
21468
21523
  }
@@ -21472,13 +21527,13 @@ async function runSkeletonPreview(args) {
21472
21527
  if (!existsSync35(sourceFile)) {
21473
21528
  log(` source image does not exist yet, drawing on a plain ground: ${sourceFile}`);
21474
21529
  } else {
21475
- const bytes = await readFile31(sourceFile);
21530
+ const bytes = await readFile32(sourceFile);
21476
21531
  if (imageMetadata(bytes)?.format !== "png") throw new UsageError(`${sourceFile} must be a PNG to preview over`);
21477
21532
  source = decodePng(bytes);
21478
21533
  }
21479
21534
  }
21480
21535
  const out = path48.resolve(args.out ?? keypointsFile.replace(/\.json$/, "") + ".preview.png");
21481
- await writeFile17(out, renderSkeletonSheet(set, { source }));
21536
+ await writeFile18(out, renderSkeletonSheet(set, { source }));
21482
21537
  log(` ${label}: ${set.frames.length} frames after the starting pose`);
21483
21538
  const shown = path48.relative(process.cwd(), out);
21484
21539
  log(` wrote ${shown.startsWith("..") ? out : shown}`);
@@ -21664,7 +21719,7 @@ async function runVersion() {
21664
21719
  for (const up of ["../package.json", "../../package.json"]) {
21665
21720
  const url = new URL(up, import.meta.url);
21666
21721
  if (!existsSync37(url)) continue;
21667
- const pkg = JSON.parse(await readFile32(url, "utf8"));
21722
+ const pkg = JSON.parse(await readFile33(url, "utf8"));
21668
21723
  if (pkg.name === "pixelkiln") {
21669
21724
  log(`${pkg.name} ${pkg.version}`);
21670
21725
  return;