pixelkiln 0.67.0 → 0.68.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 +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/GENERATORS.md +31 -0
- package/docs/MANIFEST.md +1 -0
- package/docs/PIXELLAB.md +4 -1
- package/package.json +1 -1
- package/schema/manifest.schema.json +61 -0
- package/skills/pixelkiln/references/pixellab-roadmap.md +5 -2
- package/skills/pixelkiln/references/pixellab.md +7 -1
package/dist/cli.js
CHANGED
|
@@ -689,6 +689,11 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
|
|
|
689
689
|
uiPieces: spec.uiPieces,
|
|
690
690
|
uiElements: spec.uiElements,
|
|
691
691
|
uiColorPalette: spec.uiColorPalette,
|
|
692
|
+
// Scene and mask enter as their bytes' hashes, never their paths.
|
|
693
|
+
scene: spec.scene ? {
|
|
694
|
+
image: spec.scene.sha256,
|
|
695
|
+
placement: spec.scene.placement.type === "mask" ? { type: "mask", mask: spec.scene.placement.sha256 } : spec.scene.placement
|
|
696
|
+
} : void 0,
|
|
692
697
|
styleTraits: spec.styleTraits,
|
|
693
698
|
styleImages: styleImageHashes,
|
|
694
699
|
// A character family: the engine and rotations of a base, the parent's
|
|
@@ -987,6 +992,16 @@ var CharacterPortraitSchema = z.object({
|
|
|
987
992
|
of: z.string().min(1),
|
|
988
993
|
size: CharacterPortraitSizeSchema.default(64)
|
|
989
994
|
}).strict();
|
|
995
|
+
var MapScenePlacementSchema = z.union([
|
|
996
|
+
z.object({ oval: z.number().min(0.05).max(0.95) }).strict(),
|
|
997
|
+
z.object({ rectangle: z.number().min(0.05).max(0.95) }).strict(),
|
|
998
|
+
z.object({ mask: z.string().min(1) }).strict()
|
|
999
|
+
]);
|
|
1000
|
+
var MapSceneSchema = z.object({
|
|
1001
|
+
/** Manifest-relative PNG of the scene. Any earlier asset's output file works. */
|
|
1002
|
+
image: z.string().min(1),
|
|
1003
|
+
placement: MapScenePlacementSchema.default({ oval: 0.3 })
|
|
1004
|
+
}).strict();
|
|
990
1005
|
var CharacterOutfitSchema = z.object({
|
|
991
1006
|
/** The loop (a character animation asset) to re-clothe, in the same style. */
|
|
992
1007
|
of: z.string().min(1),
|
|
@@ -1681,6 +1696,8 @@ var AssetSchema = z.object({
|
|
|
1681
1696
|
portrait: CharacterPortraitSchema.optional(),
|
|
1682
1697
|
/** `character` styles: this asset re-clothes an existing loop's frames with a reference outfit. */
|
|
1683
1698
|
outfit: CharacterOutfitSchema.optional(),
|
|
1699
|
+
/** `map` styles only: draw this object in the style of a scene image. See `MapSceneSchema`. */
|
|
1700
|
+
scene: MapSceneSchema.optional(),
|
|
1684
1701
|
/** `character` styles, `standard` humanoid bases: this character's proportions, over the style's. */
|
|
1685
1702
|
proportions: CharacterProportionsSchema.optional(),
|
|
1686
1703
|
/**
|
|
@@ -1757,7 +1774,7 @@ var AssetSchema = z.object({
|
|
|
1757
1774
|
path: ["revision"]
|
|
1758
1775
|
});
|
|
1759
1776
|
}
|
|
1760
|
-
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);
|
|
1777
|
+
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);
|
|
1761
1778
|
if (shapes.length > 1) {
|
|
1762
1779
|
context.addIssue({
|
|
1763
1780
|
code: z.ZodIssueCode.custom,
|
|
@@ -4212,6 +4229,7 @@ async function inspectRevisionReadiness(spec, lock) {
|
|
|
4212
4229
|
if (spec.character?.parentSpec) return inspectCharacterParent(spec, lock, /* @__PURE__ */ new Set());
|
|
4213
4230
|
if (spec.character?.styleAnchor) return inspectStyleAnchor(spec, lock, /* @__PURE__ */ new Set());
|
|
4214
4231
|
if (spec.objectPro?.parentSpec) return inspectObjectProParent(spec, lock, /* @__PURE__ */ new Set());
|
|
4232
|
+
if (spec.scene) return inspectScene(spec);
|
|
4215
4233
|
if (!spec.revision) return null;
|
|
4216
4234
|
return inspectRevision(spec, lock, /* @__PURE__ */ new Set());
|
|
4217
4235
|
}
|
|
@@ -4229,6 +4247,16 @@ async function inspectStyleAnchor(spec, lock, seen) {
|
|
|
4229
4247
|
}
|
|
4230
4248
|
return { ready: true, reason: `${label} is current` };
|
|
4231
4249
|
}
|
|
4250
|
+
async function inspectScene(spec) {
|
|
4251
|
+
const scene = spec.scene;
|
|
4252
|
+
const files = [{ label: "scene image", file: scene.file, sha256: scene.sha256 }];
|
|
4253
|
+
if (scene.placement.type === "mask") files.push({ label: "scene mask", file: scene.placement.file, sha256: scene.placement.sha256 });
|
|
4254
|
+
for (const { label, file, sha256: sha2562 } of files) {
|
|
4255
|
+
if (!sha2562 || !existsSync4(file)) return { ready: false, reason: `${label} does not exist yet: ${file}` };
|
|
4256
|
+
if (await sha256File(file) !== sha2562) return { ready: false, reason: `${label} changed after the manifest was resolved` };
|
|
4257
|
+
}
|
|
4258
|
+
return { ready: true, reason: "scene is current" };
|
|
4259
|
+
}
|
|
4232
4260
|
async function inspectMirror(spec, lock, seen) {
|
|
4233
4261
|
const source = spec.mirror.sourceSpec;
|
|
4234
4262
|
const label = `mirror source ${spec.styleId}/${spec.mirror.sourceAssetId}`;
|
|
@@ -6136,6 +6164,12 @@ var PixelLabClient = class {
|
|
|
6136
6164
|
if (args.shading) body.shading = args.shading;
|
|
6137
6165
|
if (args.detail) body.detail = args.detail;
|
|
6138
6166
|
if (args.seed != null) body.seed = args.seed;
|
|
6167
|
+
if (args.scene) {
|
|
6168
|
+
const encode = (image) => ({ type: "base64", base64: image.base64, format: image.format });
|
|
6169
|
+
const placement = args.scene.placement;
|
|
6170
|
+
body.background_image = encode(args.scene.image);
|
|
6171
|
+
body.inpainting = placement.type === "mask" ? { type: "mask", mask_image: encode(placement.mask) } : { type: placement.type, fraction: placement.fraction };
|
|
6172
|
+
}
|
|
6139
6173
|
return validateResponse(
|
|
6140
6174
|
MapSubmitSchema,
|
|
6141
6175
|
await this.request("/map-objects", { method: "POST", body: JSON.stringify(body) }),
|
|
@@ -7297,6 +7331,8 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
7297
7331
|
supports(generator) {
|
|
7298
7332
|
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";
|
|
7299
7333
|
}
|
|
7334
|
+
/** `/map-objects` style matching: `background_image` plus `inpainting`. */
|
|
7335
|
+
supportsMapScene = true;
|
|
7300
7336
|
/**
|
|
7301
7337
|
* `inpaint` (`/inpaint-v3`, a mask) and `image-to-image` (`/edit-images-v2`,
|
|
7302
7338
|
* no mask) both exist on PixelLab, and so do `reduce-colors`
|
|
@@ -8442,7 +8478,8 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
8442
8478
|
outline: spec.outline,
|
|
8443
8479
|
shading: spec.shading,
|
|
8444
8480
|
detail: spec.detail,
|
|
8445
|
-
seed: spec.seed
|
|
8481
|
+
seed: spec.seed,
|
|
8482
|
+
scene: spec.scene ? mapScenePayload(spec) : void 0
|
|
8446
8483
|
});
|
|
8447
8484
|
return { jobId: res.object_id, metadata: { backgroundJobId: res.background_job_id } };
|
|
8448
8485
|
}
|
|
@@ -9319,6 +9356,21 @@ function findObjectAnimation(object, name, direction, animationId) {
|
|
|
9319
9356
|
}
|
|
9320
9357
|
return null;
|
|
9321
9358
|
}
|
|
9359
|
+
function mapScenePayload(spec) {
|
|
9360
|
+
const scene = spec.scene;
|
|
9361
|
+
const read = (file, hash, label) => {
|
|
9362
|
+
const bytes = readFileSync2(file);
|
|
9363
|
+
if (!hash || sha256(bytes) !== hash) {
|
|
9364
|
+
throw new Error(`${spec.styleId}/${spec.assetId}: ${label} changed after the manifest was resolved`);
|
|
9365
|
+
}
|
|
9366
|
+
return { base64: bytes.toString("base64"), format: "png" };
|
|
9367
|
+
};
|
|
9368
|
+
const placement = scene.placement;
|
|
9369
|
+
return {
|
|
9370
|
+
image: read(scene.file, scene.sha256, "scene image"),
|
|
9371
|
+
placement: placement.type === "mask" ? { type: "mask", mask: read(placement.file, placement.sha256, "scene mask") } : placement
|
|
9372
|
+
};
|
|
9373
|
+
}
|
|
9322
9374
|
|
|
9323
9375
|
// src/providers/retrodiffusion.ts
|
|
9324
9376
|
var DEFAULT_BASE_URL2 = "https://api.retrodiffusion.ai/v1";
|
|
@@ -11021,6 +11073,7 @@ async function resolveSpecs(loaded, filter) {
|
|
|
11021
11073
|
};
|
|
11022
11074
|
}
|
|
11023
11075
|
}
|
|
11076
|
+
if (asset.scene) resolved.scene = await resolveMapScene(root, styleId, assetId, resolved.generator, asset.scene, activeProvider);
|
|
11024
11077
|
resolved.specHash = specHash(
|
|
11025
11078
|
resolved,
|
|
11026
11079
|
styleImageHashes,
|
|
@@ -11079,6 +11132,32 @@ function pngPath(file) {
|
|
|
11079
11132
|
const extension = path13.extname(file);
|
|
11080
11133
|
return extension ? `${file.slice(0, -extension.length)}.png` : `${file}.png`;
|
|
11081
11134
|
}
|
|
11135
|
+
async function resolveMapScene(root, styleId, assetId, generator, scene, provider) {
|
|
11136
|
+
if (generator !== "map") {
|
|
11137
|
+
throw new Error(`assets.${assetId}.scene: only a map object can be drawn into a scene; style "${styleId}" uses "${generator}"`);
|
|
11138
|
+
}
|
|
11139
|
+
if (!provider.supportsMapScene) {
|
|
11140
|
+
throw new Error(`assets.${assetId}.scene: provider "${provider.id}" cannot draw into a scene; PixelLab's /map-objects can`);
|
|
11141
|
+
}
|
|
11142
|
+
const file = path13.resolve(root, scene.image);
|
|
11143
|
+
const image = await optionalRevisionImage(file, "scene image", "png");
|
|
11144
|
+
const base = { file, sha256: image?.hash ?? null, width: image?.width ?? null, height: image?.height ?? null };
|
|
11145
|
+
const placement = scene.placement;
|
|
11146
|
+
if ("mask" in placement) {
|
|
11147
|
+
const maskFile = path13.resolve(root, placement.mask);
|
|
11148
|
+
const mask = await optionalRevisionImage(maskFile, "scene mask", "png");
|
|
11149
|
+
if (image && mask && (image.width !== mask.width || image.height !== mask.height)) {
|
|
11150
|
+
throw new Error(
|
|
11151
|
+
`assets.${assetId}.scene: the mask is ${mask.width}x${mask.height}; the scene ${scene.image} is ${image.width}x${image.height}`
|
|
11152
|
+
);
|
|
11153
|
+
}
|
|
11154
|
+
return {
|
|
11155
|
+
...base,
|
|
11156
|
+
placement: { type: "mask", file: maskFile, sha256: mask?.hash ?? null, width: mask?.width ?? null, height: mask?.height ?? null }
|
|
11157
|
+
};
|
|
11158
|
+
}
|
|
11159
|
+
return "oval" in placement ? { ...base, placement: { type: "oval", fraction: placement.oval } } : { ...base, placement: { type: "rectangle", fraction: placement.rectangle } };
|
|
11160
|
+
}
|
|
11082
11161
|
async function optionalRevisionImage(file, label, requiredFormat) {
|
|
11083
11162
|
if (!existsSync8(file)) return null;
|
|
11084
11163
|
const bytes = await readFile6(file);
|