pixelkiln 0.53.4 → 0.54.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
@@ -653,6 +653,8 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
653
653
  tileView: spec.tileView,
654
654
  tileFeature: spec.tileFeature,
655
655
  outlineMode: spec.outlineMode,
656
+ isometricTileSize: spec.isometricTileSize,
657
+ isometricTileShape: spec.isometricTileShape,
656
658
  styleImages: styleImageHashes,
657
659
  // A character family: the engine and rotations of a base, the parent's
658
660
  // generated bytes for a state or animation (so a regenerated parent
@@ -707,7 +709,7 @@ import path2 from "path";
707
709
  // src/types.ts
708
710
  import { z } from "zod";
709
711
  var MediaTypeSchema = z.enum(["image/png", "image/gif"]);
710
- var GeneratorSchema = z.enum(["1dir", "map", "pixflux", "tiles", "animation", "frames", "character", "terrain", "imagePro"]);
712
+ var GeneratorSchema = z.enum(["1dir", "map", "pixflux", "tiles", "animation", "frames", "character", "terrain", "imagePro", "isometricTile"]);
711
713
  var GridConfidenceSchema = z.enum(["low", "medium", "high"]);
712
714
  var RevisionModeSchema = z.enum(["image-to-image", "inpaint", "outpaint"]);
713
715
  function tileVariationCount(descriptions) {
@@ -741,6 +743,9 @@ function tilesCost(tileSize, variations) {
741
743
  if (px <= 2048) return 25;
742
744
  return 40;
743
745
  }
746
+ function isometricTileCost() {
747
+ return 1;
748
+ }
744
749
  function terrainTileCount(transitionSize) {
745
750
  return transitionSize === 1 ? 25 : 16;
746
751
  }
@@ -1070,6 +1075,20 @@ var StyleObjectSchema = z.object({
1070
1075
  /** `terrain` generator only. Camera angle; the API default is `high
1071
1076
  * top-down`. */
1072
1077
  terrainView: z.enum(["low top-down", "high top-down"]).optional(),
1078
+ /**
1079
+ * `isometricTile` generator only. `/create-isometric-tile`'s own tile
1080
+ * grid size, distinct from `size` (the generation canvas, 16-64px — the
1081
+ * API's own guidance is that sizes above 24px "often produce better
1082
+ * quality results"). The API default is 16.
1083
+ */
1084
+ isometricTileSize: z.union([z.literal(16), z.literal(32)]).optional(),
1085
+ /**
1086
+ * `isometricTile` generator only. Vertical thickness, i.e. how much
1087
+ * height/elevation variation the tile can represent on a map: `"thin
1088
+ * tile"` (~15% of canvas height), `"thick tile"` (~25%), or `"block"`
1089
+ * (~50%, the API default).
1090
+ */
1091
+ isometricTileShape: z.enum(["thin tile", "thick tile", "block"]).optional(),
1073
1092
  /**
1074
1093
  * `pixflux` only. Whether to strip the generated background.
1075
1094
  *
@@ -5249,6 +5268,15 @@ var TilesetGetSchema = z2.object({
5249
5268
  }).passthrough(),
5250
5269
  usage: UsageSchema
5251
5270
  }).passthrough();
5271
+ var IsometricTileSubmitSchema = z2.object({
5272
+ tile_id: z2.string().min(1),
5273
+ background_job_id: z2.string().min(1),
5274
+ status: z2.literal("processing").default("processing")
5275
+ }).passthrough();
5276
+ var IsometricTileGetSchema = z2.object({
5277
+ image: z2.object({ base64: z2.string().min(1), format: z2.string().default("png") }).passthrough(),
5278
+ usage: UsageSchema
5279
+ }).passthrough();
5252
5280
  var RevisionJobSubmitSchema = z2.object({
5253
5281
  background_job_id: z2.string().min(1),
5254
5282
  status: z2.string().default("processing")
@@ -5534,6 +5562,33 @@ var PixelLabClient = class {
5534
5562
  );
5535
5563
  return { tileset: res.tileset, usage: res.usage ?? null };
5536
5564
  }
5565
+ /** A single standalone isometric tile — no connectable set, no candidates. */
5566
+ async createIsometricTile(args) {
5567
+ const body = { description: args.description };
5568
+ if (args.imageWidth != null && args.imageHeight != null) {
5569
+ body.image_size = { width: args.imageWidth, height: args.imageHeight };
5570
+ }
5571
+ if (args.tileSize != null) body.isometric_tile_size = args.tileSize;
5572
+ if (args.tileShape) body.isometric_tile_shape = args.tileShape;
5573
+ if (args.outline) body.outline = args.outline;
5574
+ if (args.shading) body.shading = args.shading;
5575
+ if (args.detail) body.detail = args.detail;
5576
+ if (args.seed != null) body.seed = args.seed;
5577
+ return validateResponse(
5578
+ IsometricTileSubmitSchema,
5579
+ await this.request("/create-isometric-tile", { method: "POST", body: JSON.stringify(body) }),
5580
+ "create isometric tile"
5581
+ );
5582
+ }
5583
+ /** Throws PixelLabError(423) while the tile is still processing. */
5584
+ async getIsometricTile(tileId) {
5585
+ const res = await validateResponse(
5586
+ IsometricTileGetSchema,
5587
+ await this.request(`/isometric-tiles/${tileId}`),
5588
+ "get isometric tile"
5589
+ );
5590
+ return { image: res.image, usage: res.usage ?? null };
5591
+ }
5537
5592
  /**
5538
5593
  * Synchronous single-image generation. Returns the PNG inline rather than a
5539
5594
  * job id, and is the only endpoint that honours a forced palette;
@@ -6000,7 +6055,7 @@ var PixelLabProvider = class _PixelLabProvider {
6000
6055
  return new _PixelLabProvider(new PixelLabClient("download-only"));
6001
6056
  }
6002
6057
  supports(generator) {
6003
- return generator === "1dir" || generator === "map" || generator === "pixflux" || generator === "tiles" || generator === "terrain" || generator === "imagePro" || generator === "character";
6058
+ return generator === "1dir" || generator === "map" || generator === "pixflux" || generator === "tiles" || generator === "terrain" || generator === "imagePro" || generator === "character" || generator === "isometricTile";
6004
6059
  }
6005
6060
  /**
6006
6061
  * `inpaint` (`/inpaint-v3`, a mask) and `image-to-image` (`/edit-images-v2`,
@@ -6036,7 +6091,7 @@ var PixelLabProvider = class _PixelLabProvider {
6036
6091
  const height = spec.revision.sourceHeight ?? spec.height;
6037
6092
  return { unit: "generations", amount: generationCost(width, height, "1dir"), candidates: 1 };
6038
6093
  }
6039
- if (spec.generator === "tiles" || spec.generator === "terrain") {
6094
+ if (spec.generator === "tiles" || spec.generator === "terrain" || spec.generator === "isometricTile") {
6040
6095
  return { unit: "generations", amount: spec.cost, candidates: spec.candidates };
6041
6096
  }
6042
6097
  if (spec.generator === "character") {
@@ -6067,19 +6122,19 @@ var PixelLabProvider = class _PixelLabProvider {
6067
6122
  );
6068
6123
  }
6069
6124
  if (spec.generator === "map") {
6070
- requirePixelLabOption("view", spec.view, ["low top-down", "high top-down", "side"]);
6071
- requirePixelLabOption("outline", spec.outline, [
6125
+ requirePixelLabOption("map", "view", spec.view, ["low top-down", "high top-down", "side"]);
6126
+ requirePixelLabOption("map", "outline", spec.outline, [
6072
6127
  "single color outline",
6073
6128
  "selective outline",
6074
6129
  "lineless"
6075
6130
  ]);
6076
- requirePixelLabOption("shading", spec.shading, [
6131
+ requirePixelLabOption("map", "shading", spec.shading, [
6077
6132
  "flat shading",
6078
6133
  "basic shading",
6079
6134
  "medium shading",
6080
6135
  "detailed shading"
6081
6136
  ]);
6082
- requirePixelLabOption("detail", spec.detail, [
6137
+ requirePixelLabOption("map", "detail", spec.detail, [
6083
6138
  "low detail",
6084
6139
  "medium detail",
6085
6140
  "high detail"
@@ -6087,7 +6142,7 @@ var PixelLabProvider = class _PixelLabProvider {
6087
6142
  }
6088
6143
  if (spec.generator === "terrain") {
6089
6144
  if (spec.outline) {
6090
- requirePixelLabOption("outline", spec.outline, [
6145
+ requirePixelLabOption("terrain", "outline", spec.outline, [
6091
6146
  "single color black outline",
6092
6147
  "single color outline",
6093
6148
  "selective outline",
@@ -6095,7 +6150,7 @@ var PixelLabProvider = class _PixelLabProvider {
6095
6150
  ]);
6096
6151
  }
6097
6152
  if (spec.shading) {
6098
- requirePixelLabOption("shading", spec.shading, [
6153
+ requirePixelLabOption("terrain", "shading", spec.shading, [
6099
6154
  "flat shading",
6100
6155
  "basic shading",
6101
6156
  "medium shading",
@@ -6104,7 +6159,7 @@ var PixelLabProvider = class _PixelLabProvider {
6104
6159
  ]);
6105
6160
  }
6106
6161
  if (spec.detail) {
6107
- requirePixelLabOption("detail", spec.detail, ["low detail", "medium detail", "highly detailed"]);
6162
+ requirePixelLabOption("terrain", "detail", spec.detail, ["low detail", "medium detail", "highly detailed"]);
6108
6163
  }
6109
6164
  if (spec.terrainShapeStyle && spec.terrainMode === "pro") {
6110
6165
  throw new Error(
@@ -6125,6 +6180,30 @@ var PixelLabProvider = class _PixelLabProvider {
6125
6180
  );
6126
6181
  }
6127
6182
  }
6183
+ if (spec.generator === "isometricTile") {
6184
+ if (spec.outline) {
6185
+ requirePixelLabOption("isometricTile", "outline", spec.outline, [
6186
+ "single color outline",
6187
+ "selective outline",
6188
+ "lineless"
6189
+ ]);
6190
+ }
6191
+ if (spec.shading) {
6192
+ requirePixelLabOption("isometricTile", "shading", spec.shading, [
6193
+ "flat shading",
6194
+ "basic shading",
6195
+ "medium shading",
6196
+ "detailed shading",
6197
+ "highly detailed shading"
6198
+ ]);
6199
+ }
6200
+ if (spec.detail) {
6201
+ requirePixelLabOption("isometricTile", "detail", spec.detail, ["low detail", "medium detail", "highly detailed"]);
6202
+ }
6203
+ if (spec.width < 16 || spec.width > 64) {
6204
+ throw new Error(`PixelLab isometricTile: size must be 16 to 64 pixels, got ${spec.width}`);
6205
+ }
6206
+ }
6128
6207
  if (spec.generator === "character") this.validateCharacter(spec, styleImages);
6129
6208
  if ((spec.generator === "map" || spec.generator === "pixflux") && styleImages.length) {
6130
6209
  throw new Error(`PixelLab ${spec.generator} does not support style images`);
@@ -6139,6 +6218,11 @@ var PixelLabProvider = class _PixelLabProvider {
6139
6218
  "PixelLab imagePro does not support style images yet; /generate-image-v2's own reference_images and style_image are not modeled here"
6140
6219
  );
6141
6220
  }
6221
+ if (spec.generator === "isometricTile" && styleImages.length) {
6222
+ throw new Error(
6223
+ "PixelLab isometricTile does not support style images; /create-isometric-tile has no such field (its own init_image/color_image are not modeled here yet)"
6224
+ );
6225
+ }
6142
6226
  for (const image of styleImages) {
6143
6227
  if (image.width > 256 || image.height > 256) {
6144
6228
  throw new Error(
@@ -6521,6 +6605,20 @@ var PixelLabProvider = class _PixelLabProvider {
6521
6605
  });
6522
6606
  return { jobId: res2.tileset_id, metadata: { backgroundJobId: res2.background_job_id } };
6523
6607
  }
6608
+ if (spec.generator === "isometricTile") {
6609
+ const res2 = await this.client.createIsometricTile({
6610
+ description: spec.prompt,
6611
+ imageWidth: spec.width,
6612
+ imageHeight: spec.height,
6613
+ tileSize: spec.isometricTileSize,
6614
+ tileShape: spec.isometricTileShape,
6615
+ outline: spec.outline,
6616
+ shading: spec.shading,
6617
+ detail: spec.detail,
6618
+ seed: spec.seed
6619
+ });
6620
+ return { jobId: res2.tile_id, metadata: { backgroundJobId: res2.background_job_id } };
6621
+ }
6524
6622
  if (spec.generator === "imagePro") {
6525
6623
  const res2 = await this.client.createImagePro({
6526
6624
  description: spec.prompt,
@@ -6637,6 +6735,7 @@ var PixelLabProvider = class _PixelLabProvider {
6637
6735
  if (generator === "map") return this.pollMap(jobId, context);
6638
6736
  if (generator === "tiles") return this.pollTiles(jobId, Boolean(context?.tileFeature), context);
6639
6737
  if (generator === "terrain") return this.pollTerrain(jobId, context);
6738
+ if (generator === "isometricTile") return this.pollIsometricTile(jobId, context);
6640
6739
  if (generator === "imagePro") return this.pollImagePro(jobId);
6641
6740
  if (generator === "character") return this.pollCharacter(jobId, context);
6642
6741
  const backgroundJobId = context?.metadata?.backgroundJobId;
@@ -6847,6 +6946,31 @@ var PixelLabProvider = class _PixelLabProvider {
6847
6946
  throw err;
6848
6947
  }
6849
6948
  }
6949
+ /**
6950
+ * `/create-isometric-tile` reports progress the same way `/create-tileset`
6951
+ * does (423 while drawing, 200 once finished, no `status` field to poll),
6952
+ * and its image also comes back embedded as base64 rather than a
6953
+ * `storage_urls` link. One call is one tile with nothing to review, so
6954
+ * this goes straight to "ready" like a connectable `tiles`/`terrain` set.
6955
+ */
6956
+ async pollIsometricTile(tileId, context) {
6957
+ try {
6958
+ const { image, usage } = await this.client.getIsometricTile(tileId);
6959
+ const backgroundJobId = context?.metadata?.backgroundJobId;
6960
+ const billed = billedFromUsage(usage) ?? await this.billedForJob(backgroundJobId);
6961
+ const file = path12.join(_PixelLabProvider.cacheDir(), `${tileId}.png`);
6962
+ writeFileSync(file, Buffer.from(image.base64, "base64"));
6963
+ return {
6964
+ status: "ready",
6965
+ objectId: tileId,
6966
+ sourceUrl: `file://${file}`,
6967
+ billed
6968
+ };
6969
+ } catch (err) {
6970
+ if (err instanceof PixelLabError && err.status === 423) return { status: "processing" };
6971
+ throw err;
6972
+ }
6973
+ }
6850
6974
  async selectCandidate(jobId, index, commonTag, generator) {
6851
6975
  if (generator === "tiles") {
6852
6976
  const set = await this.client.getTilesPro(jobId);
@@ -6963,9 +7087,9 @@ function remoteCharacter(character) {
6963
7087
  animationCount: character.animation_count
6964
7088
  };
6965
7089
  }
6966
- function requirePixelLabOption(name, value, allowed) {
7090
+ function requirePixelLabOption(generator, name, value, allowed) {
6967
7091
  if (value != null && !allowed.includes(value)) {
6968
- throw new Error(`PixelLab map ${name} must be one of: ${allowed.join(", ")}`);
7092
+ throw new Error(`PixelLab ${generator} ${name} must be one of: ${allowed.join(", ")}`);
6969
7093
  }
6970
7094
  }
6971
7095
  function tilesInIndexOrder(urls) {
@@ -8206,6 +8330,10 @@ async function resolveSpecs(loaded, filter) {
8206
8330
  size = style.terrainTileSize ?? 16;
8207
8331
  width = size;
8208
8332
  height = size;
8333
+ } else if (generator === "isometricTile") {
8334
+ size = asset.size ?? style.size ?? 32;
8335
+ width = size;
8336
+ height = size;
8209
8337
  } else {
8210
8338
  width = asset.width ?? style.size ?? 64;
8211
8339
  height = asset.height ?? style.size ?? 64;
@@ -8299,8 +8427,10 @@ async function resolveSpecs(loaded, filter) {
8299
8427
  terrainRaggedness: generator === "terrain" ? style.terrainRaggedness : void 0,
8300
8428
  terrainTransitionSize: generator === "terrain" ? style.terrainTransitionSize : void 0,
8301
8429
  terrainView: generator === "terrain" ? style.terrainView : void 0,
8430
+ isometricTileSize: generator === "isometricTile" ? style.isometricTileSize : void 0,
8431
+ isometricTileShape: generator === "isometricTile" ? style.isometricTileShape : void 0,
8302
8432
  ...generator === "character" ? { character: await resolveCharacterShape(asset, style, characterKind, { root, load: loadStyleImage }) } : {},
8303
- cost: generator === "tiles" ? tilesCost(tileSize, tileVariations) : generator === "terrain" ? tilesCost(size, terrainTiles) : generationCost(width, height, generator),
8433
+ cost: generator === "tiles" ? tilesCost(tileSize, tileVariations) : generator === "terrain" ? tilesCost(size, terrainTiles) : generator === "isometricTile" ? isometricTileCost() : generationCost(width, height, generator),
8304
8434
  costUnit: "generations",
8305
8435
  candidates: generator === "tiles" ? tileVariations : generator === "terrain" ? terrainTiles : generator === "1dir" || generator === "imagePro" ? candidateCount(size) : 1
8306
8436
  };