pixelkiln 0.44.0 → 0.45.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/index.cjs CHANGED
@@ -605,6 +605,12 @@ var StyleObjectSchema = import_zod.z.object({
605
605
  isometric: import_zod.z.boolean().optional(),
606
606
  /** `character` only, `v3` bases: let PixelLab expand the prompt into a fuller one before drawing. */
607
607
  enhancePrompt: import_zod.z.boolean().optional(),
608
+ /**
609
+ * `character` only, `pro` bases: the asset id of a generated 8-direction
610
+ * character in this style whose look every base drawn from text or a
611
+ * concept follows. An asset may override it; the anchor itself ignores it.
612
+ */
613
+ styleCharacter: import_zod.z.string().min(1).optional(),
608
614
  /** Fixed seed for reproducibility where the endpoint supports it. */
609
615
  seed: import_zod.z.number().int().optional(),
610
616
  /**
@@ -777,6 +783,15 @@ var AssetSchema = import_zod.z.object({
777
783
  * still guides the result.
778
784
  */
779
785
  reference: import_zod.z.union([import_zod.z.string().min(1), import_zod.z.record(CharacterDirectionSchema, import_zod.z.string().min(1))]).optional(),
786
+ /**
787
+ * `character` styles, `pro` bases only: a manifest-relative concept
788
+ * image (a painting, a photo, a sketch; up to 1024px) that seeds the
789
+ * design instead of the prompt alone. A style image or `styleCharacter`
790
+ * may still anchor the look.
791
+ */
792
+ concept: import_zod.z.string().min(1).optional(),
793
+ /** `character` styles, `pro` bases: this base's style anchor, over the style's. */
794
+ styleCharacter: import_zod.z.string().min(1).optional(),
780
795
  /**
781
796
  * Another asset of the same style flipped left to right, made locally
782
797
  * from that asset's downloaded files at no generation cost.
@@ -858,6 +873,20 @@ var AssetSchema = import_zod.z.object({
858
873
  path: ["reference"]
859
874
  });
860
875
  }
876
+ if (asset.concept && (asset.state || asset.animation || asset.mirror)) {
877
+ context.addIssue({
878
+ code: import_zod.z.ZodIssueCode.custom,
879
+ message: "a concept image belongs on a base; a state, animation, or mirror takes its look from its parent",
880
+ path: ["concept"]
881
+ });
882
+ }
883
+ if (asset.concept && asset.reference) {
884
+ context.addIssue({
885
+ code: import_zod.z.ZodIssueCode.custom,
886
+ message: "concept and reference are mutually exclusive: a concept seeds a new design, a reference is the sprite to rotate",
887
+ path: ["concept"]
888
+ });
889
+ }
861
890
  if (typeof asset.reference === "object" && !Object.keys(asset.reference).length) {
862
891
  context.addIssue({ code: import_zod.z.ZodIssueCode.custom, message: "a reference needs at least a south image", path: ["reference"] });
863
892
  }
@@ -1539,10 +1568,12 @@ var PixelLabClient = class {
1539
1568
  image_size: imageSize,
1540
1569
  template_id: args.template,
1541
1570
  no_background: args.noBackground ?? true,
1542
- method: south ? "rotate_character" : "create_with_style",
1571
+ method: south ? "rotate_character" : args.concept ? "create_from_concept" : "create_with_style",
1543
1572
  ...args.view ? { view: args.view } : {},
1544
1573
  ...args.seed != null ? { seed: args.seed } : {},
1545
- ...south ? { reference_image: encode(south) } : args.styleReference ? { reference_image: encode(args.styleReference) } : {}
1574
+ ...south ? { reference_image: encode(south) } : args.styleReference ? { reference_image: encode(args.styleReference) } : {},
1575
+ ...!south && args.concept ? { concept_image: encode(args.concept) } : {},
1576
+ ...!south && args.styleCharacterId ? { style_character_id: args.styleCharacterId } : {}
1546
1577
  };
1547
1578
  } else {
1548
1579
  path33 = args.directions === 4 ? "/create-character-with-4-directions" : "/create-character-with-8-directions";
@@ -2304,7 +2335,9 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
2304
2335
  textGuidanceScale: spec.character.textGuidanceScale,
2305
2336
  isometric: spec.character.isometric,
2306
2337
  enhancePrompt: spec.character.enhancePrompt,
2307
- reference: spec.character.reference ? Object.fromEntries(Object.entries(spec.character.reference).map(([direction, image]) => [direction, image.sha256])) : void 0
2338
+ reference: spec.character.reference ? Object.fromEntries(Object.entries(spec.character.reference).map(([direction, image]) => [direction, image.sha256])) : void 0,
2339
+ concept: spec.character.concept?.sha256,
2340
+ styleAnchor: spec.character.styleAnchor ? { of: spec.character.styleAnchor.assetId, sha256: spec.character.styleAnchor.sha256 } : void 0
2308
2341
  } : void 0,
2309
2342
  // A mirror's bytes come from its source's recorded outputs; the plan
2310
2343
  // compares those directly, so only the choice of source is identity.
@@ -2491,6 +2524,19 @@ var PixelLabProvider = class _PixelLabProvider {
2491
2524
  }
2492
2525
  }
2493
2526
  }
2527
+ if (character.concept) {
2528
+ if (character.mode !== "pro") throw new Error(`${label}: a concept image is a pro input; the ${character.mode} engine draws from text or a reference`);
2529
+ if (character.concept.width > 1024 || character.concept.height > 1024) {
2530
+ throw new Error(`${label}: concept image is ${character.concept.width}x${character.concept.height}; PixelLab pro takes up to 1024px`);
2531
+ }
2532
+ }
2533
+ if (character.styleAnchor) {
2534
+ if (character.mode !== "pro") throw new Error(`${label}: styleCharacter is a pro input; the ${character.mode} engine has no style anchor`);
2535
+ if (character.reference) throw new Error(`${label}: a pro base rotating its own reference takes no styleCharacter`);
2536
+ if (character.styleAnchor.spec.character?.directions !== 8) {
2537
+ throw new Error(`${label}: styleCharacter ${character.styleAnchor.assetId} has ${character.styleAnchor.spec.character?.directions} directions; PixelLab wants an 8-direction anchor`);
2538
+ }
2539
+ }
2494
2540
  if (character.reference) {
2495
2541
  const given = Object.keys(character.reference);
2496
2542
  if (!character.reference.south) throw new Error(`${label}: a reference needs a south-facing sprite`);
@@ -2592,7 +2638,9 @@ var PixelLabProvider = class _PixelLabProvider {
2592
2638
  isometric: character.isometric,
2593
2639
  reference: character.reference ? readReference(spec, character.reference) : void 0,
2594
2640
  styleReference: styleImage ? { base64: styleImage.base64, format: styleImage.format } : void 0,
2595
- enhancePrompt: character.enhancePrompt
2641
+ enhancePrompt: character.enhancePrompt,
2642
+ concept: character.concept ? readPose(spec, "concept image", character.concept) : void 0,
2643
+ styleCharacterId: character.styleAnchor ? requireStyleObjectId(spec, context) : void 0
2596
2644
  });
2597
2645
  return { jobId: res2.character_id, metadata: { character: { kind: "base", characterId: res2.character_id, mode: character.mode, directions: character.directions, backgroundJobId: res2.background_job_id } } };
2598
2646
  }
@@ -3012,6 +3060,14 @@ function readReference(spec, reference) {
3012
3060
  }
3013
3061
  return images;
3014
3062
  }
3063
+ function requireStyleObjectId(spec, context) {
3064
+ if (!context?.styleObjectId) {
3065
+ throw new Error(
3066
+ `${spec.styleId}/${spec.assetId} anchors its style on ${spec.character.styleAnchor.assetId}, which has no PixelLab character id in the lockfile yet; generate the anchor first`
3067
+ );
3068
+ }
3069
+ return context.styleObjectId;
3070
+ }
3015
3071
  function readPose(spec, what, image) {
3016
3072
  const bytes = (0, import_node_fs2.readFileSync)(image.path);
3017
3073
  if (sha256(bytes) !== image.sha256) {
@@ -5711,6 +5767,18 @@ async function resolveSpecs(loaded, filter) {
5711
5767
  finalized.add(assetId);
5712
5768
  return resolved;
5713
5769
  }
5770
+ const anchorId = asset.styleCharacter ?? style.styleCharacter;
5771
+ if (resolved.character?.kind === "base" && anchorId && anchorId !== assetId) {
5772
+ const anchorSpec = await finalize(anchorId);
5773
+ if (!anchorSpec.character || anchorSpec.character.kind === "animation") {
5774
+ throw new Error(`assets.${assetId}: styleCharacter ${anchorId} is not a character base or state`);
5775
+ }
5776
+ const file = memberPath(anchorSpec.outFile, "south", 0, anchorSpec.character.directions, MediaType.PNG);
5777
+ resolved.character = {
5778
+ ...resolved.character,
5779
+ styleAnchor: { assetId: anchorId, spec: anchorSpec, file, sha256: (0, import_node_fs5.existsSync)(file) ? await sha256File(file) : null }
5780
+ };
5781
+ }
5714
5782
  const characterParent = asset.state?.of ?? asset.animation?.of;
5715
5783
  if (resolved.character && characterParent) {
5716
5784
  const parentSpec = await finalize(characterParent);
@@ -5863,6 +5931,10 @@ async function resolveCharacterShape(asset, style, kind, files) {
5863
5931
  ...style.isometric !== void 0 ? { isometric: style.isometric } : {},
5864
5932
  ...style.enhancePrompt !== void 0 ? { enhancePrompt: style.enhancePrompt } : {}
5865
5933
  };
5934
+ if (asset.concept) {
5935
+ const image = await files.load(asset.concept, "Concept image");
5936
+ shape.concept = { path: import_node_path5.default.resolve(files.root, asset.concept), sha256: image.hash, width: image.width, height: image.height, format: image.format };
5937
+ }
5866
5938
  if (asset.reference) {
5867
5939
  const byDirection = typeof asset.reference === "string" ? { south: asset.reference } : asset.reference;
5868
5940
  shape.reference = {};
@@ -7382,9 +7454,24 @@ var import_node_path11 = __toESM(require("path"), 1);
7382
7454
  async function inspectRevisionReadiness(spec, lock) {
7383
7455
  if (spec.mirror) return inspectMirror(spec, lock, /* @__PURE__ */ new Set());
7384
7456
  if (spec.character?.parentSpec) return inspectCharacterParent(spec, lock, /* @__PURE__ */ new Set());
7457
+ if (spec.character?.styleAnchor) return inspectStyleAnchor(spec, lock, /* @__PURE__ */ new Set());
7385
7458
  if (!spec.revision) return null;
7386
7459
  return inspectRevision(spec, lock, /* @__PURE__ */ new Set());
7387
7460
  }
7461
+ async function inspectStyleAnchor(spec, lock, seen) {
7462
+ const anchor = spec.character.styleAnchor;
7463
+ const label = `style anchor ${spec.styleId}/${anchor.assetId}`;
7464
+ const dependency = await inspectParent(anchor.spec, lock, seen);
7465
+ if (!dependency.ready) return { ready: false, reason: `${label} is not ready: ${dependency.reason}` };
7466
+ if (!anchor.sha256 || !(0, import_node_fs8.existsSync)(anchor.file)) return { ready: false, reason: `${label} has no generated south-facing file yet` };
7467
+ if (await sha256File(anchor.file) !== anchor.sha256) {
7468
+ return { ready: false, reason: `${label} changed after the manifest was resolved` };
7469
+ }
7470
+ if (!lock.entries[lockKey(anchor.spec.styleId, anchor.spec.assetId)]?.objectId) {
7471
+ return { ready: false, reason: `${label} has no PixelLab character id recorded` };
7472
+ }
7473
+ return { ready: true, reason: `${label} is current` };
7474
+ }
7388
7475
  async function inspectMirror(spec, lock, seen) {
7389
7476
  const source = spec.mirror.sourceSpec;
7390
7477
  const label = `mirror source ${spec.styleId}/${spec.mirror.sourceAssetId}`;
@@ -7456,6 +7543,10 @@ async function inspectParent(spec, lock, seen) {
7456
7543
  const inputs = await inspectCharacterParent(spec, lock, nextSeen);
7457
7544
  if (!inputs.ready) return inputs;
7458
7545
  }
7546
+ if (spec.character?.styleAnchor) {
7547
+ const inputs = await inspectStyleAnchor(spec, lock, nextSeen);
7548
+ if (!inputs.ready) return inputs;
7549
+ }
7459
7550
  if (spec.quality) {
7460
7551
  const quality = await inspectQualityProfile(spec, lock);
7461
7552
  return quality?.state === "approved" ? { ready: true, reason: "approved quality output is current" } : {
@@ -7495,6 +7586,14 @@ async function anyOutputModified(entry, spec) {
7495
7586
  }
7496
7587
  return false;
7497
7588
  }
7589
+ function dependencies(spec) {
7590
+ return [
7591
+ spec.mirror?.sourceSpec,
7592
+ spec.revision?.sourceSpec,
7593
+ spec.character?.parentSpec,
7594
+ spec.character?.styleAnchor?.spec
7595
+ ].filter((parent) => Boolean(parent));
7596
+ }
7498
7597
  function resumeCommandForStatus(status) {
7499
7598
  if (status === "pending" || status === "processing") return "poll";
7500
7599
  if (status === "review") return "pick";
@@ -7603,16 +7702,19 @@ async function buildPlan(specs, lock, opts = {}) {
7603
7702
  const stateOf = new Map(items.map((i) => [i.key, i.state]));
7604
7703
  for (const item of items) {
7605
7704
  if (!isActionable(item)) continue;
7606
- const parent = item.spec.mirror?.sourceSpec ?? item.spec.revision?.sourceSpec ?? item.spec.character?.parentSpec;
7607
- if (!parent) continue;
7608
- const parentKey = lockKey(parent.styleId, parent.assetId);
7609
- const parentState = stateOf.get(parentKey);
7610
- if (parentState && isActionable({ state: parentState })) {
7611
- item.state = "blocked";
7612
- item.reason = `parent ${parentKey} is being generated in this run; this follows in the next wave`;
7613
- } else if (item.spec.mirror && (parentState === "recoverable" || parentState === "in-flight")) {
7614
- item.state = "blocked";
7615
- item.reason = `mirror source ${parentKey} is ${parentState}; this follows in the next wave`;
7705
+ for (const parent of dependencies(item.spec)) {
7706
+ const parentKey = lockKey(parent.styleId, parent.assetId);
7707
+ const parentState = stateOf.get(parentKey);
7708
+ if (parentState && isActionable({ state: parentState })) {
7709
+ item.state = "blocked";
7710
+ item.reason = `parent ${parentKey} is being generated in this run; this follows in the next wave`;
7711
+ break;
7712
+ }
7713
+ if (item.spec.mirror && (parentState === "recoverable" || parentState === "in-flight")) {
7714
+ item.state = "blocked";
7715
+ item.reason = `mirror source ${parentKey} is ${parentState}; this follows in the next wave`;
7716
+ break;
7717
+ }
7616
7718
  }
7617
7719
  }
7618
7720
  const actionable = items.filter(isActionable);
@@ -10091,10 +10193,13 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
10091
10193
  try {
10092
10194
  const refs = styleImages.get(spec.styleId) ?? [];
10093
10195
  const parentEntry = spec.character?.parentSpec ? lock.entries[lockKey(spec.character.parentSpec.styleId, spec.character.parentSpec.assetId)] : void 0;
10196
+ const anchor = spec.character?.styleAnchor;
10197
+ const anchorEntry = anchor ? lock.entries[lockKey(anchor.spec.styleId, anchor.spec.assetId)] : void 0;
10094
10198
  const replacedMetadata = previousEntry?.providerMetadata?.[provider.id];
10095
10199
  const { jobId, metadata } = await provider.submit(spec, refs, {
10096
10200
  ...previousJobId ? { previousJobId } : {},
10097
10201
  ...parentEntry?.objectId ? { parentObjectId: parentEntry.objectId } : {},
10202
+ ...anchorEntry?.objectId ? { styleObjectId: anchorEntry.objectId } : {},
10098
10203
  ...replacedMetadata ? { replacedMetadata } : {},
10099
10204
  ...previousMetadata ? { previousMetadata } : {},
10100
10205
  checkpoint: async (checkpoint) => {
@@ -12703,7 +12808,7 @@ var pin_default = {
12703
12808
  godot: "4.7.2",
12704
12809
  extensionsApi: 9,
12705
12810
  protocol: 4,
12706
- release: "editor-pixelorama-v1.2.2-pk.18",
12811
+ release: "editor-pixelorama-v1.2.2-pk.26",
12707
12812
  files: {
12708
12813
  "index.apple-touch-icon.png": {
12709
12814
  sha256: "ee5434873e0fed91e9a888c3ea5a8b79beac25999308be7a6eb41bf4ce640d27",
@@ -12718,7 +12823,7 @@ var pin_default = {
12718
12823
  bytes: 7298
12719
12824
  },
12720
12825
  "index.html": {
12721
- sha256: "d9adf06036660c4b76ef5386e872bc03dc0d5b0628fd6ac660d480893ee1f47a",
12826
+ sha256: "326101ab02bb4a29a7964214c9307c7dff26e9d66b8ebaaf6e7a3987e84a05b2",
12722
12827
  bytes: 5442
12723
12828
  },
12724
12829
  "index.icon.png": {
@@ -12730,8 +12835,8 @@ var pin_default = {
12730
12835
  bytes: 279815
12731
12836
  },
12732
12837
  "index.pck": {
12733
- sha256: "a41d9c3a39aa99ea5ee49ddcedfeb8b092006aa75c0de67139f13297c17f42e4",
12734
- bytes: 6431692
12838
+ sha256: "e26292650bb996965acd1ba70e74d2b4020b063d14f1276701312c1d34cb0a83",
12839
+ bytes: 6431932
12735
12840
  },
12736
12841
  "index.png": {
12737
12842
  sha256: "6e3ca3eb4a99e4c07bdd89ebc873b440ec0ffd66ce1955160da9e7ed80d538d7",