pixelkiln 0.43.0 → 0.44.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 +134 -35
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +134 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +513 -125
- package/dist/index.d.ts +513 -125
- package/dist/index.js +134 -35
- package/dist/index.js.map +1 -1
- package/docs/MANIFEST.md +27 -0
- package/package.json +1 -1
- package/schema/manifest.schema.json +33 -0
- package/schema/recipe.schema.json +3 -0
- package/skills/pixelkiln/references/pixellab.md +5 -0
package/dist/cli.js
CHANGED
|
@@ -665,11 +665,17 @@ 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,
|
|
678
|
+
enhancePrompt: spec.character.enhancePrompt,
|
|
673
679
|
reference: spec.character.reference ? Object.fromEntries(Object.entries(spec.character.reference).map(([direction, image]) => [direction, image.sha256])) : void 0
|
|
674
680
|
} : void 0,
|
|
675
681
|
// A mirror's bytes come from its source's recorded outputs; the plan
|
|
@@ -825,11 +831,45 @@ var CharacterAnimationSchema = z.object({
|
|
|
825
831
|
/** v3 only: keep the resting pose as frame 0, so `frames` generated frames land as `frames + 1` files. */
|
|
826
832
|
keepFirstFrame: z.boolean().default(true),
|
|
827
833
|
/** `template` when a template is named, otherwise `v3`; `pro` for the sequential high-quality engine. */
|
|
828
|
-
mode: CharacterAnimationModeSchema.optional()
|
|
834
|
+
mode: CharacterAnimationModeSchema.optional(),
|
|
835
|
+
/**
|
|
836
|
+
* v3 only: a manifest-relative image of the pose to start from, instead
|
|
837
|
+
* of the character's rotation for this direction. Up to 256px.
|
|
838
|
+
*/
|
|
839
|
+
startFrame: z.string().min(1).optional(),
|
|
840
|
+
/**
|
|
841
|
+
* v3 only: a pose to animate toward. The loop then interpolates from the
|
|
842
|
+
* start frame (the rotation, or `startFrame`) to this image, which must
|
|
843
|
+
* be the same size as the start frame.
|
|
844
|
+
*/
|
|
845
|
+
endFrame: z.string().min(1).optional(),
|
|
846
|
+
/** What is being animated, when the character's own description would mislead the model. */
|
|
847
|
+
subject: z.string().min(1).optional(),
|
|
848
|
+
/** Template mode only: style hints for this loop, over the character's own. */
|
|
849
|
+
outline: z.string().min(1).optional(),
|
|
850
|
+
shading: z.string().min(1).optional(),
|
|
851
|
+
detail: z.string().min(1).optional(),
|
|
852
|
+
/** v3 text loops only: let PixelLab expand the action into a fuller motion description first. */
|
|
853
|
+
enhancePrompt: z.boolean().optional()
|
|
829
854
|
}).strict().superRefine((animation, context) => {
|
|
830
855
|
if (animation.frames !== void 0 && animation.frames % 2 !== 0) {
|
|
831
856
|
context.addIssue({ code: z.ZodIssueCode.custom, message: "frames must be even", path: ["frames"] });
|
|
832
857
|
}
|
|
858
|
+
const mode2 = animation.mode ?? (animation.template ? "template" : "v3");
|
|
859
|
+
for (const key of ["startFrame", "endFrame", "enhancePrompt"]) {
|
|
860
|
+
if (animation[key] !== void 0 && mode2 !== "v3") {
|
|
861
|
+
context.addIssue({
|
|
862
|
+
code: z.ZodIssueCode.custom,
|
|
863
|
+
message: `${key} is for v3 loops; a ${mode2} loop ${mode2 === "template" ? "starts from the character's rotation and follows its template" : "takes neither"}`,
|
|
864
|
+
path: [key]
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
for (const key of ["outline", "shading", "detail"]) {
|
|
869
|
+
if (animation[key] !== void 0 && mode2 !== "template") {
|
|
870
|
+
context.addIssue({ code: z.ZodIssueCode.custom, message: `${key} overrides apply to template loops only`, path: [key] });
|
|
871
|
+
}
|
|
872
|
+
}
|
|
833
873
|
if (animation.template && animation.mode && animation.mode !== "template") {
|
|
834
874
|
context.addIssue({
|
|
835
875
|
code: z.ZodIssueCode.custom,
|
|
@@ -967,6 +1007,8 @@ var StyleObjectSchema = z.object({
|
|
|
967
1007
|
textGuidanceScale: z.number().min(1).max(20).optional(),
|
|
968
1008
|
/** `character` only. Draw `standard` bases and every loop in isometric view. */
|
|
969
1009
|
isometric: z.boolean().optional(),
|
|
1010
|
+
/** `character` only, `v3` bases: let PixelLab expand the prompt into a fuller one before drawing. */
|
|
1011
|
+
enhancePrompt: z.boolean().optional(),
|
|
970
1012
|
/** Fixed seed for reproducibility where the endpoint supports it. */
|
|
971
1013
|
seed: z.number().int().optional(),
|
|
972
1014
|
/**
|
|
@@ -2014,6 +2056,34 @@ function skipSubBlocks(bytes, start) {
|
|
|
2014
2056
|
function cacheFileName(hash, mediaType = MediaType.PNG) {
|
|
2015
2057
|
return `${hash}${mediaExtension(mediaType)}`;
|
|
2016
2058
|
}
|
|
2059
|
+
function imageMetadata(buf) {
|
|
2060
|
+
if (buf.length >= 24 && buf.readUInt32BE(0) === 2303741511 && buf.readUInt32BE(4) === 218765834 && buf.toString("ascii", 12, 16) === "IHDR") {
|
|
2061
|
+
return { width: buf.readUInt32BE(16), height: buf.readUInt32BE(20), format: "png" };
|
|
2062
|
+
}
|
|
2063
|
+
if (buf.length < 4 || buf[0] !== 255 || buf[1] !== 216) return null;
|
|
2064
|
+
let offset = 2;
|
|
2065
|
+
while (offset + 3 < buf.length) {
|
|
2066
|
+
if (buf[offset] !== 255) return null;
|
|
2067
|
+
while (buf[offset] === 255) offset++;
|
|
2068
|
+
const marker = buf[offset++];
|
|
2069
|
+
if (marker == null || marker === 217 || marker === 218) break;
|
|
2070
|
+
if (marker === 1 || marker >= 208 && marker <= 215) continue;
|
|
2071
|
+
if (offset + 2 > buf.length) return null;
|
|
2072
|
+
const length = buf.readUInt16BE(offset);
|
|
2073
|
+
if (length < 2 || offset + length > buf.length) return null;
|
|
2074
|
+
const isStartOfFrame = marker >= 192 && marker <= 207 && ![196, 200, 204].includes(marker);
|
|
2075
|
+
if (isStartOfFrame) {
|
|
2076
|
+
if (length < 7) return null;
|
|
2077
|
+
return {
|
|
2078
|
+
width: buf.readUInt16BE(offset + 5),
|
|
2079
|
+
height: buf.readUInt16BE(offset + 3),
|
|
2080
|
+
format: "jpeg"
|
|
2081
|
+
};
|
|
2082
|
+
}
|
|
2083
|
+
offset += length;
|
|
2084
|
+
}
|
|
2085
|
+
return null;
|
|
2086
|
+
}
|
|
2017
2087
|
|
|
2018
2088
|
// src/outputs.ts
|
|
2019
2089
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -5308,7 +5378,8 @@ var PixelLabClient = class {
|
|
|
5308
5378
|
...args.outline ? { outline: args.outline } : {},
|
|
5309
5379
|
...args.detail ? { detail: args.detail } : {},
|
|
5310
5380
|
...args.seed != null ? { seed: args.seed } : {},
|
|
5311
|
-
...south ? { reference_image: encode(south) } : {}
|
|
5381
|
+
...south ? { reference_image: encode(south) } : {},
|
|
5382
|
+
...args.enhancePrompt ? { enhance_prompt: true } : {}
|
|
5312
5383
|
};
|
|
5313
5384
|
} else if (args.mode === "pro") {
|
|
5314
5385
|
path45 = "/create-character-pro";
|
|
@@ -5365,6 +5436,7 @@ var PixelLabClient = class {
|
|
|
5365
5436
|
* the action text; pro is the sequential engine. One job per direction.
|
|
5366
5437
|
*/
|
|
5367
5438
|
async animateCharacter(args) {
|
|
5439
|
+
const encode = (image) => ({ type: "base64", base64: image.base64, format: image.format });
|
|
5368
5440
|
const raw = await this.request("/animate-character", {
|
|
5369
5441
|
method: "POST",
|
|
5370
5442
|
body: JSON.stringify({
|
|
@@ -5374,10 +5446,18 @@ var PixelLabClient = class {
|
|
|
5374
5446
|
directions: args.directions,
|
|
5375
5447
|
...args.template ? { template_animation_id: args.template } : {},
|
|
5376
5448
|
...args.actionDescription ? { action_description: args.actionDescription } : {},
|
|
5449
|
+
...args.description ? { description: args.description } : {},
|
|
5377
5450
|
...args.mode === "v3" && args.frameCount ? { frame_count: args.frameCount } : {},
|
|
5378
5451
|
...args.mode === "v3" && args.keepFirstFrame === false ? { keep_first_frame: false } : {},
|
|
5452
|
+
...args.mode === "v3" && args.startFrame ? { custom_start_frame: encode(args.startFrame) } : {},
|
|
5453
|
+
...args.mode === "v3" && args.endFrame ? { end_frame: encode(args.endFrame) } : {},
|
|
5454
|
+
...args.mode === "v3" && args.enhancePrompt ? { enhance_prompt: true } : {},
|
|
5379
5455
|
...args.mode === "template" && args.textGuidanceScale !== void 0 ? { text_guidance_scale: args.textGuidanceScale } : {},
|
|
5456
|
+
...args.mode === "template" && args.outline ? { outline: args.outline } : {},
|
|
5457
|
+
...args.mode === "template" && args.shading ? { shading: args.shading } : {},
|
|
5458
|
+
...args.mode === "template" && args.detail ? { detail: args.detail } : {},
|
|
5380
5459
|
...args.isometric !== void 0 ? { isometric: args.isometric } : {},
|
|
5460
|
+
...args.paletteSwatchBase64 ? { color_image: { type: "base64", base64: args.paletteSwatchBase64, format: "png" }, force_colors: true } : {},
|
|
5381
5461
|
...args.seed != null ? { seed: args.seed } : {}
|
|
5382
5462
|
})
|
|
5383
5463
|
});
|
|
@@ -5603,6 +5683,25 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
5603
5683
|
throw new Error(`${label}: proportions apply to the mannequin template; ${character.template} is a quadruped`);
|
|
5604
5684
|
}
|
|
5605
5685
|
}
|
|
5686
|
+
if (character.kind === "base" && character.enhancePrompt !== void 0 && character.mode !== "v3") {
|
|
5687
|
+
throw new Error(`${label}: enhancePrompt applies to v3 bases; the ${character.mode} engine does not take it`);
|
|
5688
|
+
}
|
|
5689
|
+
if (character.animation) {
|
|
5690
|
+
const { startFrame, endFrame } = character.animation;
|
|
5691
|
+
for (const [name, image] of [["start frame", startFrame], ["end frame", endFrame]]) {
|
|
5692
|
+
if (image && (image.width > 256 || image.height > 256)) {
|
|
5693
|
+
throw new Error(`${label}: ${name} is ${image.width}x${image.height}; PixelLab v3 takes up to 256px`);
|
|
5694
|
+
}
|
|
5695
|
+
}
|
|
5696
|
+
if (endFrame) {
|
|
5697
|
+
const start = startFrame ?? (character.parentFile && existsSync7(character.parentFile) ? imageMetadata(readFileSync2(character.parentFile)) : null);
|
|
5698
|
+
if (start && (start.width !== endFrame.width || start.height !== endFrame.height)) {
|
|
5699
|
+
throw new Error(
|
|
5700
|
+
`${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`
|
|
5701
|
+
);
|
|
5702
|
+
}
|
|
5703
|
+
}
|
|
5704
|
+
}
|
|
5606
5705
|
if (character.reference) {
|
|
5607
5706
|
const given = Object.keys(character.reference);
|
|
5608
5707
|
if (!character.reference.south) throw new Error(`${label}: a reference needs a south-facing sprite`);
|
|
@@ -5703,7 +5802,8 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
5703
5802
|
textGuidanceScale: character.textGuidanceScale,
|
|
5704
5803
|
isometric: character.isometric,
|
|
5705
5804
|
reference: character.reference ? readReference(spec, character.reference) : void 0,
|
|
5706
|
-
styleReference: styleImage ? { base64: styleImage.base64, format: styleImage.format } : void 0
|
|
5805
|
+
styleReference: styleImage ? { base64: styleImage.base64, format: styleImage.format } : void 0,
|
|
5806
|
+
enhancePrompt: character.enhancePrompt
|
|
5707
5807
|
});
|
|
5708
5808
|
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
5809
|
}
|
|
@@ -5747,7 +5847,15 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
5747
5847
|
directions: [animation.direction],
|
|
5748
5848
|
seed: spec.seed,
|
|
5749
5849
|
textGuidanceScale: character.textGuidanceScale,
|
|
5750
|
-
isometric: character.isometric
|
|
5850
|
+
isometric: character.isometric,
|
|
5851
|
+
startFrame: animation.startFrame ? readPose(spec, "start frame", animation.startFrame) : void 0,
|
|
5852
|
+
endFrame: animation.endFrame ? readPose(spec, "end frame", animation.endFrame) : void 0,
|
|
5853
|
+
description: animation.subject,
|
|
5854
|
+
outline: animation.outline,
|
|
5855
|
+
shading: animation.shading,
|
|
5856
|
+
detail: animation.detail,
|
|
5857
|
+
enhancePrompt: animation.enhancePrompt,
|
|
5858
|
+
paletteSwatchBase64: spec.palette.length ? paletteSwatch(spec.palette).toString("base64") : void 0
|
|
5751
5859
|
});
|
|
5752
5860
|
const job = { characterId: parentId, name, direction: animation.direction, jobIds: res.background_job_ids };
|
|
5753
5861
|
return {
|
|
@@ -6115,6 +6223,13 @@ function readReference(spec, reference) {
|
|
|
6115
6223
|
}
|
|
6116
6224
|
return images;
|
|
6117
6225
|
}
|
|
6226
|
+
function readPose(spec, what, image) {
|
|
6227
|
+
const bytes = readFileSync2(image.path);
|
|
6228
|
+
if (sha256(bytes) !== image.sha256) {
|
|
6229
|
+
throw new Error(`${spec.styleId}/${spec.assetId}: ${what} changed after the manifest was resolved: ${image.path}`);
|
|
6230
|
+
}
|
|
6231
|
+
return { base64: bytes.toString("base64"), format: image.format };
|
|
6232
|
+
}
|
|
6118
6233
|
function rotationSources(character) {
|
|
6119
6234
|
const order = character.directions === 4 ? CHARACTER_DIRECTIONS_4 : CHARACTER_DIRECTIONS_8;
|
|
6120
6235
|
const sources = [];
|
|
@@ -7564,34 +7679,6 @@ async function resolveStyleImages(loaded, styleId) {
|
|
|
7564
7679
|
}
|
|
7565
7680
|
return out;
|
|
7566
7681
|
}
|
|
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
7682
|
async function resolveCharacterShape(asset, style, kind, files) {
|
|
7596
7683
|
const mode2 = style.mode ?? "standard";
|
|
7597
7684
|
const directions = mode2 === "standard" ? style.directions ?? 8 : 8;
|
|
@@ -7603,7 +7690,8 @@ async function resolveCharacterShape(asset, style, kind, files) {
|
|
|
7603
7690
|
template: style.template ?? "mannequin",
|
|
7604
7691
|
...proportions !== void 0 ? { proportions } : {},
|
|
7605
7692
|
...style.textGuidanceScale !== void 0 ? { textGuidanceScale: style.textGuidanceScale } : {},
|
|
7606
|
-
...style.isometric !== void 0 ? { isometric: style.isometric } : {}
|
|
7693
|
+
...style.isometric !== void 0 ? { isometric: style.isometric } : {},
|
|
7694
|
+
...style.enhancePrompt !== void 0 ? { enhancePrompt: style.enhancePrompt } : {}
|
|
7607
7695
|
};
|
|
7608
7696
|
if (asset.reference) {
|
|
7609
7697
|
const byDirection = typeof asset.reference === "string" ? { south: asset.reference } : asset.reference;
|
|
@@ -7627,13 +7715,24 @@ async function resolveCharacterShape(asset, style, kind, files) {
|
|
|
7627
7715
|
}
|
|
7628
7716
|
if (asset.animation) {
|
|
7629
7717
|
const animationMode = asset.animation.mode ?? (asset.animation.template ? "template" : "v3");
|
|
7718
|
+
const pose = async (rel, what) => {
|
|
7719
|
+
const image = await files.load(rel, what);
|
|
7720
|
+
return { path: path13.resolve(files.root, rel), sha256: image.hash, width: image.width, height: image.height, format: image.format };
|
|
7721
|
+
};
|
|
7630
7722
|
shape.animation = {
|
|
7631
7723
|
mode: animationMode,
|
|
7632
7724
|
...asset.animation.template ? { template: asset.animation.template } : {},
|
|
7633
7725
|
direction: asset.animation.direction,
|
|
7634
7726
|
frames: asset.animation.frames ?? 8,
|
|
7635
7727
|
fps: asset.animation.fps,
|
|
7636
|
-
keepFirstFrame: asset.animation.keepFirstFrame
|
|
7728
|
+
keepFirstFrame: asset.animation.keepFirstFrame,
|
|
7729
|
+
...asset.animation.startFrame ? { startFrame: await pose(asset.animation.startFrame, "Animation start frame") } : {},
|
|
7730
|
+
...asset.animation.endFrame ? { endFrame: await pose(asset.animation.endFrame, "Animation end frame") } : {},
|
|
7731
|
+
...asset.animation.subject ? { subject: asset.animation.subject } : {},
|
|
7732
|
+
...asset.animation.outline ? { outline: asset.animation.outline } : {},
|
|
7733
|
+
...asset.animation.shading ? { shading: asset.animation.shading } : {},
|
|
7734
|
+
...asset.animation.detail ? { detail: asset.animation.detail } : {},
|
|
7735
|
+
...asset.animation.enhancePrompt !== void 0 ? { enhancePrompt: asset.animation.enhancePrompt } : {}
|
|
7637
7736
|
};
|
|
7638
7737
|
}
|
|
7639
7738
|
return shape;
|