pixelkiln 0.70.1 → 0.72.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/PROVIDERS.md +1 -1
- package/dist/cli.js +70 -15
- package/dist/cli.js.map +1 -1
- package/dist/client/gallery.css +15 -0
- package/dist/client/gallery.js +369 -9
- package/dist/index.cjs +70 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +88 -55
- package/dist/index.d.ts +88 -55
- package/dist/index.js +70 -15
- package/dist/index.js.map +1 -1
- package/docs/CHARACTERS.md +13 -0
- package/docs/GENERATORS.md +1 -1
- package/docs/REVISIONS.md +11 -3
- package/package.json +1 -1
- package/schema/manifest.schema.json +1 -0
- package/skills/pixelkiln/references/pixellab-roadmap.md +6 -0
- package/skills/pixelkiln/references/pixellab.md +8 -1
- package/skills/pixelkiln/references/revisions.md +3 -2
package/PROVIDERS.md
CHANGED
|
@@ -214,7 +214,7 @@ conversion to dollars:
|
|
|
214
214
|
| `isometricTile` | 1 generation (measured once) |
|
|
215
215
|
| `character` base | 1 (standard), 2–9 (v3), 20–40 (pro), 6–17 (pro-flash; rotations only from a reference) |
|
|
216
216
|
| `character` state or portrait | 20–40 generations |
|
|
217
|
-
| `character` animation | 1 (template), 1 at 64px from text, 20–40 (pro) |
|
|
217
|
+
| `character` animation | 1 (template), 2–4 (`skeleton-v3`, budgeted at 4), 1 at 64px from text, 20–40 (pro) |
|
|
218
218
|
| `character` outfit | 20 generations (one 2-frame, 92×92 job) |
|
|
219
219
|
| `objectPro` base | 6 at 64px (unmeasured) |
|
|
220
220
|
| `uiAsset` | 20 generations (measured once, at 256×192) |
|
package/dist/cli.js
CHANGED
|
@@ -934,7 +934,7 @@ var CharacterProportionsSchema = z.union([
|
|
|
934
934
|
hipWidth: z.number().min(0.5).max(2).optional()
|
|
935
935
|
}).strict()
|
|
936
936
|
]);
|
|
937
|
-
var CharacterAnimationModeSchema = z.enum(["template", "v3", "pro"]);
|
|
937
|
+
var CharacterAnimationModeSchema = z.enum(["template", "skeleton-v3", "v3", "pro"]);
|
|
938
938
|
var UiPieceSchema = z.discriminatedUnion("kind", [
|
|
939
939
|
z.object({
|
|
940
940
|
id: z.string().min(1),
|
|
@@ -1048,7 +1048,11 @@ var CharacterAnimationSchema = z.object({
|
|
|
1048
1048
|
fps: z.number().int().min(1).max(60).default(8),
|
|
1049
1049
|
/** v3 only: keep the resting pose as frame 0, so `frames` generated frames land as `frames + 1` files. */
|
|
1050
1050
|
keepFirstFrame: z.boolean().default(true),
|
|
1051
|
-
/**
|
|
1051
|
+
/**
|
|
1052
|
+
* `template` when a template is named, otherwise `v3`; `skeleton-v3`
|
|
1053
|
+
* poses a template with PixelLab's skeleton model instead; `pro` for the
|
|
1054
|
+
* sequential high-quality engine.
|
|
1055
|
+
*/
|
|
1052
1056
|
mode: CharacterAnimationModeSchema.optional(),
|
|
1053
1057
|
/**
|
|
1054
1058
|
* v3 only: a manifest-relative image of the pose to start from, instead
|
|
@@ -1085,7 +1089,7 @@ var CharacterAnimationSchema = z.object({
|
|
|
1085
1089
|
if (animation[key] !== void 0 && mode2 !== "v3") {
|
|
1086
1090
|
context.addIssue({
|
|
1087
1091
|
code: z.ZodIssueCode.custom,
|
|
1088
|
-
message: `${key} is for v3 loops; a ${mode2} loop ${mode2 === "template" ? "starts from the character's rotation and follows its template" : "takes neither"}`,
|
|
1092
|
+
message: `${key} is for v3 loops; a ${mode2} loop ${mode2 === "template" || mode2 === "skeleton-v3" ? "starts from the character's rotation and follows its template" : "takes neither"}`,
|
|
1089
1093
|
path: [key]
|
|
1090
1094
|
});
|
|
1091
1095
|
}
|
|
@@ -1095,10 +1099,10 @@ var CharacterAnimationSchema = z.object({
|
|
|
1095
1099
|
context.addIssue({ code: z.ZodIssueCode.custom, message: `${key} overrides apply to template loops only`, path: [key] });
|
|
1096
1100
|
}
|
|
1097
1101
|
}
|
|
1098
|
-
if (animation.template && animation.mode && animation.mode !== "template") {
|
|
1102
|
+
if (animation.template && animation.mode && animation.mode !== "template" && animation.mode !== "skeleton-v3") {
|
|
1099
1103
|
context.addIssue({
|
|
1100
1104
|
code: z.ZodIssueCode.custom,
|
|
1101
|
-
message: `a template animation cannot use mode "${animation.mode}"`,
|
|
1105
|
+
message: `a template animation cannot use mode "${animation.mode}"; use "template" or "skeleton-v3"`,
|
|
1102
1106
|
path: ["mode"]
|
|
1103
1107
|
});
|
|
1104
1108
|
}
|
|
@@ -1109,8 +1113,8 @@ var CharacterAnimationSchema = z.object({
|
|
|
1109
1113
|
path: ["frames"]
|
|
1110
1114
|
});
|
|
1111
1115
|
}
|
|
1112
|
-
if (!animation.template && animation.mode === "template") {
|
|
1113
|
-
context.addIssue({ code: z.ZodIssueCode.custom, message:
|
|
1116
|
+
if (!animation.template && (animation.mode === "template" || animation.mode === "skeleton-v3")) {
|
|
1117
|
+
context.addIssue({ code: z.ZodIssueCode.custom, message: `${animation.mode} mode needs a template`, path: ["template"] });
|
|
1114
1118
|
}
|
|
1115
1119
|
});
|
|
1116
1120
|
var RevisionDitheringSchema = z.enum(["none", "2x2", "4x4", "8x8"]);
|
|
@@ -6666,12 +6670,20 @@ var PixelLabClient = class {
|
|
|
6666
6670
|
}
|
|
6667
6671
|
/**
|
|
6668
6672
|
* One animation of one character. Template mode costs a generation per
|
|
6669
|
-
* direction and fixes the frame count; v3
|
|
6670
|
-
* the
|
|
6673
|
+
* direction and fixes the frame count; skeleton-v3 poses the same template
|
|
6674
|
+
* with the skeleton video model (2-4 generations per direction); v3 draws
|
|
6675
|
+
* `frameCount` frames from the action text; pro is the sequential engine.
|
|
6676
|
+
* One job per direction.
|
|
6677
|
+
*
|
|
6678
|
+
* Every mode but skeleton-v3 goes to `/animate-character`, the path whose
|
|
6679
|
+
* shapes were measured live. skeleton-v3 is documented only on
|
|
6680
|
+
* `/characters/animations`, which takes the same request and answers with
|
|
6681
|
+
* the same job list (plus an `animation_group_id`).
|
|
6671
6682
|
*/
|
|
6672
6683
|
async animateCharacter(args) {
|
|
6673
6684
|
const encode = (image) => ({ type: "base64", base64: image.base64, format: image.format });
|
|
6674
|
-
const
|
|
6685
|
+
const endpoint = args.mode === "skeleton-v3" ? "/characters/animations" : "/animate-character";
|
|
6686
|
+
const raw = await this.request(endpoint, {
|
|
6675
6687
|
method: "POST",
|
|
6676
6688
|
body: JSON.stringify({
|
|
6677
6689
|
character_id: args.characterId,
|
|
@@ -6695,7 +6707,7 @@ var PixelLabClient = class {
|
|
|
6695
6707
|
...args.seed != null ? { seed: args.seed } : {}
|
|
6696
6708
|
})
|
|
6697
6709
|
});
|
|
6698
|
-
return validateResponse(AnimateSubmitSchema, raw,
|
|
6710
|
+
return validateResponse(AnimateSubmitSchema, raw, endpoint.slice(1));
|
|
6699
6711
|
}
|
|
6700
6712
|
/**
|
|
6701
6713
|
* A full-body sprite in, a bust portrait out (or the reverse, unused
|
|
@@ -7291,6 +7303,7 @@ function characterCost(spec) {
|
|
|
7291
7303
|
if (character.kind === "animation") {
|
|
7292
7304
|
const animation = character.animation;
|
|
7293
7305
|
if (animation.mode === "template") return 1;
|
|
7306
|
+
if (animation.mode === "skeleton-v3") return 4;
|
|
7294
7307
|
if (animation.mode === "v3") return Math.max(1, Math.ceil(px * animation.frames / 65536));
|
|
7295
7308
|
return generationCost(spec.width, spec.height, "1dir");
|
|
7296
7309
|
}
|
|
@@ -7883,7 +7896,7 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
7883
7896
|
`${spec.styleId}/${spec.assetId} animates "${character.animation.direction}", but its character has ${character.directions} directions: ${allowed.join(", ")}`
|
|
7884
7897
|
);
|
|
7885
7898
|
}
|
|
7886
|
-
if (character.animation.mode !== "template" && !spec.prompt.trim()) {
|
|
7899
|
+
if (character.animation.mode !== "template" && character.animation.mode !== "skeleton-v3" && !spec.prompt.trim()) {
|
|
7887
7900
|
throw new Error(`${spec.styleId}/${spec.assetId} needs a prompt describing the motion, or a template`);
|
|
7888
7901
|
}
|
|
7889
7902
|
}
|
|
@@ -11400,8 +11413,8 @@ async function resolveObjectProShape(assetId, asset, style, kind, files) {
|
|
|
11400
11413
|
);
|
|
11401
11414
|
}
|
|
11402
11415
|
}
|
|
11403
|
-
if (asset.animation.mode === "template") {
|
|
11404
|
-
throw new Error(`assets.${assetId}: objectPro animations have no "
|
|
11416
|
+
if (asset.animation.mode === "template" || asset.animation.mode === "skeleton-v3") {
|
|
11417
|
+
throw new Error(`assets.${assetId}: objectPro animations have no "${asset.animation.mode}" mode; use "v3" or "pro"`);
|
|
11405
11418
|
}
|
|
11406
11419
|
const pose = async (rel, what) => {
|
|
11407
11420
|
const image = await files.load(rel, what);
|
|
@@ -14419,6 +14432,15 @@ var SkeletonAnimationRequestSchema = z8.object({
|
|
|
14419
14432
|
/** Replace an existing `keypointsFile` with `set`. */
|
|
14420
14433
|
overwrite: z8.boolean().optional()
|
|
14421
14434
|
}).strict();
|
|
14435
|
+
var SkeletonKeypointsUpdateSchema = z8.object({
|
|
14436
|
+
action: z8.literal("update-skeleton-keypoints"),
|
|
14437
|
+
project: z8.string().min(1).optional(),
|
|
14438
|
+
styleId: z8.string().min(1),
|
|
14439
|
+
assetId: z8.string().min(1),
|
|
14440
|
+
/** The file's SHA-256 when the page loaded it; anything else on disk is a concurrent edit. */
|
|
14441
|
+
expectedSha256: z8.string().regex(/^[0-9a-f]{64}$/),
|
|
14442
|
+
set: SkeletonSetSchema
|
|
14443
|
+
}).strict();
|
|
14422
14444
|
var DEFAULT_ESTIMATE_LIMIT = 10;
|
|
14423
14445
|
function skeletonSourceFile(spec) {
|
|
14424
14446
|
if (spec.source) return path26.resolve(spec.root, spec.source);
|
|
@@ -14521,6 +14543,26 @@ async function createSkeletonAnimation(manifestPath, request) {
|
|
|
14521
14543
|
throw error;
|
|
14522
14544
|
}
|
|
14523
14545
|
}
|
|
14546
|
+
async function updateSkeletonKeypoints(ctx, request) {
|
|
14547
|
+
const key = lockKey(request.styleId, request.assetId);
|
|
14548
|
+
const spec = ctx.specs.find((candidate) => lockKey(candidate.styleId, candidate.assetId) === key);
|
|
14549
|
+
if (!spec) throw new ManifestEditError(`"${key}" is not declared by the manifest`);
|
|
14550
|
+
const file = spec.revision?.mode === "animate-skeleton" ? spec.revision.keypointsFile : void 0;
|
|
14551
|
+
if (!file) throw new ManifestEditError(`${key} is not an animate-skeleton revision`);
|
|
14552
|
+
const relative = path26.relative(ctx.loaded.root, file);
|
|
14553
|
+
if (relative.startsWith("..") || path26.isAbsolute(relative)) {
|
|
14554
|
+
throw new ManifestEditError(`${key}'s keypoints file is outside the project; edit it by hand`);
|
|
14555
|
+
}
|
|
14556
|
+
const current = existsSync18(file) ? sha256(await readFile17(file)) : null;
|
|
14557
|
+
if (current !== request.expectedSha256) {
|
|
14558
|
+
throw Object.assign(
|
|
14559
|
+
new Error(`${relative.split(path26.sep).join("/")} changed on disk since this page loaded it; press Refresh and edit again`),
|
|
14560
|
+
{ status: 409 }
|
|
14561
|
+
);
|
|
14562
|
+
}
|
|
14563
|
+
await writeFile12(file, JSON.stringify(request.set, null, 2) + "\n");
|
|
14564
|
+
return relative.split(path26.sep).join("/");
|
|
14565
|
+
}
|
|
14524
14566
|
|
|
14525
14567
|
// src/gallery/edit.ts
|
|
14526
14568
|
var Base64 = z9.string().regex(/^[A-Za-z0-9+/]*={0,2}$/).max(Math.ceil(MAX_HAND_EDIT_BYTES / 3) * 4);
|
|
@@ -14597,6 +14639,18 @@ function createGalleryEditHandler(opts) {
|
|
|
14597
14639
|
}
|
|
14598
14640
|
return opts.reload();
|
|
14599
14641
|
}
|
|
14642
|
+
const poses = SkeletonKeypointsUpdateSchema.safeParse(body);
|
|
14643
|
+
if (poses.success) {
|
|
14644
|
+
if (!opts.loadProject) throw new ManifestEditError("pose edits are not available in this gallery");
|
|
14645
|
+
const written = await updateSkeletonKeypoints(await opts.loadProject(poses.data.project), poses.data);
|
|
14646
|
+
log2(` poses saved: ${written} (${poses.data.styleId}/${poses.data.assetId} is stale until generated again)`);
|
|
14647
|
+
return opts.reload();
|
|
14648
|
+
}
|
|
14649
|
+
if (body?.action === "update-skeleton-keypoints") {
|
|
14650
|
+
throw new ManifestEditError(
|
|
14651
|
+
"invalid pose edit: " + poses.error.issues.slice(0, 3).map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")
|
|
14652
|
+
);
|
|
14653
|
+
}
|
|
14600
14654
|
const skeleton = SkeletonAnimationRequestSchema.safeParse(body);
|
|
14601
14655
|
if (skeleton.success) {
|
|
14602
14656
|
let manifestPath2;
|
|
@@ -15112,7 +15166,8 @@ async function buildGallerySnapshot(opts) {
|
|
|
15112
15166
|
mirrorOfKey: spec.mirror ? lockKey(spec.styleId, spec.mirror.sourceAssetId) : entry?.mirror ? lockKey(spec.styleId, entry.mirror.sourceAssetId) : null,
|
|
15113
15167
|
skeleton: spec.revision?.mode === "animate-skeleton" && spec.revision.keypointsFile ? {
|
|
15114
15168
|
keypointsFile: portableOutputPath(spec.revision.keypointsFile, root),
|
|
15115
|
-
set: spec.revision.skeleton ?? null
|
|
15169
|
+
set: spec.revision.skeleton ?? null,
|
|
15170
|
+
sha256: spec.revision.keypointsSha256 ?? null
|
|
15116
15171
|
} : null,
|
|
15117
15172
|
character: describeCharacter(spec, entry),
|
|
15118
15173
|
outputs,
|