pixelkiln 0.67.0 → 0.68.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
@@ -82,6 +82,8 @@ __export(index_exports, {
82
82
  ManifestEditSchema: () => ManifestEditSchema,
83
83
  ManifestInputSchema: () => ManifestInputSchema,
84
84
  ManifestSchema: () => ManifestSchema,
85
+ MapScenePlacementSchema: () => MapScenePlacementSchema,
86
+ MapSceneSchema: () => MapSceneSchema,
85
87
  MediaType: () => MediaType,
86
88
  OverwriteRefusedError: () => OverwriteRefusedError,
87
89
  PIXEL_ART_FIXER_REVISION: () => PIXEL_ART_FIXER_REVISION,
@@ -521,6 +523,16 @@ var CharacterPortraitSchema = import_zod.z.object({
521
523
  of: import_zod.z.string().min(1),
522
524
  size: CharacterPortraitSizeSchema.default(64)
523
525
  }).strict();
526
+ var MapScenePlacementSchema = import_zod.z.union([
527
+ import_zod.z.object({ oval: import_zod.z.number().min(0.05).max(0.95) }).strict(),
528
+ import_zod.z.object({ rectangle: import_zod.z.number().min(0.05).max(0.95) }).strict(),
529
+ import_zod.z.object({ mask: import_zod.z.string().min(1) }).strict()
530
+ ]);
531
+ var MapSceneSchema = import_zod.z.object({
532
+ /** Manifest-relative PNG of the scene. Any earlier asset's output file works. */
533
+ image: import_zod.z.string().min(1),
534
+ placement: MapScenePlacementSchema.default({ oval: 0.3 })
535
+ }).strict();
524
536
  var CharacterOutfitSchema = import_zod.z.object({
525
537
  /** The loop (a character animation asset) to re-clothe, in the same style. */
526
538
  of: import_zod.z.string().min(1),
@@ -1215,6 +1227,8 @@ var AssetSchema = import_zod.z.object({
1215
1227
  portrait: CharacterPortraitSchema.optional(),
1216
1228
  /** `character` styles: this asset re-clothes an existing loop's frames with a reference outfit. */
1217
1229
  outfit: CharacterOutfitSchema.optional(),
1230
+ /** `map` styles only: draw this object in the style of a scene image. See `MapSceneSchema`. */
1231
+ scene: MapSceneSchema.optional(),
1218
1232
  /** `character` styles, `standard` humanoid bases: this character's proportions, over the style's. */
1219
1233
  proportions: CharacterProportionsSchema.optional(),
1220
1234
  /**
@@ -1291,7 +1305,7 @@ var AssetSchema = import_zod.z.object({
1291
1305
  path: ["revision"]
1292
1306
  });
1293
1307
  }
1294
- const shapes = [asset.revision && "revision", asset.state && "state", asset.animation && "animation", asset.mirror && "mirror", asset.batch && "batch", asset.portrait && "portrait", asset.outfit && "outfit"].filter(Boolean);
1308
+ const shapes = [asset.revision && "revision", asset.state && "state", asset.animation && "animation", asset.mirror && "mirror", asset.batch && "batch", asset.portrait && "portrait", asset.outfit && "outfit", asset.scene && "scene"].filter(Boolean);
1295
1309
  if (shapes.length > 1) {
1296
1310
  context.addIssue({
1297
1311
  code: import_zod.z.ZodIssueCode.custom,
@@ -2087,6 +2101,12 @@ var PixelLabClient = class {
2087
2101
  if (args.shading) body.shading = args.shading;
2088
2102
  if (args.detail) body.detail = args.detail;
2089
2103
  if (args.seed != null) body.seed = args.seed;
2104
+ if (args.scene) {
2105
+ const encode = (image) => ({ type: "base64", base64: image.base64, format: image.format });
2106
+ const placement = args.scene.placement;
2107
+ body.background_image = encode(args.scene.image);
2108
+ body.inpainting = placement.type === "mask" ? { type: "mask", mask_image: encode(placement.mask) } : { type: placement.type, fraction: placement.fraction };
2109
+ }
2090
2110
  return validateResponse(
2091
2111
  MapSubmitSchema,
2092
2112
  await this.request("/map-objects", { method: "POST", body: JSON.stringify(body) }),
@@ -3709,6 +3729,11 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
3709
3729
  uiPieces: spec.uiPieces,
3710
3730
  uiElements: spec.uiElements,
3711
3731
  uiColorPalette: spec.uiColorPalette,
3732
+ // Scene and mask enter as their bytes' hashes, never their paths.
3733
+ scene: spec.scene ? {
3734
+ image: spec.scene.sha256,
3735
+ placement: spec.scene.placement.type === "mask" ? { type: "mask", mask: spec.scene.placement.sha256 } : spec.scene.placement
3736
+ } : void 0,
3712
3737
  styleTraits: spec.styleTraits,
3713
3738
  styleImages: styleImageHashes,
3714
3739
  // A character family: the engine and rotations of a base, the parent's
@@ -3940,6 +3965,8 @@ var PixelLabProvider = class _PixelLabProvider {
3940
3965
  supports(generator) {
3941
3966
  return generator === "1dir" || generator === "map" || generator === "pixflux" || generator === "tiles" || generator === "terrain" || generator === "imagePro" || generator === "character" || generator === "isometricTile" || generator === "objectPro" || generator === "uiAsset" || generator === "uiElement" || generator === "imageProFlash";
3942
3967
  }
3968
+ /** `/map-objects` style matching: `background_image` plus `inpainting`. */
3969
+ supportsMapScene = true;
3943
3970
  /**
3944
3971
  * `inpaint` (`/inpaint-v3`, a mask) and `image-to-image` (`/edit-images-v2`,
3945
3972
  * no mask) both exist on PixelLab, and so do `reduce-colors`
@@ -5085,7 +5112,8 @@ var PixelLabProvider = class _PixelLabProvider {
5085
5112
  outline: spec.outline,
5086
5113
  shading: spec.shading,
5087
5114
  detail: spec.detail,
5088
- seed: spec.seed
5115
+ seed: spec.seed,
5116
+ scene: spec.scene ? mapScenePayload(spec) : void 0
5089
5117
  });
5090
5118
  return { jobId: res.object_id, metadata: { backgroundJobId: res.background_job_id } };
5091
5119
  }
@@ -5962,6 +5990,21 @@ function findObjectAnimation(object, name, direction, animationId) {
5962
5990
  }
5963
5991
  return null;
5964
5992
  }
5993
+ function mapScenePayload(spec) {
5994
+ const scene = spec.scene;
5995
+ const read = (file, hash, label) => {
5996
+ const bytes = (0, import_node_fs2.readFileSync)(file);
5997
+ if (!hash || sha256(bytes) !== hash) {
5998
+ throw new Error(`${spec.styleId}/${spec.assetId}: ${label} changed after the manifest was resolved`);
5999
+ }
6000
+ return { base64: bytes.toString("base64"), format: "png" };
6001
+ };
6002
+ const placement = scene.placement;
6003
+ return {
6004
+ image: read(scene.file, scene.sha256, "scene image"),
6005
+ placement: placement.type === "mask" ? { type: "mask", mask: read(placement.file, placement.sha256, "scene mask") } : placement
6006
+ };
6007
+ }
5965
6008
 
5966
6009
  // src/providers/retrodiffusion.ts
5967
6010
  var DEFAULT_BASE_URL = "https://api.retrodiffusion.ai/v1";
@@ -7741,6 +7784,7 @@ var FakeProvider = class {
7741
7784
  supports(generator) {
7742
7785
  return generator === "1dir" || generator === "map" || generator === "character";
7743
7786
  }
7787
+ supportsMapScene = true;
7744
7788
  supportsRevision(_mode) {
7745
7789
  return true;
7746
7790
  }
@@ -9071,6 +9115,7 @@ async function resolveSpecs(loaded, filter) {
9071
9115
  };
9072
9116
  }
9073
9117
  }
9118
+ if (asset.scene) resolved.scene = await resolveMapScene(root, styleId, assetId, resolved.generator, asset.scene, activeProvider);
9074
9119
  resolved.specHash = specHash(
9075
9120
  resolved,
9076
9121
  styleImageHashes,
@@ -9129,6 +9174,32 @@ function pngPath(file) {
9129
9174
  const extension = import_node_path5.default.extname(file);
9130
9175
  return extension ? `${file.slice(0, -extension.length)}.png` : `${file}.png`;
9131
9176
  }
9177
+ async function resolveMapScene(root, styleId, assetId, generator, scene, provider) {
9178
+ if (generator !== "map") {
9179
+ throw new Error(`assets.${assetId}.scene: only a map object can be drawn into a scene; style "${styleId}" uses "${generator}"`);
9180
+ }
9181
+ if (!provider.supportsMapScene) {
9182
+ throw new Error(`assets.${assetId}.scene: provider "${provider.id}" cannot draw into a scene; PixelLab's /map-objects can`);
9183
+ }
9184
+ const file = import_node_path5.default.resolve(root, scene.image);
9185
+ const image = await optionalRevisionImage(file, "scene image", "png");
9186
+ const base = { file, sha256: image?.hash ?? null, width: image?.width ?? null, height: image?.height ?? null };
9187
+ const placement = scene.placement;
9188
+ if ("mask" in placement) {
9189
+ const maskFile = import_node_path5.default.resolve(root, placement.mask);
9190
+ const mask = await optionalRevisionImage(maskFile, "scene mask", "png");
9191
+ if (image && mask && (image.width !== mask.width || image.height !== mask.height)) {
9192
+ throw new Error(
9193
+ `assets.${assetId}.scene: the mask is ${mask.width}x${mask.height}; the scene ${scene.image} is ${image.width}x${image.height}`
9194
+ );
9195
+ }
9196
+ return {
9197
+ ...base,
9198
+ placement: { type: "mask", file: maskFile, sha256: mask?.hash ?? null, width: mask?.width ?? null, height: mask?.height ?? null }
9199
+ };
9200
+ }
9201
+ return "oval" in placement ? { ...base, placement: { type: "oval", fraction: placement.oval } } : { ...base, placement: { type: "rectangle", fraction: placement.rectangle } };
9202
+ }
9132
9203
  async function optionalRevisionImage(file, label, requiredFormat) {
9133
9204
  if (!(0, import_node_fs5.existsSync)(file)) return null;
9134
9205
  const bytes = await (0, import_promises3.readFile)(file);
@@ -10799,6 +10870,7 @@ async function inspectRevisionReadiness(spec, lock) {
10799
10870
  if (spec.character?.parentSpec) return inspectCharacterParent(spec, lock, /* @__PURE__ */ new Set());
10800
10871
  if (spec.character?.styleAnchor) return inspectStyleAnchor(spec, lock, /* @__PURE__ */ new Set());
10801
10872
  if (spec.objectPro?.parentSpec) return inspectObjectProParent(spec, lock, /* @__PURE__ */ new Set());
10873
+ if (spec.scene) return inspectScene(spec);
10802
10874
  if (!spec.revision) return null;
10803
10875
  return inspectRevision(spec, lock, /* @__PURE__ */ new Set());
10804
10876
  }
@@ -10816,6 +10888,16 @@ async function inspectStyleAnchor(spec, lock, seen) {
10816
10888
  }
10817
10889
  return { ready: true, reason: `${label} is current` };
10818
10890
  }
10891
+ async function inspectScene(spec) {
10892
+ const scene = spec.scene;
10893
+ const files = [{ label: "scene image", file: scene.file, sha256: scene.sha256 }];
10894
+ if (scene.placement.type === "mask") files.push({ label: "scene mask", file: scene.placement.file, sha256: scene.placement.sha256 });
10895
+ for (const { label, file, sha256: sha2562 } of files) {
10896
+ if (!sha2562 || !(0, import_node_fs8.existsSync)(file)) return { ready: false, reason: `${label} does not exist yet: ${file}` };
10897
+ if (await sha256File(file) !== sha2562) return { ready: false, reason: `${label} changed after the manifest was resolved` };
10898
+ }
10899
+ return { ready: true, reason: "scene is current" };
10900
+ }
10819
10901
  async function inspectMirror(spec, lock, seen) {
10820
10902
  const source = spec.mirror.sourceSpec;
10821
10903
  const label = `mirror source ${spec.styleId}/${spec.mirror.sourceAssetId}`;
@@ -18185,6 +18267,8 @@ async function workspaceStatus(ws, dir) {
18185
18267
  ManifestEditSchema,
18186
18268
  ManifestInputSchema,
18187
18269
  ManifestSchema,
18270
+ MapScenePlacementSchema,
18271
+ MapSceneSchema,
18188
18272
  MediaType,
18189
18273
  OverwriteRefusedError,
18190
18274
  PIXEL_ART_FIXER_REVISION,