pixelkiln 0.86.3 → 0.87.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/PROVIDERS.md +10 -10
- package/README.md +4 -4
- package/dist/cli.js +144 -18
- package/dist/cli.js.map +1 -1
- package/dist/client/gallery.js +3 -3
- package/dist/index.cjs +113 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +116 -28
- package/dist/index.d.ts +116 -28
- package/dist/index.js +107 -16
- package/dist/index.js.map +1 -1
- package/docs/CHARACTERS.md +2 -2
- package/docs/CLI.md +36 -5
- package/docs/ENDPOINTS.md +88 -11
- package/docs/GENERATORS.md +12 -12
- package/docs/MANIFEST.md +1 -1
- package/docs/PIXELLAB.md +6 -6
- package/docs/REVISIONS.md +2 -2
- package/package.json +1 -1
- package/skills/pixelkiln/SKILL.md +5 -2
- package/skills/pixelkiln/references/pixellab-roadmap.md +30 -4
- package/skills/pixelkiln/references/pixellab.md +18 -13
- package/skills/pixelkiln/references/revisions.md +2 -2
package/dist/index.js
CHANGED
|
@@ -35,17 +35,24 @@ function candidateCount(size) {
|
|
|
35
35
|
}
|
|
36
36
|
function generationCost(width, height, generator = "map") {
|
|
37
37
|
if (generator === "map" || generator === "pixflux") return 1;
|
|
38
|
-
if (generator === "imagePro") return
|
|
38
|
+
if (generator === "imagePro") return proTierCost(width, height);
|
|
39
39
|
const px = width * height;
|
|
40
|
-
if (px <= 1024) return
|
|
41
|
-
if (px <= 2048) return
|
|
42
|
-
return
|
|
40
|
+
if (px <= 1024) return PRO_TIER_PRICES[0];
|
|
41
|
+
if (px <= 2048) return PRO_TIER_PRICES[1];
|
|
42
|
+
return PRO_TIER_PRICES[2];
|
|
43
|
+
}
|
|
44
|
+
var PRO_TIER_PRICES = [10, 15, 25];
|
|
45
|
+
function proTierCost(width, height) {
|
|
46
|
+
const px = width * height;
|
|
47
|
+
if (px <= 65536) return PRO_TIER_PRICES[0];
|
|
48
|
+
if (px <= 102400) return PRO_TIER_PRICES[1];
|
|
49
|
+
return PRO_TIER_PRICES[2];
|
|
43
50
|
}
|
|
44
51
|
function tilesCost(tileSize, variations) {
|
|
45
52
|
const px = tileSize * tileSize * Math.max(1, variations);
|
|
46
|
-
if (px <= 1024) return
|
|
47
|
-
if (px <= 2048) return
|
|
48
|
-
return
|
|
53
|
+
if (px <= 1024) return PRO_TIER_PRICES[0];
|
|
54
|
+
if (px <= 2048) return PRO_TIER_PRICES[1];
|
|
55
|
+
return PRO_TIER_PRICES[2];
|
|
49
56
|
}
|
|
50
57
|
function isometricTileCost() {
|
|
51
58
|
return 1;
|
|
@@ -561,7 +568,7 @@ var MEMBER_SET_REVISION_MODES = ["reduce-colors", "correct-pixelart", "edit-anim
|
|
|
561
568
|
var StyleObjectSchema = z.object({
|
|
562
569
|
/** Generation backend for this style. Omit to inherit the manifest default. */
|
|
563
570
|
provider: z.string().min(1).optional(),
|
|
564
|
-
// `map` is the default because it is
|
|
571
|
+
// `map` is the default because it is 10-25x cheaper and correct for any
|
|
565
572
|
// asset that is not going to be rotated or animated.
|
|
566
573
|
generator: GeneratorSchema.default("map"),
|
|
567
574
|
/** Square edge length. The active provider owns its exact limit. */
|
|
@@ -2384,7 +2391,8 @@ var PixelLabClient = class {
|
|
|
2384
2391
|
* A full-body sprite in, a bust portrait out (or the reverse, unused
|
|
2385
2392
|
* today). Job-based like a state or `1dir`, not the couple-generations
|
|
2386
2393
|
* "conversion" the name suggests: the smallest `resultSize` (16px) billed
|
|
2387
|
-
* 20 generations live (docs/ENDPOINTS.md, "Characters, measured")
|
|
2394
|
+
* 20 generations live (docs/ENDPOINTS.md, "Characters, measured"), the
|
|
2395
|
+
* Pro floor before PixelLab's October 2026 cut put it at 10.
|
|
2388
2396
|
*/
|
|
2389
2397
|
async createPortraitCharacterPro(args) {
|
|
2390
2398
|
const raw = await this.request("/portrait-character-pro", {
|
|
@@ -2423,7 +2431,8 @@ var PixelLabClient = class {
|
|
|
2423
2431
|
* everything else here, but the completed job returns the result frames
|
|
2424
2432
|
* inline as base64 (`last_response.quantized_images`), not as storage
|
|
2425
2433
|
* URLs — the one PixelLab character tool that works this way. A live
|
|
2426
|
-
* 2-frame, 92x92 job billed 20 generations (docs/ENDPOINTS.md)
|
|
2434
|
+
* 2-frame, 92x92 job billed 20 generations (docs/ENDPOINTS.md), before
|
|
2435
|
+
* PixelLab's October 2026 Pro cut halved that floor to 10.
|
|
2427
2436
|
*/
|
|
2428
2437
|
async transferOutfitV2(args) {
|
|
2429
2438
|
const image = (img) => ({
|
|
@@ -2473,7 +2482,9 @@ var PixelLabClient = class {
|
|
|
2473
2482
|
* far higher than this tiering assumes; 352x352 (123904px²), 384x384
|
|
2474
2483
|
* (147456px²), and 512x512 (262144px², the tiering ceiling) all billed
|
|
2475
2484
|
* 40. Both breakpoints are now tightly bracketed: 20->25 in
|
|
2476
|
-
* (65536px², 82944px²], 25->40 in (102400px², 123904px²].
|
|
2485
|
+
* (65536px², 82944px²], 25->40 in (102400px², 123904px²]. Those prices
|
|
2486
|
+
* predate PixelLab's October 2026 Pro cut to 10/15/25; `proTierCost`
|
|
2487
|
+
* keeps the breakpoints with the new prices.
|
|
2477
2488
|
* `editImagesV2` below returns the same fields under `images`, plural
|
|
2478
2489
|
* and array-wrapped, not `image` — do not assume the two endpoints share
|
|
2479
2490
|
* one response shape. See docs/ENDPOINTS.md and docs/REVISIONS.md for
|
|
@@ -2526,8 +2537,9 @@ var PixelLabClient = class {
|
|
|
2526
2537
|
);
|
|
2527
2538
|
}
|
|
2528
2539
|
/**
|
|
2529
|
-
* PixelLab's Pro image tier, `/generate-image-v2`:
|
|
2530
|
-
*
|
|
2540
|
+
* PixelLab's Pro image tier, `/generate-image-v2`: 10 to 25 generations by
|
|
2541
|
+
* canvas since PixelLab's October 2026 Pro cut, a flat 40 before
|
|
2542
|
+
* (docs/ENDPOINTS.md, "Single-image generators, measured"), for real style
|
|
2531
2543
|
* transfer and non-square canvases up to 792x688, where `pixflux` is
|
|
2532
2544
|
* limited to 400x400 and no style reference. Like a revision, this hands
|
|
2533
2545
|
* back a plain background job with no resource of its own; unlike a
|
|
@@ -2613,6 +2625,24 @@ var PixelLabClient = class {
|
|
|
2613
2625
|
"inpaint-image-pro-flash"
|
|
2614
2626
|
);
|
|
2615
2627
|
}
|
|
2628
|
+
/**
|
|
2629
|
+
* `/image-to-pixelart-pro-flash`: a photo, painting, or render in, pixel
|
|
2630
|
+
* art out, with no size to pass: PixelLab detects the native pixel scale
|
|
2631
|
+
* and picks the output size (a 256x256 render came back 108x109, on a
|
|
2632
|
+
* transparent background). A flat 6 generations, billed exactly that live
|
|
2633
|
+
* in October 2026. Sources over 2048px on the longer side are scaled down
|
|
2634
|
+
* upstream. The finished job carries the image at `last_response.images[0]`.
|
|
2635
|
+
*/
|
|
2636
|
+
async imageToPixelartProFlash(args) {
|
|
2637
|
+
const body = { image: args.image };
|
|
2638
|
+
if (args.description) body.description = args.description;
|
|
2639
|
+
if (args.seed != null) body.seed = args.seed;
|
|
2640
|
+
return validateResponse(
|
|
2641
|
+
ProFlashImageSubmitSchema,
|
|
2642
|
+
await this.request("/image-to-pixelart-pro-flash", { method: "POST", body: JSON.stringify(body) }),
|
|
2643
|
+
"image-to-pixelart-pro-flash"
|
|
2644
|
+
);
|
|
2645
|
+
}
|
|
2616
2646
|
/**
|
|
2617
2647
|
* `/generate-ui-v2` ("Generate UI (Pro)"): one UI element (a button, a
|
|
2618
2648
|
* health bar, an inventory slot, a dialogue box) from a description,
|
|
@@ -3709,7 +3739,7 @@ function characterCost(spec) {
|
|
|
3709
3739
|
}
|
|
3710
3740
|
if (character.kind === "state" || character.kind === "portrait") return generationCost(spec.width, spec.height, "1dir");
|
|
3711
3741
|
if (character.kind === "outfit") {
|
|
3712
|
-
return
|
|
3742
|
+
return PRO_TIER_PRICES[0];
|
|
3713
3743
|
}
|
|
3714
3744
|
if (character.mode === "standard") return 1;
|
|
3715
3745
|
if (character.mode === "v3") {
|
|
@@ -3902,7 +3932,7 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
3902
3932
|
if (spec.revision) {
|
|
3903
3933
|
const width = spec.revision.sourceWidth ?? spec.width;
|
|
3904
3934
|
const height = spec.revision.sourceHeight ?? spec.height;
|
|
3905
|
-
return { unit: "generations", amount:
|
|
3935
|
+
return { unit: "generations", amount: proTierCost(width, height), candidates: 1 };
|
|
3906
3936
|
}
|
|
3907
3937
|
if (spec.generator === "tiles" || spec.generator === "terrain" || spec.generator === "isometricTile") {
|
|
3908
3938
|
return { unit: "generations", amount: spec.cost, candidates: spec.candidates };
|
|
@@ -11945,7 +11975,7 @@ async function unzoomFile(client2, input, opts = {}) {
|
|
|
11945
11975
|
}
|
|
11946
11976
|
var FONT_WEIGHTS = ["Bold", "Regular"];
|
|
11947
11977
|
var FONT_GLYPH_SIZES = [8, 16, 32, 64];
|
|
11948
|
-
var FONT_COST_GENERATIONS =
|
|
11978
|
+
var FONT_COST_GENERATIONS = 15;
|
|
11949
11979
|
function fontOutputPaths(out) {
|
|
11950
11980
|
const base = path17.resolve(out.replace(/\.(ttf|png)$/i, ""));
|
|
11951
11981
|
return { ttf: `${base}.ttf`, atlas: `${base}.png` };
|
|
@@ -11994,6 +12024,61 @@ async function generateFont(client2, opts) {
|
|
|
11994
12024
|
await sleep3(interval);
|
|
11995
12025
|
}
|
|
11996
12026
|
}
|
|
12027
|
+
var PIXELATE_COST_GENERATIONS = 6;
|
|
12028
|
+
var PIXELATE_MAX_SIDE = 2048;
|
|
12029
|
+
function defaultPixelateOut(input) {
|
|
12030
|
+
const ext = path17.extname(input);
|
|
12031
|
+
return `${ext ? input.slice(0, -ext.length) : input}.pixel.png`;
|
|
12032
|
+
}
|
|
12033
|
+
async function pixelateFile(client2, input, opts = {}) {
|
|
12034
|
+
const out = path17.resolve(opts.out ?? defaultPixelateOut(input));
|
|
12035
|
+
if (path17.resolve(input) === out) throw new Error("pixelate will not overwrite its own input; pass a different --out");
|
|
12036
|
+
if (existsSync11(out) && !opts.force) throw new Error(`${out} already exists; pass --force to replace it`);
|
|
12037
|
+
const bytes = await readFile9(input);
|
|
12038
|
+
const metadata = imageMetadata(bytes);
|
|
12039
|
+
if (!metadata) throw new Error(`${input} is not a readable PNG or JPEG`);
|
|
12040
|
+
const { width, height, format } = metadata;
|
|
12041
|
+
const sleep3 = opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
12042
|
+
const interval = opts.intervalMs ?? 5e3;
|
|
12043
|
+
const timeout = opts.timeoutMs ?? 10 * 6e4;
|
|
12044
|
+
const submitted = await client2.imageToPixelartProFlash({
|
|
12045
|
+
image: { base64: bytes.toString("base64"), format },
|
|
12046
|
+
description: opts.description,
|
|
12047
|
+
seed: opts.seed
|
|
12048
|
+
});
|
|
12049
|
+
const jobId = submitted.background_job_id;
|
|
12050
|
+
const started = Date.now();
|
|
12051
|
+
for (; ; ) {
|
|
12052
|
+
const job = await client2.getBackgroundJob(jobId);
|
|
12053
|
+
opts.onProgress?.(job.status);
|
|
12054
|
+
if (job.status === "failed") throw new Error(`PixelLab pixelate job ${jobId} failed upstream`);
|
|
12055
|
+
if (job.status === "completed") {
|
|
12056
|
+
const images = job.last_response?.images;
|
|
12057
|
+
const first = Array.isArray(images) ? images[0] : void 0;
|
|
12058
|
+
if (!first || typeof first.base64 !== "string" || !first.base64) {
|
|
12059
|
+
throw new Error(`PixelLab pixelate job ${jobId} completed without an image at last_response.images[0]`);
|
|
12060
|
+
}
|
|
12061
|
+
const png = Buffer.from(first.base64, "base64");
|
|
12062
|
+
const size = imageMetadata(png);
|
|
12063
|
+
await mkdir4(path17.dirname(out), { recursive: true });
|
|
12064
|
+
await writeFile4(out, png);
|
|
12065
|
+
return {
|
|
12066
|
+
jobId,
|
|
12067
|
+
out,
|
|
12068
|
+
original: { width, height },
|
|
12069
|
+
pixelated: { width: size?.width ?? Number(first.width), height: size?.height ?? Number(first.height) },
|
|
12070
|
+
imageId: submitted.image_id,
|
|
12071
|
+
usage: job.usage ?? null
|
|
12072
|
+
};
|
|
12073
|
+
}
|
|
12074
|
+
if (Date.now() - started > timeout) {
|
|
12075
|
+
throw new Error(
|
|
12076
|
+
`PixelLab pixelate job ${jobId} is still ${job.status} after ${Math.round(timeout / 1e3)} s; it keeps running upstream, but this command has stopped waiting`
|
|
12077
|
+
);
|
|
12078
|
+
}
|
|
12079
|
+
await sleep3(interval);
|
|
12080
|
+
}
|
|
12081
|
+
}
|
|
11997
12082
|
|
|
11998
12083
|
// src/pipeline/quality-regression.ts
|
|
11999
12084
|
import path18 from "path";
|
|
@@ -18969,8 +19054,11 @@ export {
|
|
|
18969
19054
|
MapSceneSchema,
|
|
18970
19055
|
MediaType,
|
|
18971
19056
|
OverwriteRefusedError,
|
|
19057
|
+
PIXELATE_COST_GENERATIONS,
|
|
19058
|
+
PIXELATE_MAX_SIDE,
|
|
18972
19059
|
PIXEL_ART_FIXER_REVISION,
|
|
18973
19060
|
PIXEL_ART_FIXER_URL,
|
|
19061
|
+
PRO_TIER_PRICES,
|
|
18974
19062
|
PixelKilnError,
|
|
18975
19063
|
PixelLabClient,
|
|
18976
19064
|
PixelLabError,
|
|
@@ -19026,6 +19114,7 @@ export {
|
|
|
19026
19114
|
currentEntryOutputPath,
|
|
19027
19115
|
currentOutputPath,
|
|
19028
19116
|
defaultOpenCommand,
|
|
19117
|
+
defaultPixelateOut,
|
|
19029
19118
|
defaultUnzoomOut,
|
|
19030
19119
|
detachHandEdit,
|
|
19031
19120
|
detectMediaType,
|
|
@@ -19108,11 +19197,13 @@ export {
|
|
|
19108
19197
|
parseTerrainDescriptions,
|
|
19109
19198
|
parseWorkspace,
|
|
19110
19199
|
pickHistory,
|
|
19200
|
+
pixelateFile,
|
|
19111
19201
|
pngSize,
|
|
19112
19202
|
poll,
|
|
19113
19203
|
portableOutputPath,
|
|
19114
19204
|
prepareReview,
|
|
19115
19205
|
primaryOutput,
|
|
19206
|
+
proTierCost,
|
|
19116
19207
|
proportionsPayload,
|
|
19117
19208
|
providerCredentialEnvs,
|
|
19118
19209
|
providerFactory,
|