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/cli.js CHANGED
@@ -676,7 +676,9 @@ 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
- reference: spec.character.reference ? Object.fromEntries(Object.entries(spec.character.reference).map(([direction, image]) => [direction, image.sha256])) : void 0
679
+ reference: spec.character.reference ? Object.fromEntries(Object.entries(spec.character.reference).map(([direction, image]) => [direction, image.sha256])) : void 0,
680
+ concept: spec.character.concept?.sha256,
681
+ styleAnchor: spec.character.styleAnchor ? { of: spec.character.styleAnchor.assetId, sha256: spec.character.styleAnchor.sha256 } : void 0
680
682
  } : void 0,
681
683
  // A mirror's bytes come from its source's recorded outputs; the plan
682
684
  // compares those directly, so only the choice of source is identity.
@@ -1009,6 +1011,12 @@ var StyleObjectSchema = z.object({
1009
1011
  isometric: z.boolean().optional(),
1010
1012
  /** `character` only, `v3` bases: let PixelLab expand the prompt into a fuller one before drawing. */
1011
1013
  enhancePrompt: z.boolean().optional(),
1014
+ /**
1015
+ * `character` only, `pro` bases: the asset id of a generated 8-direction
1016
+ * character in this style whose look every base drawn from text or a
1017
+ * concept follows. An asset may override it; the anchor itself ignores it.
1018
+ */
1019
+ styleCharacter: z.string().min(1).optional(),
1012
1020
  /** Fixed seed for reproducibility where the endpoint supports it. */
1013
1021
  seed: z.number().int().optional(),
1014
1022
  /**
@@ -1181,6 +1189,15 @@ var AssetSchema = z.object({
1181
1189
  * still guides the result.
1182
1190
  */
1183
1191
  reference: z.union([z.string().min(1), z.record(CharacterDirectionSchema, z.string().min(1))]).optional(),
1192
+ /**
1193
+ * `character` styles, `pro` bases only: a manifest-relative concept
1194
+ * image (a painting, a photo, a sketch; up to 1024px) that seeds the
1195
+ * design instead of the prompt alone. A style image or `styleCharacter`
1196
+ * may still anchor the look.
1197
+ */
1198
+ concept: z.string().min(1).optional(),
1199
+ /** `character` styles, `pro` bases: this base's style anchor, over the style's. */
1200
+ styleCharacter: z.string().min(1).optional(),
1184
1201
  /**
1185
1202
  * Another asset of the same style flipped left to right, made locally
1186
1203
  * from that asset's downloaded files at no generation cost.
@@ -1262,6 +1279,20 @@ var AssetSchema = z.object({
1262
1279
  path: ["reference"]
1263
1280
  });
1264
1281
  }
1282
+ if (asset.concept && (asset.state || asset.animation || asset.mirror)) {
1283
+ context.addIssue({
1284
+ code: z.ZodIssueCode.custom,
1285
+ message: "a concept image belongs on a base; a state, animation, or mirror takes its look from its parent",
1286
+ path: ["concept"]
1287
+ });
1288
+ }
1289
+ if (asset.concept && asset.reference) {
1290
+ context.addIssue({
1291
+ code: z.ZodIssueCode.custom,
1292
+ message: "concept and reference are mutually exclusive: a concept seeds a new design, a reference is the sprite to rotate",
1293
+ path: ["concept"]
1294
+ });
1295
+ }
1265
1296
  if (typeof asset.reference === "object" && !Object.keys(asset.reference).length) {
1266
1297
  context.addIssue({ code: z.ZodIssueCode.custom, message: "a reference needs at least a south image", path: ["reference"] });
1267
1298
  }
@@ -3629,9 +3660,24 @@ import path8 from "path";
3629
3660
  async function inspectRevisionReadiness(spec, lock) {
3630
3661
  if (spec.mirror) return inspectMirror(spec, lock, /* @__PURE__ */ new Set());
3631
3662
  if (spec.character?.parentSpec) return inspectCharacterParent(spec, lock, /* @__PURE__ */ new Set());
3663
+ if (spec.character?.styleAnchor) return inspectStyleAnchor(spec, lock, /* @__PURE__ */ new Set());
3632
3664
  if (!spec.revision) return null;
3633
3665
  return inspectRevision(spec, lock, /* @__PURE__ */ new Set());
3634
3666
  }
3667
+ async function inspectStyleAnchor(spec, lock, seen) {
3668
+ const anchor = spec.character.styleAnchor;
3669
+ const label = `style anchor ${spec.styleId}/${anchor.assetId}`;
3670
+ const dependency = await inspectParent(anchor.spec, lock, seen);
3671
+ if (!dependency.ready) return { ready: false, reason: `${label} is not ready: ${dependency.reason}` };
3672
+ if (!anchor.sha256 || !existsSync4(anchor.file)) return { ready: false, reason: `${label} has no generated south-facing file yet` };
3673
+ if (await sha256File(anchor.file) !== anchor.sha256) {
3674
+ return { ready: false, reason: `${label} changed after the manifest was resolved` };
3675
+ }
3676
+ if (!lock.entries[lockKey(anchor.spec.styleId, anchor.spec.assetId)]?.objectId) {
3677
+ return { ready: false, reason: `${label} has no PixelLab character id recorded` };
3678
+ }
3679
+ return { ready: true, reason: `${label} is current` };
3680
+ }
3635
3681
  async function inspectMirror(spec, lock, seen) {
3636
3682
  const source = spec.mirror.sourceSpec;
3637
3683
  const label = `mirror source ${spec.styleId}/${spec.mirror.sourceAssetId}`;
@@ -3703,6 +3749,10 @@ async function inspectParent(spec, lock, seen) {
3703
3749
  const inputs = await inspectCharacterParent(spec, lock, nextSeen);
3704
3750
  if (!inputs.ready) return inputs;
3705
3751
  }
3752
+ if (spec.character?.styleAnchor) {
3753
+ const inputs = await inspectStyleAnchor(spec, lock, nextSeen);
3754
+ if (!inputs.ready) return inputs;
3755
+ }
3706
3756
  if (spec.quality) {
3707
3757
  const quality = await inspectQualityProfile(spec, lock);
3708
3758
  return quality?.state === "approved" ? { ready: true, reason: "approved quality output is current" } : {
@@ -3742,6 +3792,14 @@ async function anyOutputModified(entry, spec) {
3742
3792
  }
3743
3793
  return false;
3744
3794
  }
3795
+ function dependencies(spec) {
3796
+ return [
3797
+ spec.mirror?.sourceSpec,
3798
+ spec.revision?.sourceSpec,
3799
+ spec.character?.parentSpec,
3800
+ spec.character?.styleAnchor?.spec
3801
+ ].filter((parent) => Boolean(parent));
3802
+ }
3745
3803
  function resumeCommandForStatus(status) {
3746
3804
  if (status === "pending" || status === "processing") return "poll";
3747
3805
  if (status === "review") return "pick";
@@ -3850,16 +3908,19 @@ async function buildPlan(specs, lock, opts = {}) {
3850
3908
  const stateOf = new Map(items.map((i) => [i.key, i.state]));
3851
3909
  for (const item of items) {
3852
3910
  if (!isActionable(item)) continue;
3853
- const parent = item.spec.mirror?.sourceSpec ?? item.spec.revision?.sourceSpec ?? item.spec.character?.parentSpec;
3854
- if (!parent) continue;
3855
- const parentKey = lockKey(parent.styleId, parent.assetId);
3856
- const parentState = stateOf.get(parentKey);
3857
- if (parentState && isActionable({ state: parentState })) {
3858
- item.state = "blocked";
3859
- item.reason = `parent ${parentKey} is being generated in this run; this follows in the next wave`;
3860
- } else if (item.spec.mirror && (parentState === "recoverable" || parentState === "in-flight")) {
3861
- item.state = "blocked";
3862
- item.reason = `mirror source ${parentKey} is ${parentState}; this follows in the next wave`;
3911
+ for (const parent of dependencies(item.spec)) {
3912
+ const parentKey = lockKey(parent.styleId, parent.assetId);
3913
+ const parentState = stateOf.get(parentKey);
3914
+ if (parentState && isActionable({ state: parentState })) {
3915
+ item.state = "blocked";
3916
+ item.reason = `parent ${parentKey} is being generated in this run; this follows in the next wave`;
3917
+ break;
3918
+ }
3919
+ if (item.spec.mirror && (parentState === "recoverable" || parentState === "in-flight")) {
3920
+ item.state = "blocked";
3921
+ item.reason = `mirror source ${parentKey} is ${parentState}; this follows in the next wave`;
3922
+ break;
3923
+ }
3863
3924
  }
3864
3925
  }
3865
3926
  const actionable = items.filter(isActionable);
@@ -5388,10 +5449,12 @@ var PixelLabClient = class {
5388
5449
  image_size: imageSize,
5389
5450
  template_id: args.template,
5390
5451
  no_background: args.noBackground ?? true,
5391
- method: south ? "rotate_character" : "create_with_style",
5452
+ method: south ? "rotate_character" : args.concept ? "create_from_concept" : "create_with_style",
5392
5453
  ...args.view ? { view: args.view } : {},
5393
5454
  ...args.seed != null ? { seed: args.seed } : {},
5394
- ...south ? { reference_image: encode(south) } : args.styleReference ? { reference_image: encode(args.styleReference) } : {}
5455
+ ...south ? { reference_image: encode(south) } : args.styleReference ? { reference_image: encode(args.styleReference) } : {},
5456
+ ...!south && args.concept ? { concept_image: encode(args.concept) } : {},
5457
+ ...!south && args.styleCharacterId ? { style_character_id: args.styleCharacterId } : {}
5395
5458
  };
5396
5459
  } else {
5397
5460
  path45 = args.directions === 4 ? "/create-character-with-4-directions" : "/create-character-with-8-directions";
@@ -5702,6 +5765,19 @@ var PixelLabProvider = class _PixelLabProvider {
5702
5765
  }
5703
5766
  }
5704
5767
  }
5768
+ if (character.concept) {
5769
+ 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`);
5770
+ if (character.concept.width > 1024 || character.concept.height > 1024) {
5771
+ throw new Error(`${label}: concept image is ${character.concept.width}x${character.concept.height}; PixelLab pro takes up to 1024px`);
5772
+ }
5773
+ }
5774
+ if (character.styleAnchor) {
5775
+ if (character.mode !== "pro") throw new Error(`${label}: styleCharacter is a pro input; the ${character.mode} engine has no style anchor`);
5776
+ if (character.reference) throw new Error(`${label}: a pro base rotating its own reference takes no styleCharacter`);
5777
+ if (character.styleAnchor.spec.character?.directions !== 8) {
5778
+ throw new Error(`${label}: styleCharacter ${character.styleAnchor.assetId} has ${character.styleAnchor.spec.character?.directions} directions; PixelLab wants an 8-direction anchor`);
5779
+ }
5780
+ }
5705
5781
  if (character.reference) {
5706
5782
  const given = Object.keys(character.reference);
5707
5783
  if (!character.reference.south) throw new Error(`${label}: a reference needs a south-facing sprite`);
@@ -5803,7 +5879,9 @@ var PixelLabProvider = class _PixelLabProvider {
5803
5879
  isometric: character.isometric,
5804
5880
  reference: character.reference ? readReference(spec, character.reference) : void 0,
5805
5881
  styleReference: styleImage ? { base64: styleImage.base64, format: styleImage.format } : void 0,
5806
- enhancePrompt: character.enhancePrompt
5882
+ enhancePrompt: character.enhancePrompt,
5883
+ concept: character.concept ? readPose(spec, "concept image", character.concept) : void 0,
5884
+ styleCharacterId: character.styleAnchor ? requireStyleObjectId(spec, context) : void 0
5807
5885
  });
5808
5886
  return { jobId: res2.character_id, metadata: { character: { kind: "base", characterId: res2.character_id, mode: character.mode, directions: character.directions, backgroundJobId: res2.background_job_id } } };
5809
5887
  }
@@ -6223,6 +6301,14 @@ function readReference(spec, reference) {
6223
6301
  }
6224
6302
  return images;
6225
6303
  }
6304
+ function requireStyleObjectId(spec, context) {
6305
+ if (!context?.styleObjectId) {
6306
+ throw new Error(
6307
+ `${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`
6308
+ );
6309
+ }
6310
+ return context.styleObjectId;
6311
+ }
6226
6312
  function readPose(spec, what, image) {
6227
6313
  const bytes = readFileSync2(image.path);
6228
6314
  if (sha256(bytes) !== image.sha256) {
@@ -7544,6 +7630,18 @@ async function resolveSpecs(loaded, filter) {
7544
7630
  finalized.add(assetId);
7545
7631
  return resolved;
7546
7632
  }
7633
+ const anchorId = asset.styleCharacter ?? style.styleCharacter;
7634
+ if (resolved.character?.kind === "base" && anchorId && anchorId !== assetId) {
7635
+ const anchorSpec = await finalize(anchorId);
7636
+ if (!anchorSpec.character || anchorSpec.character.kind === "animation") {
7637
+ throw new Error(`assets.${assetId}: styleCharacter ${anchorId} is not a character base or state`);
7638
+ }
7639
+ const file = memberPath(anchorSpec.outFile, "south", 0, anchorSpec.character.directions, MediaType.PNG);
7640
+ resolved.character = {
7641
+ ...resolved.character,
7642
+ styleAnchor: { assetId: anchorId, spec: anchorSpec, file, sha256: existsSync8(file) ? await sha256File(file) : null }
7643
+ };
7644
+ }
7547
7645
  const characterParent = asset.state?.of ?? asset.animation?.of;
7548
7646
  if (resolved.character && characterParent) {
7549
7647
  const parentSpec = await finalize(characterParent);
@@ -7693,6 +7791,10 @@ async function resolveCharacterShape(asset, style, kind, files) {
7693
7791
  ...style.isometric !== void 0 ? { isometric: style.isometric } : {},
7694
7792
  ...style.enhancePrompt !== void 0 ? { enhancePrompt: style.enhancePrompt } : {}
7695
7793
  };
7794
+ if (asset.concept) {
7795
+ const image = await files.load(asset.concept, "Concept image");
7796
+ shape.concept = { path: path13.resolve(files.root, asset.concept), sha256: image.hash, width: image.width, height: image.height, format: image.format };
7797
+ }
7696
7798
  if (asset.reference) {
7697
7799
  const byDirection = typeof asset.reference === "string" ? { south: asset.reference } : asset.reference;
7698
7800
  shape.reference = {};
@@ -11827,10 +11929,13 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
11827
11929
  try {
11828
11930
  const refs = styleImages.get(spec.styleId) ?? [];
11829
11931
  const parentEntry = spec.character?.parentSpec ? lock.entries[lockKey(spec.character.parentSpec.styleId, spec.character.parentSpec.assetId)] : void 0;
11932
+ const anchor = spec.character?.styleAnchor;
11933
+ const anchorEntry = anchor ? lock.entries[lockKey(anchor.spec.styleId, anchor.spec.assetId)] : void 0;
11830
11934
  const replacedMetadata = previousEntry?.providerMetadata?.[provider.id];
11831
11935
  const { jobId, metadata } = await provider.submit(spec, refs, {
11832
11936
  ...previousJobId ? { previousJobId } : {},
11833
11937
  ...parentEntry?.objectId ? { parentObjectId: parentEntry.objectId } : {},
11938
+ ...anchorEntry?.objectId ? { styleObjectId: anchorEntry.objectId } : {},
11834
11939
  ...replacedMetadata ? { replacedMetadata } : {},
11835
11940
  ...previousMetadata ? { previousMetadata } : {},
11836
11941
  checkpoint: async (checkpoint) => {
@@ -12910,7 +13015,7 @@ var pin_default = {
12910
13015
  godot: "4.7.2",
12911
13016
  extensionsApi: 9,
12912
13017
  protocol: 4,
12913
- release: "editor-pixelorama-v1.2.2-pk.18",
13018
+ release: "editor-pixelorama-v1.2.2-pk.26",
12914
13019
  files: {
12915
13020
  "index.apple-touch-icon.png": {
12916
13021
  sha256: "ee5434873e0fed91e9a888c3ea5a8b79beac25999308be7a6eb41bf4ce640d27",
@@ -12925,7 +13030,7 @@ var pin_default = {
12925
13030
  bytes: 7298
12926
13031
  },
12927
13032
  "index.html": {
12928
- sha256: "d9adf06036660c4b76ef5386e872bc03dc0d5b0628fd6ac660d480893ee1f47a",
13033
+ sha256: "326101ab02bb4a29a7964214c9307c7dff26e9d66b8ebaaf6e7a3987e84a05b2",
12929
13034
  bytes: 5442
12930
13035
  },
12931
13036
  "index.icon.png": {
@@ -12937,8 +13042,8 @@ var pin_default = {
12937
13042
  bytes: 279815
12938
13043
  },
12939
13044
  "index.pck": {
12940
- sha256: "a41d9c3a39aa99ea5ee49ddcedfeb8b092006aa75c0de67139f13297c17f42e4",
12941
- bytes: 6431692
13045
+ sha256: "e26292650bb996965acd1ba70e74d2b4020b063d14f1276701312c1d34cb0a83",
13046
+ bytes: 6431932
12942
13047
  },
12943
13048
  "index.png": {
12944
13049
  sha256: "6e3ca3eb4a99e4c07bdd89ebc873b440ec0ffd66ce1955160da9e7ed80d538d7",
@@ -13557,7 +13662,7 @@ async function runLifecycle(args, wave, spent) {
13557
13662
  log(` Packaging stays blocked until each refined PNG has a current human approval.`);
13558
13663
  }
13559
13664
  }
13560
- return args.command === "gen" && submitted > 0 && total.downloaded > 0 && !total.failed && specs.some((spec) => spec.character?.parentSpec || spec.revision || spec.mirror);
13665
+ return args.command === "gen" && submitted > 0 && total.downloaded > 0 && !total.failed && specs.some((spec) => spec.character?.parentSpec || spec.character?.styleAnchor || spec.revision || spec.mirror);
13561
13666
  }
13562
13667
  return false;
13563
13668
  }