pixelkiln 0.55.0 → 0.56.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 +70 -10
- package/dist/cli.js.map +1 -1
- package/dist/client/gallery.js +152 -0
- package/dist/index.cjs +70 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +262 -2
- package/dist/index.d.ts +262 -2
- package/dist/index.js +70 -10
- package/dist/index.js.map +1 -1
- package/docs/CHARACTERS.md +6 -1
- package/docs/GENERATORS.md +5 -0
- package/package.json +1 -1
- package/skills/pixelkiln/references/pixellab.md +7 -1
package/dist/client/gallery.js
CHANGED
|
@@ -1675,6 +1675,144 @@ function newRevisionForm(item) {
|
|
|
1675
1675
|
return form;
|
|
1676
1676
|
}
|
|
1677
1677
|
|
|
1678
|
+
/**
|
|
1679
|
+
* A new pose or outfit of `item`, an existing `character`/`objectPro` base
|
|
1680
|
+
* or state — the same family, restricted to `item`'s own style. A larger
|
|
1681
|
+
* canvas needs both width and height; leaving either blank keeps the
|
|
1682
|
+
* parent's.
|
|
1683
|
+
*/
|
|
1684
|
+
function newStateForm(item) {
|
|
1685
|
+
const pr = projectOf(item);
|
|
1686
|
+
const form = el('form', 'edit');
|
|
1687
|
+
form.append(el('h3', null, 'New state of ' + item.assetId));
|
|
1688
|
+
const id = el('input'); id.type = 'text'; id.placeholder = 'asset-id'; id.required = true; id.autocomplete = 'off';
|
|
1689
|
+
id.pattern = '[^\\/\\\\]+';
|
|
1690
|
+
const prompt = el('textarea'); prompt.placeholder = 'What changes, e.g. "the chest lid open".'; prompt.required = true;
|
|
1691
|
+
const paletteFromReference = el('input'); paletteFromReference.type = 'checkbox';
|
|
1692
|
+
const paletteField = el('label', 'field check'); paletteField.append(paletteFromReference, el('span', null, 'snap colours to the parent'));
|
|
1693
|
+
const width = numberInput(null, 'parent’s'), height = numberInput(null, 'parent’s');
|
|
1694
|
+
const row1 = el('div', 'row');
|
|
1695
|
+
row1.append(field('new asset id', id), field('canvas width', width), field('canvas height', height));
|
|
1696
|
+
form.append(row1, field('what changes', prompt, 'The style still adds its prefix and suffix.'), paletteField);
|
|
1697
|
+
const actions = el('div', 'actions');
|
|
1698
|
+
const save = el('button', 'primary', 'Create state'); save.type = 'submit';
|
|
1699
|
+
const cancel = el('button', null, 'Cancel'); cancel.type = 'button'; cancel.onclick = () => { ui.editing = null; renderDrawer(); };
|
|
1700
|
+
const msg = el('span', 'msg');
|
|
1701
|
+
actions.append(save, cancel, msg);
|
|
1702
|
+
form.append(actions);
|
|
1703
|
+
form.onsubmit = async (e) => {
|
|
1704
|
+
e.preventDefault();
|
|
1705
|
+
save.disabled = true; msg.className = 'msg'; msg.textContent = 'saving…';
|
|
1706
|
+
const state = { of: item.assetId };
|
|
1707
|
+
if (paletteFromReference.checked) state.paletteFromReference = true;
|
|
1708
|
+
const w = numberOrNull(width), h = numberOrNull(height);
|
|
1709
|
+
if (w !== null && h !== null) state.canvas = { width: w, height: h };
|
|
1710
|
+
const asset = { prompt: prompt.value, styles: [item.styleId], state };
|
|
1711
|
+
try {
|
|
1712
|
+
const body = { action: 'add-asset', assetId: id.value.trim(), expectedSha256: pr.manifestSha256, asset };
|
|
1713
|
+
if (item.project) body.project = item.project;
|
|
1714
|
+
snap = await postEdit(body);
|
|
1715
|
+
const newId = (item.project ? item.project + ':' : '') + item.styleId + '/' + id.value.trim();
|
|
1716
|
+
ui.editing = null;
|
|
1717
|
+
ui.notice = { id: newId, text: 'Added to the manifest. Nothing is generated until you run pixelkiln gen.' };
|
|
1718
|
+
render();
|
|
1719
|
+
if (snap.items.some((i) => i.id === newId)) openItem(newId);
|
|
1720
|
+
} catch (err) {
|
|
1721
|
+
save.disabled = false;
|
|
1722
|
+
msg.className = 'msg bad';
|
|
1723
|
+
msg.textContent = err.message + (err.status === 409 ? ' Press Refresh.' : '');
|
|
1724
|
+
}
|
|
1725
|
+
};
|
|
1726
|
+
setTimeout(() => id.focus(), 0);
|
|
1727
|
+
return form;
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
/**
|
|
1731
|
+
* A new loop of `item`, an existing `character`/`objectPro` base or state.
|
|
1732
|
+
* `objectPro` has no skeleton/template concept, so `template` and `subject`
|
|
1733
|
+
* are only offered for a `character`; `startFrame`/`endFrame` and template
|
|
1734
|
+
* `outline`/`shading`/`detail` hints need a manifest-relative image the
|
|
1735
|
+
* gallery does not offer, so hand-edit the manifest for those.
|
|
1736
|
+
*/
|
|
1737
|
+
function newAnimationForm(item) {
|
|
1738
|
+
const pr = projectOf(item);
|
|
1739
|
+
const c = item.character;
|
|
1740
|
+
const isCharacter = c.generator === 'character';
|
|
1741
|
+
const form = el('form', 'edit');
|
|
1742
|
+
form.append(el('h3', null, 'New animation of ' + item.assetId));
|
|
1743
|
+
const id = el('input'); id.type = 'text'; id.placeholder = 'asset-id'; id.required = true; id.autocomplete = 'off';
|
|
1744
|
+
id.pattern = '[^\\/\\\\]+';
|
|
1745
|
+
const prompt = el('textarea'); prompt.placeholder = 'What the loop does, e.g. "a slow idle sway".'; prompt.required = true;
|
|
1746
|
+
const mode = el('select');
|
|
1747
|
+
if (isCharacter) mode.append(new Option('template', 'template'));
|
|
1748
|
+
mode.append(new Option('v3 (text-described)', 'v3'), new Option('pro (sequential, high quality)', 'pro'));
|
|
1749
|
+
mode.value = 'v3';
|
|
1750
|
+
const template = el('input'); template.type = 'text'; template.placeholder = 'e.g. walk, breathing-idle';
|
|
1751
|
+
const templateField = field('template id', template, 'A PixelLab template; costs 1 generation regardless of frame count.');
|
|
1752
|
+
const frames = el('input'); frames.type = 'number'; frames.min = '4'; frames.max = '16'; frames.step = '2'; frames.placeholder = '8';
|
|
1753
|
+
const fps = el('input'); fps.type = 'number'; fps.min = '1'; fps.max = '60'; fps.placeholder = '8';
|
|
1754
|
+
const directionField = (() => {
|
|
1755
|
+
if (c.directions === 1) return null;
|
|
1756
|
+
const set = c.directions === 4 ? ['south', 'west', 'east', 'north'] : ['south', 'south-east', 'east', 'north-east', 'north', 'north-west', 'west', 'south-west'];
|
|
1757
|
+
const select = el('select');
|
|
1758
|
+
for (const d of set) select.append(new Option(d, d));
|
|
1759
|
+
return { select, wrap: field('direction', select) };
|
|
1760
|
+
})();
|
|
1761
|
+
const subject = isCharacter ? el('input') : null;
|
|
1762
|
+
if (subject) { subject.type = 'text'; subject.placeholder = 'optional, when the prompt alone would mislead the model'; }
|
|
1763
|
+
const row1 = el('div', 'row');
|
|
1764
|
+
row1.append(field('new asset id', id), field('mode', mode));
|
|
1765
|
+
if (directionField) row1.append(directionField.wrap);
|
|
1766
|
+
form.append(row1, field('what it does', prompt, 'The style still adds its prefix and suffix.'));
|
|
1767
|
+
if (isCharacter) form.append(templateField);
|
|
1768
|
+
const row2 = el('div', 'row'); row2.append(field('frames', frames), field('fps', fps));
|
|
1769
|
+
form.append(row2);
|
|
1770
|
+
if (subject) form.append(field('subject override', subject));
|
|
1771
|
+
const syncMode = () => {
|
|
1772
|
+
const isTemplate = isCharacter && mode.value === 'template';
|
|
1773
|
+
templateField.style.display = isTemplate ? '' : 'none';
|
|
1774
|
+
row2.style.display = isTemplate ? 'none' : '';
|
|
1775
|
+
};
|
|
1776
|
+
if (isCharacter) { mode.onchange = syncMode; syncMode(); } else { templateField.style.display = 'none'; }
|
|
1777
|
+
const actions = el('div', 'actions');
|
|
1778
|
+
const save = el('button', 'primary', 'Create animation'); save.type = 'submit';
|
|
1779
|
+
const cancel = el('button', null, 'Cancel'); cancel.type = 'button'; cancel.onclick = () => { ui.editing = null; renderDrawer(); };
|
|
1780
|
+
const msg = el('span', 'msg');
|
|
1781
|
+
actions.append(save, cancel, msg);
|
|
1782
|
+
form.append(actions);
|
|
1783
|
+
form.onsubmit = async (e) => {
|
|
1784
|
+
e.preventDefault();
|
|
1785
|
+
save.disabled = true; msg.className = 'msg'; msg.textContent = 'saving…';
|
|
1786
|
+
const animation = { of: item.assetId };
|
|
1787
|
+
if (directionField) animation.direction = directionField.select.value;
|
|
1788
|
+
if (isCharacter && mode.value === 'template' && template.value.trim()) {
|
|
1789
|
+
animation.template = template.value.trim();
|
|
1790
|
+
} else {
|
|
1791
|
+
animation.mode = mode.value;
|
|
1792
|
+
if (frames.value.trim() !== '') animation.frames = Number(frames.value);
|
|
1793
|
+
if (fps.value.trim() !== '') animation.fps = Number(fps.value);
|
|
1794
|
+
}
|
|
1795
|
+
if (subject && subject.value.trim()) animation.subject = subject.value.trim();
|
|
1796
|
+
const asset = { prompt: prompt.value, styles: [item.styleId], animation };
|
|
1797
|
+
try {
|
|
1798
|
+
const body = { action: 'add-asset', assetId: id.value.trim(), expectedSha256: pr.manifestSha256, asset };
|
|
1799
|
+
if (item.project) body.project = item.project;
|
|
1800
|
+
snap = await postEdit(body);
|
|
1801
|
+
const newId = (item.project ? item.project + ':' : '') + item.styleId + '/' + id.value.trim();
|
|
1802
|
+
ui.editing = null;
|
|
1803
|
+
ui.notice = { id: newId, text: 'Added to the manifest. Nothing is generated until you run pixelkiln gen.' };
|
|
1804
|
+
render();
|
|
1805
|
+
if (snap.items.some((i) => i.id === newId)) openItem(newId);
|
|
1806
|
+
} catch (err) {
|
|
1807
|
+
save.disabled = false;
|
|
1808
|
+
msg.className = 'msg bad';
|
|
1809
|
+
msg.textContent = err.message + (err.status === 409 ? ' Press Refresh.' : '');
|
|
1810
|
+
}
|
|
1811
|
+
};
|
|
1812
|
+
setTimeout(() => id.focus(), 0);
|
|
1813
|
+
return form;
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1678
1816
|
function editForm(item) {
|
|
1679
1817
|
const pr = projectOf(item);
|
|
1680
1818
|
const a = item.asset;
|
|
@@ -2014,6 +2152,20 @@ function renderDrawer() {
|
|
|
2014
2152
|
}
|
|
2015
2153
|
row(dl, family.length === 1 ? 'depends on this' : family.length + ' depend on this', list);
|
|
2016
2154
|
}
|
|
2155
|
+
// A loop cannot itself be posed or animated further; only a base or state can.
|
|
2156
|
+
if (canEdit && c.kind !== 'animation') {
|
|
2157
|
+
const stateKey = 'state:' + item.id, animKey = 'anim:' + item.id;
|
|
2158
|
+
const addState = el('button', ui.editing === stateKey ? null : 'add', ui.editing === stateKey ? 'Cancel' : '+ New state');
|
|
2159
|
+
addState.type = 'button';
|
|
2160
|
+
addState.onclick = () => { ui.editing = ui.editing === stateKey ? null : stateKey; ui.notice = null; renderDrawer(); };
|
|
2161
|
+
const addAnim = el('button', ui.editing === animKey ? null : 'add', ui.editing === animKey ? 'Cancel' : '+ New animation');
|
|
2162
|
+
addAnim.type = 'button';
|
|
2163
|
+
addAnim.onclick = () => { ui.editing = ui.editing === animKey ? null : animKey; ui.notice = null; renderDrawer(); };
|
|
2164
|
+
const buttons = el('div', 'actions'); buttons.append(addState, addAnim);
|
|
2165
|
+
s.append(buttons);
|
|
2166
|
+
if (ui.editing === stateKey) s.append(newStateForm(item));
|
|
2167
|
+
if (ui.editing === animKey) s.append(newAnimationForm(item));
|
|
2168
|
+
}
|
|
2017
2169
|
body.append(s);
|
|
2018
2170
|
}
|
|
2019
2171
|
|
package/dist/index.cjs
CHANGED
|
@@ -13049,6 +13049,25 @@ var AssetPatchSchema = import_zod7.z.object({
|
|
|
13049
13049
|
size: import_zod7.z.number().int().nullable().optional(),
|
|
13050
13050
|
tags: import_zod7.z.array(import_zod7.z.string()).optional()
|
|
13051
13051
|
}).strict();
|
|
13052
|
+
var NewStateSchema = import_zod7.z.object({
|
|
13053
|
+
/** Asset id of the parent this states; must already exist. */
|
|
13054
|
+
of: import_zod7.z.string().min(1),
|
|
13055
|
+
paletteFromReference: import_zod7.z.boolean().optional(),
|
|
13056
|
+
canvas: import_zod7.z.object({ width: import_zod7.z.number().int().min(16).max(256), height: import_zod7.z.number().int().min(16).max(256) }).strict().optional()
|
|
13057
|
+
}).strict();
|
|
13058
|
+
var NewAnimationSchema = import_zod7.z.object({
|
|
13059
|
+
/** Asset id of the parent this animates; must already exist. */
|
|
13060
|
+
of: import_zod7.z.string().min(1),
|
|
13061
|
+
/** A PixelLab template id (`character` only; `objectPro` has no template concept). */
|
|
13062
|
+
template: import_zod7.z.string().min(1).optional(),
|
|
13063
|
+
direction: CharacterDirectionSchema.optional(),
|
|
13064
|
+
frames: import_zod7.z.number().int().min(4).max(16).optional(),
|
|
13065
|
+
fps: import_zod7.z.number().int().min(1).max(60).optional(),
|
|
13066
|
+
mode: CharacterAnimationModeSchema.optional(),
|
|
13067
|
+
/** What is being animated, when the parent's own description would mislead the model (`character` only). */
|
|
13068
|
+
subject: import_zod7.z.string().min(1).optional(),
|
|
13069
|
+
enhancePrompt: import_zod7.z.boolean().optional()
|
|
13070
|
+
}).strict();
|
|
13052
13071
|
var NewAssetSchema = import_zod7.z.object({
|
|
13053
13072
|
prompt: import_zod7.z.string(),
|
|
13054
13073
|
category: import_zod7.z.string().optional(),
|
|
@@ -13069,8 +13088,16 @@ var NewAssetSchema = import_zod7.z.object({
|
|
|
13069
13088
|
/** Asset id of the parent this revises; must already exist. */
|
|
13070
13089
|
from: import_zod7.z.string().min(1),
|
|
13071
13090
|
strength: import_zod7.z.number().min(0).max(1).optional()
|
|
13072
|
-
}).strict().optional()
|
|
13073
|
-
|
|
13091
|
+
}).strict().optional(),
|
|
13092
|
+
/** A pose or outfit of an existing `character`/`objectPro` base or state. */
|
|
13093
|
+
state: NewStateSchema.optional(),
|
|
13094
|
+
/** A loop of an existing `character`/`objectPro` base or state. */
|
|
13095
|
+
animation: NewAnimationSchema.optional()
|
|
13096
|
+
}).strict().refine((asset) => !(asset.revision && (asset.state || asset.animation)), {
|
|
13097
|
+
message: "an asset is a revision, a state, or an animation \u2014 not more than one"
|
|
13098
|
+
}).refine((asset) => !(asset.state && asset.animation), {
|
|
13099
|
+
message: "an asset is a state or an animation \u2014 not both"
|
|
13100
|
+
});
|
|
13074
13101
|
var CANDIDATE_OPTION = {
|
|
13075
13102
|
retrodiffusion: "numImages",
|
|
13076
13103
|
comfyui: "numImages",
|
|
@@ -13238,6 +13265,12 @@ function applyEdit(raw, edit) {
|
|
|
13238
13265
|
if (edit.asset.revision && !Object.hasOwn(raw.assets, edit.asset.revision.from)) {
|
|
13239
13266
|
throw new ManifestEditError(`revision parent "${edit.asset.revision.from}" is not declared by the manifest`);
|
|
13240
13267
|
}
|
|
13268
|
+
if (edit.asset.state && !Object.hasOwn(raw.assets, edit.asset.state.of)) {
|
|
13269
|
+
throw new ManifestEditError(`state parent "${edit.asset.state.of}" is not declared by the manifest`);
|
|
13270
|
+
}
|
|
13271
|
+
if (edit.asset.animation && !Object.hasOwn(raw.assets, edit.asset.animation.of)) {
|
|
13272
|
+
throw new ManifestEditError(`animation parent "${edit.asset.animation.of}" is not declared by the manifest`);
|
|
13273
|
+
}
|
|
13241
13274
|
raw.assets[edit.assetId] = asset2;
|
|
13242
13275
|
return;
|
|
13243
13276
|
}
|
|
@@ -13714,27 +13747,54 @@ async function samePixels(a, b) {
|
|
|
13714
13747
|
}
|
|
13715
13748
|
var metadataFps = (entry) => entry ? frameSetFps(entry) : null;
|
|
13716
13749
|
function describeCharacter(spec, entry) {
|
|
13717
|
-
const
|
|
13750
|
+
const recordedCharacter = entry?.providerMetadata?.[entry.provider]?.character;
|
|
13718
13751
|
if (spec?.character) {
|
|
13719
13752
|
const character = spec.character;
|
|
13720
13753
|
return {
|
|
13754
|
+
generator: "character",
|
|
13721
13755
|
kind: character.kind,
|
|
13722
13756
|
parentKey: character.parentAssetId ? lockKey(spec.styleId, character.parentAssetId) : null,
|
|
13723
13757
|
mode: character.kind === "animation" ? character.animation.mode : character.mode,
|
|
13724
13758
|
directions: character.directions,
|
|
13725
13759
|
direction: character.animation?.direction ?? null,
|
|
13726
|
-
characterId:
|
|
13760
|
+
characterId: recordedCharacter?.characterId ?? entry?.objectId?.split("#")[0] ?? null
|
|
13761
|
+
};
|
|
13762
|
+
}
|
|
13763
|
+
if (spec?.objectPro) {
|
|
13764
|
+
const objectPro = spec.objectPro;
|
|
13765
|
+
return {
|
|
13766
|
+
generator: "objectPro",
|
|
13767
|
+
kind: objectPro.kind,
|
|
13768
|
+
parentKey: objectPro.parentAssetId ? lockKey(spec.styleId, objectPro.parentAssetId) : null,
|
|
13769
|
+
mode: objectPro.kind === "animation" ? objectPro.animation.mode : "pro-flash",
|
|
13770
|
+
directions: objectPro.directions,
|
|
13771
|
+
direction: objectPro.animation?.direction ?? null,
|
|
13772
|
+
characterId: entry?.objectId?.split("#")[0] ?? null
|
|
13727
13773
|
};
|
|
13728
13774
|
}
|
|
13729
13775
|
if (entry?.generator === "character") {
|
|
13730
|
-
const kind =
|
|
13776
|
+
const kind = recordedCharacter?.kind === "animation" || entry.outputs.some((o) => o.role?.startsWith("frame-")) ? "animation" : "base";
|
|
13777
|
+
return {
|
|
13778
|
+
generator: "character",
|
|
13779
|
+
kind,
|
|
13780
|
+
parentKey: null,
|
|
13781
|
+
mode: recordedCharacter?.mode ?? "standard",
|
|
13782
|
+
directions: recordedCharacter?.directions ?? entry.outputs.length,
|
|
13783
|
+
direction: recordedCharacter?.direction ?? null,
|
|
13784
|
+
characterId: recordedCharacter?.characterId ?? entry.objectId?.split("#")[0] ?? null
|
|
13785
|
+
};
|
|
13786
|
+
}
|
|
13787
|
+
if (entry?.generator === "objectPro") {
|
|
13788
|
+
const recordedObject = entry.providerMetadata?.[entry.provider]?.objectPro;
|
|
13789
|
+
const kind = recordedObject?.kind === "animation" || entry.outputs.some((o) => o.role?.startsWith("frame-")) ? "animation" : "base";
|
|
13731
13790
|
return {
|
|
13791
|
+
generator: "objectPro",
|
|
13732
13792
|
kind,
|
|
13733
13793
|
parentKey: null,
|
|
13734
|
-
mode:
|
|
13735
|
-
directions:
|
|
13736
|
-
direction:
|
|
13737
|
-
characterId:
|
|
13794
|
+
mode: "pro-flash",
|
|
13795
|
+
directions: recordedObject?.directions ?? entry.outputs.length,
|
|
13796
|
+
direction: recordedObject?.direction ?? null,
|
|
13797
|
+
characterId: entry.objectId?.split("#")[0] ?? null
|
|
13738
13798
|
};
|
|
13739
13799
|
}
|
|
13740
13800
|
return null;
|
|
@@ -14070,7 +14130,7 @@ async function buildGallerySnapshot(opts) {
|
|
|
14070
14130
|
view: style?.view ?? null,
|
|
14071
14131
|
noBackground: style?.noBackground ?? true,
|
|
14072
14132
|
quality: Boolean(style?.quality),
|
|
14073
|
-
characters: style?.generator === "character" ? {
|
|
14133
|
+
characters: style?.generator === "character" || style?.generator === "objectPro" ? {
|
|
14074
14134
|
bases: styleItems.filter((item) => item.character?.kind === "base").length,
|
|
14075
14135
|
states: styleItems.filter((item) => item.character?.kind === "state").length,
|
|
14076
14136
|
animations: styleItems.filter((item) => item.character?.kind === "animation").length
|