pixelkiln 0.80.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 +131 -68
- package/dist/cli.js.map +1 -1
- package/dist/client/gallery.css +55 -1
- package/dist/client/gallery.js +405 -11
- package/dist/index.cjs +105 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +100 -37
- package/dist/index.js.map +1 -1
- package/docs/CHARACTERS.md +7 -0
- package/docs/CLI.md +20 -3
- package/package.json +1 -1
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
|
|
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
|
|
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([
|
|
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
|
|
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
|
|
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
|
|
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";
|
|
@@ -15863,26 +15917,35 @@ ${css}</style>
|
|
|
15863
15917
|
<span class="project" id="project"></span>
|
|
15864
15918
|
<span class="grow"></span>
|
|
15865
15919
|
<label class="search"><input id="q" type="search" placeholder="Search assets, prompts, ids, hashes" autocomplete="off"><kbd>/</kbd></label>
|
|
15866
|
-
<
|
|
15867
|
-
<
|
|
15868
|
-
<
|
|
15869
|
-
|
|
15870
|
-
|
|
15871
|
-
|
|
15872
|
-
|
|
15873
|
-
|
|
15874
|
-
|
|
15875
|
-
|
|
15876
|
-
|
|
15877
|
-
|
|
15920
|
+
<details class="menu" id="view-menu">
|
|
15921
|
+
<summary title="Sort, group, card size, backdrop, and playback">View</summary>
|
|
15922
|
+
<div class="menu-panel">
|
|
15923
|
+
<label class="menu-row"><span>Sort</span><select id="sort" aria-label="Sort">
|
|
15924
|
+
<option value="key">style / asset</option>
|
|
15925
|
+
<option value="newest">newest first</option>
|
|
15926
|
+
<option value="oldest">oldest first</option>
|
|
15927
|
+
<option value="cost">cost</option>
|
|
15928
|
+
<option value="size">size</option>
|
|
15929
|
+
</select></label>
|
|
15930
|
+
<label class="menu-row"><span>Group</span><select id="group" aria-label="Group">
|
|
15931
|
+
<option value="style">by style</option>
|
|
15932
|
+
<option value="family">by family</option>
|
|
15933
|
+
<option value="none">none</option>
|
|
15934
|
+
</select></label>
|
|
15935
|
+
<div class="menu-row"><span>Cards</span><span class="seg" id="size" role="group" aria-label="Card size">
|
|
15936
|
+
<button type="button" data-size="s" title="Small cards: more on screen">S</button><button type="button" data-size="m" title="Medium cards">M</button><button type="button" data-size="l" title="Large cards: sprites drawn bigger">L</button>
|
|
15937
|
+
</span></div>
|
|
15938
|
+
<div class="menu-row"><span>Backdrop</span><span id="backdrop-slot"></span></div>
|
|
15939
|
+
<label class="menu-row check" title="Play every loop on the grid; without this, a loop plays while the pointer is over it"><input id="playloops" type="checkbox"> Play every loop</label>
|
|
15940
|
+
<label class="menu-row check" title="Refresh every 5 seconds while this tab is visible"><input id="auto" type="checkbox"> Refresh every 5 seconds</label>
|
|
15941
|
+
<label class="menu-row check" id="notify-chip" hidden title="A system notification when a job finishes while this tab is in the background"><input id="notify" type="checkbox"> Notify when a job finishes</label>
|
|
15942
|
+
</div>
|
|
15943
|
+
</details>
|
|
15878
15944
|
<span id="editing" class="editing" hidden title="This gallery can write the manifest. It never contacts a provider.">editing</span>
|
|
15879
15945
|
<button id="select" type="button" title="Pick several records to generate, regenerate, compare, or tag together (or ctrl/\u2318-click a card)">Select</button>
|
|
15880
15946
|
<button id="studio" type="button" hidden title="Draft a new character: its style, base, loops, and portrait, priced as you go">+ New character</button>
|
|
15881
15947
|
<button id="refresh" type="button" title="Re-read the manifest, lockfile, and disk">Refresh</button>
|
|
15882
|
-
<
|
|
15883
|
-
<label class="chip" title="Play every loop on the grid; without this, a loop plays while the pointer is over it"><input id="playloops" type="checkbox"> play loops</label>
|
|
15884
|
-
<span id="backdrop-slot"></span>
|
|
15885
|
-
<label class="chip" id="notify-chip" hidden title="A system notification when a job finishes while this tab is in the background"><input id="notify" type="checkbox"> notify</label>
|
|
15948
|
+
<button id="keys" type="button" title="Keyboard shortcuts (?)" aria-label="Keyboard shortcuts">?</button>
|
|
15886
15949
|
</div>
|
|
15887
15950
|
<div class="totals" id="totals"></div>
|
|
15888
15951
|
<div class="chips" id="chips"></div>
|
|
@@ -15891,9 +15954,9 @@ ${css}</style>
|
|
|
15891
15954
|
</header>
|
|
15892
15955
|
<main id="root"></main>
|
|
15893
15956
|
<div id="selbar" class="selbar" hidden></div>
|
|
15957
|
+
<div id="toast" class="toast" role="status" aria-live="polite" hidden></div>
|
|
15894
15958
|
<footer>
|
|
15895
|
-
Click a sprite for its full record; <kbd>
|
|
15896
|
-
is open, <kbd>,</kbd>/<kbd>.</kbd> step a loop's frames and <kbd>space</kbd> plays it, <kbd>Esc</kbd> closes it, and <kbd>/</kbd> jumps to search. This page reads the manifest, lockfile,
|
|
15959
|
+
Click a sprite for its full record; press <kbd>?</kbd> for every keyboard shortcut. This page reads the manifest, lockfile,
|
|
15897
15960
|
and disk only. It never contacts a provider<span id="foot-edit"> and never writes anything</span><span id="foot-editing" hidden>.
|
|
15898
15961
|
Editing is on: saving rewrites the manifest and nothing else</span><span id="foot-gen" hidden>.
|
|
15899
15962
|
Generation is on under the session budget shown above; every run is the same submit, poll, and fetch as <code>pixelkiln gen</code></span>.
|
|
@@ -16095,7 +16158,7 @@ async function serveGallery(opts) {
|
|
|
16095
16158
|
const asset = reviewAssets.get(jobId)?.get(url2.pathname);
|
|
16096
16159
|
if (!asset) return fail(404, "no such review asset");
|
|
16097
16160
|
try {
|
|
16098
|
-
const bytes = await
|
|
16161
|
+
const bytes = await readFile20(asset.path);
|
|
16099
16162
|
res.writeHead(200, {
|
|
16100
16163
|
"Content-Type": asset.contentType,
|
|
16101
16164
|
"Cache-Control": "no-store",
|
|
@@ -16178,7 +16241,7 @@ async function serveGallery(opts) {
|
|
|
16178
16241
|
if (!asset) return fail(404, "no such gallery media");
|
|
16179
16242
|
let bytes;
|
|
16180
16243
|
try {
|
|
16181
|
-
bytes = await
|
|
16244
|
+
bytes = await readFile20(asset.path);
|
|
16182
16245
|
} catch {
|
|
16183
16246
|
return fail(404, "gallery media is no longer on disk; refresh the page");
|
|
16184
16247
|
}
|
|
@@ -16634,10 +16697,10 @@ function ownedReferenceImageId(spec, lock, providerId) {
|
|
|
16634
16697
|
|
|
16635
16698
|
// src/pick/server.ts
|
|
16636
16699
|
import { existsSync as existsSync24 } from "fs";
|
|
16637
|
-
import { readFile as
|
|
16700
|
+
import { readFile as readFile22 } from "fs/promises";
|
|
16638
16701
|
|
|
16639
16702
|
// src/pipeline/init.ts
|
|
16640
|
-
import { readdir, readFile as
|
|
16703
|
+
import { readdir, readFile as readFile21, writeFile as writeFile15 } from "fs/promises";
|
|
16641
16704
|
import { existsSync as existsSync23 } from "fs";
|
|
16642
16705
|
import path31 from "path";
|
|
16643
16706
|
function pngSize(buf) {
|
|
@@ -16667,7 +16730,7 @@ async function scanAssets(root, opts = {}) {
|
|
|
16667
16730
|
const skipped = [];
|
|
16668
16731
|
const seen = /* @__PURE__ */ new Map();
|
|
16669
16732
|
for (const file of files.sort()) {
|
|
16670
|
-
const size = pngSize(await
|
|
16733
|
+
const size = pngSize(await readFile21(file));
|
|
16671
16734
|
if (!size) {
|
|
16672
16735
|
skipped.push(`${file} (not a readable PNG)`);
|
|
16673
16736
|
continue;
|
|
@@ -16735,7 +16798,7 @@ async function writeManifestFile(target, manifest) {
|
|
|
16735
16798
|
if (existsSync23(target)) {
|
|
16736
16799
|
throw new Error(`${target} already exists; refusing to overwrite an existing manifest.`);
|
|
16737
16800
|
}
|
|
16738
|
-
await
|
|
16801
|
+
await writeFile15(target, JSON.stringify(manifest, null, 2) + "\n");
|
|
16739
16802
|
}
|
|
16740
16803
|
|
|
16741
16804
|
// src/pick/sheet.ts
|
|
@@ -17154,7 +17217,7 @@ async function currentArt(entry, spec) {
|
|
|
17154
17217
|
let width = entry.width;
|
|
17155
17218
|
let height = entry.height;
|
|
17156
17219
|
if (contentType === "image/png") {
|
|
17157
|
-
const size = pngSize(await
|
|
17220
|
+
const size = pngSize(await readFile22(file));
|
|
17158
17221
|
if (size) ({ width, height } = size);
|
|
17159
17222
|
}
|
|
17160
17223
|
return { path: file, contentType, width, height };
|
|
@@ -17687,13 +17750,13 @@ function createGenerateHandlers(opts) {
|
|
|
17687
17750
|
}
|
|
17688
17751
|
|
|
17689
17752
|
// src/gallery/editor.ts
|
|
17690
|
-
import { readFile as
|
|
17753
|
+
import { readFile as readFile24 } from "fs/promises";
|
|
17691
17754
|
import path33 from "path";
|
|
17692
17755
|
|
|
17693
17756
|
// src/editor/install.ts
|
|
17694
17757
|
import { createHash as createHash4 } from "crypto";
|
|
17695
17758
|
import { existsSync as existsSync25 } from "fs";
|
|
17696
|
-
import { mkdir as mkdir12, readFile as
|
|
17759
|
+
import { mkdir as mkdir12, readFile as readFile23, rename as rename10, rm as rm10, stat as stat3, writeFile as writeFile16 } from "fs/promises";
|
|
17697
17760
|
import os2 from "os";
|
|
17698
17761
|
import path32 from "path";
|
|
17699
17762
|
|
|
@@ -17777,7 +17840,7 @@ function editorBaseUrl(pin = EDITOR_PIN) {
|
|
|
17777
17840
|
}
|
|
17778
17841
|
async function sha256Of(file) {
|
|
17779
17842
|
try {
|
|
17780
|
-
return createHash4("sha256").update(await
|
|
17843
|
+
return createHash4("sha256").update(await readFile23(file)).digest("hex");
|
|
17781
17844
|
} catch {
|
|
17782
17845
|
return null;
|
|
17783
17846
|
}
|
|
@@ -17851,8 +17914,8 @@ async function installEditor(opts = {}) {
|
|
|
17851
17914
|
const final = path32.join(dir, name);
|
|
17852
17915
|
const tmp = `${final}.${process.pid}.part`;
|
|
17853
17916
|
try {
|
|
17854
|
-
await
|
|
17855
|
-
await
|
|
17917
|
+
await writeFile16(tmp, bytes);
|
|
17918
|
+
await rename10(tmp, final);
|
|
17856
17919
|
} finally {
|
|
17857
17920
|
await rm10(tmp, { force: true });
|
|
17858
17921
|
}
|
|
@@ -17970,7 +18033,7 @@ function createGalleryEditorHandlers(opts = {}) {
|
|
|
17970
18033
|
if (!status.installed) return null;
|
|
17971
18034
|
let bytes;
|
|
17972
18035
|
try {
|
|
17973
|
-
bytes = await
|
|
18036
|
+
bytes = await readFile24(path33.join(dir, name));
|
|
17974
18037
|
} catch {
|
|
17975
18038
|
verified = null;
|
|
17976
18039
|
return null;
|
|
@@ -18437,7 +18500,7 @@ import { existsSync as existsSync28 } from "fs";
|
|
|
18437
18500
|
|
|
18438
18501
|
// src/pipeline/cache-health.ts
|
|
18439
18502
|
import { existsSync as existsSync27 } from "fs";
|
|
18440
|
-
import { readFile as
|
|
18503
|
+
import { readFile as readFile25, readdir as readdir2, rm as rm11 } from "fs/promises";
|
|
18441
18504
|
import path35 from "path";
|
|
18442
18505
|
async function inspectCaches(lock, lockPath, options = {}) {
|
|
18443
18506
|
if (options.prune && !existsSync27(lockPath)) {
|
|
@@ -18465,7 +18528,7 @@ async function inspectCaches(lock, lockPath, options = {}) {
|
|
|
18465
18528
|
await saveCache(remotePath, { version: 1, hashes: {} });
|
|
18466
18529
|
removed.resetRemoteHashCache = true;
|
|
18467
18530
|
} else if (remoteHashes.invalidIds.length) {
|
|
18468
|
-
const cache = parseCache(JSON.parse(await
|
|
18531
|
+
const cache = parseCache(JSON.parse(await readFile25(remotePath, "utf8")));
|
|
18469
18532
|
for (const id of remoteHashes.invalidIds) delete cache.hashes[id];
|
|
18470
18533
|
removed.remoteHashEntries = remoteHashes.invalidIds.length;
|
|
18471
18534
|
await saveCache(remotePath, cache);
|
|
@@ -18509,7 +18572,7 @@ async function inspectContentCache(contentDir, referenced) {
|
|
|
18509
18572
|
const expected = mediaType ? entry.name.slice(0, -4) : "";
|
|
18510
18573
|
let bytes;
|
|
18511
18574
|
try {
|
|
18512
|
-
bytes = await
|
|
18575
|
+
bytes = await readFile25(file);
|
|
18513
18576
|
report.bytes += bytes.length;
|
|
18514
18577
|
} catch (err) {
|
|
18515
18578
|
report.invalid.push({
|
|
@@ -18556,7 +18619,7 @@ async function inspectRemoteHashCache(remotePath) {
|
|
|
18556
18619
|
if (!report.exists) return report;
|
|
18557
18620
|
let cache;
|
|
18558
18621
|
try {
|
|
18559
|
-
cache = parseCache(JSON.parse(await
|
|
18622
|
+
cache = parseCache(JSON.parse(await readFile25(remotePath, "utf8")));
|
|
18560
18623
|
} catch (err) {
|
|
18561
18624
|
report.error = err instanceof Error ? err.message : String(err);
|
|
18562
18625
|
return report;
|
|
@@ -18883,7 +18946,7 @@ async function nearestExistingDirectory(start) {
|
|
|
18883
18946
|
}
|
|
18884
18947
|
|
|
18885
18948
|
// src/pipeline/audit.ts
|
|
18886
|
-
import { readFile as
|
|
18949
|
+
import { readFile as readFile26 } from "fs/promises";
|
|
18887
18950
|
import { existsSync as existsSync31 } from "fs";
|
|
18888
18951
|
import path38 from "path";
|
|
18889
18952
|
function paletteDistance(asset, reference) {
|
|
@@ -18926,7 +18989,7 @@ async function auditStyle(loaded, specs, styleId, lock) {
|
|
|
18926
18989
|
continue;
|
|
18927
18990
|
}
|
|
18928
18991
|
try {
|
|
18929
|
-
const png = decodePng(await
|
|
18992
|
+
const png = decodePng(await readFile26(output.absolutePath));
|
|
18930
18993
|
const palette = extractPalette(png, 12);
|
|
18931
18994
|
assets.push({
|
|
18932
18995
|
assetId: spec.assetId,
|
|
@@ -18953,7 +19016,7 @@ async function auditStyle(loaded, specs, styleId, lock) {
|
|
|
18953
19016
|
const abs = path38.resolve(loaded.root, img.path);
|
|
18954
19017
|
if (!existsSync31(abs)) continue;
|
|
18955
19018
|
try {
|
|
18956
|
-
refPalettes.push(extractPalette(decodePng(await
|
|
19019
|
+
refPalettes.push(extractPalette(decodePng(await readFile26(abs)), 12));
|
|
18957
19020
|
} catch {
|
|
18958
19021
|
}
|
|
18959
19022
|
}
|
|
@@ -19182,7 +19245,7 @@ async function runAudit(args) {
|
|
|
19182
19245
|
|
|
19183
19246
|
// src/cli/commands/pack.ts
|
|
19184
19247
|
import path40 from "path";
|
|
19185
|
-
import { readFile as
|
|
19248
|
+
import { readFile as readFile27 } from "fs/promises";
|
|
19186
19249
|
|
|
19187
19250
|
// src/pipeline/pack.ts
|
|
19188
19251
|
import { readFileSync as readFileSync5 } from "fs";
|
|
@@ -19895,7 +19958,7 @@ async function runPack(args) {
|
|
|
19895
19958
|
if (args.primaryOnly || args.outputRoles.length) {
|
|
19896
19959
|
throw new Error("--primary-only and --output-role require manifest-driven pack");
|
|
19897
19960
|
}
|
|
19898
|
-
const raw = JSON.parse(await
|
|
19961
|
+
const raw = JSON.parse(await readFile27(path40.resolve(args.inputs), "utf8"));
|
|
19899
19962
|
const inputs = resolvePackInputs(raw, args.inputs);
|
|
19900
19963
|
const { png, atlas, skipped, sources } = packSprites(inputs, { columns: args.columns });
|
|
19901
19964
|
const format = sheetFormat(args);
|
|
@@ -20124,7 +20187,7 @@ import path42 from "path";
|
|
|
20124
20187
|
|
|
20125
20188
|
// src/pixellab-utils.ts
|
|
20126
20189
|
import { existsSync as existsSync32 } from "fs";
|
|
20127
|
-
import { mkdir as mkdir13, readFile as
|
|
20190
|
+
import { mkdir as mkdir13, readFile as readFile28, writeFile as writeFile17 } from "fs/promises";
|
|
20128
20191
|
import path41 from "path";
|
|
20129
20192
|
var UNZOOM_MIN_SIDE = 256;
|
|
20130
20193
|
var UNZOOM_MAX_AREA = 2048 * 2048;
|
|
@@ -20136,7 +20199,7 @@ async function unzoomFile(client2, input, opts = {}) {
|
|
|
20136
20199
|
const out = path41.resolve(opts.out ?? defaultUnzoomOut(input));
|
|
20137
20200
|
if (path41.resolve(input) === out) throw new Error("unzoom will not overwrite its own input; pass a different --out");
|
|
20138
20201
|
if (existsSync32(out) && !opts.force) throw new Error(`${out} already exists; pass --force to replace it`);
|
|
20139
|
-
const bytes = await
|
|
20202
|
+
const bytes = await readFile28(input);
|
|
20140
20203
|
const metadata = imageMetadata(bytes);
|
|
20141
20204
|
if (!metadata) throw new Error(`${input} is not a readable PNG or JPEG`);
|
|
20142
20205
|
const { width, height, format } = metadata;
|
|
@@ -20150,7 +20213,7 @@ async function unzoomFile(client2, input, opts = {}) {
|
|
|
20150
20213
|
}
|
|
20151
20214
|
const res = await client2.unzoom({ image: { base64: bytes.toString("base64"), format }, quantize: opts.quantize });
|
|
20152
20215
|
await mkdir13(path41.dirname(out), { recursive: true });
|
|
20153
|
-
await
|
|
20216
|
+
await writeFile17(out, res.png);
|
|
20154
20217
|
return { out, original: res.originalSize, unzoomed: res.unzoomedSize, zoomFactor: res.zoomFactor, usage: res.usage };
|
|
20155
20218
|
}
|
|
20156
20219
|
var FONT_WEIGHTS = ["Bold", "Regular"];
|
|
@@ -20192,8 +20255,8 @@ async function generateFont(client2, opts) {
|
|
|
20192
20255
|
client2.download(job.download_atlas_url)
|
|
20193
20256
|
]);
|
|
20194
20257
|
await mkdir13(path41.dirname(ttf), { recursive: true });
|
|
20195
|
-
await
|
|
20196
|
-
await
|
|
20258
|
+
await writeFile17(ttf, ttfBytes);
|
|
20259
|
+
await writeFile17(atlas, atlasBytes);
|
|
20197
20260
|
return { jobId, ttf, atlas, glyphPx: job.glyph_px ?? null, usage: job.usage };
|
|
20198
20261
|
}
|
|
20199
20262
|
if (Date.now() - started > timeout) {
|
|
@@ -20274,12 +20337,12 @@ async function runFont(args) {
|
|
|
20274
20337
|
|
|
20275
20338
|
// src/cli/commands/quality.ts
|
|
20276
20339
|
import path44 from "path";
|
|
20277
|
-
import { readFile as
|
|
20340
|
+
import { readFile as readFile30 } from "fs/promises";
|
|
20278
20341
|
|
|
20279
20342
|
// src/pipeline/quality-regression.ts
|
|
20280
20343
|
import path43 from "path";
|
|
20281
20344
|
import { existsSync as existsSync33 } from "fs";
|
|
20282
|
-
import { readFile as
|
|
20345
|
+
import { readFile as readFile29 } from "fs/promises";
|
|
20283
20346
|
import { z as z12 } from "zod";
|
|
20284
20347
|
var SHA256_RE = /^[0-9a-f]{64}$/;
|
|
20285
20348
|
var HEX_RE = /^#[0-9a-f]{6}$/;
|
|
@@ -20435,7 +20498,7 @@ async function measureImageQuality(file) {
|
|
|
20435
20498
|
const absolute = path43.resolve(file);
|
|
20436
20499
|
let bytes;
|
|
20437
20500
|
try {
|
|
20438
|
-
bytes = await
|
|
20501
|
+
bytes = await readFile29(absolute);
|
|
20439
20502
|
} catch (error) {
|
|
20440
20503
|
throw new Error(`Cannot read quality image ${absolute}: ${message4(error)}`, { cause: error });
|
|
20441
20504
|
}
|
|
@@ -20522,7 +20585,7 @@ async function snapshotQualityBaseline(inputs, baselinePath, options = {}) {
|
|
|
20522
20585
|
});
|
|
20523
20586
|
const data = Buffer.from(JSON.stringify(baseline, null, 2) + "\n");
|
|
20524
20587
|
if (existsSync33(absolute) && !options.force) {
|
|
20525
|
-
const current = await
|
|
20588
|
+
const current = await readFile29(absolute);
|
|
20526
20589
|
if (!current.equals(data)) {
|
|
20527
20590
|
throw new OverwriteRefusedError(
|
|
20528
20591
|
`Quality baseline already exists with different content: ${absolute}. Pass --force to replace it.`,
|
|
@@ -20537,7 +20600,7 @@ async function readQualityBaseline(baselinePath) {
|
|
|
20537
20600
|
const absolute = path43.resolve(baselinePath);
|
|
20538
20601
|
let raw;
|
|
20539
20602
|
try {
|
|
20540
|
-
raw = JSON.parse(await
|
|
20603
|
+
raw = JSON.parse(await readFile29(absolute, "utf8"));
|
|
20541
20604
|
} catch (error) {
|
|
20542
20605
|
throw new Error(`Could not read quality baseline ${absolute}: ${message4(error)}`, { cause: error });
|
|
20543
20606
|
}
|
|
@@ -20667,7 +20730,7 @@ async function runQuality(args) {
|
|
|
20667
20730
|
if (!args.out) throw new Error("quality snapshot needs --out <pixelkiln.quality.json>.");
|
|
20668
20731
|
let raw;
|
|
20669
20732
|
try {
|
|
20670
|
-
raw = JSON.parse(await
|
|
20733
|
+
raw = JSON.parse(await readFile30(path44.resolve(args.inputs), "utf8"));
|
|
20671
20734
|
} catch (error) {
|
|
20672
20735
|
throw new Error(
|
|
20673
20736
|
`Could not read quality inputs ${path44.resolve(args.inputs)}: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -20719,7 +20782,7 @@ import path46 from "path";
|
|
|
20719
20782
|
// src/recipes.ts
|
|
20720
20783
|
import path45 from "path";
|
|
20721
20784
|
import { existsSync as existsSync34 } from "fs";
|
|
20722
|
-
import { readdir as readdir3, readFile as
|
|
20785
|
+
import { readdir as readdir3, readFile as readFile31 } from "fs/promises";
|
|
20723
20786
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
20724
20787
|
import { z as z13 } from "zod";
|
|
20725
20788
|
var SHA256_RE2 = /^[0-9a-f]{64}$/;
|
|
@@ -20907,7 +20970,7 @@ async function readRecipeFile(recipePath, bundled) {
|
|
|
20907
20970
|
const absolute = path45.resolve(recipePath);
|
|
20908
20971
|
let raw;
|
|
20909
20972
|
try {
|
|
20910
|
-
raw = JSON.parse(await
|
|
20973
|
+
raw = JSON.parse(await readFile31(absolute, "utf8"));
|
|
20911
20974
|
} catch (error) {
|
|
20912
20975
|
throw new Error(`Could not read recipe ${absolute}: ${message5(error)}`, { cause: error });
|
|
20913
20976
|
}
|
|
@@ -21049,12 +21112,12 @@ async function installRecipe(target, options = {}) {
|
|
|
21049
21112
|
];
|
|
21050
21113
|
const files = await Promise.all(sources.map(async (file) => ({
|
|
21051
21114
|
path: path45.join(destination, ...file.path.split("/")),
|
|
21052
|
-
data: await
|
|
21115
|
+
data: await readFile31(file.source)
|
|
21053
21116
|
})));
|
|
21054
21117
|
if (!options.force) {
|
|
21055
21118
|
for (const file of files) {
|
|
21056
21119
|
if (!existsSync34(file.path)) continue;
|
|
21057
|
-
const current = await
|
|
21120
|
+
const current = await readFile31(file.path);
|
|
21058
21121
|
if (!current.equals(file.data)) {
|
|
21059
21122
|
throw new OverwriteRefusedError(
|
|
21060
21123
|
`Recipe destination has local changes: ${file.path}. Choose another --out or pass --force to replace declared recipe files.`,
|
|
@@ -21294,7 +21357,7 @@ async function runRefine(args) {
|
|
|
21294
21357
|
// src/cli/commands/skeleton.ts
|
|
21295
21358
|
import path48 from "path";
|
|
21296
21359
|
import { existsSync as existsSync35 } from "fs";
|
|
21297
|
-
import { readFile as
|
|
21360
|
+
import { readFile as readFile32, writeFile as writeFile18 } from "fs/promises";
|
|
21298
21361
|
|
|
21299
21362
|
// src/skeleton-preview.ts
|
|
21300
21363
|
var RIGHT = [255, 140, 40, 255];
|
|
@@ -21410,7 +21473,7 @@ async function runEstimateSkeleton(args) {
|
|
|
21410
21473
|
}
|
|
21411
21474
|
loadEnvFiles(path48.dirname(path48.resolve(args.manifest)));
|
|
21412
21475
|
loadEnvFiles(process.cwd());
|
|
21413
|
-
const bytes = await
|
|
21476
|
+
const bytes = await readFile32(path48.resolve(image));
|
|
21414
21477
|
const metadata = imageMetadata(bytes);
|
|
21415
21478
|
if (!metadata) throw new UsageError(`${image} is not a readable PNG or JPEG`);
|
|
21416
21479
|
const sizeProblem = estimateSkeletonSizeProblem(metadata.width, metadata.height);
|
|
@@ -21422,7 +21485,7 @@ async function runEstimateSkeleton(args) {
|
|
|
21422
21485
|
const frames = args.frames ?? DEFAULT_SCAFFOLD_FRAMES;
|
|
21423
21486
|
const json = JSON.stringify(scaffoldSkeletonSet(res.keypoints, frames), null, 2);
|
|
21424
21487
|
if (args.out) {
|
|
21425
|
-
await
|
|
21488
|
+
await writeFile18(path48.resolve(args.out), json + "\n");
|
|
21426
21489
|
log(` wrote ${args.out}: the estimated pose and ${frames} frames to edit`);
|
|
21427
21490
|
log(` preview it with: pixelkiln skeleton-preview ${args.out} --from ${image}`);
|
|
21428
21491
|
} else {
|
|
@@ -21454,7 +21517,7 @@ async function runSkeletonPreview(args) {
|
|
|
21454
21517
|
if (!existsSync35(keypointsFile)) throw new UsageError(`Keypoints file does not exist yet: ${keypointsFile}`);
|
|
21455
21518
|
let json;
|
|
21456
21519
|
try {
|
|
21457
|
-
json = JSON.parse(await
|
|
21520
|
+
json = JSON.parse(await readFile32(keypointsFile, "utf8"));
|
|
21458
21521
|
} catch (error) {
|
|
21459
21522
|
throw new UsageError(`${keypointsFile} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
21460
21523
|
}
|
|
@@ -21464,13 +21527,13 @@ async function runSkeletonPreview(args) {
|
|
|
21464
21527
|
if (!existsSync35(sourceFile)) {
|
|
21465
21528
|
log(` source image does not exist yet, drawing on a plain ground: ${sourceFile}`);
|
|
21466
21529
|
} else {
|
|
21467
|
-
const bytes = await
|
|
21530
|
+
const bytes = await readFile32(sourceFile);
|
|
21468
21531
|
if (imageMetadata(bytes)?.format !== "png") throw new UsageError(`${sourceFile} must be a PNG to preview over`);
|
|
21469
21532
|
source = decodePng(bytes);
|
|
21470
21533
|
}
|
|
21471
21534
|
}
|
|
21472
21535
|
const out = path48.resolve(args.out ?? keypointsFile.replace(/\.json$/, "") + ".preview.png");
|
|
21473
|
-
await
|
|
21536
|
+
await writeFile18(out, renderSkeletonSheet(set, { source }));
|
|
21474
21537
|
log(` ${label}: ${set.frames.length} frames after the starting pose`);
|
|
21475
21538
|
const shown = path48.relative(process.cwd(), out);
|
|
21476
21539
|
log(` wrote ${shown.startsWith("..") ? out : shown}`);
|
|
@@ -21656,7 +21719,7 @@ async function runVersion() {
|
|
|
21656
21719
|
for (const up of ["../package.json", "../../package.json"]) {
|
|
21657
21720
|
const url = new URL(up, import.meta.url);
|
|
21658
21721
|
if (!existsSync37(url)) continue;
|
|
21659
|
-
const pkg = JSON.parse(await
|
|
21722
|
+
const pkg = JSON.parse(await readFile33(url, "utf8"));
|
|
21660
21723
|
if (pkg.name === "pixelkiln") {
|
|
21661
21724
|
log(`${pkg.name} ${pkg.version}`);
|
|
21662
21725
|
return;
|