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/index.cjs
CHANGED
|
@@ -427,11 +427,45 @@ var CharacterAnimationSchema = import_zod.z.object({
|
|
|
427
427
|
/** v3 only: keep the resting pose as frame 0, so `frames` generated frames land as `frames + 1` files. */
|
|
428
428
|
keepFirstFrame: import_zod.z.boolean().default(true),
|
|
429
429
|
/** `template` when a template is named, otherwise `v3`; `pro` for the sequential high-quality engine. */
|
|
430
|
-
mode: CharacterAnimationModeSchema.optional()
|
|
430
|
+
mode: CharacterAnimationModeSchema.optional(),
|
|
431
|
+
/**
|
|
432
|
+
* v3 only: a manifest-relative image of the pose to start from, instead
|
|
433
|
+
* of the character's rotation for this direction. Up to 256px.
|
|
434
|
+
*/
|
|
435
|
+
startFrame: import_zod.z.string().min(1).optional(),
|
|
436
|
+
/**
|
|
437
|
+
* v3 only: a pose to animate toward. The loop then interpolates from the
|
|
438
|
+
* start frame (the rotation, or `startFrame`) to this image, which must
|
|
439
|
+
* be the same size as the start frame.
|
|
440
|
+
*/
|
|
441
|
+
endFrame: import_zod.z.string().min(1).optional(),
|
|
442
|
+
/** What is being animated, when the character's own description would mislead the model. */
|
|
443
|
+
subject: import_zod.z.string().min(1).optional(),
|
|
444
|
+
/** Template mode only: style hints for this loop, over the character's own. */
|
|
445
|
+
outline: import_zod.z.string().min(1).optional(),
|
|
446
|
+
shading: import_zod.z.string().min(1).optional(),
|
|
447
|
+
detail: import_zod.z.string().min(1).optional(),
|
|
448
|
+
/** v3 text loops only: let PixelLab expand the action into a fuller motion description first. */
|
|
449
|
+
enhancePrompt: import_zod.z.boolean().optional()
|
|
431
450
|
}).strict().superRefine((animation, context) => {
|
|
432
451
|
if (animation.frames !== void 0 && animation.frames % 2 !== 0) {
|
|
433
452
|
context.addIssue({ code: import_zod.z.ZodIssueCode.custom, message: "frames must be even", path: ["frames"] });
|
|
434
453
|
}
|
|
454
|
+
const mode2 = animation.mode ?? (animation.template ? "template" : "v3");
|
|
455
|
+
for (const key of ["startFrame", "endFrame", "enhancePrompt"]) {
|
|
456
|
+
if (animation[key] !== void 0 && mode2 !== "v3") {
|
|
457
|
+
context.addIssue({
|
|
458
|
+
code: import_zod.z.ZodIssueCode.custom,
|
|
459
|
+
message: `${key} is for v3 loops; a ${mode2} loop ${mode2 === "template" ? "starts from the character's rotation and follows its template" : "takes neither"}`,
|
|
460
|
+
path: [key]
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
for (const key of ["outline", "shading", "detail"]) {
|
|
465
|
+
if (animation[key] !== void 0 && mode2 !== "template") {
|
|
466
|
+
context.addIssue({ code: import_zod.z.ZodIssueCode.custom, message: `${key} overrides apply to template loops only`, path: [key] });
|
|
467
|
+
}
|
|
468
|
+
}
|
|
435
469
|
if (animation.template && animation.mode && animation.mode !== "template") {
|
|
436
470
|
context.addIssue({
|
|
437
471
|
code: import_zod.z.ZodIssueCode.custom,
|
|
@@ -569,6 +603,8 @@ var StyleObjectSchema = import_zod.z.object({
|
|
|
569
603
|
textGuidanceScale: import_zod.z.number().min(1).max(20).optional(),
|
|
570
604
|
/** `character` only. Draw `standard` bases and every loop in isometric view. */
|
|
571
605
|
isometric: import_zod.z.boolean().optional(),
|
|
606
|
+
/** `character` only, `v3` bases: let PixelLab expand the prompt into a fuller one before drawing. */
|
|
607
|
+
enhancePrompt: import_zod.z.boolean().optional(),
|
|
572
608
|
/** Fixed seed for reproducibility where the endpoint supports it. */
|
|
573
609
|
seed: import_zod.z.number().int().optional(),
|
|
574
610
|
/**
|
|
@@ -1493,7 +1529,8 @@ var PixelLabClient = class {
|
|
|
1493
1529
|
...args.outline ? { outline: args.outline } : {},
|
|
1494
1530
|
...args.detail ? { detail: args.detail } : {},
|
|
1495
1531
|
...args.seed != null ? { seed: args.seed } : {},
|
|
1496
|
-
...south ? { reference_image: encode(south) } : {}
|
|
1532
|
+
...south ? { reference_image: encode(south) } : {},
|
|
1533
|
+
...args.enhancePrompt ? { enhance_prompt: true } : {}
|
|
1497
1534
|
};
|
|
1498
1535
|
} else if (args.mode === "pro") {
|
|
1499
1536
|
path33 = "/create-character-pro";
|
|
@@ -1550,6 +1587,7 @@ var PixelLabClient = class {
|
|
|
1550
1587
|
* the action text; pro is the sequential engine. One job per direction.
|
|
1551
1588
|
*/
|
|
1552
1589
|
async animateCharacter(args) {
|
|
1590
|
+
const encode = (image) => ({ type: "base64", base64: image.base64, format: image.format });
|
|
1553
1591
|
const raw = await this.request("/animate-character", {
|
|
1554
1592
|
method: "POST",
|
|
1555
1593
|
body: JSON.stringify({
|
|
@@ -1559,10 +1597,18 @@ var PixelLabClient = class {
|
|
|
1559
1597
|
directions: args.directions,
|
|
1560
1598
|
...args.template ? { template_animation_id: args.template } : {},
|
|
1561
1599
|
...args.actionDescription ? { action_description: args.actionDescription } : {},
|
|
1600
|
+
...args.description ? { description: args.description } : {},
|
|
1562
1601
|
...args.mode === "v3" && args.frameCount ? { frame_count: args.frameCount } : {},
|
|
1563
1602
|
...args.mode === "v3" && args.keepFirstFrame === false ? { keep_first_frame: false } : {},
|
|
1603
|
+
...args.mode === "v3" && args.startFrame ? { custom_start_frame: encode(args.startFrame) } : {},
|
|
1604
|
+
...args.mode === "v3" && args.endFrame ? { end_frame: encode(args.endFrame) } : {},
|
|
1605
|
+
...args.mode === "v3" && args.enhancePrompt ? { enhance_prompt: true } : {},
|
|
1564
1606
|
...args.mode === "template" && args.textGuidanceScale !== void 0 ? { text_guidance_scale: args.textGuidanceScale } : {},
|
|
1607
|
+
...args.mode === "template" && args.outline ? { outline: args.outline } : {},
|
|
1608
|
+
...args.mode === "template" && args.shading ? { shading: args.shading } : {},
|
|
1609
|
+
...args.mode === "template" && args.detail ? { detail: args.detail } : {},
|
|
1565
1610
|
...args.isometric !== void 0 ? { isometric: args.isometric } : {},
|
|
1611
|
+
...args.paletteSwatchBase64 ? { color_image: { type: "base64", base64: args.paletteSwatchBase64, format: "png" }, force_colors: true } : {},
|
|
1566
1612
|
...args.seed != null ? { seed: args.seed } : {}
|
|
1567
1613
|
})
|
|
1568
1614
|
});
|
|
@@ -2159,6 +2205,34 @@ function skipSubBlocks(bytes, start) {
|
|
|
2159
2205
|
function cacheFileName(hash, mediaType = MediaType.PNG) {
|
|
2160
2206
|
return `${hash}${mediaExtension(mediaType)}`;
|
|
2161
2207
|
}
|
|
2208
|
+
function imageMetadata(buf) {
|
|
2209
|
+
if (buf.length >= 24 && buf.readUInt32BE(0) === 2303741511 && buf.readUInt32BE(4) === 218765834 && buf.toString("ascii", 12, 16) === "IHDR") {
|
|
2210
|
+
return { width: buf.readUInt32BE(16), height: buf.readUInt32BE(20), format: "png" };
|
|
2211
|
+
}
|
|
2212
|
+
if (buf.length < 4 || buf[0] !== 255 || buf[1] !== 216) return null;
|
|
2213
|
+
let offset = 2;
|
|
2214
|
+
while (offset + 3 < buf.length) {
|
|
2215
|
+
if (buf[offset] !== 255) return null;
|
|
2216
|
+
while (buf[offset] === 255) offset++;
|
|
2217
|
+
const marker = buf[offset++];
|
|
2218
|
+
if (marker == null || marker === 217 || marker === 218) break;
|
|
2219
|
+
if (marker === 1 || marker >= 208 && marker <= 215) continue;
|
|
2220
|
+
if (offset + 2 > buf.length) return null;
|
|
2221
|
+
const length = buf.readUInt16BE(offset);
|
|
2222
|
+
if (length < 2 || offset + length > buf.length) return null;
|
|
2223
|
+
const isStartOfFrame = marker >= 192 && marker <= 207 && ![196, 200, 204].includes(marker);
|
|
2224
|
+
if (isStartOfFrame) {
|
|
2225
|
+
if (length < 7) return null;
|
|
2226
|
+
return {
|
|
2227
|
+
width: buf.readUInt16BE(offset + 5),
|
|
2228
|
+
height: buf.readUInt16BE(offset + 3),
|
|
2229
|
+
format: "jpeg"
|
|
2230
|
+
};
|
|
2231
|
+
}
|
|
2232
|
+
offset += length;
|
|
2233
|
+
}
|
|
2234
|
+
return null;
|
|
2235
|
+
}
|
|
2162
2236
|
|
|
2163
2237
|
// src/providers/pixellab.ts
|
|
2164
2238
|
var import_node_fs2 = require("fs");
|
|
@@ -2219,11 +2293,17 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
|
|
|
2219
2293
|
parent: spec.character.parentAssetId,
|
|
2220
2294
|
parentSha256: spec.character.parentSha256 ?? null,
|
|
2221
2295
|
state: spec.character.state,
|
|
2222
|
-
|
|
2296
|
+
// Pose images enter as their hashes, never their paths.
|
|
2297
|
+
animation: spec.character.animation ? {
|
|
2298
|
+
...spec.character.animation,
|
|
2299
|
+
startFrame: spec.character.animation.startFrame?.sha256,
|
|
2300
|
+
endFrame: spec.character.animation.endFrame?.sha256
|
|
2301
|
+
} : void 0,
|
|
2223
2302
|
// Absent keys keep the hashes of manifests that never set these.
|
|
2224
2303
|
proportions: spec.character.proportions,
|
|
2225
2304
|
textGuidanceScale: spec.character.textGuidanceScale,
|
|
2226
2305
|
isometric: spec.character.isometric,
|
|
2306
|
+
enhancePrompt: spec.character.enhancePrompt,
|
|
2227
2307
|
reference: spec.character.reference ? Object.fromEntries(Object.entries(spec.character.reference).map(([direction, image]) => [direction, image.sha256])) : void 0
|
|
2228
2308
|
} : void 0,
|
|
2229
2309
|
// A mirror's bytes come from its source's recorded outputs; the plan
|
|
@@ -2392,6 +2472,25 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
2392
2472
|
throw new Error(`${label}: proportions apply to the mannequin template; ${character.template} is a quadruped`);
|
|
2393
2473
|
}
|
|
2394
2474
|
}
|
|
2475
|
+
if (character.kind === "base" && character.enhancePrompt !== void 0 && character.mode !== "v3") {
|
|
2476
|
+
throw new Error(`${label}: enhancePrompt applies to v3 bases; the ${character.mode} engine does not take it`);
|
|
2477
|
+
}
|
|
2478
|
+
if (character.animation) {
|
|
2479
|
+
const { startFrame, endFrame } = character.animation;
|
|
2480
|
+
for (const [name, image] of [["start frame", startFrame], ["end frame", endFrame]]) {
|
|
2481
|
+
if (image && (image.width > 256 || image.height > 256)) {
|
|
2482
|
+
throw new Error(`${label}: ${name} is ${image.width}x${image.height}; PixelLab v3 takes up to 256px`);
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
if (endFrame) {
|
|
2486
|
+
const start = startFrame ?? (character.parentFile && (0, import_node_fs2.existsSync)(character.parentFile) ? imageMetadata((0, import_node_fs2.readFileSync)(character.parentFile)) : null);
|
|
2487
|
+
if (start && (start.width !== endFrame.width || start.height !== endFrame.height)) {
|
|
2488
|
+
throw new Error(
|
|
2489
|
+
`${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`
|
|
2490
|
+
);
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2395
2494
|
if (character.reference) {
|
|
2396
2495
|
const given = Object.keys(character.reference);
|
|
2397
2496
|
if (!character.reference.south) throw new Error(`${label}: a reference needs a south-facing sprite`);
|
|
@@ -2492,7 +2591,8 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
2492
2591
|
textGuidanceScale: character.textGuidanceScale,
|
|
2493
2592
|
isometric: character.isometric,
|
|
2494
2593
|
reference: character.reference ? readReference(spec, character.reference) : void 0,
|
|
2495
|
-
styleReference: styleImage ? { base64: styleImage.base64, format: styleImage.format } : void 0
|
|
2594
|
+
styleReference: styleImage ? { base64: styleImage.base64, format: styleImage.format } : void 0,
|
|
2595
|
+
enhancePrompt: character.enhancePrompt
|
|
2496
2596
|
});
|
|
2497
2597
|
return { jobId: res2.character_id, metadata: { character: { kind: "base", characterId: res2.character_id, mode: character.mode, directions: character.directions, backgroundJobId: res2.background_job_id } } };
|
|
2498
2598
|
}
|
|
@@ -2536,7 +2636,15 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
2536
2636
|
directions: [animation.direction],
|
|
2537
2637
|
seed: spec.seed,
|
|
2538
2638
|
textGuidanceScale: character.textGuidanceScale,
|
|
2539
|
-
isometric: character.isometric
|
|
2639
|
+
isometric: character.isometric,
|
|
2640
|
+
startFrame: animation.startFrame ? readPose(spec, "start frame", animation.startFrame) : void 0,
|
|
2641
|
+
endFrame: animation.endFrame ? readPose(spec, "end frame", animation.endFrame) : void 0,
|
|
2642
|
+
description: animation.subject,
|
|
2643
|
+
outline: animation.outline,
|
|
2644
|
+
shading: animation.shading,
|
|
2645
|
+
detail: animation.detail,
|
|
2646
|
+
enhancePrompt: animation.enhancePrompt,
|
|
2647
|
+
paletteSwatchBase64: spec.palette.length ? paletteSwatch(spec.palette).toString("base64") : void 0
|
|
2540
2648
|
});
|
|
2541
2649
|
const job = { characterId: parentId, name, direction: animation.direction, jobIds: res.background_job_ids };
|
|
2542
2650
|
return {
|
|
@@ -2904,6 +3012,13 @@ function readReference(spec, reference) {
|
|
|
2904
3012
|
}
|
|
2905
3013
|
return images;
|
|
2906
3014
|
}
|
|
3015
|
+
function readPose(spec, what, image) {
|
|
3016
|
+
const bytes = (0, import_node_fs2.readFileSync)(image.path);
|
|
3017
|
+
if (sha256(bytes) !== image.sha256) {
|
|
3018
|
+
throw new Error(`${spec.styleId}/${spec.assetId}: ${what} changed after the manifest was resolved: ${image.path}`);
|
|
3019
|
+
}
|
|
3020
|
+
return { base64: bytes.toString("base64"), format: image.format };
|
|
3021
|
+
}
|
|
2907
3022
|
function rotationSources(character) {
|
|
2908
3023
|
const order = character.directions === 4 ? CHARACTER_DIRECTIONS_4 : CHARACTER_DIRECTIONS_8;
|
|
2909
3024
|
const sources = [];
|
|
@@ -5734,34 +5849,6 @@ async function resolveStyleImages(loaded, styleId) {
|
|
|
5734
5849
|
async function styleImagesBase64(loaded, styleId) {
|
|
5735
5850
|
return (await resolveStyleImages(loaded, styleId)).map((img) => img.base64);
|
|
5736
5851
|
}
|
|
5737
|
-
function imageMetadata(buf) {
|
|
5738
|
-
if (buf.length >= 24 && buf.readUInt32BE(0) === 2303741511 && buf.readUInt32BE(4) === 218765834 && buf.toString("ascii", 12, 16) === "IHDR") {
|
|
5739
|
-
return { width: buf.readUInt32BE(16), height: buf.readUInt32BE(20), format: "png" };
|
|
5740
|
-
}
|
|
5741
|
-
if (buf.length < 4 || buf[0] !== 255 || buf[1] !== 216) return null;
|
|
5742
|
-
let offset = 2;
|
|
5743
|
-
while (offset + 3 < buf.length) {
|
|
5744
|
-
if (buf[offset] !== 255) return null;
|
|
5745
|
-
while (buf[offset] === 255) offset++;
|
|
5746
|
-
const marker = buf[offset++];
|
|
5747
|
-
if (marker == null || marker === 217 || marker === 218) break;
|
|
5748
|
-
if (marker === 1 || marker >= 208 && marker <= 215) continue;
|
|
5749
|
-
if (offset + 2 > buf.length) return null;
|
|
5750
|
-
const length = buf.readUInt16BE(offset);
|
|
5751
|
-
if (length < 2 || offset + length > buf.length) return null;
|
|
5752
|
-
const isStartOfFrame = marker >= 192 && marker <= 207 && ![196, 200, 204].includes(marker);
|
|
5753
|
-
if (isStartOfFrame) {
|
|
5754
|
-
if (length < 7) return null;
|
|
5755
|
-
return {
|
|
5756
|
-
width: buf.readUInt16BE(offset + 5),
|
|
5757
|
-
height: buf.readUInt16BE(offset + 3),
|
|
5758
|
-
format: "jpeg"
|
|
5759
|
-
};
|
|
5760
|
-
}
|
|
5761
|
-
offset += length;
|
|
5762
|
-
}
|
|
5763
|
-
return null;
|
|
5764
|
-
}
|
|
5765
5852
|
async function resolveCharacterShape(asset, style, kind, files) {
|
|
5766
5853
|
const mode2 = style.mode ?? "standard";
|
|
5767
5854
|
const directions = mode2 === "standard" ? style.directions ?? 8 : 8;
|
|
@@ -5773,7 +5860,8 @@ async function resolveCharacterShape(asset, style, kind, files) {
|
|
|
5773
5860
|
template: style.template ?? "mannequin",
|
|
5774
5861
|
...proportions !== void 0 ? { proportions } : {},
|
|
5775
5862
|
...style.textGuidanceScale !== void 0 ? { textGuidanceScale: style.textGuidanceScale } : {},
|
|
5776
|
-
...style.isometric !== void 0 ? { isometric: style.isometric } : {}
|
|
5863
|
+
...style.isometric !== void 0 ? { isometric: style.isometric } : {},
|
|
5864
|
+
...style.enhancePrompt !== void 0 ? { enhancePrompt: style.enhancePrompt } : {}
|
|
5777
5865
|
};
|
|
5778
5866
|
if (asset.reference) {
|
|
5779
5867
|
const byDirection = typeof asset.reference === "string" ? { south: asset.reference } : asset.reference;
|
|
@@ -5797,13 +5885,24 @@ async function resolveCharacterShape(asset, style, kind, files) {
|
|
|
5797
5885
|
}
|
|
5798
5886
|
if (asset.animation) {
|
|
5799
5887
|
const animationMode = asset.animation.mode ?? (asset.animation.template ? "template" : "v3");
|
|
5888
|
+
const pose = async (rel, what) => {
|
|
5889
|
+
const image = await files.load(rel, what);
|
|
5890
|
+
return { path: import_node_path5.default.resolve(files.root, rel), sha256: image.hash, width: image.width, height: image.height, format: image.format };
|
|
5891
|
+
};
|
|
5800
5892
|
shape.animation = {
|
|
5801
5893
|
mode: animationMode,
|
|
5802
5894
|
...asset.animation.template ? { template: asset.animation.template } : {},
|
|
5803
5895
|
direction: asset.animation.direction,
|
|
5804
5896
|
frames: asset.animation.frames ?? 8,
|
|
5805
5897
|
fps: asset.animation.fps,
|
|
5806
|
-
keepFirstFrame: asset.animation.keepFirstFrame
|
|
5898
|
+
keepFirstFrame: asset.animation.keepFirstFrame,
|
|
5899
|
+
...asset.animation.startFrame ? { startFrame: await pose(asset.animation.startFrame, "Animation start frame") } : {},
|
|
5900
|
+
...asset.animation.endFrame ? { endFrame: await pose(asset.animation.endFrame, "Animation end frame") } : {},
|
|
5901
|
+
...asset.animation.subject ? { subject: asset.animation.subject } : {},
|
|
5902
|
+
...asset.animation.outline ? { outline: asset.animation.outline } : {},
|
|
5903
|
+
...asset.animation.shading ? { shading: asset.animation.shading } : {},
|
|
5904
|
+
...asset.animation.detail ? { detail: asset.animation.detail } : {},
|
|
5905
|
+
...asset.animation.enhancePrompt !== void 0 ? { enhancePrompt: asset.animation.enhancePrompt } : {}
|
|
5807
5906
|
};
|
|
5808
5907
|
}
|
|
5809
5908
|
return shape;
|