pixelkiln 0.43.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 +257 -53
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +256 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +665 -126
- package/dist/index.d.ts +665 -126
- package/dist/index.js +256 -52
- package/dist/index.js.map +1 -1
- package/docs/MANIFEST.md +52 -4
- package/package.json +1 -1
- package/schema/manifest.schema.json +49 -0
- package/schema/recipe.schema.json +7 -0
- package/skills/pixelkiln/references/pixellab.md +9 -1
package/dist/cli.js
CHANGED
|
@@ -665,12 +665,20 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
|
|
|
665
665
|
parent: spec.character.parentAssetId,
|
|
666
666
|
parentSha256: spec.character.parentSha256 ?? null,
|
|
667
667
|
state: spec.character.state,
|
|
668
|
-
|
|
668
|
+
// Pose images enter as their hashes, never their paths.
|
|
669
|
+
animation: spec.character.animation ? {
|
|
670
|
+
...spec.character.animation,
|
|
671
|
+
startFrame: spec.character.animation.startFrame?.sha256,
|
|
672
|
+
endFrame: spec.character.animation.endFrame?.sha256
|
|
673
|
+
} : void 0,
|
|
669
674
|
// Absent keys keep the hashes of manifests that never set these.
|
|
670
675
|
proportions: spec.character.proportions,
|
|
671
676
|
textGuidanceScale: spec.character.textGuidanceScale,
|
|
672
677
|
isometric: spec.character.isometric,
|
|
673
|
-
|
|
678
|
+
enhancePrompt: spec.character.enhancePrompt,
|
|
679
|
+
reference: spec.character.reference ? Object.fromEntries(Object.entries(spec.character.reference).map(([direction, image]) => [direction, image.sha256])) : void 0,
|
|
680
|
+
concept: spec.character.concept?.sha256,
|
|
681
|
+
styleAnchor: spec.character.styleAnchor ? { of: spec.character.styleAnchor.assetId, sha256: spec.character.styleAnchor.sha256 } : void 0
|
|
674
682
|
} : void 0,
|
|
675
683
|
// A mirror's bytes come from its source's recorded outputs; the plan
|
|
676
684
|
// compares those directly, so only the choice of source is identity.
|
|
@@ -825,11 +833,45 @@ var CharacterAnimationSchema = z.object({
|
|
|
825
833
|
/** v3 only: keep the resting pose as frame 0, so `frames` generated frames land as `frames + 1` files. */
|
|
826
834
|
keepFirstFrame: z.boolean().default(true),
|
|
827
835
|
/** `template` when a template is named, otherwise `v3`; `pro` for the sequential high-quality engine. */
|
|
828
|
-
mode: CharacterAnimationModeSchema.optional()
|
|
836
|
+
mode: CharacterAnimationModeSchema.optional(),
|
|
837
|
+
/**
|
|
838
|
+
* v3 only: a manifest-relative image of the pose to start from, instead
|
|
839
|
+
* of the character's rotation for this direction. Up to 256px.
|
|
840
|
+
*/
|
|
841
|
+
startFrame: z.string().min(1).optional(),
|
|
842
|
+
/**
|
|
843
|
+
* v3 only: a pose to animate toward. The loop then interpolates from the
|
|
844
|
+
* start frame (the rotation, or `startFrame`) to this image, which must
|
|
845
|
+
* be the same size as the start frame.
|
|
846
|
+
*/
|
|
847
|
+
endFrame: z.string().min(1).optional(),
|
|
848
|
+
/** What is being animated, when the character's own description would mislead the model. */
|
|
849
|
+
subject: z.string().min(1).optional(),
|
|
850
|
+
/** Template mode only: style hints for this loop, over the character's own. */
|
|
851
|
+
outline: z.string().min(1).optional(),
|
|
852
|
+
shading: z.string().min(1).optional(),
|
|
853
|
+
detail: z.string().min(1).optional(),
|
|
854
|
+
/** v3 text loops only: let PixelLab expand the action into a fuller motion description first. */
|
|
855
|
+
enhancePrompt: z.boolean().optional()
|
|
829
856
|
}).strict().superRefine((animation, context) => {
|
|
830
857
|
if (animation.frames !== void 0 && animation.frames % 2 !== 0) {
|
|
831
858
|
context.addIssue({ code: z.ZodIssueCode.custom, message: "frames must be even", path: ["frames"] });
|
|
832
859
|
}
|
|
860
|
+
const mode2 = animation.mode ?? (animation.template ? "template" : "v3");
|
|
861
|
+
for (const key of ["startFrame", "endFrame", "enhancePrompt"]) {
|
|
862
|
+
if (animation[key] !== void 0 && mode2 !== "v3") {
|
|
863
|
+
context.addIssue({
|
|
864
|
+
code: z.ZodIssueCode.custom,
|
|
865
|
+
message: `${key} is for v3 loops; a ${mode2} loop ${mode2 === "template" ? "starts from the character's rotation and follows its template" : "takes neither"}`,
|
|
866
|
+
path: [key]
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
for (const key of ["outline", "shading", "detail"]) {
|
|
871
|
+
if (animation[key] !== void 0 && mode2 !== "template") {
|
|
872
|
+
context.addIssue({ code: z.ZodIssueCode.custom, message: `${key} overrides apply to template loops only`, path: [key] });
|
|
873
|
+
}
|
|
874
|
+
}
|
|
833
875
|
if (animation.template && animation.mode && animation.mode !== "template") {
|
|
834
876
|
context.addIssue({
|
|
835
877
|
code: z.ZodIssueCode.custom,
|
|
@@ -967,6 +1009,14 @@ var StyleObjectSchema = z.object({
|
|
|
967
1009
|
textGuidanceScale: z.number().min(1).max(20).optional(),
|
|
968
1010
|
/** `character` only. Draw `standard` bases and every loop in isometric view. */
|
|
969
1011
|
isometric: z.boolean().optional(),
|
|
1012
|
+
/** `character` only, `v3` bases: let PixelLab expand the prompt into a fuller one before drawing. */
|
|
1013
|
+
enhancePrompt: z.boolean().optional(),
|
|
1014
|
+
/**
|
|
1015
|
+
* `character` only, `pro` bases: the asset id of a generated 8-direction
|
|
1016
|
+
* character in this style whose look every base drawn from text or a
|
|
1017
|
+
* concept follows. An asset may override it; the anchor itself ignores it.
|
|
1018
|
+
*/
|
|
1019
|
+
styleCharacter: z.string().min(1).optional(),
|
|
970
1020
|
/** Fixed seed for reproducibility where the endpoint supports it. */
|
|
971
1021
|
seed: z.number().int().optional(),
|
|
972
1022
|
/**
|
|
@@ -1139,6 +1189,15 @@ var AssetSchema = z.object({
|
|
|
1139
1189
|
* still guides the result.
|
|
1140
1190
|
*/
|
|
1141
1191
|
reference: z.union([z.string().min(1), z.record(CharacterDirectionSchema, z.string().min(1))]).optional(),
|
|
1192
|
+
/**
|
|
1193
|
+
* `character` styles, `pro` bases only: a manifest-relative concept
|
|
1194
|
+
* image (a painting, a photo, a sketch; up to 1024px) that seeds the
|
|
1195
|
+
* design instead of the prompt alone. A style image or `styleCharacter`
|
|
1196
|
+
* may still anchor the look.
|
|
1197
|
+
*/
|
|
1198
|
+
concept: z.string().min(1).optional(),
|
|
1199
|
+
/** `character` styles, `pro` bases: this base's style anchor, over the style's. */
|
|
1200
|
+
styleCharacter: z.string().min(1).optional(),
|
|
1142
1201
|
/**
|
|
1143
1202
|
* Another asset of the same style flipped left to right, made locally
|
|
1144
1203
|
* from that asset's downloaded files at no generation cost.
|
|
@@ -1220,6 +1279,20 @@ var AssetSchema = z.object({
|
|
|
1220
1279
|
path: ["reference"]
|
|
1221
1280
|
});
|
|
1222
1281
|
}
|
|
1282
|
+
if (asset.concept && (asset.state || asset.animation || asset.mirror)) {
|
|
1283
|
+
context.addIssue({
|
|
1284
|
+
code: z.ZodIssueCode.custom,
|
|
1285
|
+
message: "a concept image belongs on a base; a state, animation, or mirror takes its look from its parent",
|
|
1286
|
+
path: ["concept"]
|
|
1287
|
+
});
|
|
1288
|
+
}
|
|
1289
|
+
if (asset.concept && asset.reference) {
|
|
1290
|
+
context.addIssue({
|
|
1291
|
+
code: z.ZodIssueCode.custom,
|
|
1292
|
+
message: "concept and reference are mutually exclusive: a concept seeds a new design, a reference is the sprite to rotate",
|
|
1293
|
+
path: ["concept"]
|
|
1294
|
+
});
|
|
1295
|
+
}
|
|
1223
1296
|
if (typeof asset.reference === "object" && !Object.keys(asset.reference).length) {
|
|
1224
1297
|
context.addIssue({ code: z.ZodIssueCode.custom, message: "a reference needs at least a south image", path: ["reference"] });
|
|
1225
1298
|
}
|
|
@@ -2014,6 +2087,34 @@ function skipSubBlocks(bytes, start) {
|
|
|
2014
2087
|
function cacheFileName(hash, mediaType = MediaType.PNG) {
|
|
2015
2088
|
return `${hash}${mediaExtension(mediaType)}`;
|
|
2016
2089
|
}
|
|
2090
|
+
function imageMetadata(buf) {
|
|
2091
|
+
if (buf.length >= 24 && buf.readUInt32BE(0) === 2303741511 && buf.readUInt32BE(4) === 218765834 && buf.toString("ascii", 12, 16) === "IHDR") {
|
|
2092
|
+
return { width: buf.readUInt32BE(16), height: buf.readUInt32BE(20), format: "png" };
|
|
2093
|
+
}
|
|
2094
|
+
if (buf.length < 4 || buf[0] !== 255 || buf[1] !== 216) return null;
|
|
2095
|
+
let offset = 2;
|
|
2096
|
+
while (offset + 3 < buf.length) {
|
|
2097
|
+
if (buf[offset] !== 255) return null;
|
|
2098
|
+
while (buf[offset] === 255) offset++;
|
|
2099
|
+
const marker = buf[offset++];
|
|
2100
|
+
if (marker == null || marker === 217 || marker === 218) break;
|
|
2101
|
+
if (marker === 1 || marker >= 208 && marker <= 215) continue;
|
|
2102
|
+
if (offset + 2 > buf.length) return null;
|
|
2103
|
+
const length = buf.readUInt16BE(offset);
|
|
2104
|
+
if (length < 2 || offset + length > buf.length) return null;
|
|
2105
|
+
const isStartOfFrame = marker >= 192 && marker <= 207 && ![196, 200, 204].includes(marker);
|
|
2106
|
+
if (isStartOfFrame) {
|
|
2107
|
+
if (length < 7) return null;
|
|
2108
|
+
return {
|
|
2109
|
+
width: buf.readUInt16BE(offset + 5),
|
|
2110
|
+
height: buf.readUInt16BE(offset + 3),
|
|
2111
|
+
format: "jpeg"
|
|
2112
|
+
};
|
|
2113
|
+
}
|
|
2114
|
+
offset += length;
|
|
2115
|
+
}
|
|
2116
|
+
return null;
|
|
2117
|
+
}
|
|
2017
2118
|
|
|
2018
2119
|
// src/outputs.ts
|
|
2019
2120
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -3559,9 +3660,24 @@ import path8 from "path";
|
|
|
3559
3660
|
async function inspectRevisionReadiness(spec, lock) {
|
|
3560
3661
|
if (spec.mirror) return inspectMirror(spec, lock, /* @__PURE__ */ new Set());
|
|
3561
3662
|
if (spec.character?.parentSpec) return inspectCharacterParent(spec, lock, /* @__PURE__ */ new Set());
|
|
3663
|
+
if (spec.character?.styleAnchor) return inspectStyleAnchor(spec, lock, /* @__PURE__ */ new Set());
|
|
3562
3664
|
if (!spec.revision) return null;
|
|
3563
3665
|
return inspectRevision(spec, lock, /* @__PURE__ */ new Set());
|
|
3564
3666
|
}
|
|
3667
|
+
async function inspectStyleAnchor(spec, lock, seen) {
|
|
3668
|
+
const anchor = spec.character.styleAnchor;
|
|
3669
|
+
const label = `style anchor ${spec.styleId}/${anchor.assetId}`;
|
|
3670
|
+
const dependency = await inspectParent(anchor.spec, lock, seen);
|
|
3671
|
+
if (!dependency.ready) return { ready: false, reason: `${label} is not ready: ${dependency.reason}` };
|
|
3672
|
+
if (!anchor.sha256 || !existsSync4(anchor.file)) return { ready: false, reason: `${label} has no generated south-facing file yet` };
|
|
3673
|
+
if (await sha256File(anchor.file) !== anchor.sha256) {
|
|
3674
|
+
return { ready: false, reason: `${label} changed after the manifest was resolved` };
|
|
3675
|
+
}
|
|
3676
|
+
if (!lock.entries[lockKey(anchor.spec.styleId, anchor.spec.assetId)]?.objectId) {
|
|
3677
|
+
return { ready: false, reason: `${label} has no PixelLab character id recorded` };
|
|
3678
|
+
}
|
|
3679
|
+
return { ready: true, reason: `${label} is current` };
|
|
3680
|
+
}
|
|
3565
3681
|
async function inspectMirror(spec, lock, seen) {
|
|
3566
3682
|
const source = spec.mirror.sourceSpec;
|
|
3567
3683
|
const label = `mirror source ${spec.styleId}/${spec.mirror.sourceAssetId}`;
|
|
@@ -3633,6 +3749,10 @@ async function inspectParent(spec, lock, seen) {
|
|
|
3633
3749
|
const inputs = await inspectCharacterParent(spec, lock, nextSeen);
|
|
3634
3750
|
if (!inputs.ready) return inputs;
|
|
3635
3751
|
}
|
|
3752
|
+
if (spec.character?.styleAnchor) {
|
|
3753
|
+
const inputs = await inspectStyleAnchor(spec, lock, nextSeen);
|
|
3754
|
+
if (!inputs.ready) return inputs;
|
|
3755
|
+
}
|
|
3636
3756
|
if (spec.quality) {
|
|
3637
3757
|
const quality = await inspectQualityProfile(spec, lock);
|
|
3638
3758
|
return quality?.state === "approved" ? { ready: true, reason: "approved quality output is current" } : {
|
|
@@ -3672,6 +3792,14 @@ async function anyOutputModified(entry, spec) {
|
|
|
3672
3792
|
}
|
|
3673
3793
|
return false;
|
|
3674
3794
|
}
|
|
3795
|
+
function dependencies(spec) {
|
|
3796
|
+
return [
|
|
3797
|
+
spec.mirror?.sourceSpec,
|
|
3798
|
+
spec.revision?.sourceSpec,
|
|
3799
|
+
spec.character?.parentSpec,
|
|
3800
|
+
spec.character?.styleAnchor?.spec
|
|
3801
|
+
].filter((parent) => Boolean(parent));
|
|
3802
|
+
}
|
|
3675
3803
|
function resumeCommandForStatus(status) {
|
|
3676
3804
|
if (status === "pending" || status === "processing") return "poll";
|
|
3677
3805
|
if (status === "review") return "pick";
|
|
@@ -3780,16 +3908,19 @@ async function buildPlan(specs, lock, opts = {}) {
|
|
|
3780
3908
|
const stateOf = new Map(items.map((i) => [i.key, i.state]));
|
|
3781
3909
|
for (const item of items) {
|
|
3782
3910
|
if (!isActionable(item)) continue;
|
|
3783
|
-
const parent
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
item.
|
|
3792
|
-
|
|
3911
|
+
for (const parent of dependencies(item.spec)) {
|
|
3912
|
+
const parentKey = lockKey(parent.styleId, parent.assetId);
|
|
3913
|
+
const parentState = stateOf.get(parentKey);
|
|
3914
|
+
if (parentState && isActionable({ state: parentState })) {
|
|
3915
|
+
item.state = "blocked";
|
|
3916
|
+
item.reason = `parent ${parentKey} is being generated in this run; this follows in the next wave`;
|
|
3917
|
+
break;
|
|
3918
|
+
}
|
|
3919
|
+
if (item.spec.mirror && (parentState === "recoverable" || parentState === "in-flight")) {
|
|
3920
|
+
item.state = "blocked";
|
|
3921
|
+
item.reason = `mirror source ${parentKey} is ${parentState}; this follows in the next wave`;
|
|
3922
|
+
break;
|
|
3923
|
+
}
|
|
3793
3924
|
}
|
|
3794
3925
|
}
|
|
3795
3926
|
const actionable = items.filter(isActionable);
|
|
@@ -5308,7 +5439,8 @@ var PixelLabClient = class {
|
|
|
5308
5439
|
...args.outline ? { outline: args.outline } : {},
|
|
5309
5440
|
...args.detail ? { detail: args.detail } : {},
|
|
5310
5441
|
...args.seed != null ? { seed: args.seed } : {},
|
|
5311
|
-
...south ? { reference_image: encode(south) } : {}
|
|
5442
|
+
...south ? { reference_image: encode(south) } : {},
|
|
5443
|
+
...args.enhancePrompt ? { enhance_prompt: true } : {}
|
|
5312
5444
|
};
|
|
5313
5445
|
} else if (args.mode === "pro") {
|
|
5314
5446
|
path45 = "/create-character-pro";
|
|
@@ -5317,10 +5449,12 @@ var PixelLabClient = class {
|
|
|
5317
5449
|
image_size: imageSize,
|
|
5318
5450
|
template_id: args.template,
|
|
5319
5451
|
no_background: args.noBackground ?? true,
|
|
5320
|
-
method: south ? "rotate_character" : "create_with_style",
|
|
5452
|
+
method: south ? "rotate_character" : args.concept ? "create_from_concept" : "create_with_style",
|
|
5321
5453
|
...args.view ? { view: args.view } : {},
|
|
5322
5454
|
...args.seed != null ? { seed: args.seed } : {},
|
|
5323
|
-
...south ? { reference_image: encode(south) } : args.styleReference ? { reference_image: encode(args.styleReference) } : {}
|
|
5455
|
+
...south ? { reference_image: encode(south) } : args.styleReference ? { reference_image: encode(args.styleReference) } : {},
|
|
5456
|
+
...!south && args.concept ? { concept_image: encode(args.concept) } : {},
|
|
5457
|
+
...!south && args.styleCharacterId ? { style_character_id: args.styleCharacterId } : {}
|
|
5324
5458
|
};
|
|
5325
5459
|
} else {
|
|
5326
5460
|
path45 = args.directions === 4 ? "/create-character-with-4-directions" : "/create-character-with-8-directions";
|
|
@@ -5365,6 +5499,7 @@ var PixelLabClient = class {
|
|
|
5365
5499
|
* the action text; pro is the sequential engine. One job per direction.
|
|
5366
5500
|
*/
|
|
5367
5501
|
async animateCharacter(args) {
|
|
5502
|
+
const encode = (image) => ({ type: "base64", base64: image.base64, format: image.format });
|
|
5368
5503
|
const raw = await this.request("/animate-character", {
|
|
5369
5504
|
method: "POST",
|
|
5370
5505
|
body: JSON.stringify({
|
|
@@ -5374,10 +5509,18 @@ var PixelLabClient = class {
|
|
|
5374
5509
|
directions: args.directions,
|
|
5375
5510
|
...args.template ? { template_animation_id: args.template } : {},
|
|
5376
5511
|
...args.actionDescription ? { action_description: args.actionDescription } : {},
|
|
5512
|
+
...args.description ? { description: args.description } : {},
|
|
5377
5513
|
...args.mode === "v3" && args.frameCount ? { frame_count: args.frameCount } : {},
|
|
5378
5514
|
...args.mode === "v3" && args.keepFirstFrame === false ? { keep_first_frame: false } : {},
|
|
5515
|
+
...args.mode === "v3" && args.startFrame ? { custom_start_frame: encode(args.startFrame) } : {},
|
|
5516
|
+
...args.mode === "v3" && args.endFrame ? { end_frame: encode(args.endFrame) } : {},
|
|
5517
|
+
...args.mode === "v3" && args.enhancePrompt ? { enhance_prompt: true } : {},
|
|
5379
5518
|
...args.mode === "template" && args.textGuidanceScale !== void 0 ? { text_guidance_scale: args.textGuidanceScale } : {},
|
|
5519
|
+
...args.mode === "template" && args.outline ? { outline: args.outline } : {},
|
|
5520
|
+
...args.mode === "template" && args.shading ? { shading: args.shading } : {},
|
|
5521
|
+
...args.mode === "template" && args.detail ? { detail: args.detail } : {},
|
|
5380
5522
|
...args.isometric !== void 0 ? { isometric: args.isometric } : {},
|
|
5523
|
+
...args.paletteSwatchBase64 ? { color_image: { type: "base64", base64: args.paletteSwatchBase64, format: "png" }, force_colors: true } : {},
|
|
5381
5524
|
...args.seed != null ? { seed: args.seed } : {}
|
|
5382
5525
|
})
|
|
5383
5526
|
});
|
|
@@ -5603,6 +5746,38 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
5603
5746
|
throw new Error(`${label}: proportions apply to the mannequin template; ${character.template} is a quadruped`);
|
|
5604
5747
|
}
|
|
5605
5748
|
}
|
|
5749
|
+
if (character.kind === "base" && character.enhancePrompt !== void 0 && character.mode !== "v3") {
|
|
5750
|
+
throw new Error(`${label}: enhancePrompt applies to v3 bases; the ${character.mode} engine does not take it`);
|
|
5751
|
+
}
|
|
5752
|
+
if (character.animation) {
|
|
5753
|
+
const { startFrame, endFrame } = character.animation;
|
|
5754
|
+
for (const [name, image] of [["start frame", startFrame], ["end frame", endFrame]]) {
|
|
5755
|
+
if (image && (image.width > 256 || image.height > 256)) {
|
|
5756
|
+
throw new Error(`${label}: ${name} is ${image.width}x${image.height}; PixelLab v3 takes up to 256px`);
|
|
5757
|
+
}
|
|
5758
|
+
}
|
|
5759
|
+
if (endFrame) {
|
|
5760
|
+
const start = startFrame ?? (character.parentFile && existsSync7(character.parentFile) ? imageMetadata(readFileSync2(character.parentFile)) : null);
|
|
5761
|
+
if (start && (start.width !== endFrame.width || start.height !== endFrame.height)) {
|
|
5762
|
+
throw new Error(
|
|
5763
|
+
`${label}: end frame is ${endFrame.width}x${endFrame.height} but the ${startFrame ? "start frame" : "character's rotation"} is ${start.width}x${start.height}; PixelLab interpolates between frames of one size`
|
|
5764
|
+
);
|
|
5765
|
+
}
|
|
5766
|
+
}
|
|
5767
|
+
}
|
|
5768
|
+
if (character.concept) {
|
|
5769
|
+
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`);
|
|
5770
|
+
if (character.concept.width > 1024 || character.concept.height > 1024) {
|
|
5771
|
+
throw new Error(`${label}: concept image is ${character.concept.width}x${character.concept.height}; PixelLab pro takes up to 1024px`);
|
|
5772
|
+
}
|
|
5773
|
+
}
|
|
5774
|
+
if (character.styleAnchor) {
|
|
5775
|
+
if (character.mode !== "pro") throw new Error(`${label}: styleCharacter is a pro input; the ${character.mode} engine has no style anchor`);
|
|
5776
|
+
if (character.reference) throw new Error(`${label}: a pro base rotating its own reference takes no styleCharacter`);
|
|
5777
|
+
if (character.styleAnchor.spec.character?.directions !== 8) {
|
|
5778
|
+
throw new Error(`${label}: styleCharacter ${character.styleAnchor.assetId} has ${character.styleAnchor.spec.character?.directions} directions; PixelLab wants an 8-direction anchor`);
|
|
5779
|
+
}
|
|
5780
|
+
}
|
|
5606
5781
|
if (character.reference) {
|
|
5607
5782
|
const given = Object.keys(character.reference);
|
|
5608
5783
|
if (!character.reference.south) throw new Error(`${label}: a reference needs a south-facing sprite`);
|
|
@@ -5703,7 +5878,10 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
5703
5878
|
textGuidanceScale: character.textGuidanceScale,
|
|
5704
5879
|
isometric: character.isometric,
|
|
5705
5880
|
reference: character.reference ? readReference(spec, character.reference) : void 0,
|
|
5706
|
-
styleReference: styleImage ? { base64: styleImage.base64, format: styleImage.format } : void 0
|
|
5881
|
+
styleReference: styleImage ? { base64: styleImage.base64, format: styleImage.format } : void 0,
|
|
5882
|
+
enhancePrompt: character.enhancePrompt,
|
|
5883
|
+
concept: character.concept ? readPose(spec, "concept image", character.concept) : void 0,
|
|
5884
|
+
styleCharacterId: character.styleAnchor ? requireStyleObjectId(spec, context) : void 0
|
|
5707
5885
|
});
|
|
5708
5886
|
return { jobId: res2.character_id, metadata: { character: { kind: "base", characterId: res2.character_id, mode: character.mode, directions: character.directions, backgroundJobId: res2.background_job_id } } };
|
|
5709
5887
|
}
|
|
@@ -5747,7 +5925,15 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
5747
5925
|
directions: [animation.direction],
|
|
5748
5926
|
seed: spec.seed,
|
|
5749
5927
|
textGuidanceScale: character.textGuidanceScale,
|
|
5750
|
-
isometric: character.isometric
|
|
5928
|
+
isometric: character.isometric,
|
|
5929
|
+
startFrame: animation.startFrame ? readPose(spec, "start frame", animation.startFrame) : void 0,
|
|
5930
|
+
endFrame: animation.endFrame ? readPose(spec, "end frame", animation.endFrame) : void 0,
|
|
5931
|
+
description: animation.subject,
|
|
5932
|
+
outline: animation.outline,
|
|
5933
|
+
shading: animation.shading,
|
|
5934
|
+
detail: animation.detail,
|
|
5935
|
+
enhancePrompt: animation.enhancePrompt,
|
|
5936
|
+
paletteSwatchBase64: spec.palette.length ? paletteSwatch(spec.palette).toString("base64") : void 0
|
|
5751
5937
|
});
|
|
5752
5938
|
const job = { characterId: parentId, name, direction: animation.direction, jobIds: res.background_job_ids };
|
|
5753
5939
|
return {
|
|
@@ -6115,6 +6301,21 @@ function readReference(spec, reference) {
|
|
|
6115
6301
|
}
|
|
6116
6302
|
return images;
|
|
6117
6303
|
}
|
|
6304
|
+
function requireStyleObjectId(spec, context) {
|
|
6305
|
+
if (!context?.styleObjectId) {
|
|
6306
|
+
throw new Error(
|
|
6307
|
+
`${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`
|
|
6308
|
+
);
|
|
6309
|
+
}
|
|
6310
|
+
return context.styleObjectId;
|
|
6311
|
+
}
|
|
6312
|
+
function readPose(spec, what, image) {
|
|
6313
|
+
const bytes = readFileSync2(image.path);
|
|
6314
|
+
if (sha256(bytes) !== image.sha256) {
|
|
6315
|
+
throw new Error(`${spec.styleId}/${spec.assetId}: ${what} changed after the manifest was resolved: ${image.path}`);
|
|
6316
|
+
}
|
|
6317
|
+
return { base64: bytes.toString("base64"), format: image.format };
|
|
6318
|
+
}
|
|
6118
6319
|
function rotationSources(character) {
|
|
6119
6320
|
const order = character.directions === 4 ? CHARACTER_DIRECTIONS_4 : CHARACTER_DIRECTIONS_8;
|
|
6120
6321
|
const sources = [];
|
|
@@ -7429,6 +7630,18 @@ async function resolveSpecs(loaded, filter) {
|
|
|
7429
7630
|
finalized.add(assetId);
|
|
7430
7631
|
return resolved;
|
|
7431
7632
|
}
|
|
7633
|
+
const anchorId = asset.styleCharacter ?? style.styleCharacter;
|
|
7634
|
+
if (resolved.character?.kind === "base" && anchorId && anchorId !== assetId) {
|
|
7635
|
+
const anchorSpec = await finalize(anchorId);
|
|
7636
|
+
if (!anchorSpec.character || anchorSpec.character.kind === "animation") {
|
|
7637
|
+
throw new Error(`assets.${assetId}: styleCharacter ${anchorId} is not a character base or state`);
|
|
7638
|
+
}
|
|
7639
|
+
const file = memberPath(anchorSpec.outFile, "south", 0, anchorSpec.character.directions, MediaType.PNG);
|
|
7640
|
+
resolved.character = {
|
|
7641
|
+
...resolved.character,
|
|
7642
|
+
styleAnchor: { assetId: anchorId, spec: anchorSpec, file, sha256: existsSync8(file) ? await sha256File(file) : null }
|
|
7643
|
+
};
|
|
7644
|
+
}
|
|
7432
7645
|
const characterParent = asset.state?.of ?? asset.animation?.of;
|
|
7433
7646
|
if (resolved.character && characterParent) {
|
|
7434
7647
|
const parentSpec = await finalize(characterParent);
|
|
@@ -7564,34 +7777,6 @@ async function resolveStyleImages(loaded, styleId) {
|
|
|
7564
7777
|
}
|
|
7565
7778
|
return out;
|
|
7566
7779
|
}
|
|
7567
|
-
function imageMetadata(buf) {
|
|
7568
|
-
if (buf.length >= 24 && buf.readUInt32BE(0) === 2303741511 && buf.readUInt32BE(4) === 218765834 && buf.toString("ascii", 12, 16) === "IHDR") {
|
|
7569
|
-
return { width: buf.readUInt32BE(16), height: buf.readUInt32BE(20), format: "png" };
|
|
7570
|
-
}
|
|
7571
|
-
if (buf.length < 4 || buf[0] !== 255 || buf[1] !== 216) return null;
|
|
7572
|
-
let offset = 2;
|
|
7573
|
-
while (offset + 3 < buf.length) {
|
|
7574
|
-
if (buf[offset] !== 255) return null;
|
|
7575
|
-
while (buf[offset] === 255) offset++;
|
|
7576
|
-
const marker = buf[offset++];
|
|
7577
|
-
if (marker == null || marker === 217 || marker === 218) break;
|
|
7578
|
-
if (marker === 1 || marker >= 208 && marker <= 215) continue;
|
|
7579
|
-
if (offset + 2 > buf.length) return null;
|
|
7580
|
-
const length = buf.readUInt16BE(offset);
|
|
7581
|
-
if (length < 2 || offset + length > buf.length) return null;
|
|
7582
|
-
const isStartOfFrame = marker >= 192 && marker <= 207 && ![196, 200, 204].includes(marker);
|
|
7583
|
-
if (isStartOfFrame) {
|
|
7584
|
-
if (length < 7) return null;
|
|
7585
|
-
return {
|
|
7586
|
-
width: buf.readUInt16BE(offset + 5),
|
|
7587
|
-
height: buf.readUInt16BE(offset + 3),
|
|
7588
|
-
format: "jpeg"
|
|
7589
|
-
};
|
|
7590
|
-
}
|
|
7591
|
-
offset += length;
|
|
7592
|
-
}
|
|
7593
|
-
return null;
|
|
7594
|
-
}
|
|
7595
7780
|
async function resolveCharacterShape(asset, style, kind, files) {
|
|
7596
7781
|
const mode2 = style.mode ?? "standard";
|
|
7597
7782
|
const directions = mode2 === "standard" ? style.directions ?? 8 : 8;
|
|
@@ -7603,8 +7788,13 @@ async function resolveCharacterShape(asset, style, kind, files) {
|
|
|
7603
7788
|
template: style.template ?? "mannequin",
|
|
7604
7789
|
...proportions !== void 0 ? { proportions } : {},
|
|
7605
7790
|
...style.textGuidanceScale !== void 0 ? { textGuidanceScale: style.textGuidanceScale } : {},
|
|
7606
|
-
...style.isometric !== void 0 ? { isometric: style.isometric } : {}
|
|
7791
|
+
...style.isometric !== void 0 ? { isometric: style.isometric } : {},
|
|
7792
|
+
...style.enhancePrompt !== void 0 ? { enhancePrompt: style.enhancePrompt } : {}
|
|
7607
7793
|
};
|
|
7794
|
+
if (asset.concept) {
|
|
7795
|
+
const image = await files.load(asset.concept, "Concept image");
|
|
7796
|
+
shape.concept = { path: path13.resolve(files.root, asset.concept), sha256: image.hash, width: image.width, height: image.height, format: image.format };
|
|
7797
|
+
}
|
|
7608
7798
|
if (asset.reference) {
|
|
7609
7799
|
const byDirection = typeof asset.reference === "string" ? { south: asset.reference } : asset.reference;
|
|
7610
7800
|
shape.reference = {};
|
|
@@ -7627,13 +7817,24 @@ async function resolveCharacterShape(asset, style, kind, files) {
|
|
|
7627
7817
|
}
|
|
7628
7818
|
if (asset.animation) {
|
|
7629
7819
|
const animationMode = asset.animation.mode ?? (asset.animation.template ? "template" : "v3");
|
|
7820
|
+
const pose = async (rel, what) => {
|
|
7821
|
+
const image = await files.load(rel, what);
|
|
7822
|
+
return { path: path13.resolve(files.root, rel), sha256: image.hash, width: image.width, height: image.height, format: image.format };
|
|
7823
|
+
};
|
|
7630
7824
|
shape.animation = {
|
|
7631
7825
|
mode: animationMode,
|
|
7632
7826
|
...asset.animation.template ? { template: asset.animation.template } : {},
|
|
7633
7827
|
direction: asset.animation.direction,
|
|
7634
7828
|
frames: asset.animation.frames ?? 8,
|
|
7635
7829
|
fps: asset.animation.fps,
|
|
7636
|
-
keepFirstFrame: asset.animation.keepFirstFrame
|
|
7830
|
+
keepFirstFrame: asset.animation.keepFirstFrame,
|
|
7831
|
+
...asset.animation.startFrame ? { startFrame: await pose(asset.animation.startFrame, "Animation start frame") } : {},
|
|
7832
|
+
...asset.animation.endFrame ? { endFrame: await pose(asset.animation.endFrame, "Animation end frame") } : {},
|
|
7833
|
+
...asset.animation.subject ? { subject: asset.animation.subject } : {},
|
|
7834
|
+
...asset.animation.outline ? { outline: asset.animation.outline } : {},
|
|
7835
|
+
...asset.animation.shading ? { shading: asset.animation.shading } : {},
|
|
7836
|
+
...asset.animation.detail ? { detail: asset.animation.detail } : {},
|
|
7837
|
+
...asset.animation.enhancePrompt !== void 0 ? { enhancePrompt: asset.animation.enhancePrompt } : {}
|
|
7637
7838
|
};
|
|
7638
7839
|
}
|
|
7639
7840
|
return shape;
|
|
@@ -11728,10 +11929,13 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
|
|
|
11728
11929
|
try {
|
|
11729
11930
|
const refs = styleImages.get(spec.styleId) ?? [];
|
|
11730
11931
|
const parentEntry = spec.character?.parentSpec ? lock.entries[lockKey(spec.character.parentSpec.styleId, spec.character.parentSpec.assetId)] : void 0;
|
|
11932
|
+
const anchor = spec.character?.styleAnchor;
|
|
11933
|
+
const anchorEntry = anchor ? lock.entries[lockKey(anchor.spec.styleId, anchor.spec.assetId)] : void 0;
|
|
11731
11934
|
const replacedMetadata = previousEntry?.providerMetadata?.[provider.id];
|
|
11732
11935
|
const { jobId, metadata } = await provider.submit(spec, refs, {
|
|
11733
11936
|
...previousJobId ? { previousJobId } : {},
|
|
11734
11937
|
...parentEntry?.objectId ? { parentObjectId: parentEntry.objectId } : {},
|
|
11938
|
+
...anchorEntry?.objectId ? { styleObjectId: anchorEntry.objectId } : {},
|
|
11735
11939
|
...replacedMetadata ? { replacedMetadata } : {},
|
|
11736
11940
|
...previousMetadata ? { previousMetadata } : {},
|
|
11737
11941
|
checkpoint: async (checkpoint) => {
|
|
@@ -12811,7 +13015,7 @@ var pin_default = {
|
|
|
12811
13015
|
godot: "4.7.2",
|
|
12812
13016
|
extensionsApi: 9,
|
|
12813
13017
|
protocol: 4,
|
|
12814
|
-
release: "editor-pixelorama-v1.2.2-pk.
|
|
13018
|
+
release: "editor-pixelorama-v1.2.2-pk.26",
|
|
12815
13019
|
files: {
|
|
12816
13020
|
"index.apple-touch-icon.png": {
|
|
12817
13021
|
sha256: "ee5434873e0fed91e9a888c3ea5a8b79beac25999308be7a6eb41bf4ce640d27",
|
|
@@ -12826,7 +13030,7 @@ var pin_default = {
|
|
|
12826
13030
|
bytes: 7298
|
|
12827
13031
|
},
|
|
12828
13032
|
"index.html": {
|
|
12829
|
-
sha256: "
|
|
13033
|
+
sha256: "326101ab02bb4a29a7964214c9307c7dff26e9d66b8ebaaf6e7a3987e84a05b2",
|
|
12830
13034
|
bytes: 5442
|
|
12831
13035
|
},
|
|
12832
13036
|
"index.icon.png": {
|
|
@@ -12838,8 +13042,8 @@ var pin_default = {
|
|
|
12838
13042
|
bytes: 279815
|
|
12839
13043
|
},
|
|
12840
13044
|
"index.pck": {
|
|
12841
|
-
sha256: "
|
|
12842
|
-
bytes:
|
|
13045
|
+
sha256: "e26292650bb996965acd1ba70e74d2b4020b063d14f1276701312c1d34cb0a83",
|
|
13046
|
+
bytes: 6431932
|
|
12843
13047
|
},
|
|
12844
13048
|
"index.png": {
|
|
12845
13049
|
sha256: "6e3ca3eb4a99e4c07bdd89ebc873b440ec0ffd66ce1955160da9e7ed80d538d7",
|
|
@@ -13458,7 +13662,7 @@ async function runLifecycle(args, wave, spent) {
|
|
|
13458
13662
|
log(` Packaging stays blocked until each refined PNG has a current human approval.`);
|
|
13459
13663
|
}
|
|
13460
13664
|
}
|
|
13461
|
-
return args.command === "gen" && submitted > 0 && total.downloaded > 0 && !total.failed && specs.some((spec) => spec.character?.parentSpec || spec.revision || spec.mirror);
|
|
13665
|
+
return args.command === "gen" && submitted > 0 && total.downloaded > 0 && !total.failed && specs.some((spec) => spec.character?.parentSpec || spec.character?.styleAnchor || spec.revision || spec.mirror);
|
|
13462
13666
|
}
|
|
13463
13667
|
return false;
|
|
13464
13668
|
}
|