pixelkiln 0.32.0 → 0.33.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 +89 -6
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +89 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +89 -6
- package/dist/index.js.map +1 -1
- package/docs/PIXELLAB.md +2 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2285,8 +2285,15 @@ function lockKey(styleId, assetId) {
|
|
|
2285
2285
|
|
|
2286
2286
|
// src/providers/pixellab.ts
|
|
2287
2287
|
function pixelLabObjectUrl(generator, objectId) {
|
|
2288
|
-
if (!objectId
|
|
2289
|
-
|
|
2288
|
+
if (!objectId) return null;
|
|
2289
|
+
if (generator === "map" || generator === "1dir") {
|
|
2290
|
+
return `https://www.pixellab.ai/create-object/${encodeURIComponent(objectId)}`;
|
|
2291
|
+
}
|
|
2292
|
+
if (generator === "tiles") {
|
|
2293
|
+
const setId = objectId.split("#")[0];
|
|
2294
|
+
return setId ? `https://www.pixellab.ai/maps/tiles/${encodeURIComponent(setId)}` : null;
|
|
2295
|
+
}
|
|
2296
|
+
return null;
|
|
2290
2297
|
}
|
|
2291
2298
|
var PixelLabProvider = class _PixelLabProvider {
|
|
2292
2299
|
constructor(client) {
|
|
@@ -2481,6 +2488,7 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
2481
2488
|
if (obj.status === "failed") return { status: "failed", error: "generation failed upstream" };
|
|
2482
2489
|
return { status: "processing" };
|
|
2483
2490
|
} catch (err) {
|
|
2491
|
+
if (err instanceof PixelLabError && err.status === 423) return { status: "processing" };
|
|
2484
2492
|
if (!(err instanceof PixelLabError) || err.status !== 404) throw err;
|
|
2485
2493
|
const survivor = await this.client.getObject(jobId).catch(() => null);
|
|
2486
2494
|
const url = firstUrl(survivor?.rotation_urls) ?? survivor?.preview_url ?? null;
|
|
@@ -9424,6 +9432,18 @@ function renderGallery(snapshot, opts = {}) {
|
|
|
9424
9432
|
background-size:12px 12px; background-position:0 0,6px 6px; display:grid; place-items:center; padding:8px; min-height:96px; }
|
|
9425
9433
|
.pair figure img { image-rendering:pixelated; display:block; max-width:100%; }
|
|
9426
9434
|
.pair figcaption { font:11px/1.4 var(--mono); color:var(--dim); background:var(--panel); padding:2px 6px; margin-top:6px; }
|
|
9435
|
+
.dot.busy { background:var(--cool); animation: pulse 1.2s ease-in-out infinite; }
|
|
9436
|
+
@keyframes pulse { 0%, 100% { opacity:1; } 50% { opacity:.25; } }
|
|
9437
|
+
@media (prefers-reduced-motion: reduce) { .dot.busy { animation:none; } }
|
|
9438
|
+
.card .busy-badge { position:absolute; left:6px; bottom:6px; display:inline-flex; align-items:center; gap:5px;
|
|
9439
|
+
background:var(--panel); color:var(--cool); border:1px solid var(--cool); padding:2px 7px; font:650 10.5px/1.4 var(--mono); }
|
|
9440
|
+
.drawer .job-strip { display:grid; grid-template-columns:auto 1fr; gap:4px 12px; align-items:baseline; margin-top:10px;
|
|
9441
|
+
padding:10px 12px; border:1px solid var(--cool); background:color-mix(in srgb, var(--cool) 8%, var(--panel-deep)); font-size:12.5px; }
|
|
9442
|
+
.drawer .job-strip .ph { display:inline-flex; align-items:center; gap:6px; font:650 12px/1.4 var(--mono); color:var(--cool); white-space:nowrap; }
|
|
9443
|
+
.drawer .job-strip .last { color:var(--dim); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; min-width:0; }
|
|
9444
|
+
.drawer .job-strip .what { grid-column:1 / -1; color:var(--text); }
|
|
9445
|
+
.drawer .job-strip .acts { grid-column:1 / -1; display:flex; gap:6px; }
|
|
9446
|
+
.drawer .job-strip .acts button { padding:3px 9px; font-size:12px; }
|
|
9427
9447
|
.card .pen { position:absolute; right:6px; top:6px; background:var(--panel); color:var(--accent-soft); border:1px solid var(--accent);
|
|
9428
9448
|
font:700 11px/1 var(--mono); padding:3px 5px; }
|
|
9429
9449
|
.hand-actions { display:flex; gap:8px; flex-wrap:wrap; margin-top:8px; }
|
|
@@ -9582,6 +9602,11 @@ const splitTags = (value) => value.split(',').map((t) => t.trim()).filter(Boolea
|
|
|
9582
9602
|
|
|
9583
9603
|
const ACTIVE_PHASES = new Set(['queued', 'submitting', 'polling', 'fetching', 'review']);
|
|
9584
9604
|
const PHASE_TONE = { queued: 'cool', submitting: 'cool', polling: 'cool', fetching: 'cool', review: 'warn', done: 'ok', failed: 'bad' };
|
|
9605
|
+
// The job still working on this record, if any: what the drawer and its card
|
|
9606
|
+
// show while a regeneration, restore, or pull is in flight.
|
|
9607
|
+
const activeJobFor = (item) => GEN.jobs.find((job) => ACTIVE_PHASES.has(job.phase) && job.project === item.project && job.keys.includes(item.key)) || null;
|
|
9608
|
+
const JOB_VERB = { generate: 'regenerating', resume: 'resuming', refresh: 'pulling upstream changes for', restore: 'restoring' };
|
|
9609
|
+
const PHASE_TEXT = { queued: 'queued', submitting: 'submitting to the provider', polling: 'waiting for the provider to finish', fetching: 'downloading the result', review: 'candidates are ready to review' };
|
|
9585
9610
|
const remainingBudget = (provider) => {
|
|
9586
9611
|
const keyed = GEN.budget.byProvider[provider];
|
|
9587
9612
|
const ceiling = keyed !== undefined ? keyed : GEN.budget.amount;
|
|
@@ -9611,10 +9636,33 @@ async function pollJobs() {
|
|
|
9611
9636
|
renderHeader();
|
|
9612
9637
|
let changed = false;
|
|
9613
9638
|
for (const job of GEN.jobs) {
|
|
9614
|
-
if (!ACTIVE_PHASES.has(job.phase) && !ui.settled.has(job.id)) {
|
|
9639
|
+
if (!ACTIVE_PHASES.has(job.phase) && !ui.settled.has(job.id)) {
|
|
9640
|
+
ui.settled.add(job.id); changed = true;
|
|
9641
|
+
// The open record hears how its job ended, where the strip was.
|
|
9642
|
+
const open = ui.open && snap.items.find((i) => i.id === ui.open);
|
|
9643
|
+
if (open && job.project === open.project && job.keys.includes(open.key)) {
|
|
9644
|
+
ui.notice = { id: open.id, text: job.phase === 'failed'
|
|
9645
|
+
? (JOB_VERB[job.mode] || job.mode).replace(/ing$/, 'ing') + ' this asset failed: ' + (job.error || 'see the log above')
|
|
9646
|
+
: job.mode === 'generate' ? 'Regenerated. The new result is on disk and recorded as this generation.'
|
|
9647
|
+
: job.mode === 'restore' ? 'Restored. The recorded bytes are back on disk.'
|
|
9648
|
+
: job.mode === 'refresh' ? (job.counts.downloaded ? 'Pulled the upstream change; the lockfile records the new bytes.' : 'Nothing changed upstream; the file was left alone.')
|
|
9649
|
+
: 'Done.' };
|
|
9650
|
+
}
|
|
9651
|
+
}
|
|
9615
9652
|
if (job.phase === 'review' && !ui.settled.has(job.id + ':review')) { ui.settled.add(job.id + ':review'); changed = true; }
|
|
9616
9653
|
}
|
|
9617
9654
|
if (changed) refresh();
|
|
9655
|
+
else {
|
|
9656
|
+
// A job in flight: keep the open record's strip and the card badges honest without a full re-render.
|
|
9657
|
+
if (ui.open && !$('dialog-host').childNodes.length) renderDrawer();
|
|
9658
|
+
for (const c of document.querySelectorAll('.card')) {
|
|
9659
|
+
const item = snap.items.find((i) => i.id === c.dataset.key);
|
|
9660
|
+
const job = item && activeJobFor(item);
|
|
9661
|
+
const badge = c.querySelector('.busy-badge');
|
|
9662
|
+
if (job && !badge) c.replaceWith(card(item));
|
|
9663
|
+
else if (!job && badge) badge.remove();
|
|
9664
|
+
}
|
|
9665
|
+
}
|
|
9618
9666
|
const active = GEN.jobs.some((job) => ACTIVE_PHASES.has(job.phase) && job.phase !== 'review');
|
|
9619
9667
|
clearTimeout(jobsTimer);
|
|
9620
9668
|
if (active) jobsTimer = setTimeout(pollJobs, 2000);
|
|
@@ -10263,6 +10311,13 @@ function card(item) {
|
|
|
10263
10311
|
if (item.editStatus === 'edited' || item.editStatus === 'regenerated-since') {
|
|
10264
10312
|
const pen = el('span', 'pen', '\u270E'); pen.title = 'hand-edited'; cell.append(pen);
|
|
10265
10313
|
}
|
|
10314
|
+
const job = activeJobFor(item);
|
|
10315
|
+
if (job) {
|
|
10316
|
+
const badge = el('span', 'busy-badge');
|
|
10317
|
+
badge.append(el('i', 'dot busy'), document.createTextNode(job.phase === 'review' ? 'review' : job.mode === 'generate' ? 'generating' : job.mode));
|
|
10318
|
+
badge.title = (JOB_VERB[job.mode] || job.mode) + ' this asset \u2014 ' + (PHASE_TEXT[job.phase] || job.phase);
|
|
10319
|
+
cell.append(badge);
|
|
10320
|
+
}
|
|
10266
10321
|
c.append(cell, body);
|
|
10267
10322
|
c.onclick = (e) => { if (e.shiftKey) toggleCompare(item.id); else openItem(item.id); };
|
|
10268
10323
|
return c;
|
|
@@ -10451,9 +10506,11 @@ function upstreamSection(item) {
|
|
|
10451
10506
|
if (item.upstreamUrl) {
|
|
10452
10507
|
const a = el('a', null, 'Open in ' + item.provider + ' \u2197');
|
|
10453
10508
|
a.href = item.upstreamUrl; a.target = '_blank'; a.rel = 'noopener';
|
|
10454
|
-
|
|
10509
|
+
const tiles = item.generator === 'tiles';
|
|
10510
|
+
row(dl, tiles ? 'tile set' : 'object', a);
|
|
10455
10511
|
row(dl, 'note', item.provider === 'pixellab'
|
|
10456
|
-
? 'The
|
|
10512
|
+
? (tiles ? 'The tile set this generation was chosen from. Edit it there with PixelLab\u2019s editor, then pull the changes back here; the lockfile records the new bytes as this generation.'
|
|
10513
|
+
: 'The account object this generation came from. Edit it there with PixelLab\u2019s editor, then pull the changes back here; the lockfile records the new bytes as this generation.')
|
|
10457
10514
|
: 'The provider\u2019s page for this object.');
|
|
10458
10515
|
}
|
|
10459
10516
|
s.append(dl);
|
|
@@ -10467,6 +10524,30 @@ function upstreamSection(item) {
|
|
|
10467
10524
|
return s;
|
|
10468
10525
|
}
|
|
10469
10526
|
|
|
10527
|
+
// What a job is doing to this record right now. The record's state line still
|
|
10528
|
+
// says what is on disk; this says what is about to replace it.
|
|
10529
|
+
function jobStrip(item, job) {
|
|
10530
|
+
const strip = el('div', 'job-strip');
|
|
10531
|
+
const ph = el('span', 'ph');
|
|
10532
|
+
ph.append(el('i', 'dot busy'), document.createTextNode(job.phase));
|
|
10533
|
+
const last = el('span', 'last', job.messages.length ? job.messages[job.messages.length - 1].trim() : '');
|
|
10534
|
+
last.title = job.messages.join('\\n');
|
|
10535
|
+
const what = el('span', 'what', (JOB_VERB[job.mode] || job.mode) + ' this asset \u2014 ' + (PHASE_TEXT[job.phase] || job.phase) +
|
|
10536
|
+
(job.mode === 'generate' && job.phase !== 'review' ? '. The current file stays until the new result is downloaded.' : '.'));
|
|
10537
|
+
strip.append(ph, last, what);
|
|
10538
|
+
const acts = el('div', 'acts');
|
|
10539
|
+
if (job.phase === 'review' && job.review.includes(item.key)) {
|
|
10540
|
+
const b = el('button', 'primary', 'Review candidates'); b.type = 'button'; b.onclick = () => openReview(job.id);
|
|
10541
|
+
acts.append(b);
|
|
10542
|
+
}
|
|
10543
|
+
const logBtn = el('button', null, ui.logs.has(job.id) ? 'Hide log' : 'Log'); logBtn.type = 'button';
|
|
10544
|
+
logBtn.onclick = () => { ui.logs.has(job.id) ? ui.logs.delete(job.id) : ui.logs.add(job.id); renderDrawer(); };
|
|
10545
|
+
acts.append(logBtn);
|
|
10546
|
+
strip.append(acts);
|
|
10547
|
+
if (ui.logs.has(job.id)) { const pre = el('pre', null, job.messages.join('\\n')); pre.style.gridColumn = '1 / -1'; strip.append(pre); }
|
|
10548
|
+
return strip;
|
|
10549
|
+
}
|
|
10550
|
+
|
|
10470
10551
|
function generateActions(item) {
|
|
10471
10552
|
const row = el('div', 'gen');
|
|
10472
10553
|
if (!GENERATION || !item.declared || item.currentSpecHash === null) return row;
|
|
@@ -11174,7 +11255,9 @@ function renderDrawer() {
|
|
|
11174
11255
|
body.append(previewHost);
|
|
11175
11256
|
if (ui.notice && ui.notice.id === item.id) body.append(el('div', 'notice', ui.notice.text));
|
|
11176
11257
|
body.append(stateNode(item.state, item.reason));
|
|
11177
|
-
|
|
11258
|
+
const job = GENERATION ? activeJobFor(item) : null;
|
|
11259
|
+
if (job) body.append(jobStrip(item, job));
|
|
11260
|
+
else if (GENERATION) body.append(generateActions(item));
|
|
11178
11261
|
// Writes are opt-in flags on the command line; say so where the buttons would be.
|
|
11179
11262
|
if ((!EDITABLE || !GENERATION) && item.declared) {
|
|
11180
11263
|
const missing = [];
|