pixelkiln 0.45.0 → 0.46.1

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 CHANGED
@@ -676,6 +676,7 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
676
676
  textGuidanceScale: spec.character.textGuidanceScale,
677
677
  isometric: spec.character.isometric,
678
678
  enhancePrompt: spec.character.enhancePrompt,
679
+ styleTraits: spec.character.styleTraits,
679
680
  reference: spec.character.reference ? Object.fromEntries(Object.entries(spec.character.reference).map(([direction, image]) => [direction, image.sha256])) : void 0,
680
681
  concept: spec.character.concept?.sha256,
681
682
  styleAnchor: spec.character.styleAnchor ? { of: spec.character.styleAnchor.assetId, sha256: spec.character.styleAnchor.sha256 } : void 0
@@ -795,7 +796,7 @@ var MIRRORED_DIRECTION = {
795
796
  west: "east",
796
797
  "south-west": "south-east"
797
798
  };
798
- var CharacterModeSchema = z.enum(["standard", "v3", "pro"]);
799
+ var CharacterModeSchema = z.enum(["standard", "v3", "pro", "pro-flash"]);
799
800
  var CHARACTER_PROPORTION_PRESETS = ["default", "chibi", "cartoon", "stylized", "realistic_male", "realistic_female", "heroic"];
800
801
  var CharacterProportionsSchema = z.union([
801
802
  z.enum(CHARACTER_PROPORTION_PRESETS),
@@ -991,14 +992,27 @@ var StyleObjectSchema = z.object({
991
992
  /**
992
993
  * `character` only. `standard` draws from a skeleton template for 1
993
994
  * generation; `v3` is the highest quality at 2 to 9 by size; `pro` is a
994
- * reference-based engine at 20 to 40 by size. States inherit the
995
- * parent's engine; animations choose their own.
995
+ * reference-based engine at 20 to 40 by size; `pro-flash` draws a
996
+ * south sprite with PixelLab's newest image model and rotates it with
997
+ * v3, 6 to 17 by size, or the rotations alone (1 at 64px) from a
998
+ * `reference`. States inherit the parent's engine; animations choose
999
+ * their own.
996
1000
  */
997
1001
  mode: CharacterModeSchema.optional(),
998
- /** `character` only. Rotations per base or state. `v3` and `pro` always draw 8. */
1002
+ /** `character` only. Rotations per base or state. Every engine but `standard` draws 8. */
999
1003
  directions: z.union([z.literal(4), z.literal(8)]).optional(),
1000
- /** `character` only. Body template: `mannequin` (default) or a quadruped (`bear`, `cat`, `dog`, `horse`, `lion`). */
1004
+ /** `character` only. Body template: `mannequin` (default), a quadruped (`bear`, `cat`, `dog`, `horse`, `lion`), or `custom` for `pro-flash`. */
1001
1005
  template: z.string().min(1).optional(),
1006
+ /**
1007
+ * `character` only, `pro-flash` bases with a style image: which traits
1008
+ * the style image lends. Each defaults to true.
1009
+ */
1010
+ styleTraits: z.object({
1011
+ palette: z.boolean().optional(),
1012
+ outline: z.boolean().optional(),
1013
+ detail: z.boolean().optional(),
1014
+ shading: z.boolean().optional()
1015
+ }).strict().optional(),
1002
1016
  /** `character` only, `standard` humanoid bases. A preset or multipliers; an asset may override it. */
1003
1017
  proportions: CharacterProportionsSchema.optional(),
1004
1018
  /**
@@ -5442,6 +5456,29 @@ var PixelLabClient = class {
5442
5456
  ...south ? { reference_image: encode(south) } : {},
5443
5457
  ...args.enhancePrompt ? { enhance_prompt: true } : {}
5444
5458
  };
5459
+ } else if (args.mode === "pro-flash") {
5460
+ path45 = "/create-character-pro-flash";
5461
+ const traits = args.styleTraits;
5462
+ body = {
5463
+ description: args.description,
5464
+ template_id: args.template,
5465
+ n_directions: 8,
5466
+ // The author's sprite sets the canvas; a size only applies to text creation.
5467
+ ...south ? { first_frame: encode(south) } : { image_size: imageSize },
5468
+ ...args.view ? { view: args.view } : {},
5469
+ ...args.seed != null ? { seed: args.seed } : {},
5470
+ ...args.styleReference && args.styleReferenceSize ? {
5471
+ style_image: { image: encode(args.styleReference), size: args.styleReferenceSize },
5472
+ ...traits ? {
5473
+ style_options: {
5474
+ ...traits.palette !== void 0 ? { color_palette: traits.palette } : {},
5475
+ ...traits.outline !== void 0 ? { outline: traits.outline } : {},
5476
+ ...traits.detail !== void 0 ? { detail: traits.detail } : {},
5477
+ ...traits.shading !== void 0 ? { shading: traits.shading } : {}
5478
+ }
5479
+ } : {}
5480
+ } : {}
5481
+ };
5445
5482
  } else if (args.mode === "pro") {
5446
5483
  path45 = "/create-character-pro";
5447
5484
  body = {
@@ -5619,12 +5656,24 @@ function characterCost(spec) {
5619
5656
  if (character.kind === "state") return generationCost(spec.width, spec.height, "1dir");
5620
5657
  if (character.mode === "standard") return 1;
5621
5658
  if (character.mode === "v3") return 1 + Math.max(1, Math.ceil(px * 8 / 65536));
5659
+ if (character.mode === "pro-flash") {
5660
+ const south = character.reference?.south;
5661
+ return south ? proFlashCharacterCost(south.width, south.height, true) : proFlashCharacterCost(spec.width, spec.height, false);
5662
+ }
5622
5663
  return generationCost(spec.width, spec.height, "1dir");
5623
5664
  }
5665
+ function proFlashCharacterCost(width, height, fromReference) {
5666
+ const side = Math.max(32, Math.ceil(Math.max(width, height) / 4) * 4);
5667
+ const rotations = Math.max(1, Math.ceil(side * side * 8 / 65536));
5668
+ if (fromReference) return rotations;
5669
+ const image = side <= 96 ? 5 : side <= 208 ? 6 : 9;
5670
+ return image + rotations;
5671
+ }
5624
5672
  function characterAnimationName(spec) {
5625
5673
  return `pixelkiln:${spec.styleId}/${spec.assetId}`;
5626
5674
  }
5627
5675
  var CHARACTER_TEMPLATES = ["mannequin", "bear", "cat", "dog", "horse", "lion"];
5676
+ var PRO_FLASH_TEMPLATES = [...CHARACTER_TEMPLATES, "custom"];
5628
5677
  function encodeAnimationJob(job) {
5629
5678
  return `character-animation:${Buffer.from(JSON.stringify(job)).toString("base64url")}`;
5630
5679
  }
@@ -5783,7 +5832,7 @@ var PixelLabProvider = class _PixelLabProvider {
5783
5832
  if (!character.reference.south) throw new Error(`${label}: a reference needs a south-facing sprite`);
5784
5833
  if (character.mode !== "standard") {
5785
5834
  if (given.length > 1) throw new Error(`${label}: PixelLab ${character.mode} rotates one south-facing reference; drop the other directions`);
5786
- const limit = character.mode === "v3" ? 256 : 168;
5835
+ const limit = character.mode === "pro" ? 168 : 256;
5787
5836
  const south = character.reference.south;
5788
5837
  if (south.width > limit || south.height > limit) {
5789
5838
  throw new Error(`${label}: reference is ${south.width}x${south.height}; PixelLab ${character.mode} takes up to ${limit}px`);
@@ -5807,26 +5856,39 @@ var PixelLabProvider = class _PixelLabProvider {
5807
5856
  }
5808
5857
  }
5809
5858
  if (styleImages.length) {
5810
- if (character.mode !== "pro") {
5859
+ if (character.mode !== "pro" && character.mode !== "pro-flash") {
5811
5860
  throw new Error(
5812
- `PixelLab ${character.mode} characters take no style images; give a base its own sprite with \`reference\`, or use mode pro for a style anchor`
5861
+ `PixelLab ${character.mode} characters take no style images; give a base its own sprite with \`reference\`, or use mode pro or pro-flash for a style anchor`
5813
5862
  );
5814
5863
  }
5815
- if (styleImages.length > 1) throw new Error("PixelLab pro characters take one style image");
5864
+ if (styleImages.length > 1) throw new Error(`PixelLab ${character.mode} characters take one style image`);
5816
5865
  if (character.kind === "base" && character.reference) {
5817
- throw new Error(`${label}: a pro base rotating its own reference takes no style image`);
5866
+ throw new Error(`${label}: a ${character.mode} base rotating its own reference takes no style image`);
5818
5867
  }
5819
5868
  const image = styleImages[0];
5820
- if (image.width > 168 || image.height > 168) {
5821
- throw new Error(`PixelLab pro character style image is ${image.width}x${image.height}; the limit is 168px`);
5869
+ const limit = character.mode === "pro" ? 168 : 256;
5870
+ if (image.width > limit || image.height > limit) {
5871
+ throw new Error(`PixelLab ${character.mode} character style image is ${image.width}x${image.height}; the limit is ${limit}px`);
5872
+ }
5873
+ if (character.mode === "pro-flash" && (image.width > spec.width || image.height > spec.height)) {
5874
+ throw new Error(
5875
+ `PixelLab pro-flash style image is ${image.width}x${image.height} but the character is ${spec.width}x${spec.height}; crop the image to its subject or raise the style's size`
5876
+ );
5822
5877
  }
5823
5878
  }
5824
- const maxSize = character.mode === "v3" ? 256 : 128;
5879
+ if (character.styleTraits && character.mode !== "pro-flash") {
5880
+ throw new Error(`${label}: styleTraits choose what a pro-flash style image lends; the ${character.mode} engine does not take them`);
5881
+ }
5882
+ const maxSize = character.mode === "v3" || character.mode === "pro-flash" ? 256 : 128;
5825
5883
  if (spec.width < 16 || spec.height < 16 || spec.width > maxSize || spec.height > maxSize) {
5826
5884
  throw new Error(`PixelLab ${character.mode} characters must be between 16 and ${maxSize} pixels`);
5827
5885
  }
5828
- if (!CHARACTER_TEMPLATES.includes(character.template)) {
5829
- throw new Error(`PixelLab character template must be one of: ${CHARACTER_TEMPLATES.join(", ")}`);
5886
+ if (character.mode === "pro-flash" && character.kind === "base" && (spec.width % 4 || spec.height % 4)) {
5887
+ throw new Error(`PixelLab pro-flash characters are sized in multiples of 4 pixels; ${spec.width}x${spec.height} is not`);
5888
+ }
5889
+ const templates = character.mode === "pro-flash" ? PRO_FLASH_TEMPLATES : CHARACTER_TEMPLATES;
5890
+ if (!templates.includes(character.template)) {
5891
+ throw new Error(`PixelLab ${character.mode} character template must be one of: ${templates.join(", ")}`);
5830
5892
  }
5831
5893
  const views = character.mode === "standard" ? ["low top-down", "high top-down", "side", "perspective", "oblique"] : ["low top-down", "high top-down", "side"];
5832
5894
  if (!views.includes(spec.view)) {
@@ -5881,7 +5943,9 @@ var PixelLabProvider = class _PixelLabProvider {
5881
5943
  styleReference: styleImage ? { base64: styleImage.base64, format: styleImage.format } : void 0,
5882
5944
  enhancePrompt: character.enhancePrompt,
5883
5945
  concept: character.concept ? readPose(spec, "concept image", character.concept) : void 0,
5884
- styleCharacterId: character.styleAnchor ? requireStyleObjectId(spec, context) : void 0
5946
+ styleCharacterId: character.styleAnchor ? requireStyleObjectId(spec, context) : void 0,
5947
+ styleReferenceSize: styleImage ? { width: styleImage.width, height: styleImage.height } : void 0,
5948
+ styleTraits: character.styleTraits
5885
5949
  });
5886
5950
  return { jobId: res2.character_id, metadata: { character: { kind: "base", characterId: res2.character_id, mode: character.mode, directions: character.directions, backgroundJobId: res2.background_job_id } } };
5887
5951
  }
@@ -7789,7 +7853,8 @@ async function resolveCharacterShape(asset, style, kind, files) {
7789
7853
  ...proportions !== void 0 ? { proportions } : {},
7790
7854
  ...style.textGuidanceScale !== void 0 ? { textGuidanceScale: style.textGuidanceScale } : {},
7791
7855
  ...style.isometric !== void 0 ? { isometric: style.isometric } : {},
7792
- ...style.enhancePrompt !== void 0 ? { enhancePrompt: style.enhancePrompt } : {}
7856
+ ...style.enhancePrompt !== void 0 ? { enhancePrompt: style.enhancePrompt } : {},
7857
+ ...style.styleTraits ? { styleTraits: style.styleTraits } : {}
7793
7858
  };
7794
7859
  if (asset.concept) {
7795
7860
  const image = await files.load(asset.concept, "Concept image");
@@ -8023,8 +8088,9 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
8023
8088
  const superseded = (entry.supersededOutputs ?? []).find(
8024
8089
  (output, oldIndex, all) => currentOutputPath(output, spec, oldIndex, all.length) === target
8025
8090
  );
8091
+ const outgoing = Boolean(superseded && currentHash === superseded.sha256);
8026
8092
  if (!opts.force) {
8027
- if (superseded && currentHash === superseded.sha256) {
8093
+ if (outgoing) {
8028
8094
  } else if (recorded || superseded) {
8029
8095
  throw new Error(
8030
8096
  `refusing to overwrite modified output ${target}; pass --force to replace it`
@@ -8035,6 +8101,14 @@ async function fetchAssets(provider, specs, lock, lockPath, opts = {}) {
8035
8101
  );
8036
8102
  }
8037
8103
  }
8104
+ if (outgoing && cacheDir) {
8105
+ try {
8106
+ const oldBytes = await readFile7(target);
8107
+ const oldMediaType = detectMediaType(oldBytes);
8108
+ if (oldMediaType) await cacheMedia(cacheDir, oldBytes, oldMediaType, currentHash);
8109
+ } catch {
8110
+ }
8111
+ }
8038
8112
  log2(
8039
8113
  ` replace ${path14.relative(process.cwd(), target)}` + (opts.force ? " (--force)" : " (previous tracked generation)")
8040
8114
  );