pixelkiln 0.71.0 → 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.
@@ -363,13 +363,22 @@
363
363
  prompt.placeholder = 'What the loop does, e.g. "a slow idle sway".';
364
364
  prompt.required = true;
365
365
  const mode = el("select");
366
- if (isCharacter) mode.append(new Option("template", "template"));
366
+ if (isCharacter) {
367
+ mode.append(
368
+ new Option("skeleton-v3 (a template, posed by the skeleton model; steadiest)", "skeleton-v3"),
369
+ new Option("template (a template, redrawn; 1 generation)", "template")
370
+ );
371
+ }
367
372
  mode.append(new Option("v3 (text-described)", "v3"), new Option("pro (sequential, high quality)", "pro"));
368
- mode.value = "v3";
373
+ mode.value = isCharacter ? "skeleton-v3" : "v3";
369
374
  const template = el("input");
370
375
  template.type = "text";
371
376
  template.placeholder = "e.g. walk, breathing-idle";
372
- const templateField = field("template id", template, "A PixelLab template; costs 1 generation regardless of frame count.");
377
+ const templateHint = {
378
+ "skeleton-v3": "A PixelLab template posed onto the character by the skeleton model, which moves it instead of redrawing it: steadier identity and colours. 2 to 4 generations and 3 to 5 minutes per direction; beta, Tier 1 plans and up.",
379
+ template: "A PixelLab template, redrawn frame by frame; 1 generation regardless of frame count."
380
+ };
381
+ const templateField = field("template id", template, templateHint.template);
373
382
  const frames = el("input");
374
383
  frames.type = "number";
375
384
  frames.min = "4";
@@ -403,9 +412,11 @@
403
412
  form.append(row2);
404
413
  if (subject) form.append(field("subject override", subject));
405
414
  const syncMode = () => {
406
- const isTemplate = isCharacter && mode.value === "template";
415
+ const isTemplate = isCharacter && (mode.value === "template" || mode.value === "skeleton-v3");
407
416
  templateField.style.display = isTemplate ? "" : "none";
417
+ if (isTemplate) templateField.querySelector("small").textContent = templateHint[mode.value];
408
418
  template.required = isTemplate;
419
+ prompt.required = !isTemplate;
409
420
  row2.style.display = isTemplate ? "none" : "";
410
421
  };
411
422
  if (isCharacter) {
@@ -433,8 +444,9 @@
433
444
  msg.textContent = "saving…";
434
445
  const animation = { of: item.assetId };
435
446
  if (directionField) animation.direction = directionField.select.value;
436
- if (isCharacter && mode.value === "template" && template.value.trim()) {
447
+ if (isCharacter && (mode.value === "template" || mode.value === "skeleton-v3") && template.value.trim()) {
437
448
  animation.template = template.value.trim();
449
+ if (mode.value === "skeleton-v3") animation.mode = "skeleton-v3";
438
450
  } else {
439
451
  animation.mode = mode.value;
440
452
  if (frames.value.trim() !== "") animation.frames = Number(frames.value);
package/dist/index.cjs CHANGED
@@ -449,7 +449,7 @@ var CharacterProportionsSchema = import_zod.z.union([
449
449
  hipWidth: import_zod.z.number().min(0.5).max(2).optional()
450
450
  }).strict()
451
451
  ]);
452
- var CharacterAnimationModeSchema = import_zod.z.enum(["template", "v3", "pro"]);
452
+ var CharacterAnimationModeSchema = import_zod.z.enum(["template", "skeleton-v3", "v3", "pro"]);
453
453
  var UiPieceSchema = import_zod.z.discriminatedUnion("kind", [
454
454
  import_zod.z.object({
455
455
  id: import_zod.z.string().min(1),
@@ -563,7 +563,11 @@ var CharacterAnimationSchema = import_zod.z.object({
563
563
  fps: import_zod.z.number().int().min(1).max(60).default(8),
564
564
  /** v3 only: keep the resting pose as frame 0, so `frames` generated frames land as `frames + 1` files. */
565
565
  keepFirstFrame: import_zod.z.boolean().default(true),
566
- /** `template` when a template is named, otherwise `v3`; `pro` for the sequential high-quality engine. */
566
+ /**
567
+ * `template` when a template is named, otherwise `v3`; `skeleton-v3`
568
+ * poses a template with PixelLab's skeleton model instead; `pro` for the
569
+ * sequential high-quality engine.
570
+ */
567
571
  mode: CharacterAnimationModeSchema.optional(),
568
572
  /**
569
573
  * v3 only: a manifest-relative image of the pose to start from, instead
@@ -600,7 +604,7 @@ var CharacterAnimationSchema = import_zod.z.object({
600
604
  if (animation[key] !== void 0 && mode2 !== "v3") {
601
605
  context.addIssue({
602
606
  code: import_zod.z.ZodIssueCode.custom,
603
- message: `${key} is for v3 loops; a ${mode2} loop ${mode2 === "template" ? "starts from the character's rotation and follows its template" : "takes neither"}`,
607
+ 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"}`,
604
608
  path: [key]
605
609
  });
606
610
  }
@@ -610,10 +614,10 @@ var CharacterAnimationSchema = import_zod.z.object({
610
614
  context.addIssue({ code: import_zod.z.ZodIssueCode.custom, message: `${key} overrides apply to template loops only`, path: [key] });
611
615
  }
612
616
  }
613
- if (animation.template && animation.mode && animation.mode !== "template") {
617
+ if (animation.template && animation.mode && animation.mode !== "template" && animation.mode !== "skeleton-v3") {
614
618
  context.addIssue({
615
619
  code: import_zod.z.ZodIssueCode.custom,
616
- message: `a template animation cannot use mode "${animation.mode}"`,
620
+ message: `a template animation cannot use mode "${animation.mode}"; use "template" or "skeleton-v3"`,
617
621
  path: ["mode"]
618
622
  });
619
623
  }
@@ -624,8 +628,8 @@ var CharacterAnimationSchema = import_zod.z.object({
624
628
  path: ["frames"]
625
629
  });
626
630
  }
627
- if (!animation.template && animation.mode === "template") {
628
- context.addIssue({ code: import_zod.z.ZodIssueCode.custom, message: "template mode needs a template", path: ["template"] });
631
+ if (!animation.template && (animation.mode === "template" || animation.mode === "skeleton-v3")) {
632
+ context.addIssue({ code: import_zod.z.ZodIssueCode.custom, message: `${animation.mode} mode needs a template`, path: ["template"] });
629
633
  }
630
634
  });
631
635
  var RevisionDitheringSchema = import_zod.z.enum(["none", "2x2", "4x4", "8x8"]);
@@ -2559,12 +2563,20 @@ var PixelLabClient = class {
2559
2563
  }
2560
2564
  /**
2561
2565
  * One animation of one character. Template mode costs a generation per
2562
- * direction and fixes the frame count; v3 draws `frameCount` frames from
2563
- * the action text; pro is the sequential engine. One job per direction.
2566
+ * direction and fixes the frame count; skeleton-v3 poses the same template
2567
+ * with the skeleton video model (2-4 generations per direction); v3 draws
2568
+ * `frameCount` frames from the action text; pro is the sequential engine.
2569
+ * One job per direction.
2570
+ *
2571
+ * Every mode but skeleton-v3 goes to `/animate-character`, the path whose
2572
+ * shapes were measured live. skeleton-v3 is documented only on
2573
+ * `/characters/animations`, which takes the same request and answers with
2574
+ * the same job list (plus an `animation_group_id`).
2564
2575
  */
2565
2576
  async animateCharacter(args) {
2566
2577
  const encode = (image) => ({ type: "base64", base64: image.base64, format: image.format });
2567
- const raw = await this.request("/animate-character", {
2578
+ const endpoint = args.mode === "skeleton-v3" ? "/characters/animations" : "/animate-character";
2579
+ const raw = await this.request(endpoint, {
2568
2580
  method: "POST",
2569
2581
  body: JSON.stringify({
2570
2582
  character_id: args.characterId,
@@ -2588,7 +2600,7 @@ var PixelLabClient = class {
2588
2600
  ...args.seed != null ? { seed: args.seed } : {}
2589
2601
  })
2590
2602
  });
2591
- return validateResponse(AnimateSubmitSchema, raw, "animate-character");
2603
+ return validateResponse(AnimateSubmitSchema, raw, endpoint.slice(1));
2592
2604
  }
2593
2605
  /**
2594
2606
  * A full-body sprite in, a bust portrait out (or the reverse, unused
@@ -3881,6 +3893,7 @@ function characterCost(spec) {
3881
3893
  if (character.kind === "animation") {
3882
3894
  const animation = character.animation;
3883
3895
  if (animation.mode === "template") return 1;
3896
+ if (animation.mode === "skeleton-v3") return 4;
3884
3897
  if (animation.mode === "v3") return Math.max(1, Math.ceil(px * animation.frames / 65536));
3885
3898
  return generationCost(spec.width, spec.height, "1dir");
3886
3899
  }
@@ -4473,7 +4486,7 @@ var PixelLabProvider = class _PixelLabProvider {
4473
4486
  `${spec.styleId}/${spec.assetId} animates "${character.animation.direction}", but its character has ${character.directions} directions: ${allowed.join(", ")}`
4474
4487
  );
4475
4488
  }
4476
- if (character.animation.mode !== "template" && !spec.prompt.trim()) {
4489
+ if (character.animation.mode !== "template" && character.animation.mode !== "skeleton-v3" && !spec.prompt.trim()) {
4477
4490
  throw new Error(`${spec.styleId}/${spec.assetId} needs a prompt describing the motion, or a template`);
4478
4491
  }
4479
4492
  }
@@ -9401,8 +9414,8 @@ async function resolveObjectProShape(assetId, asset, style, kind, files) {
9401
9414
  );
9402
9415
  }
9403
9416
  }
9404
- if (asset.animation.mode === "template") {
9405
- throw new Error(`assets.${assetId}: objectPro animations have no "template" mode; use "v3" or "pro"`);
9417
+ if (asset.animation.mode === "template" || asset.animation.mode === "skeleton-v3") {
9418
+ throw new Error(`assets.${assetId}: objectPro animations have no "${asset.animation.mode}" mode; use "v3" or "pro"`);
9406
9419
  }
9407
9420
  const pose = async (rel, what) => {
9408
9421
  const image = await files.load(rel, what);