pixelkiln 0.89.3 → 0.89.5
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/CONTRIBUTING.md +6 -0
- package/dist/cli.js +34 -5
- package/dist/cli.js.map +1 -1
- package/dist/client/gallery.js +30 -7
- package/dist/index.cjs +34 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +34 -5
- package/dist/index.js.map +1 -1
- package/docs/CHARACTERS.md +13 -1
- package/docs/ENDPOINTS.md +8 -0
- package/docs/PIXELLAB.md +2 -2
- package/package.json +1 -1
- package/skills/pixelkiln/references/pixellab.md +5 -1
package/dist/client/gallery.js
CHANGED
|
@@ -362,6 +362,15 @@
|
|
|
362
362
|
}
|
|
363
363
|
|
|
364
364
|
// src/gallery/client/src/form-kit.ts
|
|
365
|
+
var skeletonV3Allowed = (engine) => engine !== "standard";
|
|
366
|
+
var defaultLoopMode = (engine) => skeletonV3Allowed(engine) ? "skeleton-v3" : "template";
|
|
367
|
+
function syncSkeletonOption(select, engine) {
|
|
368
|
+
const option = [...select.options].find((o) => o.value === "skeleton-v3");
|
|
369
|
+
if (!option) return;
|
|
370
|
+
const allowed = skeletonV3Allowed(engine);
|
|
371
|
+
option.disabled = !allowed;
|
|
372
|
+
option.title = allowed ? "" : "PixelLab poses a template only onto a v3 character. A standard base has no 3D skeleton; use template.";
|
|
373
|
+
}
|
|
365
374
|
var nextStep = () => GENERATION ? "Generate it from its record when you are ready." : "Nothing is generated until you run pixelkiln gen.";
|
|
366
375
|
async function addAssetAndOpen(body, where, what = "Added to the manifest.") {
|
|
367
376
|
S.snap = await postEdit(body);
|
|
@@ -455,7 +464,9 @@
|
|
|
455
464
|
);
|
|
456
465
|
}
|
|
457
466
|
mode.append(new Option("v3 (text-described)", "v3"), new Option("pro (sequential, high quality)", "pro"));
|
|
458
|
-
|
|
467
|
+
const engineId = isCharacter ? c.kind === "animation" ? S.snap.styles.find((st) => st.id === item.styleId && st.project === (item.project ?? null))?.characterMode : c.mode : null;
|
|
468
|
+
if (isCharacter) syncSkeletonOption(mode, engineId);
|
|
469
|
+
mode.value = isCharacter ? defaultLoopMode(engineId) : "v3";
|
|
459
470
|
const template = el("input");
|
|
460
471
|
template.type = "text";
|
|
461
472
|
template.placeholder = "e.g. walk, breathing-idle";
|
|
@@ -5204,6 +5215,7 @@
|
|
|
5204
5215
|
const characterStyles = () => S.snap.styles.filter((s) => s.generator === "character" && s.project === projectId());
|
|
5205
5216
|
const chosenEngine = () => ENGINES.find((e) => e.id === engine.value);
|
|
5206
5217
|
const isNewStyle = () => style.value === "";
|
|
5218
|
+
const loopEngine = () => isNewStyle() ? chosenEngine().id : characterStyles().find((s) => s.id === style.value)?.characterMode ?? "standard";
|
|
5207
5219
|
const styleDirections = () => {
|
|
5208
5220
|
if (isNewStyle()) return Number(directions.value) === 4 ? FOUR : COMPASS2;
|
|
5209
5221
|
return COMPASS2;
|
|
@@ -5233,7 +5245,10 @@
|
|
|
5233
5245
|
newStyle.hidden = !isNewStyle();
|
|
5234
5246
|
styleId.required = isNewStyle();
|
|
5235
5247
|
if (!outDirTouched) outDir.value = "art/" + (styleId.value.trim() || "characters");
|
|
5236
|
-
for (const row2 of loops2)
|
|
5248
|
+
for (const row2 of loops2) {
|
|
5249
|
+
syncLoopDirections(row2);
|
|
5250
|
+
syncLoopEngine(row2);
|
|
5251
|
+
}
|
|
5237
5252
|
}
|
|
5238
5253
|
function syncLoopDirections(row2) {
|
|
5239
5254
|
const allowed = styleDirections();
|
|
@@ -5243,6 +5258,13 @@
|
|
|
5243
5258
|
if (wrap.hidden) box.checked = false;
|
|
5244
5259
|
}
|
|
5245
5260
|
}
|
|
5261
|
+
function syncLoopEngine(row2) {
|
|
5262
|
+
const engineId = loopEngine();
|
|
5263
|
+
syncSkeletonOption(row2.mode, engineId);
|
|
5264
|
+
if (!row2.picked) row2.mode.value = defaultLoopMode(engineId);
|
|
5265
|
+
else if (row2.mode.value === "skeleton-v3" && !skeletonV3Allowed(engineId)) row2.mode.value = "template";
|
|
5266
|
+
syncLoopMode(row2);
|
|
5267
|
+
}
|
|
5246
5268
|
function syncLoopMode(row2) {
|
|
5247
5269
|
const t = templated(row2.mode.value);
|
|
5248
5270
|
row2.what.placeholder = t ? "template, e.g. walk, breathing-idle, running-8-frames" : 'the motion, e.g. "swings a sword overhead"';
|
|
@@ -5305,7 +5327,7 @@
|
|
|
5305
5327
|
id.value = defaults.id ?? "";
|
|
5306
5328
|
const mode = el("select");
|
|
5307
5329
|
for (const m of LOOP_MODES) mode.append(new Option(m.label, m.id));
|
|
5308
|
-
mode.value = defaults.mode ??
|
|
5330
|
+
mode.value = defaults.mode ?? defaultLoopMode(loopEngine());
|
|
5309
5331
|
const what = el("input");
|
|
5310
5332
|
what.type = "text";
|
|
5311
5333
|
what.required = true;
|
|
@@ -5325,7 +5347,7 @@
|
|
|
5325
5347
|
remove.type = "button";
|
|
5326
5348
|
const preview = el("div", "studio-tpl-preview");
|
|
5327
5349
|
preview.hidden = true;
|
|
5328
|
-
const row2 = { id, mode, what, directions: boxes, wrap, preview };
|
|
5350
|
+
const row2 = { id, mode, picked: defaults.mode !== void 0, what, directions: boxes, wrap, preview };
|
|
5329
5351
|
what.addEventListener("input", () => syncPreview(row2));
|
|
5330
5352
|
remove.onclick = () => {
|
|
5331
5353
|
loops2.splice(loops2.indexOf(row2), 1);
|
|
@@ -5334,6 +5356,7 @@
|
|
|
5334
5356
|
keepDraft();
|
|
5335
5357
|
};
|
|
5336
5358
|
mode.onchange = () => {
|
|
5359
|
+
row2.picked = true;
|
|
5337
5360
|
syncLoopMode(row2);
|
|
5338
5361
|
schedulePrice();
|
|
5339
5362
|
};
|
|
@@ -5342,8 +5365,8 @@
|
|
|
5342
5365
|
wrap.append(line, preview, dirs, remove);
|
|
5343
5366
|
loopList.append(wrap);
|
|
5344
5367
|
loops2.push(row2);
|
|
5345
|
-
syncLoopMode(row2);
|
|
5346
5368
|
syncLoopDirections(row2);
|
|
5369
|
+
syncLoopEngine(row2);
|
|
5347
5370
|
}
|
|
5348
5371
|
function edits() {
|
|
5349
5372
|
const out = [];
|
|
@@ -5646,8 +5669,8 @@
|
|
|
5646
5669
|
note.append(discard);
|
|
5647
5670
|
form.prepend(note);
|
|
5648
5671
|
} else {
|
|
5649
|
-
addLoopRow({ id: "walk",
|
|
5650
|
-
addLoopRow({ id: "idle",
|
|
5672
|
+
addLoopRow({ id: "walk", what: "walk", directions: ["south", "west", "north"] });
|
|
5673
|
+
addLoopRow({ id: "idle", what: "breathing-idle", directions: ["south"] });
|
|
5651
5674
|
}
|
|
5652
5675
|
updatePrice();
|
|
5653
5676
|
setTimeout(() => baseId.focus(), 0);
|
package/dist/index.cjs
CHANGED
|
@@ -2438,7 +2438,8 @@ var PixelLabClient = class {
|
|
|
2438
2438
|
});
|
|
2439
2439
|
const text = await res.text();
|
|
2440
2440
|
if (!res.ok) {
|
|
2441
|
-
|
|
2441
|
+
const hint = res.status === 500 && text.includes("Failed to start any animation jobs") ? '; a skeleton-v3 loop needs a character with a stored 3D skeleton (a v3 base), use mode: "template" on a standard one' : "";
|
|
2442
|
+
throw new PixelLabError(`${init?.method ?? "GET"} ${path39} \u2192 ${res.status}${hint}`, res.status, text);
|
|
2442
2443
|
}
|
|
2443
2444
|
if (!text) return {};
|
|
2444
2445
|
try {
|
|
@@ -6064,6 +6065,11 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
6064
6065
|
if (character.animation.mode !== "template" && character.animation.mode !== "skeleton-v3" && !spec.prompt.trim()) {
|
|
6065
6066
|
throw new Error(`${spec.styleId}/${spec.assetId} needs a prompt describing the motion, or a template`);
|
|
6066
6067
|
}
|
|
6068
|
+
if (character.animation.mode === "skeleton-v3" && character.mode === "standard") {
|
|
6069
|
+
throw new Error(
|
|
6070
|
+
`${spec.styleId}/${spec.assetId}: skeleton-v3 poses a template onto a 3D skeleton, which a ${character.mode} character has none of; use a v3 base (style mode: "v3"), or mode: "template" for this loop`
|
|
6071
|
+
);
|
|
6072
|
+
}
|
|
6067
6073
|
}
|
|
6068
6074
|
}
|
|
6069
6075
|
validateObjectPro(spec, styleImages = []) {
|
|
@@ -6370,7 +6376,10 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
6370
6376
|
stateName: character.state_name ?? null,
|
|
6371
6377
|
directions: character.directions,
|
|
6372
6378
|
size: character.size,
|
|
6373
|
-
updatedAt: character.updated_at ?? null
|
|
6379
|
+
updatedAt: character.updated_at ?? null,
|
|
6380
|
+
// Kept: this metadata replaces the submit-time `character` entry,
|
|
6381
|
+
// and the job id is the only way to read the bill again later.
|
|
6382
|
+
...backgroundJobId ? { backgroundJobId } : {}
|
|
6374
6383
|
}
|
|
6375
6384
|
},
|
|
6376
6385
|
billed: await this.billedForJob(backgroundJobId)
|
|
@@ -7021,7 +7030,12 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
7021
7030
|
if (!backgroundJobId) return null;
|
|
7022
7031
|
try {
|
|
7023
7032
|
const job = await this.client.getBackgroundJob(backgroundJobId);
|
|
7024
|
-
|
|
7033
|
+
const nested = job.last_response?.billing_usage;
|
|
7034
|
+
for (const usage of [job.usage, nested]) {
|
|
7035
|
+
const billed = billedFromUsage(usage);
|
|
7036
|
+
if (billed && !(billed.unit === "usd" && billed.amount === 0)) return billed;
|
|
7037
|
+
}
|
|
7038
|
+
return null;
|
|
7025
7039
|
} catch {
|
|
7026
7040
|
return null;
|
|
7027
7041
|
}
|
|
@@ -17021,8 +17035,18 @@ GROUPS.forEach((g, gi) => {
|
|
|
17021
17035
|
preview.loading = 'lazy';
|
|
17022
17036
|
const index = document.createElement('span');
|
|
17023
17037
|
index.className = 'idx';
|
|
17024
|
-
|
|
17025
|
-
|
|
17038
|
+
// A frame's own size, not the manifest's: a 96px loop of a 64px
|
|
17039
|
+
// character is 96\xD796. Known up front for a cached file, else once loaded.
|
|
17040
|
+
const label = (w, h) => {
|
|
17041
|
+
index.textContent = (g.frameLabels?.[i] || String(i + 1)) + ' \xB7 ' + w + '\xD7' + h + ' \xB7 ' + scaleLabel;
|
|
17042
|
+
};
|
|
17043
|
+
const known = g.frameSizes?.[i];
|
|
17044
|
+
label(known ? known.width : g.width, known ? known.height : g.height);
|
|
17045
|
+
if (!known) {
|
|
17046
|
+
preview.addEventListener('load', () => {
|
|
17047
|
+
if (preview.naturalWidth && preview.naturalHeight) label(preview.naturalWidth, preview.naturalHeight);
|
|
17048
|
+
}, { once: true });
|
|
17049
|
+
}
|
|
17026
17050
|
c.append(preview, index);
|
|
17027
17051
|
if (g.recommendedIndex != null && i === recommended) {
|
|
17028
17052
|
const badge = document.createElement('span');
|
|
@@ -17284,12 +17308,15 @@ async function prepareReview(provider, lock, opts = {}) {
|
|
|
17284
17308
|
const urls = state.status === "review" ? state.candidateUrls : state.frameUrls;
|
|
17285
17309
|
const frameUrls = [];
|
|
17286
17310
|
const frameAssets = [];
|
|
17311
|
+
const frameSizes = [];
|
|
17287
17312
|
for (const [index, url] of urls.entries()) {
|
|
17288
17313
|
const cached = await cachedFrame(url);
|
|
17289
17314
|
if (!cached) {
|
|
17290
17315
|
frameUrls.push(url);
|
|
17316
|
+
frameSizes.push(null);
|
|
17291
17317
|
continue;
|
|
17292
17318
|
}
|
|
17319
|
+
frameSizes.push(cached.contentType === "image/png" ? pngSize(await (0, import_promises19.readFile)(cached.path).catch(() => Buffer.alloc(0))) : null);
|
|
17293
17320
|
const route = `${prefix}/review-frame/${encodeURIComponent(String(groups.length))}/${index}`;
|
|
17294
17321
|
frameAssets.push([route, cached]);
|
|
17295
17322
|
frameUrls.push(route);
|
|
@@ -17315,6 +17342,7 @@ async function prepareReview(provider, lock, opts = {}) {
|
|
|
17315
17342
|
prompt: entry.prompt,
|
|
17316
17343
|
reviewObjectId: entry.reviewObjectId,
|
|
17317
17344
|
frameUrls,
|
|
17345
|
+
...frameSizes.some(Boolean) ? { frameSizes } : {},
|
|
17318
17346
|
width: entry.width,
|
|
17319
17347
|
height: entry.height,
|
|
17320
17348
|
...state.status === "review-set" ? {
|
|
@@ -19197,6 +19225,7 @@ async function buildGallerySnapshot(opts) {
|
|
|
19197
19225
|
view: style?.view ?? null,
|
|
19198
19226
|
noBackground: style?.noBackground ?? true,
|
|
19199
19227
|
quality: Boolean(style?.quality),
|
|
19228
|
+
characterMode: style?.generator === "character" ? style.mode ?? "standard" : null,
|
|
19200
19229
|
characters: style?.generator === "character" || style?.generator === "objectPro" ? {
|
|
19201
19230
|
bases: styleItems.filter((item) => item.character?.kind === "base").length,
|
|
19202
19231
|
states: styleItems.filter((item) => item.character?.kind === "state").length,
|