pixelkiln 0.79.0 → 0.80.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/client/gallery.css +9 -0
- package/dist/client/gallery.js +88 -17
- package/docs/CLI.md +13 -0
- package/package.json +1 -1
package/dist/client/gallery.css
CHANGED
|
@@ -475,3 +475,12 @@ td.col-on { background:rgba(255,107,53,.08); }
|
|
|
475
475
|
.gap-price { font:11.5px/1.45 var(--mono); }
|
|
476
476
|
.gap-panel .actions { display:flex; gap:6px; flex-wrap:wrap; }
|
|
477
477
|
@media (max-width: 640px) { .lg-add { width:56px; height:56px; } }
|
|
478
|
+
|
|
479
|
+
/* ---- drawer tabs ---- */
|
|
480
|
+
.dtabs { margin-top:14px; }
|
|
481
|
+
.dtabs .tablist { display:flex; gap:0; border-bottom:1px solid var(--line-strong); position:sticky; top:0; z-index:2; background:var(--panel); }
|
|
482
|
+
.dtabs .tablist button { border:0; border-bottom:2px solid transparent; margin-bottom:-1px; padding:8px 12px; font:600 12.5px/1 var(--mono); color:var(--dim); }
|
|
483
|
+
.dtabs .tablist button:hover { color:var(--text); }
|
|
484
|
+
.dtabs .tablist button.on { color:var(--accent-soft); border-bottom-color:var(--accent); }
|
|
485
|
+
.dtabs .tabpanel[hidden] { display:none; }
|
|
486
|
+
.dtabs .tabpanel > section.meta:first-child { border-top:0; }
|
package/dist/client/gallery.js
CHANGED
|
@@ -46,10 +46,11 @@
|
|
|
46
46
|
fps: null,
|
|
47
47
|
onion: false,
|
|
48
48
|
selecting: false,
|
|
49
|
-
selected: /* @__PURE__ */ new Set()
|
|
49
|
+
selected: /* @__PURE__ */ new Set(),
|
|
50
|
+
drawerTab: "overview"
|
|
50
51
|
};
|
|
51
52
|
var PREFS_KEY = "pixelkiln.gallery.prefs";
|
|
52
|
-
var PREF_NAMES = ["playLoops", "backdrop", "notify", "onion"];
|
|
53
|
+
var PREF_NAMES = ["playLoops", "backdrop", "notify", "onion", "drawerTab"];
|
|
53
54
|
function loadPrefs() {
|
|
54
55
|
try {
|
|
55
56
|
const saved = JSON.parse(localStorage.getItem(PREFS_KEY) || "{}");
|
|
@@ -4052,6 +4053,66 @@
|
|
|
4052
4053
|
const host = document.querySelector(".drawer .dbody > div");
|
|
4053
4054
|
return host ? host.__frames ?? null : null;
|
|
4054
4055
|
}
|
|
4056
|
+
var TAB_LABEL = { overview: "Overview", files: "Files", history: "History", details: "Details" };
|
|
4057
|
+
var OVERVIEW_FORMS = /^(state|anim|poses|revise|skeleton):/;
|
|
4058
|
+
function drawerTabs(item, panels) {
|
|
4059
|
+
const wrap = el("div", "dtabs");
|
|
4060
|
+
const available = Object.keys(panels).filter((name) => panels[name].childNodes.length);
|
|
4061
|
+
if (ui.editing && OVERVIEW_FORMS.test(ui.editing)) ui.drawerTab = "overview";
|
|
4062
|
+
const current = available.includes(ui.drawerTab) ? ui.drawerTab : "overview";
|
|
4063
|
+
const list = el("div", "tablist");
|
|
4064
|
+
list.setAttribute("role", "tablist");
|
|
4065
|
+
const count = {
|
|
4066
|
+
files: item.outputs.length,
|
|
4067
|
+
history: item.history.length
|
|
4068
|
+
};
|
|
4069
|
+
const buttons = [];
|
|
4070
|
+
for (const name of available) {
|
|
4071
|
+
const b = el("button", name === current ? "on" : null, TAB_LABEL[name] + (count[name] ? " " + count[name] : ""));
|
|
4072
|
+
b.type = "button";
|
|
4073
|
+
b.id = "dtab-" + name;
|
|
4074
|
+
b.setAttribute("role", "tab");
|
|
4075
|
+
b.setAttribute("aria-selected", String(name === current));
|
|
4076
|
+
b.setAttribute("aria-controls", "dpanel-" + name);
|
|
4077
|
+
b.tabIndex = name === current ? 0 : -1;
|
|
4078
|
+
b.onclick = () => {
|
|
4079
|
+
ui.drawerTab = name;
|
|
4080
|
+
show(name);
|
|
4081
|
+
};
|
|
4082
|
+
b.onkeydown = (e) => {
|
|
4083
|
+
const at = available.indexOf(name);
|
|
4084
|
+
const next = e.key === "ArrowRight" ? available[(at + 1) % available.length] : e.key === "ArrowLeft" ? available[(at - 1 + available.length) % available.length] : null;
|
|
4085
|
+
if (!next) return;
|
|
4086
|
+
e.preventDefault();
|
|
4087
|
+
e.stopPropagation();
|
|
4088
|
+
ui.drawerTab = next;
|
|
4089
|
+
show(next);
|
|
4090
|
+
buttons[available.indexOf(next)].focus();
|
|
4091
|
+
};
|
|
4092
|
+
buttons.push(b);
|
|
4093
|
+
list.append(b);
|
|
4094
|
+
}
|
|
4095
|
+
const show = (name) => {
|
|
4096
|
+
for (const [i, b] of buttons.entries()) {
|
|
4097
|
+
const on = available[i] === name;
|
|
4098
|
+
b.classList.toggle("on", on);
|
|
4099
|
+
b.setAttribute("aria-selected", String(on));
|
|
4100
|
+
b.tabIndex = on ? 0 : -1;
|
|
4101
|
+
}
|
|
4102
|
+
for (const n of available) panels[n].hidden = n !== name;
|
|
4103
|
+
};
|
|
4104
|
+
wrap.append(list);
|
|
4105
|
+
for (const name of available) {
|
|
4106
|
+
const panel = panels[name];
|
|
4107
|
+
panel.className = "tabpanel";
|
|
4108
|
+
panel.id = "dpanel-" + name;
|
|
4109
|
+
panel.setAttribute("role", "tabpanel");
|
|
4110
|
+
panel.setAttribute("aria-labelledby", "dtab-" + name);
|
|
4111
|
+
wrap.append(panel);
|
|
4112
|
+
}
|
|
4113
|
+
show(current);
|
|
4114
|
+
return wrap;
|
|
4115
|
+
}
|
|
4055
4116
|
var CHARACTER_KIND = {
|
|
4056
4117
|
base: "base",
|
|
4057
4118
|
state: "state (a pose or outfit of its parent)",
|
|
@@ -4129,11 +4190,12 @@
|
|
|
4129
4190
|
}
|
|
4130
4191
|
if (canEdit && ui.editing === item.id) body.append(editForm(item));
|
|
4131
4192
|
if (item.error && !item.reason.includes(item.error)) body.append(stateNode("failed", item.error));
|
|
4193
|
+
const tab = { overview: el("div"), files: el("div"), history: el("div"), details: el("div") };
|
|
4194
|
+
let pane = tab.overview;
|
|
4132
4195
|
{
|
|
4133
4196
|
const { s, dl } = section("Generation");
|
|
4134
4197
|
row(dl, "provider", item.provider);
|
|
4135
4198
|
row(dl, "generator", item.generator + (item.tileFeature ? " · " + item.tileFeature : ""));
|
|
4136
|
-
row(dl, "lock status", item.status || "none (nothing submitted)");
|
|
4137
4199
|
const pr = el("div", "prompt", item.prompt);
|
|
4138
4200
|
row(dl, item.status ? "prompt sent" : "prompt", pr);
|
|
4139
4201
|
if (item.currentPrompt) row(dl, "prompt now", el("div", "prompt state-warn", item.currentPrompt));
|
|
@@ -4159,13 +4221,19 @@
|
|
|
4159
4221
|
row(dl, "estimate now", fmtCost(item.costUnit, item.estimatedCost) + (item.candidates && item.candidates > 1 ? " for " + item.candidates + " candidates" : ""));
|
|
4160
4222
|
}
|
|
4161
4223
|
if (item.candidateIndex !== null) row(dl, "candidate", "#" + (item.candidateIndex + 1) + " chosen in review");
|
|
4224
|
+
pane.append(s);
|
|
4225
|
+
}
|
|
4226
|
+
{
|
|
4227
|
+
const { s, dl } = section("Provider record");
|
|
4228
|
+
row(dl, "lock status", item.status || "none (nothing submitted)");
|
|
4162
4229
|
row(dl, "submitted", fmtWhen(item.submittedAt));
|
|
4163
4230
|
row(dl, "downloaded", fmtWhen(item.downloadedAt));
|
|
4164
4231
|
row(dl, "job id", item.jobId, { mono: true, copy: item.jobId });
|
|
4165
4232
|
row(dl, "object id", item.objectId, { mono: true, copy: item.objectId });
|
|
4166
4233
|
row(dl, "review object", item.reviewObjectId, { mono: true, copy: item.reviewObjectId });
|
|
4167
|
-
|
|
4234
|
+
tab.details.append(s);
|
|
4168
4235
|
}
|
|
4236
|
+
pane = tab.files;
|
|
4169
4237
|
{
|
|
4170
4238
|
const s = el("section", "meta");
|
|
4171
4239
|
s.append(el("h3", null, item.outputs.length === 1 ? "Output" : "Outputs (" + item.outputs.length + ")"));
|
|
@@ -4191,14 +4259,15 @@
|
|
|
4191
4259
|
box.append(dl);
|
|
4192
4260
|
s.append(box);
|
|
4193
4261
|
}
|
|
4194
|
-
|
|
4262
|
+
pane.append(s);
|
|
4195
4263
|
}
|
|
4196
4264
|
const hand = handEditSection(item);
|
|
4197
|
-
if (hand)
|
|
4265
|
+
if (hand) pane.append(hand);
|
|
4198
4266
|
const upstream = upstreamSection(item);
|
|
4199
|
-
if (upstream)
|
|
4267
|
+
if (upstream) pane.append(upstream);
|
|
4200
4268
|
const versions = historySection(item);
|
|
4201
|
-
if (versions)
|
|
4269
|
+
if (versions) tab.history.append(versions);
|
|
4270
|
+
pane = tab.overview;
|
|
4202
4271
|
if (item.character) {
|
|
4203
4272
|
const c = item.character;
|
|
4204
4273
|
const { s, dl } = section("Character");
|
|
@@ -4249,19 +4318,19 @@
|
|
|
4249
4318
|
if (ui.editing === stateKey) s.append(newStateForm(item));
|
|
4250
4319
|
if (ui.editing === animKey) s.append(newAnimationForm(item));
|
|
4251
4320
|
}
|
|
4252
|
-
|
|
4321
|
+
pane.append(s);
|
|
4253
4322
|
}
|
|
4254
4323
|
if (item.mirrorOfKey) {
|
|
4255
4324
|
const { s, dl } = section("Mirror");
|
|
4256
4325
|
row(dl, "flipped from", keyLink(item, item.mirrorOfKey));
|
|
4257
4326
|
row(dl, "cost", "none; made locally from that asset's files");
|
|
4258
|
-
|
|
4327
|
+
pane.append(s);
|
|
4259
4328
|
}
|
|
4260
4329
|
const mirrors = S.snap.items.filter((i) => i.mirrorOfKey === item.key && i.project === item.project);
|
|
4261
4330
|
if (mirrors.length) {
|
|
4262
4331
|
const { s, dl } = section(mirrors.length === 1 ? "Mirror of this asset" : "Mirrors of this asset");
|
|
4263
4332
|
for (const m of mirrors) row(dl, m.character && m.character.direction ? m.character.direction : "flipped", keyLink(item, m.key));
|
|
4264
|
-
|
|
4333
|
+
pane.append(s);
|
|
4265
4334
|
}
|
|
4266
4335
|
if (item.revision || item.revisionParentKey) {
|
|
4267
4336
|
const { s, dl } = section("Lineage");
|
|
@@ -4297,7 +4366,7 @@
|
|
|
4297
4366
|
row(dl, "poses", el("span", "state-warn", "the keypoints file does not exist yet"));
|
|
4298
4367
|
}
|
|
4299
4368
|
}
|
|
4300
|
-
|
|
4369
|
+
pane.append(s);
|
|
4301
4370
|
}
|
|
4302
4371
|
const children = S.snap.items.filter((i) => i.revisionParentKey === item.key && i.project === item.project);
|
|
4303
4372
|
const reviseKey = "revise:" + item.id;
|
|
@@ -4330,8 +4399,9 @@
|
|
|
4330
4399
|
if (ui.editing === reviseKey) s.append(newRevisionForm(item));
|
|
4331
4400
|
if (ui.editing === skeletonKey) s.append(skeletonAnimationForm(item));
|
|
4332
4401
|
}
|
|
4333
|
-
|
|
4402
|
+
pane.append(s);
|
|
4334
4403
|
}
|
|
4404
|
+
pane = tab.details;
|
|
4335
4405
|
if (item.quality) {
|
|
4336
4406
|
const q = item.quality;
|
|
4337
4407
|
const { s, dl } = section("Quality");
|
|
@@ -4363,7 +4433,7 @@
|
|
|
4363
4433
|
row(dl, "output", q.output, { mono: true });
|
|
4364
4434
|
row(dl, "record", q.record + (q.recordExists ? "" : " (not written yet)"), { mono: true });
|
|
4365
4435
|
if (q.check && !q.check.safe) row(dl, "check", el("div", "state-warn", q.check.reasons.join("\n")));
|
|
4366
|
-
|
|
4436
|
+
pane.append(s);
|
|
4367
4437
|
if (q.outputs.some((o) => o.url)) {
|
|
4368
4438
|
const strip = el("div", "members");
|
|
4369
4439
|
for (const o of q.outputs.filter((o2) => o2.url)) {
|
|
@@ -4398,15 +4468,16 @@
|
|
|
4398
4468
|
row(dl, "category", item.category);
|
|
4399
4469
|
row(dl, "tags", item.tags && item.tags.length ? item.tags.join(", ") : null);
|
|
4400
4470
|
row(dl, "source art", item.source, { mono: true });
|
|
4401
|
-
|
|
4471
|
+
pane.append(s);
|
|
4402
4472
|
}
|
|
4403
4473
|
if (item.asset) {
|
|
4404
4474
|
const declared = Object.fromEntries(Object.entries(item.asset).filter(([, v]) => !(Array.isArray(v) && !v.length) && !(v && typeof v === "object" && !Array.isArray(v) && !Object.keys(v).length)));
|
|
4405
|
-
|
|
4475
|
+
pane.append(jsonDetails("Manifest asset", declared));
|
|
4406
4476
|
}
|
|
4407
4477
|
if (item.providerMetadata && Object.keys(item.providerMetadata).length) {
|
|
4408
|
-
|
|
4478
|
+
pane.append(jsonDetails("Provider metadata", item.providerMetadata));
|
|
4409
4479
|
}
|
|
4480
|
+
body.append(drawerTabs(item, tab));
|
|
4410
4481
|
drawer.append(head, body);
|
|
4411
4482
|
host.append(scrim, drawer);
|
|
4412
4483
|
close.focus({ preventScroll: true });
|
package/docs/CLI.md
CHANGED
|
@@ -458,6 +458,19 @@ provider, and never writes anything. Stop it with Ctrl+C. See the
|
|
|
458
458
|
[Getting started guide](GETTING_STARTED.md#start-a-new-project) for a
|
|
459
459
|
screenshot.
|
|
460
460
|
|
|
461
|
+
A record's drawer keeps the preview, its state, and its actions at the
|
|
462
|
+
top. The rest of the record is split into tabs:
|
|
463
|
+
|
|
464
|
+
- **Overview:** the generation, the character family, mirrors, lineage and
|
|
465
|
+
revisions, and the forms that add to them.
|
|
466
|
+
- **Files:** outputs, hand edits, and upstream edits.
|
|
467
|
+
- **History:** earlier generations, which can be brought back.
|
|
468
|
+
- **Details:** the provider record (job and object ids, times), the quality
|
|
469
|
+
record, identity, and the raw manifest asset and provider metadata.
|
|
470
|
+
|
|
471
|
+
The tab in view stays as you step through records, and ← and → move
|
|
472
|
+
between tabs once one has focus.
|
|
473
|
+
|
|
461
474
|
#### Looking at sprites
|
|
462
475
|
|
|
463
476
|
- **Loops on the grid:** a loop's card plays while the pointer is over it.
|