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 +174 -6
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +178 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1030 -135
- package/dist/index.d.ts +1030 -135
- package/dist/index.js +176 -6
- package/dist/index.js.map +1 -1
- package/docs/ENDPOINTS.md +68 -5
- package/docs/GENERATORS.md +46 -0
- package/docs/MANIFEST.md +3 -1
- package/package.json +1 -1
- package/schema/manifest.schema.json +156 -2
- package/schema/recipe.schema.json +6 -1
- package/skills/pixelkiln/references/pixellab-roadmap.md +37 -15
- package/skills/pixelkiln/references/pixellab.md +38 -0
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
|
|
649
|
-
// undefined for every other generator. `tileSize` is
|
|
650
|
-
// absent; width/height are derived from it, so it is
|
|
651
|
-
|
|
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,
|