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 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
- /** `template` when a template is named, otherwise `v3`; `pro` for the sequential high-quality engine. */
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: "template mode needs a template", path: ["template"] });
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 draws `frameCount` frames from
6670
- * the action text; pro is the sequential engine. One job per direction.
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 raw = await this.request("/animate-character", {
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, "animate-character");
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 "template" mode; use "v3" or "pro"`);
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,