pixelkiln 0.59.0 → 0.60.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
@@ -645,16 +645,20 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
645
645
  detail: spec.detail ?? null,
646
646
  seed: spec.seed ?? null,
647
647
  palette: spec.palette,
648
- // `noBackground` only reaches the wire for pixflux; the tile fields are
649
- // undefined for every other generator. `tileSize` is intentionally
650
- // absent; width/height are derived from it, so it is already covered.
651
- noBackground: spec.generator === "pixflux" || spec.provider !== "pixellab" ? spec.noBackground : void 0,
648
+ // `noBackground` only reaches the wire for pixflux and uiAsset; the
649
+ // tile fields are undefined for every other generator. `tileSize` is
650
+ // intentionally absent; width/height are derived from it, so it is
651
+ // already covered.
652
+ noBackground: spec.generator === "pixflux" || spec.generator === "uiAsset" || spec.provider !== "pixellab" ? spec.noBackground : void 0,
652
653
  tileType: spec.tileType,
653
654
  tileView: spec.tileView,
654
655
  tileFeature: spec.tileFeature,
655
656
  outlineMode: spec.outlineMode,
656
657
  isometricTileSize: spec.isometricTileSize,
657
658
  isometricTileShape: spec.isometricTileShape,
659
+ uiPieces: spec.uiPieces,
660
+ uiElements: spec.uiElements,
661
+ uiColorPalette: spec.uiColorPalette,
658
662
  styleImages: styleImageHashes,
659
663
  // A character family: the engine and rotations of a base, the parent's
660
664
  // generated bytes for a state or animation (so a regenerated parent
@@ -740,7 +744,7 @@ import path2 from "path";
740
744
  // src/types.ts
741
745
  import { z } from "zod";
742
746
  var MediaTypeSchema = z.enum(["image/png", "image/gif"]);
743
- var GeneratorSchema = z.enum(["1dir", "map", "pixflux", "tiles", "animation", "frames", "character", "terrain", "imagePro", "isometricTile", "objectPro"]);
747
+ var GeneratorSchema = z.enum(["1dir", "map", "pixflux", "tiles", "animation", "frames", "character", "terrain", "imagePro", "isometricTile", "objectPro", "uiAsset"]);
744
748
  var GridConfidenceSchema = z.enum(["low", "medium", "high"]);
745
749
  var RevisionModeSchema = z.enum([
746
750
  "image-to-image",
@@ -862,6 +866,50 @@ var CharacterProportionsSchema = z.union([
862
866
  }).strict()
863
867
  ]);
864
868
  var CharacterAnimationModeSchema = z.enum(["template", "v3", "pro"]);
869
+ var UiPieceSchema = z.discriminatedUnion("kind", [
870
+ z.object({
871
+ id: z.string().min(1),
872
+ kind: z.literal("rounded_rect"),
873
+ label: z.string().optional(),
874
+ x: z.number(),
875
+ y: z.number(),
876
+ w: z.number(),
877
+ h: z.number(),
878
+ radius: z.number().min(0).optional()
879
+ }).strict(),
880
+ z.object({
881
+ id: z.string().min(1),
882
+ kind: z.literal("circle"),
883
+ label: z.string().optional(),
884
+ x: z.number(),
885
+ y: z.number(),
886
+ r: z.number()
887
+ }).strict(),
888
+ z.object({
889
+ id: z.string().min(1),
890
+ kind: z.literal("polygon"),
891
+ label: z.string().optional(),
892
+ x: z.number(),
893
+ y: z.number(),
894
+ r: z.number(),
895
+ sides: z.number().int().min(3),
896
+ phase: z.number().optional()
897
+ }).strict()
898
+ ]);
899
+ var UiElementSchema = z.enum([
900
+ "button",
901
+ "icon_button",
902
+ "toolbar",
903
+ "tab",
904
+ "panel",
905
+ "window",
906
+ "health_bar",
907
+ "avatar",
908
+ "triangle",
909
+ "pentagon",
910
+ "hexagon",
911
+ "octagon"
912
+ ]);
865
913
  var AssetBatchSchema = z.object({
866
914
  /** The batch leader: another `1dir` asset in the same style. */
867
915
  of: z.string().min(1),
@@ -1164,6 +1212,12 @@ var StyleObjectSchema = z.object({
1164
1212
  * in place of `terrainShapeStyle`. The API default is `standard`.
1165
1213
  */
1166
1214
  terrainMode: z.enum(["standard", "pro"]).optional(),
1215
+ /**
1216
+ * `uiAsset` generator only. A natural-language palette hint sent as-is
1217
+ * (e.g. "brown and gold"), distinct from `palette`'s hex-color array —
1218
+ * this is a prompt-level steer, not a local quantization target.
1219
+ */
1220
+ uiColorPalette: z.string().max(200).optional(),
1167
1221
  /**
1168
1222
  * `terrain` generator only, `terrainMode: "standard"`. Procedural
1169
1223
  * boundary geometry: `square` or `round`, 16px or 32px tiles only.
@@ -1447,6 +1501,10 @@ var AssetSchema = z.object({
1447
1501
  * `AssetBatchSchema`.
1448
1502
  */
1449
1503
  batch: AssetBatchSchema.optional(),
1504
+ /** `uiAsset` styles: precise shape regions the panel is composited from. Combine with `elements`; omit both for a default full-canvas panel. */
1505
+ pieces: z.array(UiPieceSchema).min(1).optional(),
1506
+ /** `uiAsset` styles: named, auto-positioned UI element scaffolds. Combine with `pieces`; omit both for a default full-canvas panel. */
1507
+ elements: z.array(UiElementSchema).min(1).optional(),
1450
1508
  /** `character` styles, `standard` humanoid bases: this character's proportions, over the style's. */
1451
1509
  proportions: CharacterProportionsSchema.optional(),
1452
1510
  /**
@@ -5476,6 +5534,19 @@ var IsometricTileGetSchema = z2.object({
5476
5534
  image: z2.object({ base64: z2.string().min(1), format: z2.string().default("png") }).passthrough(),
5477
5535
  usage: UsageSchema
5478
5536
  }).passthrough();
5537
+ var UiAssetSubmitSchema = z2.object({
5538
+ ui_asset_id: z2.string().min(1),
5539
+ background_job_id: z2.string().min(1),
5540
+ status: z2.string().default("processing"),
5541
+ usage: UsageSchema
5542
+ }).passthrough();
5543
+ var UiAssetGetSchema = z2.object({
5544
+ id: z2.string().min(1),
5545
+ status: z2.string().nullable().default(null),
5546
+ image_url: z2.string().nullable().optional(),
5547
+ progress_percent: z2.number().nullable().optional(),
5548
+ eta_seconds: z2.number().nullable().optional()
5549
+ }).passthrough();
5479
5550
  var RevisionJobSubmitSchema = z2.object({
5480
5551
  background_job_id: z2.string().min(1),
5481
5552
  status: z2.string().default("processing")
@@ -5836,6 +5907,48 @@ var PixelLabClient = class {
5836
5907
  );
5837
5908
  return { image: res.image, usage: res.usage ?? null };
5838
5909
  }
5910
+ /**
5911
+ * `/create-ui-asset`: one composited panel image from a shape template —
5912
+ * `pieces` (precise rect/circle/polygon regions on a virtual 0–512
5913
+ * editor canvas) and/or named `elements` (auto-positioned scaffolds:
5914
+ * button, icon_button, toolbar, tab, panel, window, health_bar, avatar,
5915
+ * triangle, pentagon, hexagon, octagon), or neither for a default
5916
+ * full-canvas rounded-rect panel. Unlike every other generator here, the
5917
+ * result is one flat image with no per-piece sub-regions or nine-slice
5918
+ * metadata returned — cropping a `pieces` layout into separate files
5919
+ * would be pixelkiln's own local work, not modeled yet.
5920
+ */
5921
+ async createUiAsset(args) {
5922
+ const body = {
5923
+ description: args.description,
5924
+ image_size: { width: args.width, height: args.height }
5925
+ };
5926
+ if (args.pieces?.length) body.pieces = args.pieces;
5927
+ if (args.elements?.length) body.elements = args.elements;
5928
+ if (args.styleImage) body.style_image = args.styleImage;
5929
+ if (args.colorPalette) body.color_palette = args.colorPalette;
5930
+ if (args.noBackground != null) body.no_background = args.noBackground;
5931
+ if (args.seed != null) body.seed = args.seed;
5932
+ const res = await validateResponse(
5933
+ UiAssetSubmitSchema,
5934
+ await this.request("/create-ui-asset", { method: "POST", body: JSON.stringify(body) }),
5935
+ "create ui asset"
5936
+ );
5937
+ return { ui_asset_id: res.ui_asset_id, background_job_id: res.background_job_id, usage: res.usage };
5938
+ }
5939
+ async getUiAsset(uiAssetId) {
5940
+ const res = await validateResponse(
5941
+ UiAssetGetSchema,
5942
+ await this.request(`/ui-assets/${encodeURIComponent(uiAssetId)}`),
5943
+ "get ui asset"
5944
+ );
5945
+ return {
5946
+ status: res.status,
5947
+ imageUrl: res.image_url ?? null,
5948
+ progressPercent: res.progress_percent ?? null,
5949
+ etaSeconds: res.eta_seconds ?? null
5950
+ };
5951
+ }
5839
5952
  /**
5840
5953
  * Synchronous single-image generation. Returns the PNG inline rather than a
5841
5954
  * job id, and is the only endpoint that honours a forced palette;
@@ -6506,7 +6619,7 @@ var PixelLabProvider = class _PixelLabProvider {
6506
6619
  return new _PixelLabProvider(new PixelLabClient("download-only"));
6507
6620
  }
6508
6621
  supports(generator) {
6509
- return generator === "1dir" || generator === "map" || generator === "pixflux" || generator === "tiles" || generator === "terrain" || generator === "imagePro" || generator === "character" || generator === "isometricTile" || generator === "objectPro";
6622
+ return generator === "1dir" || generator === "map" || generator === "pixflux" || generator === "tiles" || generator === "terrain" || generator === "imagePro" || generator === "character" || generator === "isometricTile" || generator === "objectPro" || generator === "uiAsset";
6510
6623
  }
6511
6624
  /**
6512
6625
  * `inpaint` (`/inpaint-v3`, a mask) and `image-to-image` (`/edit-images-v2`,
@@ -6622,6 +6735,11 @@ var PixelLabProvider = class _PixelLabProvider {
6622
6735
  `PixelLab imagePro is ${spec.width}x${spec.height}; the API takes 16 to 792 wide and 16 to 688 tall (the exact ceiling also depends on aspect ratio)`
6623
6736
  );
6624
6737
  }
6738
+ if (spec.generator === "uiAsset" && (spec.width < 192 || spec.height < 192 || spec.width > 688 || spec.height > 688)) {
6739
+ throw new Error(
6740
+ `PixelLab uiAsset is ${spec.width}x${spec.height}; the API takes 192 to 688 pixels per side (the exact ceiling also depends on aspect ratio \u2014 square tops out at 512, 16:9 at 688x384, 9:16 at 384x688, 4:3 at 600x448, 3:4 at 448x600)`
6741
+ );
6742
+ }
6625
6743
  if (spec.generator === "map") {
6626
6744
  requirePixelLabOption("map", "view", spec.view, ["low top-down", "high top-down", "side"]);
6627
6745
  requirePixelLabOption("map", "outline", spec.outline, [
@@ -7259,6 +7377,20 @@ var PixelLabProvider = class _PixelLabProvider {
7259
7377
  if (spec.revision) return this.submitRevision(spec);
7260
7378
  if (spec.generator === "character") return this.submitCharacter(spec, styleImages, context);
7261
7379
  if (spec.generator === "objectPro") return this.submitObjectPro(spec, styleImages, context);
7380
+ if (spec.generator === "uiAsset") {
7381
+ const res2 = await this.client.createUiAsset({
7382
+ description: spec.prompt,
7383
+ width: spec.width,
7384
+ height: spec.height,
7385
+ pieces: spec.uiPieces,
7386
+ elements: spec.uiElements,
7387
+ styleImage: styleImages[0] ? { base64: styleImages[0].base64, format: styleImages[0].format } : void 0,
7388
+ colorPalette: spec.uiColorPalette,
7389
+ noBackground: spec.noBackground,
7390
+ seed: spec.seed
7391
+ });
7392
+ return { jobId: res2.ui_asset_id, metadata: { backgroundJobId: res2.background_job_id } };
7393
+ }
7262
7394
  if (spec.generator === "pixflux") {
7263
7395
  const swatch = spec.palette.length ? paletteSwatch(spec.palette).toString("base64") : void 0;
7264
7396
  const { png } = await this.client.createImagePixflux({
@@ -7535,6 +7667,7 @@ var PixelLabProvider = class _PixelLabProvider {
7535
7667
  if (generator === "imagePro") return this.pollImagePro(jobId);
7536
7668
  if (generator === "character") return this.pollCharacter(jobId, context);
7537
7669
  if (generator === "objectPro") return this.pollObjectPro(jobId, context);
7670
+ if (generator === "uiAsset") return this.pollUiAsset(jobId, context);
7538
7671
  const backgroundJobId = context?.metadata?.backgroundJobId;
7539
7672
  const obj = await this.client.getObject(jobId);
7540
7673
  if (obj.status === "review") {
@@ -7854,6 +7987,29 @@ var PixelLabProvider = class _PixelLabProvider {
7854
7987
  throw err;
7855
7988
  }
7856
7989
  }
7990
+ /**
7991
+ * `/ui-assets/{id}` reports progress with its own `status` field
7992
+ * ("processing"/"completed"/"failed") rather than a 423, and its result is
7993
+ * one flat composited image at a hosted `image_url` — no per-piece
7994
+ * sub-images, no nine-slice metadata, confirmed absent from the response
7995
+ * schema. There is nothing to review: one job is one ready image, like
7996
+ * `isometricTile`.
7997
+ */
7998
+ async pollUiAsset(uiAssetId, context) {
7999
+ const asset = await this.client.getUiAsset(uiAssetId);
8000
+ if (asset.status === "failed") return { status: "failed", error: "UI asset generation failed upstream" };
8001
+ if (asset.status !== "completed" || !asset.imageUrl) {
8002
+ return { status: "processing", progressPercent: asset.progressPercent, etaSeconds: asset.etaSeconds };
8003
+ }
8004
+ const backgroundJobId = context?.metadata?.backgroundJobId;
8005
+ return {
8006
+ status: "ready",
8007
+ objectId: uiAssetId,
8008
+ sourceUrl: asset.imageUrl,
8009
+ sources: [{ url: asset.imageUrl }],
8010
+ billed: await this.billedForJob(backgroundJobId)
8011
+ };
8012
+ }
7857
8013
  async selectCandidate(jobId, index, commonTag, generator) {
7858
8014
  if (generator === "tiles") {
7859
8015
  const set = await this.client.getTilesPro(jobId);
@@ -9243,6 +9399,10 @@ async function resolveSpecs(loaded, filter) {
9243
9399
  size = asset.size ?? style.size ?? 64;
9244
9400
  width = size;
9245
9401
  height = size;
9402
+ } else if (generator === "uiAsset") {
9403
+ width = asset.width ?? style.size ?? 256;
9404
+ height = asset.height ?? style.size ?? 256;
9405
+ size = Math.max(width, height);
9246
9406
  } else {
9247
9407
  width = asset.width ?? style.size ?? 64;
9248
9408
  height = asset.height ?? style.size ?? 64;
@@ -9253,6 +9413,11 @@ async function resolveSpecs(loaded, filter) {
9253
9413
  `assets.${assetId}: ${asset.state ? "state" : "animation"} needs a character or objectPro style; "${styleId}" generates ${generator}`
9254
9414
  );
9255
9415
  }
9416
+ if ((asset.pieces || asset.elements) && generator !== "uiAsset") {
9417
+ throw new Error(
9418
+ `assets.${assetId}: ${asset.pieces ? "pieces" : "elements"} needs a uiAsset style; "${styleId}" generates ${generator}`
9419
+ );
9420
+ }
9256
9421
  const characterKind = asset.animation ? "animation" : asset.state ? "state" : "base";
9257
9422
  const subject = asset.promptByStyle[styleId] ?? asset.prompt ?? "";
9258
9423
  let terrainLowerDescription;
@@ -9299,6 +9464,9 @@ async function resolveSpecs(loaded, filter) {
9299
9464
  shading: style.shading,
9300
9465
  detail: style.detail,
9301
9466
  seed: style.seed,
9467
+ uiPieces: generator === "uiAsset" ? asset.pieces : void 0,
9468
+ uiElements: generator === "uiAsset" ? asset.elements : void 0,
9469
+ uiColorPalette: generator === "uiAsset" ? style.uiColorPalette : void 0,
9302
9470
  palette: style.palette,
9303
9471
  enforcePalette: style.enforcePalette,
9304
9472
  noBackground: style.noBackground,