pixelkiln 0.29.0 → 0.31.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/README.md +44 -44
- package/dist/cli.js +162 -119
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +537 -142
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +416 -78
- package/dist/index.d.ts +416 -78
- package/dist/index.js +502 -133
- package/dist/index.js.map +1 -1
- package/docs/AGENTS.md +5 -0
- package/docs/ARCHITECTURE.md +14 -0
- package/docs/CLI.md +14 -10
- package/docs/GETTING_STARTED.md +46 -3
- package/docs/LIBRARY.md +48 -2
- package/docs/MANIFEST.md +2 -2
- package/docs/README.md +3 -3
- package/docs/TILES.md +3 -1
- package/package.json +1 -1
- package/skills/pixelkiln/SKILL.md +6 -2
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import path30 from "path";
|
|
5
|
-
import { existsSync as
|
|
5
|
+
import { existsSync as existsSync24 } from "fs";
|
|
6
6
|
import { readFile as readFile25 } from "fs/promises";
|
|
7
7
|
|
|
8
8
|
// src/env.ts
|
|
@@ -3478,10 +3478,11 @@ registerProvider({
|
|
|
3478
3478
|
|
|
3479
3479
|
// src/manifest.ts
|
|
3480
3480
|
import { readFile as readFile3 } from "fs/promises";
|
|
3481
|
-
import { existsSync as
|
|
3481
|
+
import { existsSync as existsSync5 } from "fs";
|
|
3482
3482
|
import path6 from "path";
|
|
3483
3483
|
|
|
3484
3484
|
// src/outputs.ts
|
|
3485
|
+
import { existsSync as existsSync4 } from "fs";
|
|
3485
3486
|
import path5 from "path";
|
|
3486
3487
|
|
|
3487
3488
|
// src/lock.ts
|
|
@@ -3678,12 +3679,15 @@ function memberPath(file, role, index, total, mediaType) {
|
|
|
3678
3679
|
const safeRole = (role ?? fallbackOutputRole(index)).replace(/[^a-zA-Z0-9_-]+/g, "-");
|
|
3679
3680
|
return `${stem}-${safeRole}${ext}`;
|
|
3680
3681
|
}
|
|
3681
|
-
function
|
|
3682
|
-
return entry.
|
|
3682
|
+
function isMemberSetEntry(entry) {
|
|
3683
|
+
return entry.outputs.length > 1 && entry.outputs.every((output) => !output.mediaType || output.mediaType === "image/png");
|
|
3684
|
+
}
|
|
3685
|
+
function sourceIsStem(source, entry, manifestDir) {
|
|
3686
|
+
return isMemberSetEntry(entry) && !existsSync4(path5.resolve(manifestDir, source));
|
|
3683
3687
|
}
|
|
3684
3688
|
function sourceOutputPath(source, entry, index, manifestDir) {
|
|
3685
3689
|
const absolute = path5.resolve(manifestDir, source);
|
|
3686
|
-
if (!
|
|
3690
|
+
if (!sourceIsStem(source, entry, manifestDir)) return absolute;
|
|
3687
3691
|
const output = entry.outputs[index];
|
|
3688
3692
|
return memberPath(absolute, output.role, index, entry.outputs.length, output.mediaType);
|
|
3689
3693
|
}
|
|
@@ -3771,7 +3775,7 @@ function resolveSpecOutputs(spec, lock, manifestDir) {
|
|
|
3771
3775
|
// src/manifest.ts
|
|
3772
3776
|
async function loadManifest(manifestPath) {
|
|
3773
3777
|
const abs = path6.resolve(manifestPath);
|
|
3774
|
-
if (!
|
|
3778
|
+
if (!existsSync5(abs)) throw new Error(`No manifest at ${abs}`);
|
|
3775
3779
|
const input = ManifestInputSchema.safeParse(JSON.parse(await readFile3(abs, "utf8")));
|
|
3776
3780
|
if (!input.success) {
|
|
3777
3781
|
const issues = formatManifestIssues(input.error.issues);
|
|
@@ -3943,7 +3947,7 @@ async function resolveSpecs(loaded, filter) {
|
|
|
3943
3947
|
const abs = path6.resolve(root, rel);
|
|
3944
3948
|
let hit = styleImageCache.get(abs);
|
|
3945
3949
|
if (!hit) {
|
|
3946
|
-
if (!
|
|
3950
|
+
if (!existsSync5(abs)) throw new Error(`Style image not found: ${abs}`);
|
|
3947
3951
|
const buf = await readFile3(abs);
|
|
3948
3952
|
const metadata = imageMetadata(buf);
|
|
3949
3953
|
if (!metadata) throw new Error(`Style image is not a readable PNG or JPEG: ${abs}`);
|
|
@@ -4184,7 +4188,7 @@ function pngPath(file) {
|
|
|
4184
4188
|
return extension ? `${file.slice(0, -extension.length)}.png` : `${file}.png`;
|
|
4185
4189
|
}
|
|
4186
4190
|
async function optionalRevisionImage(file, label, requiredFormat) {
|
|
4187
|
-
if (!
|
|
4191
|
+
if (!existsSync5(file)) return null;
|
|
4188
4192
|
const bytes = await readFile3(file);
|
|
4189
4193
|
const metadata = imageMetadata(bytes);
|
|
4190
4194
|
if (!metadata) throw new Error(`${label} is not a readable PNG or JPEG: ${file}`);
|
|
@@ -4349,7 +4353,7 @@ function packStyle(lock, styleId, manifestDir, options = {}) {
|
|
|
4349
4353
|
inputs.push({ id, path: path7.resolve(manifestDir, source) });
|
|
4350
4354
|
continue;
|
|
4351
4355
|
}
|
|
4352
|
-
if (source &&
|
|
4356
|
+
if (source && sourceIsStem(source, entry, manifestDir)) {
|
|
4353
4357
|
outputs = outputs.map((output) => ({ ...output, absolutePath: sourceOutputPath(source, entry, output.index, manifestDir) }));
|
|
4354
4358
|
}
|
|
4355
4359
|
let selected = outputs;
|
|
@@ -4474,7 +4478,7 @@ function mountStyle(lock, styleId, manifestDir, mount, cells, sources = {}, outp
|
|
|
4474
4478
|
const cell = cells[id];
|
|
4475
4479
|
const source = sources[id];
|
|
4476
4480
|
const entry = lock.entries[`${styleId}/${id}`];
|
|
4477
|
-
if (source && !(entry &&
|
|
4481
|
+
if (source && !(entry && sourceIsStem(source, entry, manifestDir))) {
|
|
4478
4482
|
placements.push({ id, path: path7.resolve(manifestDir, source), cell });
|
|
4479
4483
|
continue;
|
|
4480
4484
|
}
|
|
@@ -4528,11 +4532,11 @@ function mountStyle(lock, styleId, manifestDir, mount, cells, sources = {}, outp
|
|
|
4528
4532
|
}
|
|
4529
4533
|
|
|
4530
4534
|
// src/pipeline/plan.ts
|
|
4531
|
-
import { existsSync as
|
|
4535
|
+
import { existsSync as existsSync9 } from "fs";
|
|
4532
4536
|
import path13 from "path";
|
|
4533
4537
|
|
|
4534
4538
|
// src/pipeline/quality-profile.ts
|
|
4535
|
-
import { existsSync as
|
|
4539
|
+
import { existsSync as existsSync7 } from "fs";
|
|
4536
4540
|
import path11 from "path";
|
|
4537
4541
|
|
|
4538
4542
|
// src/pipeline/refine.ts
|
|
@@ -5000,7 +5004,7 @@ async function writeArtifactBundle(files, options = {}) {
|
|
|
5000
5004
|
|
|
5001
5005
|
// src/pipeline/audit.ts
|
|
5002
5006
|
import { readFile as readFile5 } from "fs/promises";
|
|
5003
|
-
import { existsSync as
|
|
5007
|
+
import { existsSync as existsSync6 } from "fs";
|
|
5004
5008
|
import path9 from "path";
|
|
5005
5009
|
function colorDistance(a, b) {
|
|
5006
5010
|
const rmean = (a.r + b.r) / 2;
|
|
@@ -5046,7 +5050,7 @@ async function auditStyle(loaded, specs, styleId, lock) {
|
|
|
5046
5050
|
const unreadable = [];
|
|
5047
5051
|
for (const spec of mine) {
|
|
5048
5052
|
for (const output of resolveSpecOutputs(spec, lock, loaded.root)) {
|
|
5049
|
-
if (!
|
|
5053
|
+
if (!existsSync6(output.absolutePath)) {
|
|
5050
5054
|
missing.push(output.id);
|
|
5051
5055
|
continue;
|
|
5052
5056
|
}
|
|
@@ -5076,7 +5080,7 @@ async function auditStyle(loaded, specs, styleId, lock) {
|
|
|
5076
5080
|
const refPalettes = [];
|
|
5077
5081
|
for (const img of style.styleImages) {
|
|
5078
5082
|
const abs = path9.resolve(loaded.root, img.path);
|
|
5079
|
-
if (!
|
|
5083
|
+
if (!existsSync6(abs)) continue;
|
|
5080
5084
|
try {
|
|
5081
5085
|
refPalettes.push(extractPalette(decodePng(await readFile5(abs)), 12));
|
|
5082
5086
|
} catch {
|
|
@@ -5705,7 +5709,7 @@ async function qualitySource(spec, lock) {
|
|
|
5705
5709
|
return { items: null, reason: "frame-set quality requires ordered downloaded provider outputs" };
|
|
5706
5710
|
}
|
|
5707
5711
|
const source = path11.resolve(spec.root, spec.source);
|
|
5708
|
-
return
|
|
5712
|
+
return existsSync7(source) ? { items: [{ role: "source", path: source }] } : { items: null, reason: `declared source is missing: ${spec.source}` };
|
|
5709
5713
|
}
|
|
5710
5714
|
const entry = lock.entries[lockKey(spec.styleId, spec.assetId)];
|
|
5711
5715
|
if (!entry) return { items: null, reason: "raw provider output is not in the lockfile" };
|
|
@@ -5734,7 +5738,7 @@ async function qualitySource(spec, lock) {
|
|
|
5734
5738
|
}
|
|
5735
5739
|
if (role) roles.add(role);
|
|
5736
5740
|
const source = currentEntryOutputPath(entry, spec, index);
|
|
5737
|
-
if (!
|
|
5741
|
+
if (!existsSync7(source)) return { items: null, reason: `raw provider output is missing: ${source}` };
|
|
5738
5742
|
if (await sha256File(source) !== output.sha256) {
|
|
5739
5743
|
return { items: null, reason: "raw provider output was modified after download" };
|
|
5740
5744
|
}
|
|
@@ -5753,7 +5757,7 @@ async function inspectQualityProfile(spec, lock) {
|
|
|
5753
5757
|
}
|
|
5754
5758
|
const sourcePaths = source.items.map((item) => item.path);
|
|
5755
5759
|
const expectedOutputs = spec.generator === "frames" ? source.items.map((item, index) => qualityFrameOutputPath(output, item.role, index)) : [output];
|
|
5756
|
-
if (!
|
|
5760
|
+
if (!existsSync7(record)) {
|
|
5757
5761
|
return {
|
|
5758
5762
|
key,
|
|
5759
5763
|
state: "needs-refinement",
|
|
@@ -5934,7 +5938,7 @@ async function requireApprovedQualitySources(specs, lock, assetIds, outputRoles)
|
|
|
5934
5938
|
}
|
|
5935
5939
|
|
|
5936
5940
|
// src/pipeline/revision.ts
|
|
5937
|
-
import { existsSync as
|
|
5941
|
+
import { existsSync as existsSync8 } from "fs";
|
|
5938
5942
|
import path12 from "path";
|
|
5939
5943
|
async function inspectRevisionReadiness(spec, lock) {
|
|
5940
5944
|
if (!spec.revision) return null;
|
|
@@ -5950,14 +5954,14 @@ async function inspectRevision(spec, lock, seen) {
|
|
|
5950
5954
|
reason: `revision source ${spec.styleId}/${revision.sourceAssetId} is not ready: ${dependency.reason}`
|
|
5951
5955
|
};
|
|
5952
5956
|
}
|
|
5953
|
-
if (!revision.sourceSha256 || !
|
|
5957
|
+
if (!revision.sourceSha256 || !existsSync8(revision.sourceFile)) {
|
|
5954
5958
|
return { ready: false, reason: `revision source is missing: ${revision.sourceFile}` };
|
|
5955
5959
|
}
|
|
5956
5960
|
if (await sha256File(revision.sourceFile) !== revision.sourceSha256) {
|
|
5957
5961
|
return { ready: false, reason: "revision source changed after the manifest was resolved" };
|
|
5958
5962
|
}
|
|
5959
5963
|
if (revision.maskFile) {
|
|
5960
|
-
if (!revision.maskSha256 || !
|
|
5964
|
+
if (!revision.maskSha256 || !existsSync8(revision.maskFile)) {
|
|
5961
5965
|
return { ready: false, reason: `revision mask is missing: ${revision.maskFile}` };
|
|
5962
5966
|
}
|
|
5963
5967
|
if (await sha256File(revision.maskFile) !== revision.maskSha256) {
|
|
@@ -5988,7 +5992,7 @@ async function inspectParent(spec, lock, seen) {
|
|
|
5988
5992
|
}
|
|
5989
5993
|
if (spec.source) {
|
|
5990
5994
|
const source = path12.resolve(spec.root, spec.source);
|
|
5991
|
-
return
|
|
5995
|
+
return existsSync8(source) ? { ready: true, reason: "declared source is present" } : { ready: false, reason: `declared source is missing: ${spec.source}` };
|
|
5992
5996
|
}
|
|
5993
5997
|
const entry = lock.entries[key];
|
|
5994
5998
|
if (!entry) return { ready: false, reason: "parent is not in the lockfile" };
|
|
@@ -6002,7 +6006,7 @@ async function inspectParent(spec, lock, seen) {
|
|
|
6002
6006
|
for (let index = 0; index < entry.outputs.length; index++) {
|
|
6003
6007
|
const output = entry.outputs[index];
|
|
6004
6008
|
const file = currentEntryOutputPath(entry, spec, index);
|
|
6005
|
-
if (!
|
|
6009
|
+
if (!existsSync8(file)) return { ready: false, reason: `parent output is missing: ${file}` };
|
|
6006
6010
|
if (await sha256File(file) !== output.sha256) {
|
|
6007
6011
|
return { ready: false, reason: `parent output was modified: ${file}` };
|
|
6008
6012
|
}
|
|
@@ -6057,7 +6061,7 @@ async function buildPlan(specs, lock, opts = {}) {
|
|
|
6057
6061
|
state = "missing";
|
|
6058
6062
|
reason = "--force";
|
|
6059
6063
|
} else if (!entry && spec.source && spec.generator !== "frames") {
|
|
6060
|
-
if (
|
|
6064
|
+
if (existsSync9(path13.resolve(spec.root, spec.source))) {
|
|
6061
6065
|
state = "ok";
|
|
6062
6066
|
reason = `placed from ${spec.source}; not generated`;
|
|
6063
6067
|
} else {
|
|
@@ -6065,7 +6069,7 @@ async function buildPlan(specs, lock, opts = {}) {
|
|
|
6065
6069
|
reason = `declared source is missing: ${spec.source}`;
|
|
6066
6070
|
}
|
|
6067
6071
|
} else if (!entry) {
|
|
6068
|
-
if (
|
|
6072
|
+
if (existsSync9(spec.outFile)) {
|
|
6069
6073
|
state = "untracked";
|
|
6070
6074
|
reason = "file on disk, no provenance recorded";
|
|
6071
6075
|
} else {
|
|
@@ -6097,9 +6101,9 @@ async function buildPlan(specs, lock, opts = {}) {
|
|
|
6097
6101
|
} else if (entry.status !== "downloaded") {
|
|
6098
6102
|
state = "in-flight";
|
|
6099
6103
|
reason = `awaiting ${entry.status}`;
|
|
6100
|
-
} else if (entry.outputs.length === 0 || entry.outputs.some((_output, index) => !
|
|
6104
|
+
} else if (entry.outputs.length === 0 || entry.outputs.some((_output, index) => !existsSync9(currentEntryOutputPath(entry, spec, index)))) {
|
|
6101
6105
|
state = "orphaned";
|
|
6102
|
-
reason = entry.outputs.length === 0 ? "no recorded output files" : `missing on disk: ${entry.outputs.filter((_output, index) => !
|
|
6106
|
+
reason = entry.outputs.length === 0 ? "no recorded output files" : `missing on disk: ${entry.outputs.filter((_output, index) => !existsSync9(currentEntryOutputPath(entry, spec, index))).map((o) => o.role ?? "asset").join(", ")}`;
|
|
6103
6107
|
} else if (await anyOutputModified(entry, spec)) {
|
|
6104
6108
|
state = "orphaned";
|
|
6105
6109
|
reason = "output modified since download";
|
|
@@ -6427,7 +6431,7 @@ async function poll(provider, lock, lockPath, opts = {}) {
|
|
|
6427
6431
|
}
|
|
6428
6432
|
|
|
6429
6433
|
// src/pipeline/fetch.ts
|
|
6430
|
-
import { existsSync as
|
|
6434
|
+
import { existsSync as existsSync10 } from "fs";
|
|
6431
6435
|
import { mkdir as mkdir3, readFile as readFile7, rename as rename3, rm as rm4, writeFile as writeFile3 } from "fs/promises";
|
|
6432
6436
|
import path14 from "path";
|
|
6433
6437
|
|
|
@@ -6483,7 +6487,7 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
|
6483
6487
|
if (opts.refresh) return e.outputs.length > 0 && Boolean(e.sourceUrls?.length || e.sourceUrl);
|
|
6484
6488
|
if (!opts.repair) return false;
|
|
6485
6489
|
const spec = specByKey.get(key);
|
|
6486
|
-
return e.outputs.length === 0 || !spec || e.outputs.some((output) => !
|
|
6490
|
+
return e.outputs.length === 0 || !spec || e.outputs.some((output) => !existsSync10(resolveOutputPath(output.path, spec.root)));
|
|
6487
6491
|
});
|
|
6488
6492
|
const concurrency = Math.min(Math.max(1, opts.concurrency ?? 8), Math.max(1, pending.length));
|
|
6489
6493
|
let cursor = 0;
|
|
@@ -6531,7 +6535,7 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
|
6531
6535
|
continue;
|
|
6532
6536
|
}
|
|
6533
6537
|
}
|
|
6534
|
-
if (
|
|
6538
|
+
if (existsSync10(target)) {
|
|
6535
6539
|
const currentHash = await sha256File(target);
|
|
6536
6540
|
if (recorded && currentHash === recorded.sha256) {
|
|
6537
6541
|
if (!fresh) {
|
|
@@ -6642,7 +6646,7 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
|
|
|
6642
6646
|
}
|
|
6643
6647
|
async function readCachedMedia(cacheDir, hash, mediaType) {
|
|
6644
6648
|
const file = path14.join(cacheDir, cacheFileName(hash, mediaType));
|
|
6645
|
-
if (!
|
|
6649
|
+
if (!existsSync10(file)) return null;
|
|
6646
6650
|
try {
|
|
6647
6651
|
const buf = await readFile7(file);
|
|
6648
6652
|
if (sha256(buf) !== hash) return null;
|
|
@@ -6663,7 +6667,7 @@ async function cacheMedia(cacheDir, buf, mediaType, knownHash) {
|
|
|
6663
6667
|
try {
|
|
6664
6668
|
await rename3(tmp, file);
|
|
6665
6669
|
} catch (err) {
|
|
6666
|
-
if (!
|
|
6670
|
+
if (!existsSync10(file)) throw err;
|
|
6667
6671
|
} finally {
|
|
6668
6672
|
await rm4(tmp, { force: true });
|
|
6669
6673
|
}
|
|
@@ -6696,7 +6700,7 @@ async function pushTags(provider, specs, lock, opts = {}) {
|
|
|
6696
6700
|
}
|
|
6697
6701
|
|
|
6698
6702
|
// src/pipeline/doctor.ts
|
|
6699
|
-
import { constants, existsSync as
|
|
6703
|
+
import { constants, existsSync as existsSync11 } from "fs";
|
|
6700
6704
|
import { access, stat as stat2 } from "fs/promises";
|
|
6701
6705
|
import path15 from "path";
|
|
6702
6706
|
async function doctor(loaded, specs, lock, lockPath, opts = {}) {
|
|
@@ -6768,7 +6772,7 @@ async function doctor(loaded, specs, lock, lockPath, opts = {}) {
|
|
|
6768
6772
|
cacheDir,
|
|
6769
6773
|
cacheFileName(output.sha256, output.mediaType ?? MediaType.PNG)
|
|
6770
6774
|
);
|
|
6771
|
-
if (
|
|
6775
|
+
if (existsSync11(cached) && await sha256File(cached) === output.sha256) {
|
|
6772
6776
|
hasCache = true;
|
|
6773
6777
|
break;
|
|
6774
6778
|
}
|
|
@@ -6871,13 +6875,13 @@ async function nearestExistingDirectory(start) {
|
|
|
6871
6875
|
}
|
|
6872
6876
|
|
|
6873
6877
|
// src/pipeline/adopt.ts
|
|
6874
|
-
import { existsSync as
|
|
6878
|
+
import { existsSync as existsSync13 } from "fs";
|
|
6875
6879
|
import { readFile as readFile9, writeFile as writeFile5 } from "fs/promises";
|
|
6876
6880
|
import path16 from "path";
|
|
6877
6881
|
|
|
6878
6882
|
// src/cache.ts
|
|
6879
6883
|
import { mkdir as mkdir4, readFile as readFile8, rename as rename4, rm as rm5, writeFile as writeFile4 } from "fs/promises";
|
|
6880
|
-
import { existsSync as
|
|
6884
|
+
import { existsSync as existsSync12 } from "fs";
|
|
6881
6885
|
import pathModule from "path";
|
|
6882
6886
|
import { z as z3 } from "zod";
|
|
6883
6887
|
var HashCacheSchema = z3.object({
|
|
@@ -6893,7 +6897,7 @@ function parseCache(value) {
|
|
|
6893
6897
|
return HashCacheSchema.parse(value);
|
|
6894
6898
|
}
|
|
6895
6899
|
async function loadCache(path31) {
|
|
6896
|
-
if (!
|
|
6900
|
+
if (!existsSync12(path31)) return { version: 1, hashes: {} };
|
|
6897
6901
|
try {
|
|
6898
6902
|
const parsed = HashCacheSchema.safeParse(JSON.parse(await readFile8(path31, "utf8")));
|
|
6899
6903
|
if (!parsed.success) return { version: 1, hashes: {} };
|
|
@@ -6949,7 +6953,7 @@ async function adopt(provider, specs, lock, lockPath, opts = {}) {
|
|
|
6949
6953
|
const localByHash = /* @__PURE__ */ new Map();
|
|
6950
6954
|
const unmatchedLocal = [];
|
|
6951
6955
|
for (const spec of specs) {
|
|
6952
|
-
if (!
|
|
6956
|
+
if (!existsSync13(spec.outFile)) {
|
|
6953
6957
|
unmatchedLocal.push(`${spec.styleId}/${spec.assetId} (no file at ${spec.outFile})`);
|
|
6954
6958
|
continue;
|
|
6955
6959
|
}
|
|
@@ -7106,12 +7110,12 @@ function formatUnmatchedRemote(objects, limit = 15) {
|
|
|
7106
7110
|
}
|
|
7107
7111
|
|
|
7108
7112
|
// src/pick/server.ts
|
|
7109
|
-
import { existsSync as
|
|
7113
|
+
import { existsSync as existsSync15 } from "fs";
|
|
7110
7114
|
import { readFile as readFile12 } from "fs/promises";
|
|
7111
7115
|
|
|
7112
7116
|
// src/pipeline/init.ts
|
|
7113
7117
|
import { readdir, readFile as readFile10, writeFile as writeFile6 } from "fs/promises";
|
|
7114
|
-
import { existsSync as
|
|
7118
|
+
import { existsSync as existsSync14 } from "fs";
|
|
7115
7119
|
import path17 from "path";
|
|
7116
7120
|
function pngSize(buf) {
|
|
7117
7121
|
if (buf.length < 24) return null;
|
|
@@ -7204,7 +7208,7 @@ function mode(values) {
|
|
|
7204
7208
|
return [...counts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? 64;
|
|
7205
7209
|
}
|
|
7206
7210
|
async function writeManifestFile(target, manifest) {
|
|
7207
|
-
if (
|
|
7211
|
+
if (existsSync14(target)) {
|
|
7208
7212
|
throw new Error(`${target} already exists \u2014 refusing to overwrite an existing manifest.`);
|
|
7209
7213
|
}
|
|
7210
7214
|
await writeFile6(target, JSON.stringify(manifest, null, 2) + "\n");
|
|
@@ -7690,7 +7694,7 @@ async function currentArt(entry, spec) {
|
|
|
7690
7694
|
const previous = primaryOutput({ ...entry, outputs: entry.supersededOutputs });
|
|
7691
7695
|
if (!previous) return null;
|
|
7692
7696
|
const file = resolveOutputPath(previous.path, spec.root);
|
|
7693
|
-
if (!
|
|
7697
|
+
if (!existsSync15(file)) return null;
|
|
7694
7698
|
const contentType = previous.mediaType ?? mediaTypeFromExtension(file);
|
|
7695
7699
|
if (!contentType) return null;
|
|
7696
7700
|
let width = entry.width;
|
|
@@ -7861,13 +7865,13 @@ async function runPicker(provider, lock, lockPath, opts = {}) {
|
|
|
7861
7865
|
|
|
7862
7866
|
// src/gallery/snapshot.ts
|
|
7863
7867
|
import { createHash as createHash3 } from "crypto";
|
|
7864
|
-
import { existsSync as
|
|
7868
|
+
import { existsSync as existsSync18 } from "fs";
|
|
7865
7869
|
import { readFile as readFile16, stat as stat3 } from "fs/promises";
|
|
7866
7870
|
import path22 from "path";
|
|
7867
7871
|
|
|
7868
7872
|
// src/workspace.ts
|
|
7869
7873
|
import { mkdir as mkdir5, readFile as readFile13, rename as rename5, rm as rm6, writeFile as writeFile7 } from "fs/promises";
|
|
7870
|
-
import { existsSync as
|
|
7874
|
+
import { existsSync as existsSync16 } from "fs";
|
|
7871
7875
|
import path18 from "path";
|
|
7872
7876
|
import { z as z4 } from "zod";
|
|
7873
7877
|
var WorkspaceProjectSchema = z4.object({
|
|
@@ -7893,7 +7897,7 @@ ${parsed.error.issues.slice(0, 5).map((i) => ` ${i.path.join(".")}: ${i.message
|
|
|
7893
7897
|
);
|
|
7894
7898
|
}
|
|
7895
7899
|
async function loadWorkspace(workspacePath) {
|
|
7896
|
-
if (!
|
|
7900
|
+
if (!existsSync16(workspacePath)) return { version: 1, projects: [] };
|
|
7897
7901
|
let raw;
|
|
7898
7902
|
try {
|
|
7899
7903
|
raw = JSON.parse(await readFile13(workspacePath, "utf8"));
|
|
@@ -7945,14 +7949,14 @@ function validateWorkspace(ws, dir) {
|
|
|
7945
7949
|
message: `project "${project.id}" stores an absolute path \u2014 the catalog will not resolve correctly if this tree is cloned or moved elsewhere`
|
|
7946
7950
|
});
|
|
7947
7951
|
}
|
|
7948
|
-
if (!
|
|
7952
|
+
if (!existsSync16(manifestPath)) {
|
|
7949
7953
|
diagnostics.push({
|
|
7950
7954
|
id: "missing-manifest",
|
|
7951
7955
|
level: "error",
|
|
7952
7956
|
message: `project "${project.id}" manifest not found: ${manifestPath}`
|
|
7953
7957
|
});
|
|
7954
7958
|
}
|
|
7955
|
-
if (!
|
|
7959
|
+
if (!existsSync16(lockPath)) {
|
|
7956
7960
|
diagnostics.push({
|
|
7957
7961
|
id: "missing-lock",
|
|
7958
7962
|
level: "error",
|
|
@@ -8253,7 +8257,7 @@ async function applyManifestEdit(manifestPath, edit) {
|
|
|
8253
8257
|
|
|
8254
8258
|
// src/pipeline/hand-edit.ts
|
|
8255
8259
|
import { spawn as spawn2 } from "child_process";
|
|
8256
|
-
import { existsSync as
|
|
8260
|
+
import { existsSync as existsSync17 } from "fs";
|
|
8257
8261
|
import { copyFile, mkdir as mkdir6, readFile as readFile15, rename as rename7, rm as rm8, writeFile as writeFile9 } from "fs/promises";
|
|
8258
8262
|
import path20 from "path";
|
|
8259
8263
|
import { z as z6 } from "zod";
|
|
@@ -8269,7 +8273,7 @@ function handEditPath(loaded, spec) {
|
|
|
8269
8273
|
function handEditBases(spec, lock) {
|
|
8270
8274
|
const entry = lock.entries[lockKey(spec.styleId, spec.assetId)];
|
|
8271
8275
|
if (entry) {
|
|
8272
|
-
if (entry.outputs.length !== 1 && !
|
|
8276
|
+
if (entry.outputs.length !== 1 && !isMemberSetEntry(entry)) return null;
|
|
8273
8277
|
const members = entry.outputs.map((output, index) => ({
|
|
8274
8278
|
role: entry.outputs.length === 1 ? null : output.role ?? null,
|
|
8275
8279
|
path: currentEntryOutputPath(entry, spec, index),
|
|
@@ -8277,15 +8281,15 @@ function handEditBases(spec, lock) {
|
|
|
8277
8281
|
}));
|
|
8278
8282
|
if (members.some((member) => member.mediaType && member.mediaType !== "image/png")) return null;
|
|
8279
8283
|
if (entry.outputs.length === 1 && !primaryOutput(entry)) return null;
|
|
8280
|
-
if (!members.every((member) =>
|
|
8284
|
+
if (!members.every((member) => existsSync17(member.path))) return null;
|
|
8281
8285
|
return members.map(({ role, path: file }) => ({ role, path: file }));
|
|
8282
8286
|
}
|
|
8283
|
-
return
|
|
8287
|
+
return existsSync17(spec.outFile) ? [{ role: null, path: spec.outFile }] : null;
|
|
8284
8288
|
}
|
|
8285
8289
|
function handEditMembers(loaded, spec, lock, editPath) {
|
|
8286
8290
|
const entry = lock.entries[lockKey(spec.styleId, spec.assetId)];
|
|
8287
8291
|
const bases = handEditBases(spec, lock);
|
|
8288
|
-
if (entry &&
|
|
8292
|
+
if (entry && isMemberSetEntry(entry)) {
|
|
8289
8293
|
return entry.outputs.map((output, index) => ({
|
|
8290
8294
|
role: output.role ?? null,
|
|
8291
8295
|
path: memberPath(editPath, output.role, index, entry.outputs.length, output.mediaType),
|
|
@@ -8296,9 +8300,14 @@ function handEditMembers(loaded, spec, lock, editPath) {
|
|
|
8296
8300
|
}
|
|
8297
8301
|
async function startHandEdit(loaded, lock, spec, opts = {}) {
|
|
8298
8302
|
const entry = lock.entries[lockKey(spec.styleId, spec.assetId)];
|
|
8299
|
-
if (entry && entry.outputs.length > 1 && !
|
|
8303
|
+
if (entry && entry.outputs.length > 1 && !isMemberSetEntry(entry)) {
|
|
8300
8304
|
throw new ManifestEditError(
|
|
8301
|
-
`${spec.styleId}/${spec.assetId} is a
|
|
8305
|
+
`${spec.styleId}/${spec.assetId} is a set of ${entry.outputs.length} outputs that are not all PNG; hand edits cover PNG images and sets of them`
|
|
8306
|
+
);
|
|
8307
|
+
}
|
|
8308
|
+
if (entry && spec.source && isMemberSetEntry(entry) && !sourceIsStem(spec.source, entry, loaded.root)) {
|
|
8309
|
+
throw new ManifestEditError(
|
|
8310
|
+
`${spec.styleId}/${spec.assetId} declares ${spec.source}, one committed file placed for the whole set; clear that source (pixelkiln edit detach) to hand-edit the members one by one`
|
|
8302
8311
|
);
|
|
8303
8312
|
}
|
|
8304
8313
|
const existing = spec.source ? path20.resolve(loaded.root, spec.source) : null;
|
|
@@ -8306,7 +8315,7 @@ async function startHandEdit(loaded, lock, spec, opts = {}) {
|
|
|
8306
8315
|
const members = handEditMembers(loaded, spec, lock, editPath);
|
|
8307
8316
|
let created = false;
|
|
8308
8317
|
for (const member of members) {
|
|
8309
|
-
if (
|
|
8318
|
+
if (existsSync17(member.path)) continue;
|
|
8310
8319
|
if (!member.base) {
|
|
8311
8320
|
throw new ManifestEditError(
|
|
8312
8321
|
`${spec.styleId}/${spec.assetId} has no generated PNG on disk to start from; generate or restore it first`
|
|
@@ -8404,18 +8413,18 @@ async function readHandEditCompanion(editPath) {
|
|
|
8404
8413
|
var MAX_HAND_EDIT_BYTES = 16 * 1024 * 1024;
|
|
8405
8414
|
async function saveHandEdit(loaded, lock, spec, input) {
|
|
8406
8415
|
const entry = lock.entries[lockKey(spec.styleId, spec.assetId)];
|
|
8407
|
-
const
|
|
8416
|
+
const memberSet = entry ? isMemberSetEntry(entry) : false;
|
|
8408
8417
|
const incoming = input.frames ?? (input.png ? [{ role: null, png: input.png }] : []);
|
|
8409
8418
|
if (!incoming.length) throw new ManifestEditError("the save carried no image");
|
|
8410
8419
|
if (input.project && input.project.length > MAX_HAND_EDIT_BYTES) {
|
|
8411
8420
|
throw new ManifestEditError(`the project file is ${input.project.length} bytes; the limit is ${MAX_HAND_EDIT_BYTES}`);
|
|
8412
8421
|
}
|
|
8413
|
-
if (
|
|
8422
|
+
if (memberSet) {
|
|
8414
8423
|
const expected = entry.outputs.map((output) => output.role ?? null);
|
|
8415
8424
|
const got = incoming.map((frame) => frame.role);
|
|
8416
8425
|
if (got.length !== expected.length || expected.some((role, index) => got[index] !== role)) {
|
|
8417
8426
|
throw new ManifestEditError(
|
|
8418
|
-
`${spec.styleId}/${spec.assetId} is a set of ${expected.length}
|
|
8427
|
+
`${spec.styleId}/${spec.assetId} is a set of ${expected.length} members (${expected.join(", ")}); the editor returned ${got.length} (${got.map((role) => role ?? "new").join(", ")}) \u2014 keep the frames it opened with`
|
|
8419
8428
|
);
|
|
8420
8429
|
}
|
|
8421
8430
|
} else if (incoming.length !== 1) {
|
|
@@ -8429,7 +8438,7 @@ async function saveHandEdit(loaded, lock, spec, input) {
|
|
|
8429
8438
|
validateMedia(frame.png, MediaType.PNG);
|
|
8430
8439
|
return decodePng(frame.png);
|
|
8431
8440
|
} catch (error) {
|
|
8432
|
-
const which =
|
|
8441
|
+
const which = memberSet ? `member ${frame.role ?? index}` : "the edit";
|
|
8433
8442
|
throw new ManifestEditError(`${which} is not a valid PNG: ${error instanceof Error ? error.message : String(error)}`);
|
|
8434
8443
|
}
|
|
8435
8444
|
});
|
|
@@ -8437,7 +8446,7 @@ async function saveHandEdit(loaded, lock, spec, input) {
|
|
|
8437
8446
|
const expectedSize = bases?.[0] ? decodePng(await readFile15(bases[0].path)) : { width: spec.width, height: spec.height };
|
|
8438
8447
|
for (const [index, image] of decoded.entries()) {
|
|
8439
8448
|
if (image.width !== expectedSize.width || image.height !== expectedSize.height) {
|
|
8440
|
-
const which =
|
|
8449
|
+
const which = memberSet ? `member ${incoming[index].role ?? index}` : "the edit";
|
|
8441
8450
|
throw new ManifestEditError(
|
|
8442
8451
|
`${which} is ${image.width}\xD7${image.height}; ${spec.styleId}/${spec.assetId} is ${expectedSize.width}\xD7${expectedSize.height}`
|
|
8443
8452
|
);
|
|
@@ -8671,7 +8680,7 @@ async function describeQuality(media, root, inspection) {
|
|
|
8671
8680
|
outputPaths.map((file) => describeOutput(media, root, path22.resolve(root, file)))
|
|
8672
8681
|
);
|
|
8673
8682
|
const recordPath = path22.resolve(root, inspection.record);
|
|
8674
|
-
const recordExists =
|
|
8683
|
+
const recordExists = existsSync18(recordPath);
|
|
8675
8684
|
let options = null;
|
|
8676
8685
|
let check = null;
|
|
8677
8686
|
if (recordExists) {
|
|
@@ -8753,11 +8762,11 @@ async function buildGallerySnapshot(opts) {
|
|
|
8753
8762
|
);
|
|
8754
8763
|
if (spec.source) {
|
|
8755
8764
|
const editPath = path22.resolve(root, spec.source);
|
|
8756
|
-
const memberCount =
|
|
8765
|
+
const memberCount = sourceIsStem(spec.source, entry, root) ? entry.outputs.length : 1;
|
|
8757
8766
|
const editShas = await Promise.all(
|
|
8758
8767
|
Array.from({ length: memberCount }, async (_, index) => {
|
|
8759
8768
|
const file = sourceOutputPath(spec.source, entry, index, root);
|
|
8760
|
-
return { file, sha256:
|
|
8769
|
+
return { file, sha256: existsSync18(file) ? await sha256File(file) : null };
|
|
8761
8770
|
})
|
|
8762
8771
|
);
|
|
8763
8772
|
edits = await Promise.all(editShas.map(({ file, sha256: editSha }) => describeOutput(media, root, file, { sha256: editSha })));
|
|
@@ -8765,7 +8774,7 @@ async function buildGallerySnapshot(opts) {
|
|
|
8765
8774
|
const companion = await readHandEditCompanion(editPath);
|
|
8766
8775
|
if (companion) {
|
|
8767
8776
|
const projectPath = handEditProjectPath(editPath);
|
|
8768
|
-
const project = companion.project &&
|
|
8777
|
+
const project = companion.project && existsSync18(projectPath) ? await fileInfo(projectPath) : null;
|
|
8769
8778
|
let projectUrl = null;
|
|
8770
8779
|
if (project) {
|
|
8771
8780
|
const id = galleryMediaId(projectPath);
|
|
@@ -8802,7 +8811,7 @@ async function buildGallerySnapshot(opts) {
|
|
|
8802
8811
|
}
|
|
8803
8812
|
} else if (spec.source) {
|
|
8804
8813
|
outputs = [await describeOutput(media, root, path22.resolve(root, spec.source))];
|
|
8805
|
-
} else if (!entry &&
|
|
8814
|
+
} else if (!entry && existsSync18(spec.outFile)) {
|
|
8806
8815
|
outputs = [await describeOutput(media, root, spec.outFile)];
|
|
8807
8816
|
} else {
|
|
8808
8817
|
outputs = [];
|
|
@@ -9355,6 +9364,7 @@ function renderGallery(snapshot, opts = {}) {
|
|
|
9355
9364
|
.sheet.editor-host { width:min(1400px, 96vw); grid-template-rows:auto 1fr; }
|
|
9356
9365
|
.sheet.editor-host .rbar b { font:650 12.5px/1.4 var(--mono); color:var(--text); }
|
|
9357
9366
|
.sheet.editor-host .rbar .acts { margin-left:auto; display:flex; gap:8px; align-items:center; }
|
|
9367
|
+
.sheet.editor-host .rbar .chip { color:var(--dim); font-size:12.5px; display:inline-flex; align-items:center; gap:5px; margin-right:6px; }
|
|
9358
9368
|
.sheet.editor-host .rbar .st { display:inline-flex; align-items:center; gap:6px; font:650 11.5px/1 var(--mono); }
|
|
9359
9369
|
.sheet.editor-host .rbar .msg { color:var(--bad); font-size:12.5px; }
|
|
9360
9370
|
.sheet.editor-host .stage { position:relative; min-height:0; }
|
|
@@ -9879,15 +9889,20 @@ function openEditorSheet(item) {
|
|
|
9879
9889
|
const bar = el('div', 'rbar');
|
|
9880
9890
|
const close = el('button', null, 'Close'); close.type = 'button';
|
|
9881
9891
|
close.onclick = () => closeEditorSheet();
|
|
9882
|
-
const title = el('b', null, (item.project ? item.project + ':' : '') + item.styleId + '/' + item.assetId + ' \xB7 ' + item.width + '\xD7' + item.height + (item.outputs.length > 1 ? ' \xB7 ' + item.outputs.length + '
|
|
9892
|
+
const title = el('b', null, (item.project ? item.project + ':' : '') + item.styleId + '/' + item.assetId + ' \xB7 ' + item.width + '\xD7' + item.height + (item.outputs.length > 1 ? ' \xB7 ' + item.outputs.length + ' ' + memberNoun(item, item.outputs.length) : ''));
|
|
9883
9893
|
const status = el('span', 'st');
|
|
9884
9894
|
const acts = el('div', 'acts');
|
|
9885
9895
|
const msg = el('span', 'msg');
|
|
9896
|
+
const compare = el('label', 'chip'); compare.hidden = true;
|
|
9897
|
+
compare.title = 'The generated art on a locked layer at 50%, for comparison; never part of the saved file';
|
|
9898
|
+
const compareBox = el('input'); compareBox.type = 'checkbox'; compareBox.checked = true;
|
|
9899
|
+
compareBox.onchange = () => showReference(compareBox.checked);
|
|
9900
|
+
compare.append(compareBox, document.createTextNode(' show generated'));
|
|
9886
9901
|
const save = el('button', 'primary', 'Save to project'); save.type = 'button';
|
|
9887
9902
|
save.onclick = () => requestEditorSave(false);
|
|
9888
9903
|
const saveClose = el('button', null, 'Save & close'); saveClose.type = 'button';
|
|
9889
9904
|
saveClose.onclick = () => requestEditorSave(true);
|
|
9890
|
-
acts.append(msg, save, saveClose);
|
|
9905
|
+
acts.append(msg, compare, save, saveClose);
|
|
9891
9906
|
bar.append(close, title, status, acts);
|
|
9892
9907
|
const stage = el('div', 'stage');
|
|
9893
9908
|
const frame = el('iframe');
|
|
@@ -9900,7 +9915,7 @@ function openEditorSheet(item) {
|
|
|
9900
9915
|
stage.append(frame, loading);
|
|
9901
9916
|
panel.append(bar, stage);
|
|
9902
9917
|
host.append(scrim, panel);
|
|
9903
|
-
SHEET = { item, frame, loading, status, msg, save, saveClose, dirty: false, opened: false, ready: null, pending: null, requests: 0, source: src, sentProject: false, roles: null };
|
|
9918
|
+
SHEET = { item, frame, loading, status, msg, save, saveClose, compare, dirty: false, opened: false, ready: null, pending: null, requests: 0, source: src, sentProject: false, sentReference: false, reference: false, roles: null };
|
|
9904
9919
|
sheetStatus('cool', 'loading');
|
|
9905
9920
|
sheetButtons();
|
|
9906
9921
|
close.focus({ preventScroll: true });
|
|
@@ -9916,11 +9931,15 @@ async function sendOpen() {
|
|
|
9916
9931
|
const projectUrl = sources.fromEdit && item.editMeta && item.editMeta.projectUrl && SHEET.ready.version >= 2 ? item.editMeta.projectUrl : null;
|
|
9917
9932
|
const set = sources.files.length > 1;
|
|
9918
9933
|
if (set && SHEET.ready.version < 3) {
|
|
9919
|
-
SHEET.msg.textContent = 'This editor build cannot open
|
|
9934
|
+
SHEET.msg.textContent = 'This editor build cannot open sets; reinstall it with pixelkiln tools install editor.';
|
|
9920
9935
|
sheetStatus('bad', 'unsupported');
|
|
9921
9936
|
return;
|
|
9922
9937
|
}
|
|
9938
|
+
// When an edit is being opened, the generated art rides along as a
|
|
9939
|
+
// reference layer: what the editor shows through at 50% for comparison.
|
|
9940
|
+
const referenceFiles = sources.fromEdit && SHEET.ready.version >= 4 && item.outputs.every((o) => o.exists && o.url) ? item.outputs : null;
|
|
9923
9941
|
const pngs = [];
|
|
9942
|
+
const reference = [];
|
|
9924
9943
|
let pxo = null;
|
|
9925
9944
|
try {
|
|
9926
9945
|
for (const file of sources.files) {
|
|
@@ -9932,6 +9951,10 @@ async function sendOpen() {
|
|
|
9932
9951
|
const p = await fetch(projectUrl, { cache: 'no-store' });
|
|
9933
9952
|
if (p.ok) pxo = await p.arrayBuffer();
|
|
9934
9953
|
}
|
|
9954
|
+
for (const file of referenceFiles || []) {
|
|
9955
|
+
const res = await fetch(file.url, { cache: 'no-store' });
|
|
9956
|
+
if (res.ok) reference.push({ role: file.role || null, png: await res.arrayBuffer() });
|
|
9957
|
+
}
|
|
9935
9958
|
} catch (err) {
|
|
9936
9959
|
if (SHEET) { SHEET.msg.textContent = 'Could not load the image: ' + err.message; sheetStatus('bad', 'failed'); }
|
|
9937
9960
|
return;
|
|
@@ -9948,16 +9971,26 @@ async function sendOpen() {
|
|
|
9948
9971
|
const transfer = [pngs[0]];
|
|
9949
9972
|
if (set) {
|
|
9950
9973
|
message.frames = pngs.map((png, i) => ({ role: SHEET.roles[i], png }));
|
|
9951
|
-
|
|
9974
|
+
// A tile set is not an animation; a slow rate keeps its timeline still-ish.
|
|
9975
|
+
message.fps = isFrameSet(item) ? (item.fps || 12) : 1;
|
|
9952
9976
|
for (const png of pngs.slice(1)) transfer.push(png);
|
|
9953
9977
|
// The first frame is both png (older editors) and frames[0]; copy it so both transfer.
|
|
9954
9978
|
message.png = pngs[0].slice(0);
|
|
9955
9979
|
transfer.push(message.png);
|
|
9956
9980
|
}
|
|
9957
9981
|
if (pxo) { message.pxo = pxo; transfer.push(pxo); }
|
|
9982
|
+
if (reference.length === (referenceFiles || []).length && reference.length) {
|
|
9983
|
+
message.reference = reference;
|
|
9984
|
+
for (const r of reference) transfer.push(r.png);
|
|
9985
|
+
}
|
|
9986
|
+
SHEET.sentReference = !!message.reference;
|
|
9958
9987
|
SHEET.frame.contentWindow.postMessage(message, location.origin, transfer);
|
|
9959
9988
|
sheetStatus('cool', 'opening');
|
|
9960
9989
|
}
|
|
9990
|
+
function showReference(visible) {
|
|
9991
|
+
if (!SHEET || !SHEET.opened || !SHEET.reference) return;
|
|
9992
|
+
SHEET.frame.contentWindow.postMessage({ type: 'pixelkiln:reference', visible }, location.origin);
|
|
9993
|
+
}
|
|
9961
9994
|
function requestEditorSave(close) {
|
|
9962
9995
|
if (!SHEET || !SHEET.opened || SHEET.pending) return;
|
|
9963
9996
|
const request = 'save-' + (++SHEET.requests);
|
|
@@ -9981,10 +10014,13 @@ async function onEditorMessage(m) {
|
|
|
9981
10014
|
case 'pixelkiln:opened':
|
|
9982
10015
|
SHEET.opened = true;
|
|
9983
10016
|
SHEET.loading.remove();
|
|
9984
|
-
|
|
10017
|
+
SHEET.reference = !!m.reference;
|
|
10018
|
+
SHEET.compare.hidden = !SHEET.reference;
|
|
10019
|
+
if (m.source === 'pxo') sheetStatus('ok', 'editing the edit file' + (m.frames > 1 ? 's' : '') + ' with ' + (m.layers === 1 ? 'the layer' : m.layers + ' layers') + (m.frames > 1 ? ' and ' + m.frames + ' ' + memberNoun(SHEET.item, m.frames) : '') + ' restored');
|
|
9985
10020
|
else if (SHEET.sentProject) sheetStatus('warn', 'editing the flattened edit file' + (m.frames > 1 ? 's' : '') + '; the layer file could not be opened');
|
|
9986
|
-
else if (m.source === 'frames') sheetStatus('ok', (SHEET.item.edits.length ? 'editing the edit files' : 'editing a copy of the generated frames
|
|
10021
|
+
else if (m.source === 'frames') sheetStatus('ok', (SHEET.item.edits.length ? 'editing the edit files' : 'editing a copy of the generated ' + memberNoun(SHEET.item, m.frames)) + ' as ' + m.frames + ' frames' + (isFrameSet(SHEET.item) ? '' : ', one per ' + memberNoun(SHEET.item, 1)));
|
|
9987
10022
|
else sheetStatus('ok', SHEET.item.edit && SHEET.item.edit.exists ? 'editing the edit file' : 'editing a copy of the generated art');
|
|
10023
|
+
if (SHEET.reference) SHEET.status.append(document.createTextNode(' \xB7 generated art shown through at 50%'));
|
|
9988
10024
|
sheetButtons();
|
|
9989
10025
|
break;
|
|
9990
10026
|
case 'pixelkiln:dirty':
|
|
@@ -10010,7 +10046,7 @@ async function onEditorMessage(m) {
|
|
|
10010
10046
|
if (saved) SHEET.item = saved;
|
|
10011
10047
|
SHEET.dirty = false;
|
|
10012
10048
|
SHEET.pending = null;
|
|
10013
|
-
ui.notice = { id: SHEET.item.id, text: 'Saved ' + (saved && saved.edits.length > 1 ? saved.edits.length + '
|
|
10049
|
+
ui.notice = { id: SHEET.item.id, text: 'Saved ' + (saved && saved.edits.length > 1 ? saved.edits.length + ' ' + memberNoun(saved, saved.edits.length) + ' to ' + saved.source : saved && saved.edit ? saved.edit.path : 'the edit') + '. mount and pack place ' + (saved && saved.edits.length > 1 ? 'them' : 'it') + ' in place of the generated art.' };
|
|
10014
10050
|
render();
|
|
10015
10051
|
if (pending.close) { closeEditorSheet(true); return; }
|
|
10016
10052
|
sheetStatus('ok', 'saved');
|
|
@@ -10583,16 +10619,18 @@ async function postHandEdit(item, action, extra) {
|
|
|
10583
10619
|
if (item.project) body.project = item.project;
|
|
10584
10620
|
snap = await postEdit(body);
|
|
10585
10621
|
}
|
|
10586
|
-
// A single PNG
|
|
10587
|
-
// member
|
|
10588
|
-
const editableOutputs = (item) => item.outputs.length &&
|
|
10589
|
-
|
|
10622
|
+
// A single PNG or a set of them \u2014 frames, tiles \u2014 is hand-editable, one file
|
|
10623
|
+
// per member; a set with a GIF in it is not.
|
|
10624
|
+
const editableOutputs = (item) => item.outputs.length && item.outputs.every((o) => o.exists && o.mediaType === 'image/png');
|
|
10625
|
+
// What the members of a set are called: frames for an animation, tiles for a tile set.
|
|
10626
|
+
const memberNoun = (item, n) => isFrameSet(item) ? (n === 1 ? 'frame' : 'frames')
|
|
10627
|
+
: item.outputs.every((o) => o.role && /^tile/i.test(o.role)) ? (n === 1 ? 'tile' : 'tiles') : (n === 1 ? 'member' : 'members');
|
|
10590
10628
|
function handEditSection(item) {
|
|
10591
10629
|
const canStart = EDITABLE && item.declared && item.asset && projectOf(item)?.manifestSha256 && editableOutputs(item);
|
|
10592
10630
|
if (!item.edit && !canStart) return null;
|
|
10593
10631
|
const set = item.edits.length > 1;
|
|
10594
10632
|
const s = el('section', 'meta');
|
|
10595
|
-
s.append(el('h3', null, set ? 'Hand edit (' + item.edits.length + '
|
|
10633
|
+
s.append(el('h3', null, set ? 'Hand edit (' + item.edits.length + ' ' + memberNoun(item, item.edits.length) + ')' : 'Hand edit'));
|
|
10596
10634
|
if (item.edit) {
|
|
10597
10635
|
const pair = el('div', 'pair');
|
|
10598
10636
|
const shared = displayScale(item.width, item.height, 200, 160);
|
|
@@ -10608,7 +10646,7 @@ function handEditSection(item) {
|
|
|
10608
10646
|
};
|
|
10609
10647
|
// For a set, the member the preview is on; the strip there steps through them.
|
|
10610
10648
|
const at = set ? Math.min(ui.member, item.edits.length - 1) : 0;
|
|
10611
|
-
const which = set ? ' \xB7 ' + (item.outputs[at].role || '
|
|
10649
|
+
const which = set ? ' \xB7 ' + (item.outputs[at].role || memberNoun(item, 1) + ' ' + (at + 1)) : '';
|
|
10612
10650
|
pair.append(fig(item.outputs[at], 'generated' + which), fig(item.edits[at], 'edit' + which));
|
|
10613
10651
|
s.append(pair);
|
|
10614
10652
|
const dl = el('dl');
|
|
@@ -10617,9 +10655,9 @@ function handEditSection(item) {
|
|
|
10617
10655
|
row(dl, 'status', st);
|
|
10618
10656
|
row(dl, 'why', EDIT_STATUS_TEXT[item.editStatus] || '');
|
|
10619
10657
|
if (set) {
|
|
10620
|
-
row(dl, 'files', item.source + ' \u2014 one per
|
|
10658
|
+
row(dl, 'files', item.source + ' \u2014 one per ' + memberNoun(item, 1) + ': ' + item.edits.map((e) => e.path.split('/').pop()).join(', '), { mono: true, copy: item.edits.map((e) => e.absolutePath).join('\\n') });
|
|
10621
10659
|
const differing = item.editChanged.filter(Boolean).length;
|
|
10622
|
-
row(dl, 'changed', differing + ' of ' + item.edits.length + '
|
|
10660
|
+
row(dl, 'changed', differing + ' of ' + item.edits.length + ' ' + memberNoun(item, item.edits.length) + ' differ from the generated art' +
|
|
10623
10661
|
(differing ? ': ' + item.edits.filter((e, i) => item.editChanged[i]).map((e) => item.outputs[item.edits.indexOf(e)].role || e.path.split('/').pop()).join(', ') : ''));
|
|
10624
10662
|
} else {
|
|
10625
10663
|
row(dl, 'file', item.edit.path, { mono: true, copy: item.edit.absolutePath });
|
|
@@ -10633,8 +10671,8 @@ function handEditSection(item) {
|
|
|
10633
10671
|
}
|
|
10634
10672
|
s.append(dl);
|
|
10635
10673
|
} else {
|
|
10636
|
-
s.append(el('div', 'state-dim',
|
|
10637
|
-
? 'Touch the
|
|
10674
|
+
s.append(el('div', 'state-dim', item.outputs.length > 1
|
|
10675
|
+
? 'Touch the ' + memberNoun(item, 2) + ' up in your own editor or in the browser, where they open as ' + (isFrameSet(item) ? 'one animation' : 'the frames of one project') + '. The generated files stay untouched; the edit is one sibling file per ' + memberNoun(item, 1) + ' that the manifest points at, and mount, pack, and export place those instead.'
|
|
10638
10676
|
: 'Touch it up in your own editor. The generated file stays untouched; the edit is a sibling file the manifest points at, and mount and pack place that instead.'));
|
|
10639
10677
|
}
|
|
10640
10678
|
if (EDITABLE && item.declared && projectOf(item)?.manifestSha256) {
|
|
@@ -10665,8 +10703,9 @@ function handEditSection(item) {
|
|
|
10665
10703
|
go('Open in desktop editor', 'start-edit', { open: true }, 'Opened ' + item.edit.path + ' in your editor. Save there, then Refresh.');
|
|
10666
10704
|
go('Detach edit', 'detach-edit', {}, 'Detached. The file' + (set ? 's are' : ' is') + ' still at ' + item.source + '; the generated art is placed again.');
|
|
10667
10705
|
} else if (canStart) {
|
|
10668
|
-
|
|
10669
|
-
go('
|
|
10706
|
+
const many = item.outputs.length > 1;
|
|
10707
|
+
go('Edit by hand', 'start-edit', { open: true }, 'Created the edit file' + (many ? 's and opened the first ' + memberNoun(item, 1) : ' and opened it') + ' in your editor. Save there, then Refresh to see it here.');
|
|
10708
|
+
go('Create edit file' + (many ? 's' : '') + ' only', 'start-edit', { open: false }, 'Created the edit file' + (many ? 's' : '') + ' and declared ' + (many ? 'them' : 'it') + ' in the manifest.');
|
|
10670
10709
|
}
|
|
10671
10710
|
acts.append(msg);
|
|
10672
10711
|
s.append(acts);
|
|
@@ -11969,7 +12008,7 @@ import path24 from "path";
|
|
|
11969
12008
|
|
|
11970
12009
|
// src/editor/install.ts
|
|
11971
12010
|
import { createHash as createHash4 } from "crypto";
|
|
11972
|
-
import { existsSync as
|
|
12011
|
+
import { existsSync as existsSync19 } from "fs";
|
|
11973
12012
|
import { mkdir as mkdir7, readFile as readFile18, rename as rename8, rm as rm9, stat as stat4, writeFile as writeFile10 } from "fs/promises";
|
|
11974
12013
|
import os2 from "os";
|
|
11975
12014
|
import path23 from "path";
|
|
@@ -11979,8 +12018,8 @@ var pin_default = {
|
|
|
11979
12018
|
pixelorama: "v1.2.2",
|
|
11980
12019
|
godot: "4.7.2",
|
|
11981
12020
|
extensionsApi: 9,
|
|
11982
|
-
protocol:
|
|
11983
|
-
release: "editor-pixelorama-v1.2.2-pk.
|
|
12021
|
+
protocol: 4,
|
|
12022
|
+
release: "editor-pixelorama-v1.2.2-pk.18",
|
|
11984
12023
|
files: {
|
|
11985
12024
|
"index.apple-touch-icon.png": {
|
|
11986
12025
|
sha256: "ee5434873e0fed91e9a888c3ea5a8b79beac25999308be7a6eb41bf4ce640d27",
|
|
@@ -11995,7 +12034,7 @@ var pin_default = {
|
|
|
11995
12034
|
bytes: 7298
|
|
11996
12035
|
},
|
|
11997
12036
|
"index.html": {
|
|
11998
|
-
sha256: "
|
|
12037
|
+
sha256: "d9adf06036660c4b76ef5386e872bc03dc0d5b0628fd6ac660d480893ee1f47a",
|
|
11999
12038
|
bytes: 5442
|
|
12000
12039
|
},
|
|
12001
12040
|
"index.icon.png": {
|
|
@@ -12007,8 +12046,8 @@ var pin_default = {
|
|
|
12007
12046
|
bytes: 279815
|
|
12008
12047
|
},
|
|
12009
12048
|
"index.pck": {
|
|
12010
|
-
sha256: "
|
|
12011
|
-
bytes:
|
|
12049
|
+
sha256: "a41d9c3a39aa99ea5ee49ddcedfeb8b092006aa75c0de67139f13297c17f42e4",
|
|
12050
|
+
bytes: 6431692
|
|
12012
12051
|
},
|
|
12013
12052
|
"index.png": {
|
|
12014
12053
|
sha256: "6e3ca3eb4a99e4c07bdd89ebc873b440ec0ffd66ce1955160da9e7ed80d538d7",
|
|
@@ -12068,7 +12107,7 @@ async function editorStatus(opts = {}) {
|
|
|
12068
12107
|
for (const [name, expected] of Object.entries(pin.files)) {
|
|
12069
12108
|
totalBytes += expected.bytes;
|
|
12070
12109
|
const file = path23.join(dir, name);
|
|
12071
|
-
const info =
|
|
12110
|
+
const info = existsSync19(file) ? await stat4(file).catch(() => null) : null;
|
|
12072
12111
|
if (info && info.size === expected.bytes && await sha256Of(file) === expected.sha256) {
|
|
12073
12112
|
installedBytes += expected.bytes;
|
|
12074
12113
|
} else {
|
|
@@ -12263,7 +12302,7 @@ function createGalleryEditorHandlers(opts = {}) {
|
|
|
12263
12302
|
|
|
12264
12303
|
// src/pipeline/salvage.ts
|
|
12265
12304
|
import { readFile as readFile20 } from "fs/promises";
|
|
12266
|
-
import { existsSync as
|
|
12305
|
+
import { existsSync as existsSync20 } from "fs";
|
|
12267
12306
|
import path25 from "path";
|
|
12268
12307
|
function providerClaimId(provider, objectId) {
|
|
12269
12308
|
return `${encodeURIComponent(provider)}:${encodeURIComponent(objectId)}`;
|
|
@@ -12271,7 +12310,7 @@ function providerClaimId(provider, objectId) {
|
|
|
12271
12310
|
async function loadClaims(lockPaths, opts = {}) {
|
|
12272
12311
|
const claimed = /* @__PURE__ */ new Set();
|
|
12273
12312
|
for (const p of lockPaths) {
|
|
12274
|
-
if (!
|
|
12313
|
+
if (!existsSync20(p)) throw new Error(`Claim lockfile not found: ${p}`);
|
|
12275
12314
|
let parsed;
|
|
12276
12315
|
try {
|
|
12277
12316
|
parsed = parseLock(JSON.parse(await readFile20(p, "utf8")));
|
|
@@ -12332,7 +12371,7 @@ async function loadSiblingManifests(ownManifestPath, workspaceManifestPaths, cla
|
|
|
12332
12371
|
const siblings = [];
|
|
12333
12372
|
for (const siblingManifestPath of siblingManifestPaths) {
|
|
12334
12373
|
if (path25.resolve(siblingManifestPath) === own) continue;
|
|
12335
|
-
if (!
|
|
12374
|
+
if (!existsSync20(siblingManifestPath)) continue;
|
|
12336
12375
|
try {
|
|
12337
12376
|
const { manifest } = await loadManifest(siblingManifestPath);
|
|
12338
12377
|
siblings.push({ label: path25.basename(path25.dirname(siblingManifestPath)), manifest });
|
|
@@ -12554,11 +12593,11 @@ async function workspaceStatus(ws, dir) {
|
|
|
12554
12593
|
}
|
|
12555
12594
|
|
|
12556
12595
|
// src/pipeline/cache-health.ts
|
|
12557
|
-
import { existsSync as
|
|
12596
|
+
import { existsSync as existsSync21 } from "fs";
|
|
12558
12597
|
import { readFile as readFile21, readdir as readdir2, rm as rm10 } from "fs/promises";
|
|
12559
12598
|
import path26 from "path";
|
|
12560
12599
|
async function inspectCaches(lock, lockPath, options = {}) {
|
|
12561
|
-
if (options.prune && !
|
|
12600
|
+
if (options.prune && !existsSync21(lockPath)) {
|
|
12562
12601
|
throw new Error(`Refusing to prune without an existing lockfile at ${path26.resolve(lockPath)}`);
|
|
12563
12602
|
}
|
|
12564
12603
|
const contentDir = path26.resolve(path26.dirname(lockPath), ".pixelkiln", "cache");
|
|
@@ -12603,7 +12642,7 @@ async function inspectCaches(lock, lockPath, options = {}) {
|
|
|
12603
12642
|
async function inspectContentCache(contentDir, referenced) {
|
|
12604
12643
|
const report = {
|
|
12605
12644
|
path: contentDir,
|
|
12606
|
-
exists:
|
|
12645
|
+
exists: existsSync21(contentDir),
|
|
12607
12646
|
files: 0,
|
|
12608
12647
|
bytes: 0,
|
|
12609
12648
|
valid: 0,
|
|
@@ -12667,7 +12706,7 @@ async function inspectContentCache(contentDir, referenced) {
|
|
|
12667
12706
|
async function inspectRemoteHashCache(remotePath) {
|
|
12668
12707
|
const report = {
|
|
12669
12708
|
path: remotePath,
|
|
12670
|
-
exists:
|
|
12709
|
+
exists: existsSync21(remotePath),
|
|
12671
12710
|
entries: 0,
|
|
12672
12711
|
valid: 0,
|
|
12673
12712
|
invalidIds: [],
|
|
@@ -12717,8 +12756,11 @@ function normalizeTileRules(raw) {
|
|
|
12717
12756
|
};
|
|
12718
12757
|
}
|
|
12719
12758
|
function exportTileset(entry, spec, options) {
|
|
12720
|
-
|
|
12759
|
+
let outputs = resolveSpecEntryOutputs(entry, spec);
|
|
12721
12760
|
if (!outputs.length) throw new Error(`${spec.styleId}/${spec.assetId} has no downloaded outputs`);
|
|
12761
|
+
if (spec.source && sourceIsStem(spec.source, entry, options.manifestDir)) {
|
|
12762
|
+
outputs = outputs.map((output) => ({ ...output, absolutePath: sourceOutputPath(spec.source, entry, output.index, options.manifestDir) }));
|
|
12763
|
+
}
|
|
12722
12764
|
const packed = packSprites(
|
|
12723
12765
|
outputs.map((output) => ({
|
|
12724
12766
|
id: output.role ?? fallbackOutputRole(output.index),
|
|
@@ -13212,7 +13254,7 @@ async function runSalvage(provider, orphans, ctx, opts = {}) {
|
|
|
13212
13254
|
|
|
13213
13255
|
// src/recipes.ts
|
|
13214
13256
|
import path28 from "path";
|
|
13215
|
-
import { existsSync as
|
|
13257
|
+
import { existsSync as existsSync22 } from "fs";
|
|
13216
13258
|
import { readdir as readdir3, readFile as readFile23 } from "fs/promises";
|
|
13217
13259
|
import { fileURLToPath } from "url";
|
|
13218
13260
|
import { z as z9 } from "zod";
|
|
@@ -13415,7 +13457,7 @@ async function readRecipeFile(recipePath, bundled) {
|
|
|
13415
13457
|
return { recipe: parsed.data, path: absolute, dir: path28.dirname(absolute), bundled };
|
|
13416
13458
|
}
|
|
13417
13459
|
async function walkRecipeFiles(root) {
|
|
13418
|
-
if (!
|
|
13460
|
+
if (!existsSync22(root)) return [];
|
|
13419
13461
|
const out = [];
|
|
13420
13462
|
for (const entry of await readdir3(root, { withFileTypes: true })) {
|
|
13421
13463
|
const resolved = path28.join(root, entry.name);
|
|
@@ -13453,7 +13495,7 @@ function parseSelector(selector) {
|
|
|
13453
13495
|
}
|
|
13454
13496
|
async function resolveRecipe(target, bundledRoot = bundledRecipeRoot()) {
|
|
13455
13497
|
const local = path28.resolve(target);
|
|
13456
|
-
if (
|
|
13498
|
+
if (existsSync22(local)) {
|
|
13457
13499
|
const recipePath = path28.basename(local) === RECIPE_FILE ? local : path28.join(local, RECIPE_FILE);
|
|
13458
13500
|
return readRecipeFile(recipePath, false);
|
|
13459
13501
|
}
|
|
@@ -13469,7 +13511,7 @@ async function resolveRecipe(target, bundledRoot = bundledRecipeRoot()) {
|
|
|
13469
13511
|
}
|
|
13470
13512
|
async function verifyFile(root, file) {
|
|
13471
13513
|
const absolute = path28.resolve(root, ...file.path.split("/"));
|
|
13472
|
-
if (!
|
|
13514
|
+
if (!existsSync22(absolute)) {
|
|
13473
13515
|
return { path: file.path, expectedSha256: file.sha256, actualSha256: null, status: "missing" };
|
|
13474
13516
|
}
|
|
13475
13517
|
const actualSha256 = await sha256File(absolute);
|
|
@@ -13547,7 +13589,7 @@ async function installRecipe(target, options = {}) {
|
|
|
13547
13589
|
})));
|
|
13548
13590
|
if (!options.force) {
|
|
13549
13591
|
for (const file of files) {
|
|
13550
|
-
if (!
|
|
13592
|
+
if (!existsSync22(file.path)) continue;
|
|
13551
13593
|
const current = await readFile23(file.path);
|
|
13552
13594
|
if (!current.equals(file.data)) {
|
|
13553
13595
|
throw new Error(
|
|
@@ -13572,7 +13614,7 @@ async function installRecipe(target, options = {}) {
|
|
|
13572
13614
|
|
|
13573
13615
|
// src/pipeline/quality-regression.ts
|
|
13574
13616
|
import path29 from "path";
|
|
13575
|
-
import { existsSync as
|
|
13617
|
+
import { existsSync as existsSync23 } from "fs";
|
|
13576
13618
|
import { readFile as readFile24 } from "fs/promises";
|
|
13577
13619
|
import { z as z10 } from "zod";
|
|
13578
13620
|
var SHA256_RE2 = /^[0-9a-f]{64}$/;
|
|
@@ -13815,7 +13857,7 @@ async function snapshotQualityBaseline(inputs, baselinePath, options = {}) {
|
|
|
13815
13857
|
cases
|
|
13816
13858
|
});
|
|
13817
13859
|
const data = Buffer.from(JSON.stringify(baseline, null, 2) + "\n");
|
|
13818
|
-
if (
|
|
13860
|
+
if (existsSync23(absolute) && !options.force) {
|
|
13819
13861
|
const current = await readFile24(absolute);
|
|
13820
13862
|
if (!current.equals(data)) {
|
|
13821
13863
|
throw new Error(`Quality baseline already exists with different content: ${absolute}. Pass --force to replace it.`);
|
|
@@ -14056,7 +14098,7 @@ async function provenanceFile(id, file) {
|
|
|
14056
14098
|
return {
|
|
14057
14099
|
id,
|
|
14058
14100
|
path: absolute,
|
|
14059
|
-
sha256:
|
|
14101
|
+
sha256: existsSync24(absolute) ? await sha256File(absolute) : null,
|
|
14060
14102
|
included: true
|
|
14061
14103
|
};
|
|
14062
14104
|
}
|
|
@@ -14696,7 +14738,7 @@ async function confirm(question, auto) {
|
|
|
14696
14738
|
return answer === "y" || answer === "yes";
|
|
14697
14739
|
}
|
|
14698
14740
|
async function requireCompleteWorkspaceClaims(workspacePath) {
|
|
14699
|
-
if (!
|
|
14741
|
+
if (!existsSync24(workspacePath)) {
|
|
14700
14742
|
throw new Error(`Workspace catalog not found: ${workspacePath}`);
|
|
14701
14743
|
}
|
|
14702
14744
|
const dir = path30.dirname(path30.resolve(workspacePath));
|
|
@@ -14955,7 +14997,7 @@ async function main() {
|
|
|
14955
14997
|
if (args.command === "init") {
|
|
14956
14998
|
if (!args.from) throw new Error("init needs --from <dir> pointing at your existing PNGs.");
|
|
14957
14999
|
const root = path30.resolve(args.from);
|
|
14958
|
-
if (!
|
|
15000
|
+
if (!existsSync24(root)) throw new Error(`No directory at ${root}`);
|
|
14959
15001
|
const generator = args.generator ?? "map";
|
|
14960
15002
|
if (generator !== "1dir" && generator !== "map") {
|
|
14961
15003
|
throw new Error(`--generator must be "1dir" or "map", got "${args.generator}".`);
|
|
@@ -15075,7 +15117,7 @@ async function main() {
|
|
|
15075
15117
|
log(` registered "${id}" in ${path30.relative(process.cwd(), workspacePath)}`);
|
|
15076
15118
|
log(` manifest: ${project.manifest}`);
|
|
15077
15119
|
log(` lock: ${project.lock}`);
|
|
15078
|
-
if (!
|
|
15120
|
+
if (!existsSync24(lockPath)) {
|
|
15079
15121
|
log(
|
|
15080
15122
|
` warning: no lockfile there yet \u2014 this project contributes no claims until one is generated`
|
|
15081
15123
|
);
|
|
@@ -15099,7 +15141,7 @@ async function main() {
|
|
|
15099
15141
|
return;
|
|
15100
15142
|
}
|
|
15101
15143
|
if (args.subcommand === "list") {
|
|
15102
|
-
if (!
|
|
15144
|
+
if (!existsSync24(workspacePath)) {
|
|
15103
15145
|
throw new Error(`Workspace catalog not found: ${workspacePath}`);
|
|
15104
15146
|
}
|
|
15105
15147
|
const ws = await loadWorkspace(workspacePath);
|
|
@@ -15119,7 +15161,7 @@ async function main() {
|
|
|
15119
15161
|
return;
|
|
15120
15162
|
}
|
|
15121
15163
|
if (args.subcommand === "status") {
|
|
15122
|
-
if (!
|
|
15164
|
+
if (!existsSync24(workspacePath)) {
|
|
15123
15165
|
throw new Error(`Workspace catalog not found: ${workspacePath}`);
|
|
15124
15166
|
}
|
|
15125
15167
|
const ws = await loadWorkspace(workspacePath);
|
|
@@ -15175,7 +15217,7 @@ async function main() {
|
|
|
15175
15217
|
}
|
|
15176
15218
|
if (args.command === "gallery" && args.workspace) {
|
|
15177
15219
|
const workspacePath = path30.resolve(args.workspace);
|
|
15178
|
-
if (!
|
|
15220
|
+
if (!existsSync24(workspacePath)) {
|
|
15179
15221
|
throw new Error(`Workspace catalog not found: ${workspacePath}`);
|
|
15180
15222
|
}
|
|
15181
15223
|
const filter = { styles: args.styles, assets: args.assets };
|
|
@@ -15210,7 +15252,7 @@ async function main() {
|
|
|
15210
15252
|
});
|
|
15211
15253
|
return;
|
|
15212
15254
|
}
|
|
15213
|
-
if (!
|
|
15255
|
+
if (!existsSync24(path30.resolve(args.manifest))) {
|
|
15214
15256
|
throw new Error(
|
|
15215
15257
|
`No manifest at ${path30.resolve(args.manifest)}. Pass --manifest, or run \`pixelkiln init --from <dir>\`.`
|
|
15216
15258
|
);
|
|
@@ -15316,13 +15358,14 @@ async function main() {
|
|
|
15316
15358
|
}
|
|
15317
15359
|
const started = await startHandEdit(loaded, lock, spec);
|
|
15318
15360
|
const set = started.members.length > 1;
|
|
15319
|
-
|
|
15361
|
+
const kind = spec.generator === "frames" ? "frames" : "members";
|
|
15362
|
+
log(` ${started.created ? "created" : "found"} ${started.source}${set ? ` (${started.members.length} ${kind})` : ""}${started.declared ? " and declared it as the asset's source" : ""}`);
|
|
15320
15363
|
if (set) for (const member of started.members) log(` ${member.role ?? ""} ${path30.relative(process.cwd(), member.path)}`);
|
|
15321
15364
|
if (args.noOpen) {
|
|
15322
15365
|
log(` edit ${set ? "them" : "it"} with your own tool, then run pixelkiln plan; mount and pack place ${set ? "them" : "it"} in place of the generated art`);
|
|
15323
15366
|
} else {
|
|
15324
15367
|
const command = openInEditor(started.members[0].path);
|
|
15325
|
-
log(` opened ${set ?
|
|
15368
|
+
log(` opened ${set ? `the first ${kind === "frames" ? "frame" : "member"} ` : ""}with: ${command}${process.env.PIXELKILN_EDITOR ? "" : " (set PIXELKILN_EDITOR to choose the program)"}`);
|
|
15326
15369
|
}
|
|
15327
15370
|
return;
|
|
15328
15371
|
}
|
|
@@ -15458,7 +15501,7 @@ async function main() {
|
|
|
15458
15501
|
let intact = true;
|
|
15459
15502
|
for (const output of entry.outputs) {
|
|
15460
15503
|
const file = resolveOutputPath(output.path, item.spec.root);
|
|
15461
|
-
if (!
|
|
15504
|
+
if (!existsSync24(file) || await sha256File(file) !== output.sha256) {
|
|
15462
15505
|
intact = false;
|
|
15463
15506
|
break;
|
|
15464
15507
|
}
|
|
@@ -15840,7 +15883,7 @@ async function main() {
|
|
|
15840
15883
|
const lockPaths = [
|
|
15841
15884
|
.../* @__PURE__ */ new Set([
|
|
15842
15885
|
...workspaceLockPaths,
|
|
15843
|
-
...
|
|
15886
|
+
...existsSync24(ownLock) ? [ownLock] : [],
|
|
15844
15887
|
...args.claims.map((c) => path30.resolve(c))
|
|
15845
15888
|
])
|
|
15846
15889
|
];
|