pixelkiln 0.21.0 → 0.23.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 +246 -38
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +223 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +70 -11
- package/dist/index.d.ts +70 -11
- package/dist/index.js +231 -23
- package/dist/index.js.map +1 -1
- package/docs/CLI.md +9 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import path26 from "path";
|
|
5
5
|
import { existsSync as existsSync21 } from "fs";
|
|
6
|
-
import { readFile as
|
|
6
|
+
import { readFile as readFile22 } from "fs/promises";
|
|
7
7
|
|
|
8
8
|
// src/env.ts
|
|
9
9
|
import { readFileSync, existsSync } from "fs";
|
|
@@ -7790,7 +7790,7 @@ async function runPicker(provider, lock, lockPath, opts = {}) {
|
|
|
7790
7790
|
// src/gallery/snapshot.ts
|
|
7791
7791
|
import { createHash as createHash3 } from "crypto";
|
|
7792
7792
|
import { existsSync as existsSync16 } from "fs";
|
|
7793
|
-
import { stat as stat3 } from "fs/promises";
|
|
7793
|
+
import { readFile as readFile15, stat as stat3 } from "fs/promises";
|
|
7794
7794
|
import path20 from "path";
|
|
7795
7795
|
|
|
7796
7796
|
// src/workspace.ts
|
|
@@ -7980,8 +7980,17 @@ var ManifestEditSchema = z5.discriminatedUnion("action", [
|
|
|
7980
7980
|
expectedSha256: HexSha,
|
|
7981
7981
|
patch: z5.object({
|
|
7982
7982
|
/** Candidates per generation, written to the provider's own option. */
|
|
7983
|
-
candidates: z5.number().int().min(1).max(64)
|
|
7984
|
-
|
|
7983
|
+
candidates: z5.number().int().min(1).max(64).optional(),
|
|
7984
|
+
/** Empty clears the style's own value (inherit, or the default). */
|
|
7985
|
+
promptPrefix: z5.string().optional(),
|
|
7986
|
+
promptSuffix: z5.string().optional(),
|
|
7987
|
+
/** `#rrggbb` values; null or empty clears the style's own palette. */
|
|
7988
|
+
palette: z5.array(z5.string().regex(/^#?[0-9a-f]{6}$/i, "expected a six-digit hex colour")).max(256).nullable().optional(),
|
|
7989
|
+
/** Provider view name (PixelLab: low top-down, high top-down, side); empty clears. */
|
|
7990
|
+
view: z5.string().max(64).nullable().optional(),
|
|
7991
|
+
/** pixflux only; null clears the style's own value. */
|
|
7992
|
+
noBackground: z5.boolean().nullable().optional()
|
|
7993
|
+
}).strict().refine((patch) => Object.keys(patch).length > 0, { message: "nothing to change" })
|
|
7985
7994
|
}).strict()
|
|
7986
7995
|
]);
|
|
7987
7996
|
var ManifestDriftError = class extends Error {
|
|
@@ -8019,16 +8028,27 @@ function applyEdit(raw, edit) {
|
|
|
8019
8028
|
if (edit.action === "patch-style") {
|
|
8020
8029
|
const style = raw.styles && Object.hasOwn(raw.styles, edit.styleId) ? raw.styles[edit.styleId] : void 0;
|
|
8021
8030
|
if (!style) throw new ManifestEditError(`style "${edit.styleId}" is not declared by the manifest`);
|
|
8022
|
-
const
|
|
8023
|
-
|
|
8024
|
-
|
|
8025
|
-
|
|
8026
|
-
|
|
8027
|
-
|
|
8031
|
+
const { patch: patch2 } = edit;
|
|
8032
|
+
if (patch2.candidates !== void 0) {
|
|
8033
|
+
const provider = styleProvider(raw, edit.styleId);
|
|
8034
|
+
const option = CANDIDATE_OPTION[provider];
|
|
8035
|
+
if (!option) {
|
|
8036
|
+
throw new ManifestEditError(
|
|
8037
|
+
provider === "pixellab" ? "PixelLab's candidate count follows the generator and size: map and pixflux return one image; a 1dir style returns 4\u201364 for its size" : `provider "${provider}" has no candidate-count option`
|
|
8038
|
+
);
|
|
8039
|
+
}
|
|
8040
|
+
const options = { ...style.providerOptions ?? {} };
|
|
8041
|
+
options[provider] = { ...options[provider] ?? {}, [option]: patch2.candidates };
|
|
8042
|
+
style.providerOptions = options;
|
|
8028
8043
|
}
|
|
8029
|
-
|
|
8030
|
-
|
|
8031
|
-
|
|
8044
|
+
if (patch2.promptPrefix !== void 0) setOrDelete(style, "promptPrefix", patch2.promptPrefix || null);
|
|
8045
|
+
if (patch2.promptSuffix !== void 0) setOrDelete(style, "promptSuffix", patch2.promptSuffix || null);
|
|
8046
|
+
if (patch2.palette !== void 0) {
|
|
8047
|
+
const colors = (patch2.palette ?? []).map((color) => "#" + color.replace(/^#/, "").toLowerCase());
|
|
8048
|
+
setOrDelete(style, "palette", colors.length ? colors : null);
|
|
8049
|
+
}
|
|
8050
|
+
if (patch2.view !== void 0) setOrDelete(style, "view", patch2.view?.trim() || null);
|
|
8051
|
+
if (patch2.noBackground !== void 0) setOrDelete(style, "noBackground", patch2.noBackground);
|
|
8032
8052
|
return;
|
|
8033
8053
|
}
|
|
8034
8054
|
if (edit.action === "add-asset") {
|
|
@@ -8253,6 +8273,14 @@ async function buildGallerySnapshot(opts) {
|
|
|
8253
8273
|
const media = /* @__PURE__ */ new Map();
|
|
8254
8274
|
const plan = await buildPlan(specs, lock);
|
|
8255
8275
|
const items = [];
|
|
8276
|
+
const manifestText = await readFile15(loaded.path, "utf8");
|
|
8277
|
+
const manifestSha256 = sha256(manifestText);
|
|
8278
|
+
let rawStyles = {};
|
|
8279
|
+
try {
|
|
8280
|
+
const raw = JSON.parse(manifestText);
|
|
8281
|
+
if (raw && typeof raw === "object" && raw.styles && typeof raw.styles === "object") rawStyles = raw.styles;
|
|
8282
|
+
} catch {
|
|
8283
|
+
}
|
|
8256
8284
|
for (const planItem of plan.items) {
|
|
8257
8285
|
const { spec, key } = planItem;
|
|
8258
8286
|
const entry = lock.entries[key];
|
|
@@ -8387,6 +8415,8 @@ async function buildGallerySnapshot(opts) {
|
|
|
8387
8415
|
const counts = new Set(styleItems.filter((item) => item.declared && item.candidates !== null).map((item) => item.candidates));
|
|
8388
8416
|
const provider = style ? style.provider ?? loaded.manifest.provider : styleItems[0]?.provider ?? loaded.manifest.provider;
|
|
8389
8417
|
const actionableItems = styleItems.filter((item) => item.declared && (item.state === "missing" || item.state === "stale" || item.state === "failed"));
|
|
8418
|
+
const declaredItems = styleItems.filter((item) => item.declared);
|
|
8419
|
+
const rawStyle = Object.hasOwn(rawStyles, id) ? rawStyles[id] : void 0;
|
|
8390
8420
|
return {
|
|
8391
8421
|
id,
|
|
8392
8422
|
project: null,
|
|
@@ -8395,11 +8425,22 @@ async function buildGallerySnapshot(opts) {
|
|
|
8395
8425
|
provider,
|
|
8396
8426
|
generator: style?.generator ?? styleItems[0]?.generator ?? "map",
|
|
8397
8427
|
outDir: style?.outDir ?? "",
|
|
8428
|
+
promptPrefix: style?.promptPrefix ?? "",
|
|
8429
|
+
promptSuffix: style?.promptSuffix ?? "",
|
|
8398
8430
|
palette: style?.palette ?? [],
|
|
8431
|
+
view: style?.view ?? null,
|
|
8432
|
+
noBackground: style?.noBackground ?? true,
|
|
8399
8433
|
quality: Boolean(style?.quality),
|
|
8400
8434
|
tags: style?.tags ?? [],
|
|
8435
|
+
extends: typeof rawStyle?.extends === "string" ? rawStyle.extends : null,
|
|
8436
|
+
ownFields: rawStyle ? Object.keys(rawStyle).filter((key) => key !== "extends") : [],
|
|
8401
8437
|
items: styleItems.length,
|
|
8402
8438
|
spendByUnit: spend,
|
|
8439
|
+
regenerate: {
|
|
8440
|
+
assets: declaredItems.length,
|
|
8441
|
+
cost: declaredItems.reduce((sum, item) => sum + (item.estimatedCost ?? 0), 0),
|
|
8442
|
+
costUnit: declaredItems[0]?.costUnit ?? styleItems[0]?.costUnit ?? "generations"
|
|
8443
|
+
},
|
|
8403
8444
|
candidates: counts.size === 1 ? [...counts][0] : null,
|
|
8404
8445
|
candidatesEditable: Boolean(style) && Object.hasOwn(CANDIDATE_OPTION, provider),
|
|
8405
8446
|
actionable: {
|
|
@@ -8420,7 +8461,7 @@ async function buildGallerySnapshot(opts) {
|
|
|
8420
8461
|
root,
|
|
8421
8462
|
provider: loaded.manifest.provider,
|
|
8422
8463
|
account: null,
|
|
8423
|
-
manifestSha256
|
|
8464
|
+
manifestSha256,
|
|
8424
8465
|
entries: Object.keys(lock.entries).length,
|
|
8425
8466
|
items: items.length,
|
|
8426
8467
|
spendByUnit: spendByUnit(lock),
|
|
@@ -8457,6 +8498,7 @@ async function buildWorkspaceGallerySnapshot(opts) {
|
|
|
8457
8498
|
const lock = await loadLock(lockPath);
|
|
8458
8499
|
let projectItems = [];
|
|
8459
8500
|
let projectStyles = [];
|
|
8501
|
+
let manifestSha256 = null;
|
|
8460
8502
|
if (!excluded) {
|
|
8461
8503
|
const specs = await resolveSpecs(loaded, { styles: styleFilter, assets: assetFilter });
|
|
8462
8504
|
normalizeLockOutputPaths(lock, specs);
|
|
@@ -8469,6 +8511,7 @@ async function buildWorkspaceGallerySnapshot(opts) {
|
|
|
8469
8511
|
now: opts.now
|
|
8470
8512
|
});
|
|
8471
8513
|
for (const [id, file] of build.media) media.set(id, file);
|
|
8514
|
+
manifestSha256 = build.snapshot.project?.manifestSha256 ?? null;
|
|
8472
8515
|
projectItems = build.snapshot.items.map((item) => ({
|
|
8473
8516
|
...item,
|
|
8474
8517
|
id: `${project.id}:${item.key}`,
|
|
@@ -8486,7 +8529,7 @@ async function buildWorkspaceGallerySnapshot(opts) {
|
|
|
8486
8529
|
root: loaded.root,
|
|
8487
8530
|
provider: loaded.manifest.provider,
|
|
8488
8531
|
account: project.account ?? null,
|
|
8489
|
-
manifestSha256: await
|
|
8532
|
+
manifestSha256: manifestSha256 ?? sha256(await readFile15(manifestPath, "utf8")),
|
|
8490
8533
|
entries: Object.keys(lock.entries).length,
|
|
8491
8534
|
items: projectItems.length,
|
|
8492
8535
|
spendByUnit: spendByUnit(lock),
|
|
@@ -8544,7 +8587,7 @@ async function buildWorkspaceGallerySnapshot(opts) {
|
|
|
8544
8587
|
import { spawn as spawn2 } from "child_process";
|
|
8545
8588
|
import { randomBytes } from "crypto";
|
|
8546
8589
|
import { createServer as createServer2 } from "http";
|
|
8547
|
-
import { readFile as
|
|
8590
|
+
import { readFile as readFile16 } from "fs/promises";
|
|
8548
8591
|
|
|
8549
8592
|
// src/gallery/page.ts
|
|
8550
8593
|
function renderGallery(snapshot, opts = {}) {
|
|
@@ -8741,6 +8784,12 @@ function renderGallery(snapshot, opts = {}) {
|
|
|
8741
8784
|
.shead .cand { display:inline-flex; align-items:center; gap:6px; font-size:12px; color:var(--dim); }
|
|
8742
8785
|
.shead .cand input { width:56px; background:var(--panel-deep); border:1px solid var(--line); color:var(--text);
|
|
8743
8786
|
padding:2px 6px; font:12px/1.4 inherit; border-radius:0; }
|
|
8787
|
+
form.edit.style-form { margin:0 0 14px; }
|
|
8788
|
+
form.edit .blast { padding:8px 10px; border:1px solid var(--line); font-size:12.5px; color:var(--dim); }
|
|
8789
|
+
form.edit .blast.hot { border-color:var(--warn); color:var(--text); }
|
|
8790
|
+
form.edit .blast b { color:var(--warn); font-weight:600; }
|
|
8791
|
+
form.edit .pal { margin-top:4px; }
|
|
8792
|
+
.style .notice { margin:0 0 14px; }
|
|
8744
8793
|
#jobs { width:min(100%,var(--content)); margin:0 auto; padding:0 22px 12px; display:grid; gap:6px; }
|
|
8745
8794
|
#jobs:empty { display:none; }
|
|
8746
8795
|
.job { border:1px solid var(--line); background:var(--panel-deep); padding:8px 12px; display:grid;
|
|
@@ -8861,6 +8910,7 @@ function renderGallery(snapshot, opts = {}) {
|
|
|
8861
8910
|
</footer>
|
|
8862
8911
|
<div id="drawer-host"></div>
|
|
8863
8912
|
<div id="dialog-host"></div>
|
|
8913
|
+
<datalist id="view-options"><option value="low top-down"><option value="high top-down"><option value="side"><option value="sidescroller"></datalist>
|
|
8864
8914
|
<script>
|
|
8865
8915
|
const INITIAL = ${data};
|
|
8866
8916
|
const SESSION = ${session};
|
|
@@ -9192,7 +9242,8 @@ function renderHeader() {
|
|
|
9192
9242
|
t.lastChild.append(el('b', null, n), document.createTextNode(' lock ' + (n === 1 ? 'entry' : 'entries')));
|
|
9193
9243
|
const declared = snap.items.filter((i) => i.declared).length;
|
|
9194
9244
|
t.append(el('span', null, declared + ' declared by ' + (snap.workspace ? 'a manifest' : 'the manifest')));
|
|
9195
|
-
|
|
9245
|
+
const recorded = fmtSpend(snap.totals.spendByUnit);
|
|
9246
|
+
t.append(el('span', null, recorded === 'no spend recorded' ? recorded : recorded + ' recorded'));
|
|
9196
9247
|
if (snap.filter.styles.length || snap.filter.assets.length) {
|
|
9197
9248
|
t.append(el('span', 'state-warn', 'filtered: ' +
|
|
9198
9249
|
[snap.filter.styles.length ? '--style ' + snap.filter.styles.join(',') : '',
|
|
@@ -9386,7 +9437,12 @@ function renderMain(items) {
|
|
|
9386
9437
|
g.onclick = () => generateDialog(snap.items.filter((i) => i.project === s.project && s.actionable.keys.includes(i.key)), { project: s.project });
|
|
9387
9438
|
tools.append(g);
|
|
9388
9439
|
}
|
|
9440
|
+
const styleKey = 'style:' + (s.project || '') + ':' + s.id;
|
|
9389
9441
|
if (EDITABLE && s.outDir) {
|
|
9442
|
+
const es = el('button', null, ui.editing === styleKey ? 'Cancel' : 'Edit style');
|
|
9443
|
+
es.type = 'button';
|
|
9444
|
+
es.onclick = () => { ui.editing = ui.editing === styleKey ? null : styleKey; ui.notice = null; render(); };
|
|
9445
|
+
tools.append(es);
|
|
9390
9446
|
const add = el('button', 'add', ui.editing === addKey ? 'Cancel' : '+ Add asset');
|
|
9391
9447
|
add.type = 'button';
|
|
9392
9448
|
add.onclick = () => { ui.editing = ui.editing === addKey ? null : addKey; render(); };
|
|
@@ -9394,6 +9450,8 @@ function renderMain(items) {
|
|
|
9394
9450
|
}
|
|
9395
9451
|
if (tools.childNodes.length) head.append(tools);
|
|
9396
9452
|
wrap.append(head);
|
|
9453
|
+
if (ui.notice && ui.notice.id === styleKey) wrap.append(el('div', 'notice', ui.notice.text));
|
|
9454
|
+
if (ui.editing === styleKey) wrap.append(styleForm(s));
|
|
9397
9455
|
if (ui.editing === addKey) wrap.append(addAssetForm(s));
|
|
9398
9456
|
}
|
|
9399
9457
|
const grid = el('div', 'grid');
|
|
@@ -9627,6 +9685,156 @@ function openCompare() {
|
|
|
9627
9685
|
|
|
9628
9686
|
// ---- editing (only when the server minted a session) ---------------------
|
|
9629
9687
|
|
|
9688
|
+
// Styles a parent edit reaches: every descendant (via extends) that does not
|
|
9689
|
+
// declare the field itself. Child-wins means an omitted field is inherited,
|
|
9690
|
+
// so the count follows the manifest's own rule rather than guessing.
|
|
9691
|
+
function inheritorsOf(style, field) {
|
|
9692
|
+
const byId = new Map(snap.styles.filter((x) => x.project === style.project).map((x) => [x.id, x]));
|
|
9693
|
+
const out = [];
|
|
9694
|
+
for (const other of byId.values()) {
|
|
9695
|
+
if (other.id === style.id) continue;
|
|
9696
|
+
let cur = other, blocked = false, hops = 0;
|
|
9697
|
+
while (cur && cur.id !== style.id && hops++ < 32) {
|
|
9698
|
+
if (cur.ownFields.includes(field)) { blocked = true; break; }
|
|
9699
|
+
cur = cur.extends ? byId.get(cur.extends) : null;
|
|
9700
|
+
}
|
|
9701
|
+
if (cur && cur.id === style.id && !blocked) out.push(other.id);
|
|
9702
|
+
}
|
|
9703
|
+
return out;
|
|
9704
|
+
}
|
|
9705
|
+
// noBackground reaches the request only for pixflux and non-PixelLab
|
|
9706
|
+
// providers; elsewhere a change is recorded but alters nothing.
|
|
9707
|
+
const fieldReaches = (field, style) =>
|
|
9708
|
+
field !== 'noBackground' || style.generator === 'pixflux' || style.provider !== 'pixellab';
|
|
9709
|
+
function blastRadius(style, fields) {
|
|
9710
|
+
const byId = new Map(snap.styles.filter((x) => x.project === style.project).map((x) => [x.id, x]));
|
|
9711
|
+
const styleIds = new Set();
|
|
9712
|
+
for (const field of fields) {
|
|
9713
|
+
for (const id of [style.id, ...inheritorsOf(style, field)]) {
|
|
9714
|
+
if (fieldReaches(field, byId.get(id) || style)) styleIds.add(id);
|
|
9715
|
+
}
|
|
9716
|
+
}
|
|
9717
|
+
const affected = snap.items.filter((i) => i.project === style.project && i.declared && styleIds.has(i.styleId));
|
|
9718
|
+
const units = new Map();
|
|
9719
|
+
for (const i of affected) if (i.estimatedCost !== null) units.set(i.costUnit, (units.get(i.costUnit) || 0) + i.estimatedCost);
|
|
9720
|
+
return {
|
|
9721
|
+
styles: [...styleIds],
|
|
9722
|
+
assets: affected.length,
|
|
9723
|
+
cost: [...units].map(([unit, n]) => fmtCost(unit, Math.round(n * 100) / 100)).join(' + '),
|
|
9724
|
+
};
|
|
9725
|
+
}
|
|
9726
|
+
const parsePalette = (value) => value.split(/[s,]+/).map((c) => c.trim()).filter(Boolean)
|
|
9727
|
+
.map((c) => '#' + c.replace(/^#/, '').toLowerCase());
|
|
9728
|
+
const paletteValid = (colors) => colors.every((c) => /^#[0-9a-f]{6}$/.test(c));
|
|
9729
|
+
|
|
9730
|
+
function styleForm(style) {
|
|
9731
|
+
const pr = snap.workspace ? snap.workspace.projects.find((x) => x.id === style.project) : snap.project;
|
|
9732
|
+
const own = (field) => style.ownFields.includes(field);
|
|
9733
|
+
const provenance = (field) => own(field) ? 'set on this style'
|
|
9734
|
+
: style.extends ? 'inherited from ' + style.extends + ' \u2014 saving sets a value on this style itself; ' + style.extends + ' is unchanged'
|
|
9735
|
+
: 'default \u2014 saving sets a value on this style itself';
|
|
9736
|
+
const form = el('form', 'edit style-form');
|
|
9737
|
+
form.append(el('h3', null, 'Edit style ' + style.id));
|
|
9738
|
+
const prefix = el('input'); prefix.type = 'text'; prefix.value = style.promptPrefix; prefix.placeholder = 'none';
|
|
9739
|
+
const suffix = el('input'); suffix.type = 'text'; suffix.value = style.promptSuffix; suffix.placeholder = 'none';
|
|
9740
|
+
const palette = el('input'); palette.type = 'text'; palette.value = style.palette.join(', '); palette.placeholder = '#rrggbb, #rrggbb \u2014 leave empty for no forced palette';
|
|
9741
|
+
const view = el('input'); view.type = 'text'; view.value = style.view || ''; view.placeholder = 'provider default';
|
|
9742
|
+
view.setAttribute('list', 'view-options');
|
|
9743
|
+
const noBg = el('select');
|
|
9744
|
+
noBg.append(new Option(style.extends ? 'inherit from ' + style.extends : 'default (on)', 'default'), new Option('on \u2014 strip the generated background', 'on'), new Option('off \u2014 keep the background (scenes, banners)', 'off'));
|
|
9745
|
+
noBg.value = own('noBackground') ? (style.noBackground ? 'on' : 'off') : 'default';
|
|
9746
|
+
const swatches = el('div', 'pal');
|
|
9747
|
+
const drawSwatches = () => {
|
|
9748
|
+
swatches.textContent = '';
|
|
9749
|
+
for (const c of parsePalette(palette.value)) { const i = el('i'); i.style.background = c; i.title = c; swatches.append(i); }
|
|
9750
|
+
};
|
|
9751
|
+
drawSwatches();
|
|
9752
|
+
form.append(
|
|
9753
|
+
field('prompt prefix', prefix, 'Prepended to every asset prompt in this style. ' + provenance('promptPrefix')),
|
|
9754
|
+
field('prompt suffix', suffix, 'Appended to every asset prompt in this style. ' + provenance('promptSuffix')),
|
|
9755
|
+
field('palette', palette, 'Forced colours where the provider supports them (pixflux). ' + provenance('palette')),
|
|
9756
|
+
swatches,
|
|
9757
|
+
);
|
|
9758
|
+
const row = el('div', 'row');
|
|
9759
|
+
row.append(
|
|
9760
|
+
field('view', view, 'PixelLab accepts low top-down, high top-down, side; Retro Diffusion reads sidescroller. ' + provenance('view')),
|
|
9761
|
+
field('background', noBg, (fieldReaches('noBackground', style) ? 'Sent for this style. ' : 'Not sent for ' + style.provider + ' ' + style.generator + '; recorded only. ') + provenance('noBackground')),
|
|
9762
|
+
);
|
|
9763
|
+
form.append(row);
|
|
9764
|
+
const blast = el('div', 'blast');
|
|
9765
|
+
const changed = () => {
|
|
9766
|
+
const out = [];
|
|
9767
|
+
if (prefix.value !== style.promptPrefix) out.push('promptPrefix');
|
|
9768
|
+
if (suffix.value !== style.promptSuffix) out.push('promptSuffix');
|
|
9769
|
+
if (parsePalette(palette.value).join(',') !== style.palette.map((c) => c.toLowerCase()).join(',')) out.push('palette');
|
|
9770
|
+
if (view.value.trim() !== (style.view || '')) out.push('view');
|
|
9771
|
+
const bgNow = own('noBackground') ? (style.noBackground ? 'on' : 'off') : 'default';
|
|
9772
|
+
if (noBg.value !== bgNow) out.push('noBackground');
|
|
9773
|
+
return out;
|
|
9774
|
+
};
|
|
9775
|
+
const updateBlast = () => {
|
|
9776
|
+
const fields = changed();
|
|
9777
|
+
drawSwatches();
|
|
9778
|
+
if (!fields.length) { blast.className = 'blast'; blast.textContent = 'No changes yet. A style change alters the request for every asset that uses it.'; return; }
|
|
9779
|
+
const r = blastRadius(style, fields);
|
|
9780
|
+
if (!r.assets) {
|
|
9781
|
+
blast.className = 'blast';
|
|
9782
|
+
blast.textContent = 'Recorded in the manifest only: nothing in ' + style.id + ' sends this field, so no request changes.';
|
|
9783
|
+
return;
|
|
9784
|
+
}
|
|
9785
|
+
blast.className = 'blast hot';
|
|
9786
|
+
blast.textContent = '';
|
|
9787
|
+
const others = r.styles.filter((id) => id !== style.id);
|
|
9788
|
+
blast.append(el('b', null, 'Affects ' + r.assets + (r.assets === 1 ? ' asset' : ' assets')));
|
|
9789
|
+
blast.append(document.createTextNode(others.length
|
|
9790
|
+
? ' in ' + style.id + ' and ' + others.length + (others.length === 1 ? ' style that inherits it (' : ' styles that inherit it (') + others.join(', ') + ').'
|
|
9791
|
+
: ' in ' + style.id + '.'));
|
|
9792
|
+
blast.append(document.createTextNode(' Generated ones become stale; regenerating all of them is about ' + (r.cost || 'nothing') + '. Nothing is spent until you generate.'));
|
|
9793
|
+
};
|
|
9794
|
+
for (const input of [prefix, suffix, palette, view]) input.addEventListener('input', updateBlast);
|
|
9795
|
+
noBg.addEventListener('change', updateBlast);
|
|
9796
|
+
updateBlast();
|
|
9797
|
+
form.append(blast);
|
|
9798
|
+
const actions = el('div', 'actions');
|
|
9799
|
+
const save = el('button', 'primary', 'Save to manifest'); save.type = 'submit';
|
|
9800
|
+
const cancel = el('button', null, 'Cancel'); cancel.type = 'button'; cancel.onclick = () => { ui.editing = null; render(); };
|
|
9801
|
+
const msg = el('span', 'msg');
|
|
9802
|
+
actions.append(save, cancel, msg);
|
|
9803
|
+
form.append(actions);
|
|
9804
|
+
form.onsubmit = async (e) => {
|
|
9805
|
+
e.preventDefault();
|
|
9806
|
+
const fields = changed();
|
|
9807
|
+
if (!fields.length) { ui.editing = null; render(); return; }
|
|
9808
|
+
const colors = parsePalette(palette.value);
|
|
9809
|
+
if (fields.includes('palette') && !paletteValid(colors)) { msg.className = 'msg bad'; msg.textContent = 'palette must be six-digit hex colours'; return; }
|
|
9810
|
+
const patch = {};
|
|
9811
|
+
if (fields.includes('promptPrefix')) patch.promptPrefix = prefix.value;
|
|
9812
|
+
if (fields.includes('promptSuffix')) patch.promptSuffix = suffix.value;
|
|
9813
|
+
if (fields.includes('palette')) patch.palette = colors;
|
|
9814
|
+
if (fields.includes('view')) patch.view = view.value.trim();
|
|
9815
|
+
if (fields.includes('noBackground')) patch.noBackground = noBg.value === 'default' ? null : noBg.value === 'on';
|
|
9816
|
+
save.disabled = true; msg.className = 'msg'; msg.textContent = 'saving\u2026';
|
|
9817
|
+
const r = blastRadius(style, fields);
|
|
9818
|
+
try {
|
|
9819
|
+
const body = { action: 'patch-style', styleId: style.id, expectedSha256: pr.manifestSha256, patch };
|
|
9820
|
+
if (style.project) body.project = style.project;
|
|
9821
|
+
snap = await postEdit(body);
|
|
9822
|
+
ui.editing = null;
|
|
9823
|
+
ui.notice = { id: 'style:' + (style.project || '') + ':' + style.id,
|
|
9824
|
+
text: r.assets
|
|
9825
|
+
? 'Saved. The request for ' + r.assets + (r.assets === 1 ? ' asset' : ' assets') + ' changed' + (r.cost ? ' \u2014 about ' + r.cost + ' to regenerate.' : '.') + ' Nothing is spent until you generate.'
|
|
9826
|
+
: 'Saved. Recorded in the manifest; no request changed.' };
|
|
9827
|
+
render();
|
|
9828
|
+
} catch (err) {
|
|
9829
|
+
save.disabled = false;
|
|
9830
|
+
msg.className = 'msg bad';
|
|
9831
|
+
msg.textContent = err.message + (err.status === 409 ? ' \u2014 press Refresh.' : '');
|
|
9832
|
+
}
|
|
9833
|
+
};
|
|
9834
|
+
setTimeout(() => prefix.focus(), 0);
|
|
9835
|
+
return form;
|
|
9836
|
+
}
|
|
9837
|
+
|
|
9630
9838
|
function addAssetForm(style) {
|
|
9631
9839
|
const pr = snap.workspace ? snap.workspace.projects.find((x) => x.id === style.project) : snap.project;
|
|
9632
9840
|
const siblings = snap.styles.filter((x) => x.project === style.project);
|
|
@@ -10352,7 +10560,7 @@ async function serveGallery(opts) {
|
|
|
10352
10560
|
const asset = reviewAssets.get(jobId)?.get(url2.pathname);
|
|
10353
10561
|
if (!asset) return fail(404, "no such review asset");
|
|
10354
10562
|
try {
|
|
10355
|
-
const bytes = await
|
|
10563
|
+
const bytes = await readFile16(asset.path);
|
|
10356
10564
|
res.writeHead(200, {
|
|
10357
10565
|
"Content-Type": asset.contentType,
|
|
10358
10566
|
"Cache-Control": "no-store",
|
|
@@ -10401,7 +10609,7 @@ async function serveGallery(opts) {
|
|
|
10401
10609
|
if (!asset) return fail(404, "no such gallery media");
|
|
10402
10610
|
let bytes;
|
|
10403
10611
|
try {
|
|
10404
|
-
bytes = await
|
|
10612
|
+
bytes = await readFile16(asset.path);
|
|
10405
10613
|
} catch {
|
|
10406
10614
|
return fail(404, "gallery media is no longer on disk; refresh the page");
|
|
10407
10615
|
}
|
|
@@ -10691,7 +10899,7 @@ function createGenerateHandlers(opts) {
|
|
|
10691
10899
|
}
|
|
10692
10900
|
|
|
10693
10901
|
// src/pipeline/salvage.ts
|
|
10694
|
-
import { readFile as
|
|
10902
|
+
import { readFile as readFile17 } from "fs/promises";
|
|
10695
10903
|
import { existsSync as existsSync17 } from "fs";
|
|
10696
10904
|
import path21 from "path";
|
|
10697
10905
|
function providerClaimId(provider, objectId) {
|
|
@@ -10703,7 +10911,7 @@ async function loadClaims(lockPaths, opts = {}) {
|
|
|
10703
10911
|
if (!existsSync17(p)) throw new Error(`Claim lockfile not found: ${p}`);
|
|
10704
10912
|
let parsed;
|
|
10705
10913
|
try {
|
|
10706
|
-
parsed = parseLock(JSON.parse(await
|
|
10914
|
+
parsed = parseLock(JSON.parse(await readFile17(p, "utf8")));
|
|
10707
10915
|
} catch {
|
|
10708
10916
|
throw new Error(`Claim lockfile is malformed: ${p}`);
|
|
10709
10917
|
}
|
|
@@ -10984,7 +11192,7 @@ async function workspaceStatus(ws, dir) {
|
|
|
10984
11192
|
|
|
10985
11193
|
// src/pipeline/cache-health.ts
|
|
10986
11194
|
import { existsSync as existsSync18 } from "fs";
|
|
10987
|
-
import { readFile as
|
|
11195
|
+
import { readFile as readFile18, readdir as readdir2, rm as rm8 } from "fs/promises";
|
|
10988
11196
|
import path22 from "path";
|
|
10989
11197
|
async function inspectCaches(lock, lockPath, options = {}) {
|
|
10990
11198
|
if (options.prune && !existsSync18(lockPath)) {
|
|
@@ -11014,7 +11222,7 @@ async function inspectCaches(lock, lockPath, options = {}) {
|
|
|
11014
11222
|
await saveCache(remotePath, { version: 1, hashes: {} });
|
|
11015
11223
|
removed.resetRemoteHashCache = true;
|
|
11016
11224
|
} else if (remoteHashes.invalidIds.length) {
|
|
11017
|
-
const cache = parseCache(JSON.parse(await
|
|
11225
|
+
const cache = parseCache(JSON.parse(await readFile18(remotePath, "utf8")));
|
|
11018
11226
|
for (const id of remoteHashes.invalidIds) delete cache.hashes[id];
|
|
11019
11227
|
removed.remoteHashEntries = remoteHashes.invalidIds.length;
|
|
11020
11228
|
await saveCache(remotePath, cache);
|
|
@@ -11058,7 +11266,7 @@ async function inspectContentCache(contentDir, referenced) {
|
|
|
11058
11266
|
const expected = mediaType ? entry.name.slice(0, -4) : "";
|
|
11059
11267
|
let bytes;
|
|
11060
11268
|
try {
|
|
11061
|
-
bytes = await
|
|
11269
|
+
bytes = await readFile18(file);
|
|
11062
11270
|
report.bytes += bytes.length;
|
|
11063
11271
|
} catch (err) {
|
|
11064
11272
|
report.invalid.push({
|
|
@@ -11105,7 +11313,7 @@ async function inspectRemoteHashCache(remotePath) {
|
|
|
11105
11313
|
if (!report.exists) return report;
|
|
11106
11314
|
let cache;
|
|
11107
11315
|
try {
|
|
11108
|
-
cache = parseCache(JSON.parse(await
|
|
11316
|
+
cache = parseCache(JSON.parse(await readFile18(remotePath, "utf8")));
|
|
11109
11317
|
} catch (err) {
|
|
11110
11318
|
report.error = err instanceof Error ? err.message : String(err);
|
|
11111
11319
|
return report;
|
|
@@ -11376,7 +11584,7 @@ function isRecord2(value) {
|
|
|
11376
11584
|
}
|
|
11377
11585
|
|
|
11378
11586
|
// src/pick/salvage-server.ts
|
|
11379
|
-
import { mkdir as mkdir6, writeFile as writeFile9, readFile as
|
|
11587
|
+
import { mkdir as mkdir6, writeFile as writeFile9, readFile as readFile19 } from "fs/promises";
|
|
11380
11588
|
import path23 from "path";
|
|
11381
11589
|
|
|
11382
11590
|
// src/pick/salvage-sheet.ts
|
|
@@ -11629,7 +11837,7 @@ async function runSalvage(provider, orphans, ctx, opts = {}) {
|
|
|
11629
11837
|
}
|
|
11630
11838
|
}
|
|
11631
11839
|
await applyTags(provider, decisions, existingTags, { onProgress: log2 });
|
|
11632
|
-
const raw = JSON.parse(await
|
|
11840
|
+
const raw = JSON.parse(await readFile19(ctx.manifestPath, "utf8"));
|
|
11633
11841
|
for (const id of importedAssetIds) raw.assets[id] = ctx.manifest.assets[id];
|
|
11634
11842
|
await writeFile9(ctx.manifestPath, JSON.stringify(raw, null, 2) + "\n");
|
|
11635
11843
|
await saveLock(ctx.lockPath, ctx.lock);
|
|
@@ -11641,7 +11849,7 @@ async function runSalvage(provider, orphans, ctx, opts = {}) {
|
|
|
11641
11849
|
// src/recipes.ts
|
|
11642
11850
|
import path24 from "path";
|
|
11643
11851
|
import { existsSync as existsSync19 } from "fs";
|
|
11644
|
-
import { readdir as readdir3, readFile as
|
|
11852
|
+
import { readdir as readdir3, readFile as readFile20 } from "fs/promises";
|
|
11645
11853
|
import { fileURLToPath } from "url";
|
|
11646
11854
|
import { z as z7 } from "zod";
|
|
11647
11855
|
var SHA256_RE = /^[0-9a-f]{64}$/;
|
|
@@ -11829,7 +12037,7 @@ async function readRecipeFile(recipePath, bundled) {
|
|
|
11829
12037
|
const absolute = path24.resolve(recipePath);
|
|
11830
12038
|
let raw;
|
|
11831
12039
|
try {
|
|
11832
|
-
raw = JSON.parse(await
|
|
12040
|
+
raw = JSON.parse(await readFile20(absolute, "utf8"));
|
|
11833
12041
|
} catch (error) {
|
|
11834
12042
|
throw new Error(`Could not read recipe ${absolute}: ${message4(error)}`, { cause: error });
|
|
11835
12043
|
}
|
|
@@ -11971,12 +12179,12 @@ async function installRecipe(target, options = {}) {
|
|
|
11971
12179
|
];
|
|
11972
12180
|
const files = await Promise.all(sources.map(async (file) => ({
|
|
11973
12181
|
path: path24.join(destination, ...file.path.split("/")),
|
|
11974
|
-
data: await
|
|
12182
|
+
data: await readFile20(file.source)
|
|
11975
12183
|
})));
|
|
11976
12184
|
if (!options.force) {
|
|
11977
12185
|
for (const file of files) {
|
|
11978
12186
|
if (!existsSync19(file.path)) continue;
|
|
11979
|
-
const current = await
|
|
12187
|
+
const current = await readFile20(file.path);
|
|
11980
12188
|
if (!current.equals(file.data)) {
|
|
11981
12189
|
throw new Error(
|
|
11982
12190
|
`Recipe destination has local changes: ${file.path}. Choose another --out or pass --force to replace declared recipe files.`
|
|
@@ -12001,7 +12209,7 @@ async function installRecipe(target, options = {}) {
|
|
|
12001
12209
|
// src/pipeline/quality-regression.ts
|
|
12002
12210
|
import path25 from "path";
|
|
12003
12211
|
import { existsSync as existsSync20 } from "fs";
|
|
12004
|
-
import { readFile as
|
|
12212
|
+
import { readFile as readFile21 } from "fs/promises";
|
|
12005
12213
|
import { z as z8 } from "zod";
|
|
12006
12214
|
var SHA256_RE2 = /^[0-9a-f]{64}$/;
|
|
12007
12215
|
var HEX_RE = /^#[0-9a-f]{6}$/;
|
|
@@ -12157,7 +12365,7 @@ async function measureImageQuality(file) {
|
|
|
12157
12365
|
const absolute = path25.resolve(file);
|
|
12158
12366
|
let bytes;
|
|
12159
12367
|
try {
|
|
12160
|
-
bytes = await
|
|
12368
|
+
bytes = await readFile21(absolute);
|
|
12161
12369
|
} catch (error) {
|
|
12162
12370
|
throw new Error(`Cannot read quality image ${absolute}: ${message5(error)}`, { cause: error });
|
|
12163
12371
|
}
|
|
@@ -12244,7 +12452,7 @@ async function snapshotQualityBaseline(inputs, baselinePath, options = {}) {
|
|
|
12244
12452
|
});
|
|
12245
12453
|
const data = Buffer.from(JSON.stringify(baseline, null, 2) + "\n");
|
|
12246
12454
|
if (existsSync20(absolute) && !options.force) {
|
|
12247
|
-
const current = await
|
|
12455
|
+
const current = await readFile21(absolute);
|
|
12248
12456
|
if (!current.equals(data)) {
|
|
12249
12457
|
throw new Error(`Quality baseline already exists with different content: ${absolute}. Pass --force to replace it.`);
|
|
12250
12458
|
}
|
|
@@ -12256,7 +12464,7 @@ async function readQualityBaseline(baselinePath) {
|
|
|
12256
12464
|
const absolute = path25.resolve(baselinePath);
|
|
12257
12465
|
let raw;
|
|
12258
12466
|
try {
|
|
12259
|
-
raw = JSON.parse(await
|
|
12467
|
+
raw = JSON.parse(await readFile21(absolute, "utf8"));
|
|
12260
12468
|
} catch (error) {
|
|
12261
12469
|
throw new Error(`Could not read quality baseline ${absolute}: ${message5(error)}`, { cause: error });
|
|
12262
12470
|
}
|
|
@@ -13054,7 +13262,7 @@ async function main() {
|
|
|
13054
13262
|
}
|
|
13055
13263
|
if (args.command === "--version" || args.command === "-v") {
|
|
13056
13264
|
const pkg = JSON.parse(
|
|
13057
|
-
await
|
|
13265
|
+
await readFile22(new URL("../package.json", import.meta.url), "utf8")
|
|
13058
13266
|
);
|
|
13059
13267
|
log(`${pkg.name} ${pkg.version}`);
|
|
13060
13268
|
return;
|
|
@@ -13065,7 +13273,7 @@ async function main() {
|
|
|
13065
13273
|
if (!args.out) throw new Error("quality snapshot needs --out <pixelkiln.quality.json>.");
|
|
13066
13274
|
let raw;
|
|
13067
13275
|
try {
|
|
13068
|
-
raw = JSON.parse(await
|
|
13276
|
+
raw = JSON.parse(await readFile22(path26.resolve(args.inputs), "utf8"));
|
|
13069
13277
|
} catch (error) {
|
|
13070
13278
|
throw new Error(
|
|
13071
13279
|
`Could not read quality inputs ${path26.resolve(args.inputs)}: ${error instanceof Error ? error.message : String(error)}`,
|
|
@@ -13356,7 +13564,7 @@ async function main() {
|
|
|
13356
13564
|
if (args.primaryOnly || args.outputRoles.length) {
|
|
13357
13565
|
throw new Error("--primary-only and --output-role require manifest-driven pack");
|
|
13358
13566
|
}
|
|
13359
|
-
const raw = JSON.parse(await
|
|
13567
|
+
const raw = JSON.parse(await readFile22(path26.resolve(args.inputs), "utf8"));
|
|
13360
13568
|
const inputs = resolvePackInputs(raw, args.inputs);
|
|
13361
13569
|
const { png, atlas, skipped, sources } = packSprites(inputs, { columns: args.columns });
|
|
13362
13570
|
const base = path26.resolve(args.out.replace(/\.png$/, ""));
|