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.cjs
CHANGED
|
@@ -605,6 +605,12 @@ var StyleObjectSchema = import_zod.z.object({
|
|
|
605
605
|
isometric: import_zod.z.boolean().optional(),
|
|
606
606
|
/** `character` only, `v3` bases: let PixelLab expand the prompt into a fuller one before drawing. */
|
|
607
607
|
enhancePrompt: import_zod.z.boolean().optional(),
|
|
608
|
+
/**
|
|
609
|
+
* `character` only, `pro` bases: the asset id of a generated 8-direction
|
|
610
|
+
* character in this style whose look every base drawn from text or a
|
|
611
|
+
* concept follows. An asset may override it; the anchor itself ignores it.
|
|
612
|
+
*/
|
|
613
|
+
styleCharacter: import_zod.z.string().min(1).optional(),
|
|
608
614
|
/** Fixed seed for reproducibility where the endpoint supports it. */
|
|
609
615
|
seed: import_zod.z.number().int().optional(),
|
|
610
616
|
/**
|
|
@@ -777,6 +783,15 @@ var AssetSchema = import_zod.z.object({
|
|
|
777
783
|
* still guides the result.
|
|
778
784
|
*/
|
|
779
785
|
reference: import_zod.z.union([import_zod.z.string().min(1), import_zod.z.record(CharacterDirectionSchema, import_zod.z.string().min(1))]).optional(),
|
|
786
|
+
/**
|
|
787
|
+
* `character` styles, `pro` bases only: a manifest-relative concept
|
|
788
|
+
* image (a painting, a photo, a sketch; up to 1024px) that seeds the
|
|
789
|
+
* design instead of the prompt alone. A style image or `styleCharacter`
|
|
790
|
+
* may still anchor the look.
|
|
791
|
+
*/
|
|
792
|
+
concept: import_zod.z.string().min(1).optional(),
|
|
793
|
+
/** `character` styles, `pro` bases: this base's style anchor, over the style's. */
|
|
794
|
+
styleCharacter: import_zod.z.string().min(1).optional(),
|
|
780
795
|
/**
|
|
781
796
|
* Another asset of the same style flipped left to right, made locally
|
|
782
797
|
* from that asset's downloaded files at no generation cost.
|
|
@@ -858,6 +873,20 @@ var AssetSchema = import_zod.z.object({
|
|
|
858
873
|
path: ["reference"]
|
|
859
874
|
});
|
|
860
875
|
}
|
|
876
|
+
if (asset.concept && (asset.state || asset.animation || asset.mirror)) {
|
|
877
|
+
context.addIssue({
|
|
878
|
+
code: import_zod.z.ZodIssueCode.custom,
|
|
879
|
+
message: "a concept image belongs on a base; a state, animation, or mirror takes its look from its parent",
|
|
880
|
+
path: ["concept"]
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
if (asset.concept && asset.reference) {
|
|
884
|
+
context.addIssue({
|
|
885
|
+
code: import_zod.z.ZodIssueCode.custom,
|
|
886
|
+
message: "concept and reference are mutually exclusive: a concept seeds a new design, a reference is the sprite to rotate",
|
|
887
|
+
path: ["concept"]
|
|
888
|
+
});
|
|
889
|
+
}
|
|
861
890
|
if (typeof asset.reference === "object" && !Object.keys(asset.reference).length) {
|
|
862
891
|
context.addIssue({ code: import_zod.z.ZodIssueCode.custom, message: "a reference needs at least a south image", path: ["reference"] });
|
|
863
892
|
}
|
|
@@ -1539,10 +1568,12 @@ var PixelLabClient = class {
|
|
|
1539
1568
|
image_size: imageSize,
|
|
1540
1569
|
template_id: args.template,
|
|
1541
1570
|
no_background: args.noBackground ?? true,
|
|
1542
|
-
method: south ? "rotate_character" : "create_with_style",
|
|
1571
|
+
method: south ? "rotate_character" : args.concept ? "create_from_concept" : "create_with_style",
|
|
1543
1572
|
...args.view ? { view: args.view } : {},
|
|
1544
1573
|
...args.seed != null ? { seed: args.seed } : {},
|
|
1545
|
-
...south ? { reference_image: encode(south) } : args.styleReference ? { reference_image: encode(args.styleReference) } : {}
|
|
1574
|
+
...south ? { reference_image: encode(south) } : args.styleReference ? { reference_image: encode(args.styleReference) } : {},
|
|
1575
|
+
...!south && args.concept ? { concept_image: encode(args.concept) } : {},
|
|
1576
|
+
...!south && args.styleCharacterId ? { style_character_id: args.styleCharacterId } : {}
|
|
1546
1577
|
};
|
|
1547
1578
|
} else {
|
|
1548
1579
|
path33 = args.directions === 4 ? "/create-character-with-4-directions" : "/create-character-with-8-directions";
|
|
@@ -2304,7 +2335,9 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
|
|
|
2304
2335
|
textGuidanceScale: spec.character.textGuidanceScale,
|
|
2305
2336
|
isometric: spec.character.isometric,
|
|
2306
2337
|
enhancePrompt: spec.character.enhancePrompt,
|
|
2307
|
-
reference: spec.character.reference ? Object.fromEntries(Object.entries(spec.character.reference).map(([direction, image]) => [direction, image.sha256])) : void 0
|
|
2338
|
+
reference: spec.character.reference ? Object.fromEntries(Object.entries(spec.character.reference).map(([direction, image]) => [direction, image.sha256])) : void 0,
|
|
2339
|
+
concept: spec.character.concept?.sha256,
|
|
2340
|
+
styleAnchor: spec.character.styleAnchor ? { of: spec.character.styleAnchor.assetId, sha256: spec.character.styleAnchor.sha256 } : void 0
|
|
2308
2341
|
} : void 0,
|
|
2309
2342
|
// A mirror's bytes come from its source's recorded outputs; the plan
|
|
2310
2343
|
// compares those directly, so only the choice of source is identity.
|
|
@@ -2491,6 +2524,19 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
2491
2524
|
}
|
|
2492
2525
|
}
|
|
2493
2526
|
}
|
|
2527
|
+
if (character.concept) {
|
|
2528
|
+
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`);
|
|
2529
|
+
if (character.concept.width > 1024 || character.concept.height > 1024) {
|
|
2530
|
+
throw new Error(`${label}: concept image is ${character.concept.width}x${character.concept.height}; PixelLab pro takes up to 1024px`);
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
if (character.styleAnchor) {
|
|
2534
|
+
if (character.mode !== "pro") throw new Error(`${label}: styleCharacter is a pro input; the ${character.mode} engine has no style anchor`);
|
|
2535
|
+
if (character.reference) throw new Error(`${label}: a pro base rotating its own reference takes no styleCharacter`);
|
|
2536
|
+
if (character.styleAnchor.spec.character?.directions !== 8) {
|
|
2537
|
+
throw new Error(`${label}: styleCharacter ${character.styleAnchor.assetId} has ${character.styleAnchor.spec.character?.directions} directions; PixelLab wants an 8-direction anchor`);
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2494
2540
|
if (character.reference) {
|
|
2495
2541
|
const given = Object.keys(character.reference);
|
|
2496
2542
|
if (!character.reference.south) throw new Error(`${label}: a reference needs a south-facing sprite`);
|
|
@@ -2592,7 +2638,9 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
2592
2638
|
isometric: character.isometric,
|
|
2593
2639
|
reference: character.reference ? readReference(spec, character.reference) : void 0,
|
|
2594
2640
|
styleReference: styleImage ? { base64: styleImage.base64, format: styleImage.format } : void 0,
|
|
2595
|
-
enhancePrompt: character.enhancePrompt
|
|
2641
|
+
enhancePrompt: character.enhancePrompt,
|
|
2642
|
+
concept: character.concept ? readPose(spec, "concept image", character.concept) : void 0,
|
|
2643
|
+
styleCharacterId: character.styleAnchor ? requireStyleObjectId(spec, context) : void 0
|
|
2596
2644
|
});
|
|
2597
2645
|
return { jobId: res2.character_id, metadata: { character: { kind: "base", characterId: res2.character_id, mode: character.mode, directions: character.directions, backgroundJobId: res2.background_job_id } } };
|
|
2598
2646
|
}
|
|
@@ -3012,6 +3060,14 @@ function readReference(spec, reference) {
|
|
|
3012
3060
|
}
|
|
3013
3061
|
return images;
|
|
3014
3062
|
}
|
|
3063
|
+
function requireStyleObjectId(spec, context) {
|
|
3064
|
+
if (!context?.styleObjectId) {
|
|
3065
|
+
throw new Error(
|
|
3066
|
+
`${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`
|
|
3067
|
+
);
|
|
3068
|
+
}
|
|
3069
|
+
return context.styleObjectId;
|
|
3070
|
+
}
|
|
3015
3071
|
function readPose(spec, what, image) {
|
|
3016
3072
|
const bytes = (0, import_node_fs2.readFileSync)(image.path);
|
|
3017
3073
|
if (sha256(bytes) !== image.sha256) {
|
|
@@ -5711,6 +5767,18 @@ async function resolveSpecs(loaded, filter) {
|
|
|
5711
5767
|
finalized.add(assetId);
|
|
5712
5768
|
return resolved;
|
|
5713
5769
|
}
|
|
5770
|
+
const anchorId = asset.styleCharacter ?? style.styleCharacter;
|
|
5771
|
+
if (resolved.character?.kind === "base" && anchorId && anchorId !== assetId) {
|
|
5772
|
+
const anchorSpec = await finalize(anchorId);
|
|
5773
|
+
if (!anchorSpec.character || anchorSpec.character.kind === "animation") {
|
|
5774
|
+
throw new Error(`assets.${assetId}: styleCharacter ${anchorId} is not a character base or state`);
|
|
5775
|
+
}
|
|
5776
|
+
const file = memberPath(anchorSpec.outFile, "south", 0, anchorSpec.character.directions, MediaType.PNG);
|
|
5777
|
+
resolved.character = {
|
|
5778
|
+
...resolved.character,
|
|
5779
|
+
styleAnchor: { assetId: anchorId, spec: anchorSpec, file, sha256: (0, import_node_fs5.existsSync)(file) ? await sha256File(file) : null }
|
|
5780
|
+
};
|
|
5781
|
+
}
|
|
5714
5782
|
const characterParent = asset.state?.of ?? asset.animation?.of;
|
|
5715
5783
|
if (resolved.character && characterParent) {
|
|
5716
5784
|
const parentSpec = await finalize(characterParent);
|
|
@@ -5863,6 +5931,10 @@ async function resolveCharacterShape(asset, style, kind, files) {
|
|
|
5863
5931
|
...style.isometric !== void 0 ? { isometric: style.isometric } : {},
|
|
5864
5932
|
...style.enhancePrompt !== void 0 ? { enhancePrompt: style.enhancePrompt } : {}
|
|
5865
5933
|
};
|
|
5934
|
+
if (asset.concept) {
|
|
5935
|
+
const image = await files.load(asset.concept, "Concept image");
|
|
5936
|
+
shape.concept = { path: import_node_path5.default.resolve(files.root, asset.concept), sha256: image.hash, width: image.width, height: image.height, format: image.format };
|
|
5937
|
+
}
|
|
5866
5938
|
if (asset.reference) {
|
|
5867
5939
|
const byDirection = typeof asset.reference === "string" ? { south: asset.reference } : asset.reference;
|
|
5868
5940
|
shape.reference = {};
|
|
@@ -7382,9 +7454,24 @@ var import_node_path11 = __toESM(require("path"), 1);
|
|
|
7382
7454
|
async function inspectRevisionReadiness(spec, lock) {
|
|
7383
7455
|
if (spec.mirror) return inspectMirror(spec, lock, /* @__PURE__ */ new Set());
|
|
7384
7456
|
if (spec.character?.parentSpec) return inspectCharacterParent(spec, lock, /* @__PURE__ */ new Set());
|
|
7457
|
+
if (spec.character?.styleAnchor) return inspectStyleAnchor(spec, lock, /* @__PURE__ */ new Set());
|
|
7385
7458
|
if (!spec.revision) return null;
|
|
7386
7459
|
return inspectRevision(spec, lock, /* @__PURE__ */ new Set());
|
|
7387
7460
|
}
|
|
7461
|
+
async function inspectStyleAnchor(spec, lock, seen) {
|
|
7462
|
+
const anchor = spec.character.styleAnchor;
|
|
7463
|
+
const label = `style anchor ${spec.styleId}/${anchor.assetId}`;
|
|
7464
|
+
const dependency = await inspectParent(anchor.spec, lock, seen);
|
|
7465
|
+
if (!dependency.ready) return { ready: false, reason: `${label} is not ready: ${dependency.reason}` };
|
|
7466
|
+
if (!anchor.sha256 || !(0, import_node_fs8.existsSync)(anchor.file)) return { ready: false, reason: `${label} has no generated south-facing file yet` };
|
|
7467
|
+
if (await sha256File(anchor.file) !== anchor.sha256) {
|
|
7468
|
+
return { ready: false, reason: `${label} changed after the manifest was resolved` };
|
|
7469
|
+
}
|
|
7470
|
+
if (!lock.entries[lockKey(anchor.spec.styleId, anchor.spec.assetId)]?.objectId) {
|
|
7471
|
+
return { ready: false, reason: `${label} has no PixelLab character id recorded` };
|
|
7472
|
+
}
|
|
7473
|
+
return { ready: true, reason: `${label} is current` };
|
|
7474
|
+
}
|
|
7388
7475
|
async function inspectMirror(spec, lock, seen) {
|
|
7389
7476
|
const source = spec.mirror.sourceSpec;
|
|
7390
7477
|
const label = `mirror source ${spec.styleId}/${spec.mirror.sourceAssetId}`;
|
|
@@ -7456,6 +7543,10 @@ async function inspectParent(spec, lock, seen) {
|
|
|
7456
7543
|
const inputs = await inspectCharacterParent(spec, lock, nextSeen);
|
|
7457
7544
|
if (!inputs.ready) return inputs;
|
|
7458
7545
|
}
|
|
7546
|
+
if (spec.character?.styleAnchor) {
|
|
7547
|
+
const inputs = await inspectStyleAnchor(spec, lock, nextSeen);
|
|
7548
|
+
if (!inputs.ready) return inputs;
|
|
7549
|
+
}
|
|
7459
7550
|
if (spec.quality) {
|
|
7460
7551
|
const quality = await inspectQualityProfile(spec, lock);
|
|
7461
7552
|
return quality?.state === "approved" ? { ready: true, reason: "approved quality output is current" } : {
|
|
@@ -7495,6 +7586,14 @@ async function anyOutputModified(entry, spec) {
|
|
|
7495
7586
|
}
|
|
7496
7587
|
return false;
|
|
7497
7588
|
}
|
|
7589
|
+
function dependencies(spec) {
|
|
7590
|
+
return [
|
|
7591
|
+
spec.mirror?.sourceSpec,
|
|
7592
|
+
spec.revision?.sourceSpec,
|
|
7593
|
+
spec.character?.parentSpec,
|
|
7594
|
+
spec.character?.styleAnchor?.spec
|
|
7595
|
+
].filter((parent) => Boolean(parent));
|
|
7596
|
+
}
|
|
7498
7597
|
function resumeCommandForStatus(status) {
|
|
7499
7598
|
if (status === "pending" || status === "processing") return "poll";
|
|
7500
7599
|
if (status === "review") return "pick";
|
|
@@ -7603,16 +7702,19 @@ async function buildPlan(specs, lock, opts = {}) {
|
|
|
7603
7702
|
const stateOf = new Map(items.map((i) => [i.key, i.state]));
|
|
7604
7703
|
for (const item of items) {
|
|
7605
7704
|
if (!isActionable(item)) continue;
|
|
7606
|
-
const parent
|
|
7607
|
-
|
|
7608
|
-
|
|
7609
|
-
|
|
7610
|
-
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
|
|
7614
|
-
item.
|
|
7615
|
-
|
|
7705
|
+
for (const parent of dependencies(item.spec)) {
|
|
7706
|
+
const parentKey = lockKey(parent.styleId, parent.assetId);
|
|
7707
|
+
const parentState = stateOf.get(parentKey);
|
|
7708
|
+
if (parentState && isActionable({ state: parentState })) {
|
|
7709
|
+
item.state = "blocked";
|
|
7710
|
+
item.reason = `parent ${parentKey} is being generated in this run; this follows in the next wave`;
|
|
7711
|
+
break;
|
|
7712
|
+
}
|
|
7713
|
+
if (item.spec.mirror && (parentState === "recoverable" || parentState === "in-flight")) {
|
|
7714
|
+
item.state = "blocked";
|
|
7715
|
+
item.reason = `mirror source ${parentKey} is ${parentState}; this follows in the next wave`;
|
|
7716
|
+
break;
|
|
7717
|
+
}
|
|
7616
7718
|
}
|
|
7617
7719
|
}
|
|
7618
7720
|
const actionable = items.filter(isActionable);
|
|
@@ -10091,10 +10193,13 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
|
|
|
10091
10193
|
try {
|
|
10092
10194
|
const refs = styleImages.get(spec.styleId) ?? [];
|
|
10093
10195
|
const parentEntry = spec.character?.parentSpec ? lock.entries[lockKey(spec.character.parentSpec.styleId, spec.character.parentSpec.assetId)] : void 0;
|
|
10196
|
+
const anchor = spec.character?.styleAnchor;
|
|
10197
|
+
const anchorEntry = anchor ? lock.entries[lockKey(anchor.spec.styleId, anchor.spec.assetId)] : void 0;
|
|
10094
10198
|
const replacedMetadata = previousEntry?.providerMetadata?.[provider.id];
|
|
10095
10199
|
const { jobId, metadata } = await provider.submit(spec, refs, {
|
|
10096
10200
|
...previousJobId ? { previousJobId } : {},
|
|
10097
10201
|
...parentEntry?.objectId ? { parentObjectId: parentEntry.objectId } : {},
|
|
10202
|
+
...anchorEntry?.objectId ? { styleObjectId: anchorEntry.objectId } : {},
|
|
10098
10203
|
...replacedMetadata ? { replacedMetadata } : {},
|
|
10099
10204
|
...previousMetadata ? { previousMetadata } : {},
|
|
10100
10205
|
checkpoint: async (checkpoint) => {
|
|
@@ -12703,7 +12808,7 @@ var pin_default = {
|
|
|
12703
12808
|
godot: "4.7.2",
|
|
12704
12809
|
extensionsApi: 9,
|
|
12705
12810
|
protocol: 4,
|
|
12706
|
-
release: "editor-pixelorama-v1.2.2-pk.
|
|
12811
|
+
release: "editor-pixelorama-v1.2.2-pk.26",
|
|
12707
12812
|
files: {
|
|
12708
12813
|
"index.apple-touch-icon.png": {
|
|
12709
12814
|
sha256: "ee5434873e0fed91e9a888c3ea5a8b79beac25999308be7a6eb41bf4ce640d27",
|
|
@@ -12718,7 +12823,7 @@ var pin_default = {
|
|
|
12718
12823
|
bytes: 7298
|
|
12719
12824
|
},
|
|
12720
12825
|
"index.html": {
|
|
12721
|
-
sha256: "
|
|
12826
|
+
sha256: "326101ab02bb4a29a7964214c9307c7dff26e9d66b8ebaaf6e7a3987e84a05b2",
|
|
12722
12827
|
bytes: 5442
|
|
12723
12828
|
},
|
|
12724
12829
|
"index.icon.png": {
|
|
@@ -12730,8 +12835,8 @@ var pin_default = {
|
|
|
12730
12835
|
bytes: 279815
|
|
12731
12836
|
},
|
|
12732
12837
|
"index.pck": {
|
|
12733
|
-
sha256: "
|
|
12734
|
-
bytes:
|
|
12838
|
+
sha256: "e26292650bb996965acd1ba70e74d2b4020b063d14f1276701312c1d34cb0a83",
|
|
12839
|
+
bytes: 6431932
|
|
12735
12840
|
},
|
|
12736
12841
|
"index.png": {
|
|
12737
12842
|
sha256: "6e3ca3eb4a99e4c07bdd89ebc873b440ec0ffd66ce1955160da9e7ed80d538d7",
|