pixelkiln 0.67.0 → 0.68.1
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 +30 -15
- package/README.md +18 -18
- package/dist/cli.js +81 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +86 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +434 -34
- package/dist/index.d.ts +434 -34
- package/dist/index.js +84 -2
- package/dist/index.js.map +1 -1
- package/docs/AGENTS.md +1 -1
- package/docs/CHARACTERS.md +4 -1
- package/docs/CLI.md +32 -25
- package/docs/GENERATORS.md +41 -1
- package/docs/MANIFEST.md +5 -2
- package/docs/PIXELLAB.md +18 -4
- package/docs/README.md +2 -2
- package/package.json +1 -1
- package/schema/manifest.schema.json +61 -0
- package/skills/pixelkiln/SKILL.md +10 -4
- package/skills/pixelkiln/references/pixellab-roadmap.md +57 -82
- package/skills/pixelkiln/references/pixellab.md +29 -12
- package/skills/pixelkiln/references/revisions.md +18 -4
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,
|