pixelkiln 0.44.0 → 0.45.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 +124 -19
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +123 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +152 -1
- package/dist/index.d.ts +152 -1
- package/dist/index.js +123 -18
- package/dist/index.js.map +1 -1
- package/docs/MANIFEST.md +25 -4
- package/package.json +1 -1
- package/schema/manifest.schema.json +16 -0
- package/schema/recipe.schema.json +4 -0
- package/skills/pixelkiln/references/pixellab.md +4 -1
package/dist/index.js
CHANGED
|
@@ -306,6 +306,12 @@ var StyleObjectSchema = z.object({
|
|
|
306
306
|
isometric: z.boolean().optional(),
|
|
307
307
|
/** `character` only, `v3` bases: let PixelLab expand the prompt into a fuller one before drawing. */
|
|
308
308
|
enhancePrompt: z.boolean().optional(),
|
|
309
|
+
/**
|
|
310
|
+
* `character` only, `pro` bases: the asset id of a generated 8-direction
|
|
311
|
+
* character in this style whose look every base drawn from text or a
|
|
312
|
+
* concept follows. An asset may override it; the anchor itself ignores it.
|
|
313
|
+
*/
|
|
314
|
+
styleCharacter: z.string().min(1).optional(),
|
|
309
315
|
/** Fixed seed for reproducibility where the endpoint supports it. */
|
|
310
316
|
seed: z.number().int().optional(),
|
|
311
317
|
/**
|
|
@@ -478,6 +484,15 @@ var AssetSchema = z.object({
|
|
|
478
484
|
* still guides the result.
|
|
479
485
|
*/
|
|
480
486
|
reference: z.union([z.string().min(1), z.record(CharacterDirectionSchema, z.string().min(1))]).optional(),
|
|
487
|
+
/**
|
|
488
|
+
* `character` styles, `pro` bases only: a manifest-relative concept
|
|
489
|
+
* image (a painting, a photo, a sketch; up to 1024px) that seeds the
|
|
490
|
+
* design instead of the prompt alone. A style image or `styleCharacter`
|
|
491
|
+
* may still anchor the look.
|
|
492
|
+
*/
|
|
493
|
+
concept: z.string().min(1).optional(),
|
|
494
|
+
/** `character` styles, `pro` bases: this base's style anchor, over the style's. */
|
|
495
|
+
styleCharacter: z.string().min(1).optional(),
|
|
481
496
|
/**
|
|
482
497
|
* Another asset of the same style flipped left to right, made locally
|
|
483
498
|
* from that asset's downloaded files at no generation cost.
|
|
@@ -559,6 +574,20 @@ var AssetSchema = z.object({
|
|
|
559
574
|
path: ["reference"]
|
|
560
575
|
});
|
|
561
576
|
}
|
|
577
|
+
if (asset.concept && (asset.state || asset.animation || asset.mirror)) {
|
|
578
|
+
context.addIssue({
|
|
579
|
+
code: z.ZodIssueCode.custom,
|
|
580
|
+
message: "a concept image belongs on a base; a state, animation, or mirror takes its look from its parent",
|
|
581
|
+
path: ["concept"]
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
if (asset.concept && asset.reference) {
|
|
585
|
+
context.addIssue({
|
|
586
|
+
code: z.ZodIssueCode.custom,
|
|
587
|
+
message: "concept and reference are mutually exclusive: a concept seeds a new design, a reference is the sprite to rotate",
|
|
588
|
+
path: ["concept"]
|
|
589
|
+
});
|
|
590
|
+
}
|
|
562
591
|
if (typeof asset.reference === "object" && !Object.keys(asset.reference).length) {
|
|
563
592
|
context.addIssue({ code: z.ZodIssueCode.custom, message: "a reference needs at least a south image", path: ["reference"] });
|
|
564
593
|
}
|
|
@@ -1240,10 +1269,12 @@ var PixelLabClient = class {
|
|
|
1240
1269
|
image_size: imageSize,
|
|
1241
1270
|
template_id: args.template,
|
|
1242
1271
|
no_background: args.noBackground ?? true,
|
|
1243
|
-
method: south ? "rotate_character" : "create_with_style",
|
|
1272
|
+
method: south ? "rotate_character" : args.concept ? "create_from_concept" : "create_with_style",
|
|
1244
1273
|
...args.view ? { view: args.view } : {},
|
|
1245
1274
|
...args.seed != null ? { seed: args.seed } : {},
|
|
1246
|
-
...south ? { reference_image: encode(south) } : args.styleReference ? { reference_image: encode(args.styleReference) } : {}
|
|
1275
|
+
...south ? { reference_image: encode(south) } : args.styleReference ? { reference_image: encode(args.styleReference) } : {},
|
|
1276
|
+
...!south && args.concept ? { concept_image: encode(args.concept) } : {},
|
|
1277
|
+
...!south && args.styleCharacterId ? { style_character_id: args.styleCharacterId } : {}
|
|
1247
1278
|
};
|
|
1248
1279
|
} else {
|
|
1249
1280
|
path33 = args.directions === 4 ? "/create-character-with-4-directions" : "/create-character-with-8-directions";
|
|
@@ -2005,7 +2036,9 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
|
|
|
2005
2036
|
textGuidanceScale: spec.character.textGuidanceScale,
|
|
2006
2037
|
isometric: spec.character.isometric,
|
|
2007
2038
|
enhancePrompt: spec.character.enhancePrompt,
|
|
2008
|
-
reference: spec.character.reference ? Object.fromEntries(Object.entries(spec.character.reference).map(([direction, image]) => [direction, image.sha256])) : void 0
|
|
2039
|
+
reference: spec.character.reference ? Object.fromEntries(Object.entries(spec.character.reference).map(([direction, image]) => [direction, image.sha256])) : void 0,
|
|
2040
|
+
concept: spec.character.concept?.sha256,
|
|
2041
|
+
styleAnchor: spec.character.styleAnchor ? { of: spec.character.styleAnchor.assetId, sha256: spec.character.styleAnchor.sha256 } : void 0
|
|
2009
2042
|
} : void 0,
|
|
2010
2043
|
// A mirror's bytes come from its source's recorded outputs; the plan
|
|
2011
2044
|
// compares those directly, so only the choice of source is identity.
|
|
@@ -2192,6 +2225,19 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
2192
2225
|
}
|
|
2193
2226
|
}
|
|
2194
2227
|
}
|
|
2228
|
+
if (character.concept) {
|
|
2229
|
+
if (character.mode !== "pro") throw new Error(`${label}: a concept image is a pro input; the ${character.mode} engine draws from text or a reference`);
|
|
2230
|
+
if (character.concept.width > 1024 || character.concept.height > 1024) {
|
|
2231
|
+
throw new Error(`${label}: concept image is ${character.concept.width}x${character.concept.height}; PixelLab pro takes up to 1024px`);
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
if (character.styleAnchor) {
|
|
2235
|
+
if (character.mode !== "pro") throw new Error(`${label}: styleCharacter is a pro input; the ${character.mode} engine has no style anchor`);
|
|
2236
|
+
if (character.reference) throw new Error(`${label}: a pro base rotating its own reference takes no styleCharacter`);
|
|
2237
|
+
if (character.styleAnchor.spec.character?.directions !== 8) {
|
|
2238
|
+
throw new Error(`${label}: styleCharacter ${character.styleAnchor.assetId} has ${character.styleAnchor.spec.character?.directions} directions; PixelLab wants an 8-direction anchor`);
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2195
2241
|
if (character.reference) {
|
|
2196
2242
|
const given = Object.keys(character.reference);
|
|
2197
2243
|
if (!character.reference.south) throw new Error(`${label}: a reference needs a south-facing sprite`);
|
|
@@ -2293,7 +2339,9 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
2293
2339
|
isometric: character.isometric,
|
|
2294
2340
|
reference: character.reference ? readReference(spec, character.reference) : void 0,
|
|
2295
2341
|
styleReference: styleImage ? { base64: styleImage.base64, format: styleImage.format } : void 0,
|
|
2296
|
-
enhancePrompt: character.enhancePrompt
|
|
2342
|
+
enhancePrompt: character.enhancePrompt,
|
|
2343
|
+
concept: character.concept ? readPose(spec, "concept image", character.concept) : void 0,
|
|
2344
|
+
styleCharacterId: character.styleAnchor ? requireStyleObjectId(spec, context) : void 0
|
|
2297
2345
|
});
|
|
2298
2346
|
return { jobId: res2.character_id, metadata: { character: { kind: "base", characterId: res2.character_id, mode: character.mode, directions: character.directions, backgroundJobId: res2.background_job_id } } };
|
|
2299
2347
|
}
|
|
@@ -2713,6 +2761,14 @@ function readReference(spec, reference) {
|
|
|
2713
2761
|
}
|
|
2714
2762
|
return images;
|
|
2715
2763
|
}
|
|
2764
|
+
function requireStyleObjectId(spec, context) {
|
|
2765
|
+
if (!context?.styleObjectId) {
|
|
2766
|
+
throw new Error(
|
|
2767
|
+
`${spec.styleId}/${spec.assetId} anchors its style on ${spec.character.styleAnchor.assetId}, which has no PixelLab character id in the lockfile yet; generate the anchor first`
|
|
2768
|
+
);
|
|
2769
|
+
}
|
|
2770
|
+
return context.styleObjectId;
|
|
2771
|
+
}
|
|
2716
2772
|
function readPose(spec, what, image) {
|
|
2717
2773
|
const bytes = readFileSync(image.path);
|
|
2718
2774
|
if (sha256(bytes) !== image.sha256) {
|
|
@@ -5412,6 +5468,18 @@ async function resolveSpecs(loaded, filter) {
|
|
|
5412
5468
|
finalized.add(assetId);
|
|
5413
5469
|
return resolved;
|
|
5414
5470
|
}
|
|
5471
|
+
const anchorId = asset.styleCharacter ?? style.styleCharacter;
|
|
5472
|
+
if (resolved.character?.kind === "base" && anchorId && anchorId !== assetId) {
|
|
5473
|
+
const anchorSpec = await finalize(anchorId);
|
|
5474
|
+
if (!anchorSpec.character || anchorSpec.character.kind === "animation") {
|
|
5475
|
+
throw new Error(`assets.${assetId}: styleCharacter ${anchorId} is not a character base or state`);
|
|
5476
|
+
}
|
|
5477
|
+
const file = memberPath(anchorSpec.outFile, "south", 0, anchorSpec.character.directions, MediaType.PNG);
|
|
5478
|
+
resolved.character = {
|
|
5479
|
+
...resolved.character,
|
|
5480
|
+
styleAnchor: { assetId: anchorId, spec: anchorSpec, file, sha256: existsSync4(file) ? await sha256File(file) : null }
|
|
5481
|
+
};
|
|
5482
|
+
}
|
|
5415
5483
|
const characterParent = asset.state?.of ?? asset.animation?.of;
|
|
5416
5484
|
if (resolved.character && characterParent) {
|
|
5417
5485
|
const parentSpec = await finalize(characterParent);
|
|
@@ -5564,6 +5632,10 @@ async function resolveCharacterShape(asset, style, kind, files) {
|
|
|
5564
5632
|
...style.isometric !== void 0 ? { isometric: style.isometric } : {},
|
|
5565
5633
|
...style.enhancePrompt !== void 0 ? { enhancePrompt: style.enhancePrompt } : {}
|
|
5566
5634
|
};
|
|
5635
|
+
if (asset.concept) {
|
|
5636
|
+
const image = await files.load(asset.concept, "Concept image");
|
|
5637
|
+
shape.concept = { path: path5.resolve(files.root, asset.concept), sha256: image.hash, width: image.width, height: image.height, format: image.format };
|
|
5638
|
+
}
|
|
5567
5639
|
if (asset.reference) {
|
|
5568
5640
|
const byDirection = typeof asset.reference === "string" ? { south: asset.reference } : asset.reference;
|
|
5569
5641
|
shape.reference = {};
|
|
@@ -7083,9 +7155,24 @@ import path11 from "path";
|
|
|
7083
7155
|
async function inspectRevisionReadiness(spec, lock) {
|
|
7084
7156
|
if (spec.mirror) return inspectMirror(spec, lock, /* @__PURE__ */ new Set());
|
|
7085
7157
|
if (spec.character?.parentSpec) return inspectCharacterParent(spec, lock, /* @__PURE__ */ new Set());
|
|
7158
|
+
if (spec.character?.styleAnchor) return inspectStyleAnchor(spec, lock, /* @__PURE__ */ new Set());
|
|
7086
7159
|
if (!spec.revision) return null;
|
|
7087
7160
|
return inspectRevision(spec, lock, /* @__PURE__ */ new Set());
|
|
7088
7161
|
}
|
|
7162
|
+
async function inspectStyleAnchor(spec, lock, seen) {
|
|
7163
|
+
const anchor = spec.character.styleAnchor;
|
|
7164
|
+
const label = `style anchor ${spec.styleId}/${anchor.assetId}`;
|
|
7165
|
+
const dependency = await inspectParent(anchor.spec, lock, seen);
|
|
7166
|
+
if (!dependency.ready) return { ready: false, reason: `${label} is not ready: ${dependency.reason}` };
|
|
7167
|
+
if (!anchor.sha256 || !existsSync7(anchor.file)) return { ready: false, reason: `${label} has no generated south-facing file yet` };
|
|
7168
|
+
if (await sha256File(anchor.file) !== anchor.sha256) {
|
|
7169
|
+
return { ready: false, reason: `${label} changed after the manifest was resolved` };
|
|
7170
|
+
}
|
|
7171
|
+
if (!lock.entries[lockKey(anchor.spec.styleId, anchor.spec.assetId)]?.objectId) {
|
|
7172
|
+
return { ready: false, reason: `${label} has no PixelLab character id recorded` };
|
|
7173
|
+
}
|
|
7174
|
+
return { ready: true, reason: `${label} is current` };
|
|
7175
|
+
}
|
|
7089
7176
|
async function inspectMirror(spec, lock, seen) {
|
|
7090
7177
|
const source = spec.mirror.sourceSpec;
|
|
7091
7178
|
const label = `mirror source ${spec.styleId}/${spec.mirror.sourceAssetId}`;
|
|
@@ -7157,6 +7244,10 @@ async function inspectParent(spec, lock, seen) {
|
|
|
7157
7244
|
const inputs = await inspectCharacterParent(spec, lock, nextSeen);
|
|
7158
7245
|
if (!inputs.ready) return inputs;
|
|
7159
7246
|
}
|
|
7247
|
+
if (spec.character?.styleAnchor) {
|
|
7248
|
+
const inputs = await inspectStyleAnchor(spec, lock, nextSeen);
|
|
7249
|
+
if (!inputs.ready) return inputs;
|
|
7250
|
+
}
|
|
7160
7251
|
if (spec.quality) {
|
|
7161
7252
|
const quality = await inspectQualityProfile(spec, lock);
|
|
7162
7253
|
return quality?.state === "approved" ? { ready: true, reason: "approved quality output is current" } : {
|
|
@@ -7196,6 +7287,14 @@ async function anyOutputModified(entry, spec) {
|
|
|
7196
7287
|
}
|
|
7197
7288
|
return false;
|
|
7198
7289
|
}
|
|
7290
|
+
function dependencies(spec) {
|
|
7291
|
+
return [
|
|
7292
|
+
spec.mirror?.sourceSpec,
|
|
7293
|
+
spec.revision?.sourceSpec,
|
|
7294
|
+
spec.character?.parentSpec,
|
|
7295
|
+
spec.character?.styleAnchor?.spec
|
|
7296
|
+
].filter((parent) => Boolean(parent));
|
|
7297
|
+
}
|
|
7199
7298
|
function resumeCommandForStatus(status) {
|
|
7200
7299
|
if (status === "pending" || status === "processing") return "poll";
|
|
7201
7300
|
if (status === "review") return "pick";
|
|
@@ -7304,16 +7403,19 @@ async function buildPlan(specs, lock, opts = {}) {
|
|
|
7304
7403
|
const stateOf = new Map(items.map((i) => [i.key, i.state]));
|
|
7305
7404
|
for (const item of items) {
|
|
7306
7405
|
if (!isActionable(item)) continue;
|
|
7307
|
-
const parent
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
item.
|
|
7316
|
-
|
|
7406
|
+
for (const parent of dependencies(item.spec)) {
|
|
7407
|
+
const parentKey = lockKey(parent.styleId, parent.assetId);
|
|
7408
|
+
const parentState = stateOf.get(parentKey);
|
|
7409
|
+
if (parentState && isActionable({ state: parentState })) {
|
|
7410
|
+
item.state = "blocked";
|
|
7411
|
+
item.reason = `parent ${parentKey} is being generated in this run; this follows in the next wave`;
|
|
7412
|
+
break;
|
|
7413
|
+
}
|
|
7414
|
+
if (item.spec.mirror && (parentState === "recoverable" || parentState === "in-flight")) {
|
|
7415
|
+
item.state = "blocked";
|
|
7416
|
+
item.reason = `mirror source ${parentKey} is ${parentState}; this follows in the next wave`;
|
|
7417
|
+
break;
|
|
7418
|
+
}
|
|
7317
7419
|
}
|
|
7318
7420
|
}
|
|
7319
7421
|
const actionable = items.filter(isActionable);
|
|
@@ -9792,10 +9894,13 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
|
|
|
9792
9894
|
try {
|
|
9793
9895
|
const refs = styleImages.get(spec.styleId) ?? [];
|
|
9794
9896
|
const parentEntry = spec.character?.parentSpec ? lock.entries[lockKey(spec.character.parentSpec.styleId, spec.character.parentSpec.assetId)] : void 0;
|
|
9897
|
+
const anchor = spec.character?.styleAnchor;
|
|
9898
|
+
const anchorEntry = anchor ? lock.entries[lockKey(anchor.spec.styleId, anchor.spec.assetId)] : void 0;
|
|
9795
9899
|
const replacedMetadata = previousEntry?.providerMetadata?.[provider.id];
|
|
9796
9900
|
const { jobId, metadata } = await provider.submit(spec, refs, {
|
|
9797
9901
|
...previousJobId ? { previousJobId } : {},
|
|
9798
9902
|
...parentEntry?.objectId ? { parentObjectId: parentEntry.objectId } : {},
|
|
9903
|
+
...anchorEntry?.objectId ? { styleObjectId: anchorEntry.objectId } : {},
|
|
9799
9904
|
...replacedMetadata ? { replacedMetadata } : {},
|
|
9800
9905
|
...previousMetadata ? { previousMetadata } : {},
|
|
9801
9906
|
checkpoint: async (checkpoint) => {
|
|
@@ -12404,7 +12509,7 @@ var pin_default = {
|
|
|
12404
12509
|
godot: "4.7.2",
|
|
12405
12510
|
extensionsApi: 9,
|
|
12406
12511
|
protocol: 4,
|
|
12407
|
-
release: "editor-pixelorama-v1.2.2-pk.
|
|
12512
|
+
release: "editor-pixelorama-v1.2.2-pk.26",
|
|
12408
12513
|
files: {
|
|
12409
12514
|
"index.apple-touch-icon.png": {
|
|
12410
12515
|
sha256: "ee5434873e0fed91e9a888c3ea5a8b79beac25999308be7a6eb41bf4ce640d27",
|
|
@@ -12419,7 +12524,7 @@ var pin_default = {
|
|
|
12419
12524
|
bytes: 7298
|
|
12420
12525
|
},
|
|
12421
12526
|
"index.html": {
|
|
12422
|
-
sha256: "
|
|
12527
|
+
sha256: "326101ab02bb4a29a7964214c9307c7dff26e9d66b8ebaaf6e7a3987e84a05b2",
|
|
12423
12528
|
bytes: 5442
|
|
12424
12529
|
},
|
|
12425
12530
|
"index.icon.png": {
|
|
@@ -12431,8 +12536,8 @@ var pin_default = {
|
|
|
12431
12536
|
bytes: 279815
|
|
12432
12537
|
},
|
|
12433
12538
|
"index.pck": {
|
|
12434
|
-
sha256: "
|
|
12435
|
-
bytes:
|
|
12539
|
+
sha256: "e26292650bb996965acd1ba70e74d2b4020b063d14f1276701312c1d34cb0a83",
|
|
12540
|
+
bytes: 6431932
|
|
12436
12541
|
},
|
|
12437
12542
|
"index.png": {
|
|
12438
12543
|
sha256: "6e3ca3eb4a99e4c07bdd89ebc873b440ec0ffd66ce1955160da9e7ed80d538d7",
|