pixelkiln 0.53.4 → 0.55.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 +704 -31
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +706 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +599 -115
- package/dist/index.d.ts +599 -115
- package/dist/index.js +705 -31
- package/dist/index.js.map +1 -1
- package/docs/CHARACTERS.md +6 -0
- package/docs/ENDPOINTS.md +47 -2
- package/docs/GENERATORS.md +89 -0
- package/docs/MANIFEST.md +1 -1
- package/package.json +1 -1
- package/schema/manifest.schema.json +50 -2
- package/schema/recipe.schema.json +25 -1
- package/skills/pixelkiln/references/pixellab-roadmap.md +40 -11
- package/skills/pixelkiln/references/pixellab.md +57 -0
package/dist/cli.js
CHANGED
|
@@ -653,6 +653,8 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
|
|
|
653
653
|
tileView: spec.tileView,
|
|
654
654
|
tileFeature: spec.tileFeature,
|
|
655
655
|
outlineMode: spec.outlineMode,
|
|
656
|
+
isometricTileSize: spec.isometricTileSize,
|
|
657
|
+
isometricTileShape: spec.isometricTileShape,
|
|
656
658
|
styleImages: styleImageHashes,
|
|
657
659
|
// A character family: the engine and rotations of a base, the parent's
|
|
658
660
|
// generated bytes for a state or animation (so a regenerated parent
|
|
@@ -681,6 +683,23 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
|
|
|
681
683
|
concept: spec.character.concept?.sha256,
|
|
682
684
|
styleAnchor: spec.character.styleAnchor ? { of: spec.character.styleAnchor.assetId, sha256: spec.character.styleAnchor.sha256 } : void 0
|
|
683
685
|
} : void 0,
|
|
686
|
+
// An objectPro family: same shape as character above, minus the
|
|
687
|
+
// fields it has none of (mode/template/proportions/isometric/concept/
|
|
688
|
+
// styleAnchor) — see ResolvedObjectPro.
|
|
689
|
+
objectPro: spec.objectPro ? {
|
|
690
|
+
kind: spec.objectPro.kind,
|
|
691
|
+
directions: spec.objectPro.directions,
|
|
692
|
+
parent: spec.objectPro.parentAssetId,
|
|
693
|
+
parentSha256: spec.objectPro.parentSha256 ?? null,
|
|
694
|
+
state: spec.objectPro.state,
|
|
695
|
+
animation: spec.objectPro.animation ? {
|
|
696
|
+
...spec.objectPro.animation,
|
|
697
|
+
startFrame: spec.objectPro.animation.startFrame?.sha256,
|
|
698
|
+
endFrame: spec.objectPro.animation.endFrame?.sha256
|
|
699
|
+
} : void 0,
|
|
700
|
+
styleTraits: spec.objectPro.styleTraits,
|
|
701
|
+
reference: spec.objectPro.reference?.sha256
|
|
702
|
+
} : void 0,
|
|
684
703
|
// A mirror's bytes come from its source's recorded outputs; the plan
|
|
685
704
|
// compares those directly, so only the choice of source is identity.
|
|
686
705
|
mirror: spec.mirror ? { of: spec.mirror.sourceAssetId } : void 0,
|
|
@@ -707,7 +726,7 @@ import path2 from "path";
|
|
|
707
726
|
// src/types.ts
|
|
708
727
|
import { z } from "zod";
|
|
709
728
|
var MediaTypeSchema = z.enum(["image/png", "image/gif"]);
|
|
710
|
-
var GeneratorSchema = z.enum(["1dir", "map", "pixflux", "tiles", "animation", "frames", "character", "terrain", "imagePro"]);
|
|
729
|
+
var GeneratorSchema = z.enum(["1dir", "map", "pixflux", "tiles", "animation", "frames", "character", "terrain", "imagePro", "isometricTile", "objectPro"]);
|
|
711
730
|
var GridConfidenceSchema = z.enum(["low", "medium", "high"]);
|
|
712
731
|
var RevisionModeSchema = z.enum(["image-to-image", "inpaint", "outpaint"]);
|
|
713
732
|
function tileVariationCount(descriptions) {
|
|
@@ -741,6 +760,9 @@ function tilesCost(tileSize, variations) {
|
|
|
741
760
|
if (px <= 2048) return 25;
|
|
742
761
|
return 40;
|
|
743
762
|
}
|
|
763
|
+
function isometricTileCost() {
|
|
764
|
+
return 1;
|
|
765
|
+
}
|
|
744
766
|
function terrainTileCount(transitionSize) {
|
|
745
767
|
return transitionSize === 1 ? 25 : 16;
|
|
746
768
|
}
|
|
@@ -1070,6 +1092,28 @@ var StyleObjectSchema = z.object({
|
|
|
1070
1092
|
/** `terrain` generator only. Camera angle; the API default is `high
|
|
1071
1093
|
* top-down`. */
|
|
1072
1094
|
terrainView: z.enum(["low top-down", "high top-down"]).optional(),
|
|
1095
|
+
/**
|
|
1096
|
+
* `isometricTile` generator only. `/create-isometric-tile`'s own tile
|
|
1097
|
+
* grid size, distinct from `size` (the generation canvas, 16-64px — the
|
|
1098
|
+
* API's own guidance is that sizes above 24px "often produce better
|
|
1099
|
+
* quality results"). The API default is 16.
|
|
1100
|
+
*/
|
|
1101
|
+
isometricTileSize: z.union([z.literal(16), z.literal(32)]).optional(),
|
|
1102
|
+
/**
|
|
1103
|
+
* `isometricTile` generator only. Vertical thickness, i.e. how much
|
|
1104
|
+
* height/elevation variation the tile can represent on a map: `"thin
|
|
1105
|
+
* tile"` (~15% of canvas height), `"thick tile"` (~25%), or `"block"`
|
|
1106
|
+
* (~50%, the API default).
|
|
1107
|
+
*/
|
|
1108
|
+
isometricTileShape: z.enum(["thin tile", "thick tile", "block"]).optional(),
|
|
1109
|
+
/**
|
|
1110
|
+
* `objectPro` generator only. Rotations captured for a base: 1 (the API
|
|
1111
|
+
* still draws and stores a single facing) or the full 8. The API default
|
|
1112
|
+
* is 8. Unlike `character`, `objectPro` has no skeleton/template modes
|
|
1113
|
+
* and so no `mode`/`template` fields of its own — `/create-object-pro-flash`
|
|
1114
|
+
* is the only base-creation path.
|
|
1115
|
+
*/
|
|
1116
|
+
objectDirections: z.union([z.literal(1), z.literal(8)]).optional(),
|
|
1073
1117
|
/**
|
|
1074
1118
|
* `pixflux` only. Whether to strip the generated background.
|
|
1075
1119
|
*
|
|
@@ -1274,25 +1318,41 @@ var AssetSchema = z.object({
|
|
|
1274
1318
|
revision: RevisionSchema.optional(),
|
|
1275
1319
|
/**
|
|
1276
1320
|
* The provider's own id for art that already exists on the account, so
|
|
1277
|
-
* `adopt` can map it without matching bytes: an object id
|
|
1278
|
-
*
|
|
1279
|
-
*
|
|
1321
|
+
* `adopt` can map it without matching bytes: an object id (an
|
|
1322
|
+
* `objectPro` base and state included, since they share PixelLab's
|
|
1323
|
+
* `/objects` identity space), a character id, or `<id>#<animation group
|
|
1324
|
+
* id>` for a character or `objectPro` animation. Not part of the spec's
|
|
1325
|
+
* identity.
|
|
1280
1326
|
*/
|
|
1281
1327
|
remoteId: z.string().min(1).optional(),
|
|
1282
|
-
/**
|
|
1328
|
+
/**
|
|
1329
|
+
* `character` or `objectPro` styles: this asset is a pose or edit of
|
|
1330
|
+
* another asset in the same family. `objectPro` ignores
|
|
1331
|
+
* `paletteFromReference`/`canvas` — the API has no equivalent, and
|
|
1332
|
+
* always inherits the parent's exact canvas.
|
|
1333
|
+
*/
|
|
1283
1334
|
state: CharacterStateSchema.optional(),
|
|
1284
|
-
/**
|
|
1335
|
+
/**
|
|
1336
|
+
* `character` or `objectPro` styles: this asset is a loop of another
|
|
1337
|
+
* asset in the same family, in one direction. `objectPro` has no
|
|
1338
|
+
* skeleton/template concept, so `template`/`subject`/`outline`/
|
|
1339
|
+
* `shading`/`detail` are rejected on an `objectPro` animation rather
|
|
1340
|
+
* than silently ignored — describe the motion in the asset's own
|
|
1341
|
+
* `prompt` instead.
|
|
1342
|
+
*/
|
|
1285
1343
|
animation: CharacterAnimationSchema.optional(),
|
|
1286
1344
|
/** `character` styles, `standard` humanoid bases: this character's proportions, over the style's. */
|
|
1287
1345
|
proportions: CharacterProportionsSchema.optional(),
|
|
1288
1346
|
/**
|
|
1289
|
-
* `character` styles, bases only: the
|
|
1290
|
-
* PixelLab rotates into the other directions instead of
|
|
1291
|
-
* the prompt. A manifest-relative PNG or JPEG of the
|
|
1292
|
-
* sprite, or an object keyed by direction (`{ "south":
|
|
1293
|
-
* ... }`); quadrupeds in `standard` mode need
|
|
1294
|
-
* `standard` takes more than south. `
|
|
1295
|
-
*
|
|
1347
|
+
* `character` or `objectPro` styles, bases only: the subject's own
|
|
1348
|
+
* sprite, which PixelLab rotates into the other directions instead of
|
|
1349
|
+
* drawing from the prompt. A manifest-relative PNG or JPEG of the
|
|
1350
|
+
* south-facing sprite, or an object keyed by direction (`{ "south":
|
|
1351
|
+
* ..., "east": ... }`); quadrupeds in `standard` character mode need
|
|
1352
|
+
* south and east, and only `standard` takes more than south. `objectPro`
|
|
1353
|
+
* only ever reads the south image, since `/create-object-pro-flash`
|
|
1354
|
+
* rotates from one frame. `standard` wants each image at the style's
|
|
1355
|
+
* size; v3 accepts up to 256px, pro up to 168px. The prompt
|
|
1296
1356
|
* still guides the result.
|
|
1297
1357
|
*/
|
|
1298
1358
|
reference: z.union([z.string().min(1), z.record(CharacterDirectionSchema, z.string().min(1))]).optional(),
|
|
@@ -3778,6 +3838,7 @@ async function inspectRevisionReadiness(spec, lock) {
|
|
|
3778
3838
|
if (spec.mirror) return inspectMirror(spec, lock, /* @__PURE__ */ new Set());
|
|
3779
3839
|
if (spec.character?.parentSpec) return inspectCharacterParent(spec, lock, /* @__PURE__ */ new Set());
|
|
3780
3840
|
if (spec.character?.styleAnchor) return inspectStyleAnchor(spec, lock, /* @__PURE__ */ new Set());
|
|
3841
|
+
if (spec.objectPro?.parentSpec) return inspectObjectProParent(spec, lock, /* @__PURE__ */ new Set());
|
|
3781
3842
|
if (!spec.revision) return null;
|
|
3782
3843
|
return inspectRevision(spec, lock, /* @__PURE__ */ new Set());
|
|
3783
3844
|
}
|
|
@@ -3819,6 +3880,24 @@ async function inspectCharacterParent(spec, lock, seen) {
|
|
|
3819
3880
|
}
|
|
3820
3881
|
return { ready: true, reason: `${label} is current` };
|
|
3821
3882
|
}
|
|
3883
|
+
async function inspectObjectProParent(spec, lock, seen) {
|
|
3884
|
+
const object = spec.objectPro;
|
|
3885
|
+
const parentSpec = object.parentSpec;
|
|
3886
|
+
const label = `${object.kind} parent ${spec.styleId}/${object.parentAssetId}`;
|
|
3887
|
+
const dependency = await inspectParent(parentSpec, lock, seen);
|
|
3888
|
+
if (!dependency.ready) return { ready: false, reason: `${label} is not ready: ${dependency.reason}` };
|
|
3889
|
+
if (!object.parentSha256 || !object.parentFile || !existsSync4(object.parentFile)) {
|
|
3890
|
+
return { ready: false, reason: `${label} has no generated south-facing file yet` };
|
|
3891
|
+
}
|
|
3892
|
+
if (await sha256File(object.parentFile) !== object.parentSha256) {
|
|
3893
|
+
return { ready: false, reason: `${label} changed after the manifest was resolved` };
|
|
3894
|
+
}
|
|
3895
|
+
const parentEntry = lock.entries[lockKey(parentSpec.styleId, parentSpec.assetId)];
|
|
3896
|
+
if (!parentEntry?.objectId) {
|
|
3897
|
+
return { ready: false, reason: `${label} has no PixelLab object id recorded` };
|
|
3898
|
+
}
|
|
3899
|
+
return { ready: true, reason: `${label} is current` };
|
|
3900
|
+
}
|
|
3822
3901
|
async function inspectRevision(spec, lock, seen) {
|
|
3823
3902
|
const revision = spec.revision;
|
|
3824
3903
|
if (!revision) return { ready: true, reason: "asset has no revision inputs" };
|
|
@@ -3870,6 +3949,10 @@ async function inspectParent(spec, lock, seen) {
|
|
|
3870
3949
|
const inputs = await inspectStyleAnchor(spec, lock, nextSeen);
|
|
3871
3950
|
if (!inputs.ready) return inputs;
|
|
3872
3951
|
}
|
|
3952
|
+
if (spec.objectPro?.parentSpec) {
|
|
3953
|
+
const inputs = await inspectObjectProParent(spec, lock, nextSeen);
|
|
3954
|
+
if (!inputs.ready) return inputs;
|
|
3955
|
+
}
|
|
3873
3956
|
if (spec.quality) {
|
|
3874
3957
|
const quality = await inspectQualityProfile(spec, lock);
|
|
3875
3958
|
return quality?.state === "approved" ? { ready: true, reason: "approved quality output is current" } : {
|
|
@@ -3914,7 +3997,8 @@ function dependencies(spec) {
|
|
|
3914
3997
|
spec.mirror?.sourceSpec,
|
|
3915
3998
|
spec.revision?.sourceSpec,
|
|
3916
3999
|
spec.character?.parentSpec,
|
|
3917
|
-
spec.character?.styleAnchor?.spec
|
|
4000
|
+
spec.character?.styleAnchor?.spec,
|
|
4001
|
+
spec.objectPro?.parentSpec
|
|
3918
4002
|
].filter((parent) => Boolean(parent));
|
|
3919
4003
|
}
|
|
3920
4004
|
function resumeCommandForStatus(status) {
|
|
@@ -5249,13 +5333,36 @@ var TilesetGetSchema = z2.object({
|
|
|
5249
5333
|
}).passthrough(),
|
|
5250
5334
|
usage: UsageSchema
|
|
5251
5335
|
}).passthrough();
|
|
5336
|
+
var IsometricTileSubmitSchema = z2.object({
|
|
5337
|
+
tile_id: z2.string().min(1),
|
|
5338
|
+
background_job_id: z2.string().min(1),
|
|
5339
|
+
status: z2.literal("processing").default("processing")
|
|
5340
|
+
}).passthrough();
|
|
5341
|
+
var IsometricTileGetSchema = z2.object({
|
|
5342
|
+
image: z2.object({ base64: z2.string().min(1), format: z2.string().default("png") }).passthrough(),
|
|
5343
|
+
usage: UsageSchema
|
|
5344
|
+
}).passthrough();
|
|
5252
5345
|
var RevisionJobSubmitSchema = z2.object({
|
|
5253
5346
|
background_job_id: z2.string().min(1),
|
|
5254
5347
|
status: z2.string().default("processing")
|
|
5255
5348
|
}).passthrough();
|
|
5349
|
+
var ObjectAnimationGroupSchema = z2.object({
|
|
5350
|
+
animation_group_id: z2.string().min(1),
|
|
5351
|
+
display_name: z2.string().nullable().default(null),
|
|
5352
|
+
description: z2.string().default(""),
|
|
5353
|
+
frame_count: z2.number().int().default(0),
|
|
5354
|
+
directions: z2.array(
|
|
5355
|
+
z2.object({
|
|
5356
|
+
direction: z2.string(),
|
|
5357
|
+
storage_urls: z2.record(z2.unknown()).default({}),
|
|
5358
|
+
created_at: z2.string()
|
|
5359
|
+
}).passthrough()
|
|
5360
|
+
).default([])
|
|
5361
|
+
}).passthrough();
|
|
5256
5362
|
var PixelLabObjectSchema = z2.object({
|
|
5257
5363
|
id: z2.string().min(1),
|
|
5258
5364
|
name: z2.string().nullable().default(null),
|
|
5365
|
+
state_name: z2.string().nullable().optional(),
|
|
5259
5366
|
prompt: z2.string().default(""),
|
|
5260
5367
|
size: z2.object({ width: z2.number().int().positive(), height: z2.number().int().positive() }),
|
|
5261
5368
|
directions: z2.number().default(0),
|
|
@@ -5267,7 +5374,9 @@ var PixelLabObjectSchema = z2.object({
|
|
|
5267
5374
|
tags: z2.array(z2.string()).default([]),
|
|
5268
5375
|
status: z2.string().nullable().default(null),
|
|
5269
5376
|
progress_percent: z2.number().nullable().optional(),
|
|
5270
|
-
eta_seconds: z2.number().nullable().optional()
|
|
5377
|
+
eta_seconds: z2.number().nullable().optional(),
|
|
5378
|
+
group_id: z2.string().nullable().optional(),
|
|
5379
|
+
animations: z2.array(ObjectAnimationGroupSchema).default([])
|
|
5271
5380
|
}).passthrough();
|
|
5272
5381
|
var MapObjectSchema = z2.object({
|
|
5273
5382
|
object_id: z2.string().min(1),
|
|
@@ -5292,6 +5401,28 @@ var AnimateSubmitSchema = z2.object({
|
|
|
5292
5401
|
status: z2.string().default("processing"),
|
|
5293
5402
|
usage: UsageSchema
|
|
5294
5403
|
}).passthrough();
|
|
5404
|
+
var ObjectProSubmitSchema = z2.object({
|
|
5405
|
+
object_id: z2.string().min(1),
|
|
5406
|
+
background_job_id: z2.string().min(1),
|
|
5407
|
+
status: z2.string().default("processing"),
|
|
5408
|
+
usage: UsageSchema
|
|
5409
|
+
}).passthrough();
|
|
5410
|
+
var DirectionSubmissionSchema = z2.object({
|
|
5411
|
+
direction: z2.string(),
|
|
5412
|
+
status: z2.string(),
|
|
5413
|
+
background_job_id: z2.string().nullable().default(null),
|
|
5414
|
+
animation_id: z2.string().nullable().default(null)
|
|
5415
|
+
}).passthrough();
|
|
5416
|
+
var AnimateObjectSubmitSchema = z2.object({
|
|
5417
|
+
animation_group_id: z2.string().min(1),
|
|
5418
|
+
object_id: z2.string().min(1),
|
|
5419
|
+
mode: z2.string(),
|
|
5420
|
+
frame_count: z2.number().int(),
|
|
5421
|
+
display_name: z2.string().nullable().default(null),
|
|
5422
|
+
description: z2.string().default(""),
|
|
5423
|
+
submissions: z2.array(DirectionSubmissionSchema),
|
|
5424
|
+
usage: UsageSchema
|
|
5425
|
+
}).passthrough();
|
|
5295
5426
|
var CharacterAnimationDirectionSchema = z2.object({
|
|
5296
5427
|
direction: z2.string(),
|
|
5297
5428
|
frame_count: z2.number().int().min(0),
|
|
@@ -5534,6 +5665,33 @@ var PixelLabClient = class {
|
|
|
5534
5665
|
);
|
|
5535
5666
|
return { tileset: res.tileset, usage: res.usage ?? null };
|
|
5536
5667
|
}
|
|
5668
|
+
/** A single standalone isometric tile — no connectable set, no candidates. */
|
|
5669
|
+
async createIsometricTile(args) {
|
|
5670
|
+
const body = { description: args.description };
|
|
5671
|
+
if (args.imageWidth != null && args.imageHeight != null) {
|
|
5672
|
+
body.image_size = { width: args.imageWidth, height: args.imageHeight };
|
|
5673
|
+
}
|
|
5674
|
+
if (args.tileSize != null) body.isometric_tile_size = args.tileSize;
|
|
5675
|
+
if (args.tileShape) body.isometric_tile_shape = args.tileShape;
|
|
5676
|
+
if (args.outline) body.outline = args.outline;
|
|
5677
|
+
if (args.shading) body.shading = args.shading;
|
|
5678
|
+
if (args.detail) body.detail = args.detail;
|
|
5679
|
+
if (args.seed != null) body.seed = args.seed;
|
|
5680
|
+
return validateResponse(
|
|
5681
|
+
IsometricTileSubmitSchema,
|
|
5682
|
+
await this.request("/create-isometric-tile", { method: "POST", body: JSON.stringify(body) }),
|
|
5683
|
+
"create isometric tile"
|
|
5684
|
+
);
|
|
5685
|
+
}
|
|
5686
|
+
/** Throws PixelLabError(423) while the tile is still processing. */
|
|
5687
|
+
async getIsometricTile(tileId) {
|
|
5688
|
+
const res = await validateResponse(
|
|
5689
|
+
IsometricTileGetSchema,
|
|
5690
|
+
await this.request(`/isometric-tiles/${tileId}`),
|
|
5691
|
+
"get isometric tile"
|
|
5692
|
+
);
|
|
5693
|
+
return { image: res.image, usage: res.usage ?? null };
|
|
5694
|
+
}
|
|
5537
5695
|
/**
|
|
5538
5696
|
* Synchronous single-image generation. Returns the PNG inline rather than a
|
|
5539
5697
|
* job id, and is the only endpoint that honours a forced palette;
|
|
@@ -5567,6 +5725,81 @@ var PixelLabClient = class {
|
|
|
5567
5725
|
"get object"
|
|
5568
5726
|
);
|
|
5569
5727
|
}
|
|
5728
|
+
/**
|
|
5729
|
+
* A standalone object with no skeleton, one call draws the south sprite
|
|
5730
|
+
* (or takes the author's) and rotates it with v3 — the character
|
|
5731
|
+
* pro-flash pattern, minus `template_id` (objects have no skeleton) and
|
|
5732
|
+
* with a real `n_directions` choice (1 or 8, character pro-flash is
|
|
5733
|
+
* always 8). Async only: `/create-object-pro-flash` has no sync response.
|
|
5734
|
+
*/
|
|
5735
|
+
async createObjectProFlash(args) {
|
|
5736
|
+
const encode = (image) => ({ type: "base64", base64: image.base64, format: image.format });
|
|
5737
|
+
const traits = args.styleTraits;
|
|
5738
|
+
const body = {
|
|
5739
|
+
description: args.description,
|
|
5740
|
+
n_directions: args.directions,
|
|
5741
|
+
...args.reference ? { first_frame: encode(args.reference) } : { image_size: { width: args.size, height: args.size } },
|
|
5742
|
+
...args.view ? { view: args.view } : {},
|
|
5743
|
+
...args.seed != null ? { seed: args.seed } : {},
|
|
5744
|
+
...args.styleReference && args.styleReferenceSize ? {
|
|
5745
|
+
style_image: { image: encode(args.styleReference), size: args.styleReferenceSize },
|
|
5746
|
+
...traits ? {
|
|
5747
|
+
style_options: {
|
|
5748
|
+
...traits.palette !== void 0 ? { color_palette: traits.palette } : {},
|
|
5749
|
+
...traits.outline !== void 0 ? { outline: traits.outline } : {},
|
|
5750
|
+
...traits.detail !== void 0 ? { detail: traits.detail } : {},
|
|
5751
|
+
...traits.shading !== void 0 ? { shading: traits.shading } : {}
|
|
5752
|
+
}
|
|
5753
|
+
} : {}
|
|
5754
|
+
} : {}
|
|
5755
|
+
};
|
|
5756
|
+
return validateResponse(
|
|
5757
|
+
ObjectProSubmitSchema,
|
|
5758
|
+
await this.request("/create-object-pro-flash", { method: "POST", body: JSON.stringify(body) }),
|
|
5759
|
+
"create-object-pro-flash"
|
|
5760
|
+
);
|
|
5761
|
+
}
|
|
5762
|
+
/** A text edit of an existing object, applied to its whole rotation set and stored as a sibling. Always inherits the parent's exact canvas — no size override exists on this endpoint. */
|
|
5763
|
+
async createObjectProState(args) {
|
|
5764
|
+
const raw = await this.request(`/objects/${encodeURIComponent(args.objectId)}/states`, {
|
|
5765
|
+
method: "POST",
|
|
5766
|
+
body: JSON.stringify({
|
|
5767
|
+
edit_description: args.editDescription,
|
|
5768
|
+
...args.stateName ? { state_name: args.stateName } : {},
|
|
5769
|
+
...args.seed != null ? { seed: args.seed } : {}
|
|
5770
|
+
})
|
|
5771
|
+
});
|
|
5772
|
+
return validateResponse(ObjectProSubmitSchema, raw, "create object state");
|
|
5773
|
+
}
|
|
5774
|
+
/**
|
|
5775
|
+
* One direction of one object's animation. Unlike character animation,
|
|
5776
|
+
* this endpoint's own `replace_existing` flag regenerates an
|
|
5777
|
+
* already-animated direction directly (character has no such flag and
|
|
5778
|
+
* pixelkiln instead deletes the prior take itself before resubmitting;
|
|
5779
|
+
* see `submitCharacter`). A 1-direction object must not receive
|
|
5780
|
+
* `directions` at all -- the API 400s if it does.
|
|
5781
|
+
*/
|
|
5782
|
+
async animateObject(args) {
|
|
5783
|
+
const encode = (image) => ({ type: "base64", base64: image.base64, format: image.format });
|
|
5784
|
+
const body = {
|
|
5785
|
+
mode: args.mode,
|
|
5786
|
+
...args.directions === 8 ? { directions: [args.direction] } : {},
|
|
5787
|
+
...args.animationDescription ? { animation_description: args.animationDescription } : {},
|
|
5788
|
+
...args.animationGroupId ? { animation_group_id: args.animationGroupId } : {},
|
|
5789
|
+
...args.displayName ? { display_name: args.displayName } : {},
|
|
5790
|
+
...args.frameCount != null ? { frame_count: args.frameCount } : {},
|
|
5791
|
+
...args.keepFirstFrame === false ? { keep_first_frame: false } : {},
|
|
5792
|
+
...args.startFrame ? { custom_start_frame: encode(args.startFrame) } : {},
|
|
5793
|
+
...args.endFrame ? { end_frame: encode(args.endFrame) } : {},
|
|
5794
|
+
...args.enhancePrompt ? { enhance_prompt: true } : {},
|
|
5795
|
+
...args.replaceExisting ? { replace_existing: true } : {}
|
|
5796
|
+
};
|
|
5797
|
+
const raw = await this.request(`/objects/${encodeURIComponent(args.objectId)}/animations`, {
|
|
5798
|
+
method: "POST",
|
|
5799
|
+
body: JSON.stringify(body)
|
|
5800
|
+
});
|
|
5801
|
+
return validateResponse(AnimateObjectSubmitSchema, raw, "animate object");
|
|
5802
|
+
}
|
|
5570
5803
|
async getMapObject(objectId) {
|
|
5571
5804
|
return validateResponse(
|
|
5572
5805
|
MapObjectSchema,
|
|
@@ -5966,6 +6199,29 @@ function proFlashCharacterCost(width, height, fromReference) {
|
|
|
5966
6199
|
function characterAnimationName(spec) {
|
|
5967
6200
|
return `pixelkiln:${spec.styleId}/${spec.assetId}`;
|
|
5968
6201
|
}
|
|
6202
|
+
function objectAnimationName(spec) {
|
|
6203
|
+
return `pixelkiln:${spec.styleId}/${spec.assetId}`;
|
|
6204
|
+
}
|
|
6205
|
+
function proFlashObjectCost(width, height, directions, fromReference) {
|
|
6206
|
+
const side = Math.max(32, Math.ceil(Math.max(width, height) / 4) * 4);
|
|
6207
|
+
const rotations = Math.max(1, Math.ceil(side * side * directions / 65536));
|
|
6208
|
+
if (fromReference) return rotations;
|
|
6209
|
+
const image = side <= 96 ? 5 : side <= 208 ? 6 : 9;
|
|
6210
|
+
return image + rotations;
|
|
6211
|
+
}
|
|
6212
|
+
function objectProCost(spec) {
|
|
6213
|
+
const object = spec.objectPro;
|
|
6214
|
+
if (!object) return generationCost(spec.width, spec.height, "1dir");
|
|
6215
|
+
const px = spec.width * spec.height;
|
|
6216
|
+
if (object.kind === "animation") {
|
|
6217
|
+
const animation = object.animation;
|
|
6218
|
+
if (animation.mode === "pro") return generationCost(spec.width, spec.height, "1dir");
|
|
6219
|
+
return Math.max(1, Math.ceil(px * animation.frames / 65536));
|
|
6220
|
+
}
|
|
6221
|
+
if (object.kind === "state") return generationCost(spec.width, spec.height, "1dir");
|
|
6222
|
+
const south = object.reference;
|
|
6223
|
+
return south ? proFlashObjectCost(south.width, south.height, object.directions, true) : proFlashObjectCost(spec.width, spec.height, object.directions, false);
|
|
6224
|
+
}
|
|
5969
6225
|
var CHARACTER_TEMPLATES = ["mannequin", "bear", "cat", "dog", "horse", "lion"];
|
|
5970
6226
|
var PRO_FLASH_TEMPLATES = [...CHARACTER_TEMPLATES, "custom"];
|
|
5971
6227
|
function encodeAnimationJob(job) {
|
|
@@ -5982,6 +6238,20 @@ function decodeAnimationJob(jobId) {
|
|
|
5982
6238
|
}
|
|
5983
6239
|
return null;
|
|
5984
6240
|
}
|
|
6241
|
+
function encodeObjectAnimationJob(job) {
|
|
6242
|
+
return `object-animation:${Buffer.from(JSON.stringify(job)).toString("base64url")}`;
|
|
6243
|
+
}
|
|
6244
|
+
function decodeObjectAnimationJob(jobId) {
|
|
6245
|
+
if (!jobId.startsWith("object-animation:")) return null;
|
|
6246
|
+
try {
|
|
6247
|
+
const value = JSON.parse(Buffer.from(jobId.slice("object-animation:".length), "base64url").toString("utf8"));
|
|
6248
|
+
if (typeof value?.objectId === "string" && typeof value.name === "string" && typeof value.direction === "string") {
|
|
6249
|
+
return { objectId: value.objectId, name: value.name, direction: value.direction, jobId: typeof value.jobId === "string" ? value.jobId : null };
|
|
6250
|
+
}
|
|
6251
|
+
} catch {
|
|
6252
|
+
}
|
|
6253
|
+
return null;
|
|
6254
|
+
}
|
|
5985
6255
|
var PixelLabProvider = class _PixelLabProvider {
|
|
5986
6256
|
constructor(client2) {
|
|
5987
6257
|
this.client = client2;
|
|
@@ -6000,7 +6270,7 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
6000
6270
|
return new _PixelLabProvider(new PixelLabClient("download-only"));
|
|
6001
6271
|
}
|
|
6002
6272
|
supports(generator) {
|
|
6003
|
-
return generator === "1dir" || generator === "map" || generator === "pixflux" || generator === "tiles" || generator === "terrain" || generator === "imagePro" || generator === "character";
|
|
6273
|
+
return generator === "1dir" || generator === "map" || generator === "pixflux" || generator === "tiles" || generator === "terrain" || generator === "imagePro" || generator === "character" || generator === "isometricTile" || generator === "objectPro";
|
|
6004
6274
|
}
|
|
6005
6275
|
/**
|
|
6006
6276
|
* `inpaint` (`/inpaint-v3`, a mask) and `image-to-image` (`/edit-images-v2`,
|
|
@@ -6036,12 +6306,15 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
6036
6306
|
const height = spec.revision.sourceHeight ?? spec.height;
|
|
6037
6307
|
return { unit: "generations", amount: generationCost(width, height, "1dir"), candidates: 1 };
|
|
6038
6308
|
}
|
|
6039
|
-
if (spec.generator === "tiles" || spec.generator === "terrain") {
|
|
6309
|
+
if (spec.generator === "tiles" || spec.generator === "terrain" || spec.generator === "isometricTile") {
|
|
6040
6310
|
return { unit: "generations", amount: spec.cost, candidates: spec.candidates };
|
|
6041
6311
|
}
|
|
6042
6312
|
if (spec.generator === "character") {
|
|
6043
6313
|
return { unit: "generations", amount: characterCost(spec), candidates: 1 };
|
|
6044
6314
|
}
|
|
6315
|
+
if (spec.generator === "objectPro") {
|
|
6316
|
+
return { unit: "generations", amount: objectProCost(spec), candidates: 1 };
|
|
6317
|
+
}
|
|
6045
6318
|
return {
|
|
6046
6319
|
unit: "generations",
|
|
6047
6320
|
amount: generationCost(spec.width, spec.height, spec.generator),
|
|
@@ -6067,19 +6340,19 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
6067
6340
|
);
|
|
6068
6341
|
}
|
|
6069
6342
|
if (spec.generator === "map") {
|
|
6070
|
-
requirePixelLabOption("view", spec.view, ["low top-down", "high top-down", "side"]);
|
|
6071
|
-
requirePixelLabOption("outline", spec.outline, [
|
|
6343
|
+
requirePixelLabOption("map", "view", spec.view, ["low top-down", "high top-down", "side"]);
|
|
6344
|
+
requirePixelLabOption("map", "outline", spec.outline, [
|
|
6072
6345
|
"single color outline",
|
|
6073
6346
|
"selective outline",
|
|
6074
6347
|
"lineless"
|
|
6075
6348
|
]);
|
|
6076
|
-
requirePixelLabOption("shading", spec.shading, [
|
|
6349
|
+
requirePixelLabOption("map", "shading", spec.shading, [
|
|
6077
6350
|
"flat shading",
|
|
6078
6351
|
"basic shading",
|
|
6079
6352
|
"medium shading",
|
|
6080
6353
|
"detailed shading"
|
|
6081
6354
|
]);
|
|
6082
|
-
requirePixelLabOption("detail", spec.detail, [
|
|
6355
|
+
requirePixelLabOption("map", "detail", spec.detail, [
|
|
6083
6356
|
"low detail",
|
|
6084
6357
|
"medium detail",
|
|
6085
6358
|
"high detail"
|
|
@@ -6087,7 +6360,7 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
6087
6360
|
}
|
|
6088
6361
|
if (spec.generator === "terrain") {
|
|
6089
6362
|
if (spec.outline) {
|
|
6090
|
-
requirePixelLabOption("outline", spec.outline, [
|
|
6363
|
+
requirePixelLabOption("terrain", "outline", spec.outline, [
|
|
6091
6364
|
"single color black outline",
|
|
6092
6365
|
"single color outline",
|
|
6093
6366
|
"selective outline",
|
|
@@ -6095,7 +6368,7 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
6095
6368
|
]);
|
|
6096
6369
|
}
|
|
6097
6370
|
if (spec.shading) {
|
|
6098
|
-
requirePixelLabOption("shading", spec.shading, [
|
|
6371
|
+
requirePixelLabOption("terrain", "shading", spec.shading, [
|
|
6099
6372
|
"flat shading",
|
|
6100
6373
|
"basic shading",
|
|
6101
6374
|
"medium shading",
|
|
@@ -6104,7 +6377,7 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
6104
6377
|
]);
|
|
6105
6378
|
}
|
|
6106
6379
|
if (spec.detail) {
|
|
6107
|
-
requirePixelLabOption("detail", spec.detail, ["low detail", "medium detail", "highly detailed"]);
|
|
6380
|
+
requirePixelLabOption("terrain", "detail", spec.detail, ["low detail", "medium detail", "highly detailed"]);
|
|
6108
6381
|
}
|
|
6109
6382
|
if (spec.terrainShapeStyle && spec.terrainMode === "pro") {
|
|
6110
6383
|
throw new Error(
|
|
@@ -6125,7 +6398,32 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
6125
6398
|
);
|
|
6126
6399
|
}
|
|
6127
6400
|
}
|
|
6401
|
+
if (spec.generator === "isometricTile") {
|
|
6402
|
+
if (spec.outline) {
|
|
6403
|
+
requirePixelLabOption("isometricTile", "outline", spec.outline, [
|
|
6404
|
+
"single color outline",
|
|
6405
|
+
"selective outline",
|
|
6406
|
+
"lineless"
|
|
6407
|
+
]);
|
|
6408
|
+
}
|
|
6409
|
+
if (spec.shading) {
|
|
6410
|
+
requirePixelLabOption("isometricTile", "shading", spec.shading, [
|
|
6411
|
+
"flat shading",
|
|
6412
|
+
"basic shading",
|
|
6413
|
+
"medium shading",
|
|
6414
|
+
"detailed shading",
|
|
6415
|
+
"highly detailed shading"
|
|
6416
|
+
]);
|
|
6417
|
+
}
|
|
6418
|
+
if (spec.detail) {
|
|
6419
|
+
requirePixelLabOption("isometricTile", "detail", spec.detail, ["low detail", "medium detail", "highly detailed"]);
|
|
6420
|
+
}
|
|
6421
|
+
if (spec.width < 16 || spec.width > 64) {
|
|
6422
|
+
throw new Error(`PixelLab isometricTile: size must be 16 to 64 pixels, got ${spec.width}`);
|
|
6423
|
+
}
|
|
6424
|
+
}
|
|
6128
6425
|
if (spec.generator === "character") this.validateCharacter(spec, styleImages);
|
|
6426
|
+
if (spec.generator === "objectPro") this.validateObjectPro(spec, styleImages);
|
|
6129
6427
|
if ((spec.generator === "map" || spec.generator === "pixflux") && styleImages.length) {
|
|
6130
6428
|
throw new Error(`PixelLab ${spec.generator} does not support style images`);
|
|
6131
6429
|
}
|
|
@@ -6139,6 +6437,11 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
6139
6437
|
"PixelLab imagePro does not support style images yet; /generate-image-v2's own reference_images and style_image are not modeled here"
|
|
6140
6438
|
);
|
|
6141
6439
|
}
|
|
6440
|
+
if (spec.generator === "isometricTile" && styleImages.length) {
|
|
6441
|
+
throw new Error(
|
|
6442
|
+
"PixelLab isometricTile does not support style images; /create-isometric-tile has no such field (its own init_image/color_image are not modeled here yet)"
|
|
6443
|
+
);
|
|
6444
|
+
}
|
|
6142
6445
|
for (const image of styleImages) {
|
|
6143
6446
|
if (image.width > 256 || image.height > 256) {
|
|
6144
6447
|
throw new Error(
|
|
@@ -6281,6 +6584,63 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
6281
6584
|
}
|
|
6282
6585
|
}
|
|
6283
6586
|
}
|
|
6587
|
+
validateObjectPro(spec, styleImages = []) {
|
|
6588
|
+
const object = spec.objectPro;
|
|
6589
|
+
if (!object) throw new Error(`${spec.styleId}/${spec.assetId} has no objectPro shape`);
|
|
6590
|
+
const label = `${spec.styleId}/${spec.assetId}`;
|
|
6591
|
+
if (object.kind === "base" && object.reference && styleImages.length) {
|
|
6592
|
+
throw new Error(`${label}: a base rotating its own reference sprite takes no style image`);
|
|
6593
|
+
}
|
|
6594
|
+
if (object.reference && (object.reference.width > 256 || object.reference.height > 256)) {
|
|
6595
|
+
throw new Error(`${label}: reference is ${object.reference.width}x${object.reference.height}; PixelLab objectPro takes up to 256px`);
|
|
6596
|
+
}
|
|
6597
|
+
if (styleImages.length) {
|
|
6598
|
+
if (styleImages.length > 1) throw new Error(`PixelLab objectPro takes one style image`);
|
|
6599
|
+
const image = styleImages[0];
|
|
6600
|
+
if (image.width > 256 || image.height > 256) {
|
|
6601
|
+
throw new Error(`PixelLab objectPro style image is ${image.width}x${image.height}; the limit is 256px`);
|
|
6602
|
+
}
|
|
6603
|
+
if (image.width > spec.width || image.height > spec.height) {
|
|
6604
|
+
throw new Error(
|
|
6605
|
+
`PixelLab objectPro style image is ${image.width}x${image.height} but the object is ${spec.width}x${spec.height}; crop the image to its subject or raise the style's size`
|
|
6606
|
+
);
|
|
6607
|
+
}
|
|
6608
|
+
}
|
|
6609
|
+
if (object.kind === "base" && (spec.width < 16 || spec.height < 16 || spec.width > 256 || spec.height > 256)) {
|
|
6610
|
+
throw new Error(`PixelLab objectPro bases must be between 16 and 256 pixels`);
|
|
6611
|
+
}
|
|
6612
|
+
if (object.kind === "base" && (spec.width % 4 || spec.height % 4)) {
|
|
6613
|
+
throw new Error(`PixelLab objectPro bases are sized in multiples of 4 pixels; ${spec.width}x${spec.height} is not`);
|
|
6614
|
+
}
|
|
6615
|
+
if (object.kind !== "base" && !object.parentAssetId) {
|
|
6616
|
+
throw new Error(`${spec.styleId}/${spec.assetId} is a ${object.kind} with no parent`);
|
|
6617
|
+
}
|
|
6618
|
+
if (object.animation) {
|
|
6619
|
+
const allowed = object.directions === 1 ? ["south"] : CHARACTER_DIRECTIONS_8;
|
|
6620
|
+
if (!allowed.includes(object.animation.direction)) {
|
|
6621
|
+
throw new Error(
|
|
6622
|
+
`${label} animates "${object.animation.direction}", but its object has ${object.directions} direction${object.directions === 1 ? "" : "s"}: ${allowed.join(", ")}`
|
|
6623
|
+
);
|
|
6624
|
+
}
|
|
6625
|
+
if (!spec.prompt.trim()) {
|
|
6626
|
+
throw new Error(`${label} needs a prompt describing the motion`);
|
|
6627
|
+
}
|
|
6628
|
+
const { startFrame, endFrame } = object.animation;
|
|
6629
|
+
for (const [name, image] of [["start frame", startFrame], ["end frame", endFrame]]) {
|
|
6630
|
+
if (image && (image.width > 256 || image.height > 256)) {
|
|
6631
|
+
throw new Error(`${label}: ${name} is ${image.width}x${image.height}; PixelLab v3 takes up to 256px`);
|
|
6632
|
+
}
|
|
6633
|
+
}
|
|
6634
|
+
if (endFrame) {
|
|
6635
|
+
const start = startFrame ?? (object.parentFile && existsSync7(object.parentFile) ? imageMetadata(readFileSync2(object.parentFile)) : null);
|
|
6636
|
+
if (start && (start.width !== endFrame.width || start.height !== endFrame.height)) {
|
|
6637
|
+
throw new Error(
|
|
6638
|
+
`${label}: end frame is ${endFrame.width}x${endFrame.height} but the ${startFrame ? "start frame" : "object's rotation"} is ${start.width}x${start.height}; PixelLab interpolates between frames of one size`
|
|
6639
|
+
);
|
|
6640
|
+
}
|
|
6641
|
+
}
|
|
6642
|
+
}
|
|
6643
|
+
}
|
|
6284
6644
|
/**
|
|
6285
6645
|
* A base is one request that answers with the character id; a state is
|
|
6286
6646
|
* the same with the parent's id; an animation names itself after the
|
|
@@ -6374,6 +6734,78 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
6374
6734
|
metadata: { character: { kind: "animation", characterId: parentId, animationName: name, direction: animation.direction, mode: animation.mode, backgroundJobIds: res.background_job_ids } }
|
|
6375
6735
|
};
|
|
6376
6736
|
}
|
|
6737
|
+
/**
|
|
6738
|
+
* Same shape as `submitCharacter`, minus the mode branching (objectPro has
|
|
6739
|
+
* exactly one base-creation path) and minus the client-side "find and
|
|
6740
|
+
* delete the prior take" dance for animation: `/objects/{id}/animations`
|
|
6741
|
+
* has its own `replace_existing` flag, so this always passes it rather
|
|
6742
|
+
* than fetching the object first to check.
|
|
6743
|
+
*/
|
|
6744
|
+
async submitObjectPro(spec, styleImages, context) {
|
|
6745
|
+
const object = spec.objectPro;
|
|
6746
|
+
if (object.kind === "base") {
|
|
6747
|
+
const styleImage = styleImages[0];
|
|
6748
|
+
const res2 = await this.client.createObjectProFlash({
|
|
6749
|
+
description: spec.prompt,
|
|
6750
|
+
directions: object.directions,
|
|
6751
|
+
size: spec.width,
|
|
6752
|
+
view: spec.view,
|
|
6753
|
+
seed: spec.seed,
|
|
6754
|
+
reference: object.reference ? readPose(spec, "reference sprite", object.reference) : void 0,
|
|
6755
|
+
styleReference: styleImage ? { base64: styleImage.base64, format: styleImage.format } : void 0,
|
|
6756
|
+
styleReferenceSize: styleImage ? { width: styleImage.width, height: styleImage.height } : void 0,
|
|
6757
|
+
styleTraits: object.styleTraits
|
|
6758
|
+
});
|
|
6759
|
+
return { jobId: res2.object_id, metadata: { objectPro: { kind: "base", objectId: res2.object_id, directions: object.directions, backgroundJobId: res2.background_job_id } } };
|
|
6760
|
+
}
|
|
6761
|
+
const parentId = context?.parentObjectId;
|
|
6762
|
+
if (!parentId) {
|
|
6763
|
+
throw new Error(
|
|
6764
|
+
`${spec.styleId}/${spec.assetId} is a ${object.kind} of ${object.parentAssetId}, which has no PixelLab object id in the lockfile yet; generate the parent first`
|
|
6765
|
+
);
|
|
6766
|
+
}
|
|
6767
|
+
if (object.kind === "state") {
|
|
6768
|
+
const res2 = await this.client.createObjectProState({
|
|
6769
|
+
objectId: parentId,
|
|
6770
|
+
editDescription: spec.prompt,
|
|
6771
|
+
stateName: spec.assetId.slice(0, 50),
|
|
6772
|
+
seed: spec.seed
|
|
6773
|
+
});
|
|
6774
|
+
return { jobId: res2.object_id, metadata: { objectPro: { kind: "state", objectId: res2.object_id, parentObjectId: parentId, directions: object.directions, backgroundJobId: res2.background_job_id } } };
|
|
6775
|
+
}
|
|
6776
|
+
const animation = object.animation;
|
|
6777
|
+
const name = objectAnimationName(spec);
|
|
6778
|
+
const res = await this.client.animateObject({
|
|
6779
|
+
objectId: parentId,
|
|
6780
|
+
directions: object.directions,
|
|
6781
|
+
direction: animation.direction,
|
|
6782
|
+
animationDescription: spec.prompt.trim() || void 0,
|
|
6783
|
+
displayName: name,
|
|
6784
|
+
mode: animation.mode === "pro" ? "pro" : "v3",
|
|
6785
|
+
frameCount: animation.frames,
|
|
6786
|
+
keepFirstFrame: animation.keepFirstFrame,
|
|
6787
|
+
startFrame: animation.startFrame ? readPose(spec, "start frame", animation.startFrame) : void 0,
|
|
6788
|
+
endFrame: animation.endFrame ? readPose(spec, "end frame", animation.endFrame) : void 0,
|
|
6789
|
+
enhancePrompt: animation.enhancePrompt,
|
|
6790
|
+
replaceExisting: true
|
|
6791
|
+
});
|
|
6792
|
+
const submission = res.submissions.find((s) => s.direction === animation.direction) ?? res.submissions[0];
|
|
6793
|
+
const job = { objectId: parentId, name, direction: animation.direction, jobId: submission?.background_job_id ?? null };
|
|
6794
|
+
return {
|
|
6795
|
+
jobId: encodeObjectAnimationJob(job),
|
|
6796
|
+
metadata: {
|
|
6797
|
+
objectPro: {
|
|
6798
|
+
kind: "animation",
|
|
6799
|
+
objectId: parentId,
|
|
6800
|
+
animationName: name,
|
|
6801
|
+
direction: animation.direction,
|
|
6802
|
+
mode: animation.mode,
|
|
6803
|
+
animationGroupId: res.animation_group_id,
|
|
6804
|
+
animationId: submission?.animation_id ?? null
|
|
6805
|
+
}
|
|
6806
|
+
}
|
|
6807
|
+
};
|
|
6808
|
+
}
|
|
6377
6809
|
async pollCharacter(jobId, context) {
|
|
6378
6810
|
const animation = decodeAnimationJob(jobId);
|
|
6379
6811
|
if (animation) return this.pollCharacterAnimation(animation, context);
|
|
@@ -6457,10 +6889,93 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
6457
6889
|
if (found) return review(found.frames, found.animationId, found.groupId, null);
|
|
6458
6890
|
return cleanedUp ? { status: "failed", error: `animation job is gone upstream and no "${job.name}" ${job.direction} animation exists on the character` } : { status: "processing" };
|
|
6459
6891
|
}
|
|
6892
|
+
async pollObjectPro(jobId, context) {
|
|
6893
|
+
const animation = decodeObjectAnimationJob(jobId);
|
|
6894
|
+
if (animation) return this.pollObjectProAnimation(animation, context);
|
|
6895
|
+
const object = await this.client.getObject(jobId);
|
|
6896
|
+
if (object.status === "failed") return { status: "failed", error: "object generation failed upstream" };
|
|
6897
|
+
if (object.status !== "completed" || !object.rotation_urls) return { status: "processing" };
|
|
6898
|
+
const sources = objectRotationSources(object);
|
|
6899
|
+
if (!sources.length) return { status: "failed", error: "object completed with no rotation images" };
|
|
6900
|
+
const backgroundJobId = context?.metadata?.objectPro?.backgroundJobId;
|
|
6901
|
+
return {
|
|
6902
|
+
status: "ready",
|
|
6903
|
+
objectId: object.id,
|
|
6904
|
+
sourceUrl: sources[0].url,
|
|
6905
|
+
sources,
|
|
6906
|
+
metadata: {
|
|
6907
|
+
objectPro: {
|
|
6908
|
+
objectId: object.id,
|
|
6909
|
+
groupId: object.group_id ?? null,
|
|
6910
|
+
name: object.name,
|
|
6911
|
+
stateName: object.state_name ?? null,
|
|
6912
|
+
directions: object.directions,
|
|
6913
|
+
size: object.size
|
|
6914
|
+
}
|
|
6915
|
+
},
|
|
6916
|
+
billed: await this.billedForJob(backgroundJobId)
|
|
6917
|
+
};
|
|
6918
|
+
}
|
|
6919
|
+
/**
|
|
6920
|
+
* Same resilience shape as `pollCharacterAnimation`: the background job is
|
|
6921
|
+
* the authoritative record while it lasts, the object's own `animations`
|
|
6922
|
+
* list (matched by the `pixelkiln:` display name this asset submitted, or
|
|
6923
|
+
* a recorded animation id) is the fallback once PixelLab cleans the job
|
|
6924
|
+
* up. Never declares failure from absence alone -- a `rate_limited`
|
|
6925
|
+
* `DirectionSubmission` has no background job id at all, so this keeps
|
|
6926
|
+
* reporting "processing" rather than guessing a job that never existed
|
|
6927
|
+
* has failed.
|
|
6928
|
+
*/
|
|
6929
|
+
async pollObjectProAnimation(job, context) {
|
|
6930
|
+
const fps = context?.spec?.objectPro?.animation?.fps ?? 8;
|
|
6931
|
+
const review = (frames, animationId, groupId, billed) => ({
|
|
6932
|
+
status: "review-set",
|
|
6933
|
+
objectId: `${job.objectId}#${groupId ?? animationId ?? job.name}`,
|
|
6934
|
+
frameUrls: frames,
|
|
6935
|
+
sources: frames.map((url, index) => ({ url, role: `frame-${String(index).padStart(2, "0")}` })),
|
|
6936
|
+
fps,
|
|
6937
|
+
metadata: {
|
|
6938
|
+
frameSet: { fps, count: frames.length },
|
|
6939
|
+
objectPro: {
|
|
6940
|
+
kind: "animation",
|
|
6941
|
+
objectId: job.objectId,
|
|
6942
|
+
animationId,
|
|
6943
|
+
animationGroupId: groupId,
|
|
6944
|
+
animationName: job.name,
|
|
6945
|
+
direction: job.direction
|
|
6946
|
+
}
|
|
6947
|
+
},
|
|
6948
|
+
billed
|
|
6949
|
+
});
|
|
6950
|
+
if (job.jobId) {
|
|
6951
|
+
const status = await this.client.getBackgroundJob(job.jobId).catch((err) => {
|
|
6952
|
+
if (err instanceof PixelLabError && err.status === 404) return null;
|
|
6953
|
+
throw err;
|
|
6954
|
+
});
|
|
6955
|
+
if (status) {
|
|
6956
|
+
if (status.status === "failed") return { status: "failed", error: "animation generation failed upstream" };
|
|
6957
|
+
if (status.status !== "completed") return { status: "processing" };
|
|
6958
|
+
const done = status.last_response ?? {};
|
|
6959
|
+
const frames = done.storage_urls?.frames;
|
|
6960
|
+
const animationId = typeof done.animation_id === "string" ? done.animation_id : null;
|
|
6961
|
+
if (Array.isArray(frames) && frames.length && frames.every((f) => typeof f === "string")) {
|
|
6962
|
+
const object2 = await this.client.getObject(job.objectId);
|
|
6963
|
+
const group = findObjectAnimation(object2, job.name, job.direction, animationId);
|
|
6964
|
+
return review(frames, animationId, group?.groupId ?? null, billedFromUsage(status.usage));
|
|
6965
|
+
}
|
|
6966
|
+
}
|
|
6967
|
+
}
|
|
6968
|
+
const recorded = context?.metadata?.objectPro?.animationId ?? null;
|
|
6969
|
+
const object = await this.client.getObject(job.objectId);
|
|
6970
|
+
const found = findObjectAnimation(object, job.name, job.direction, recorded);
|
|
6971
|
+
if (found) return review(found.frames, found.animationId, found.groupId, null);
|
|
6972
|
+
return { status: "processing" };
|
|
6973
|
+
}
|
|
6460
6974
|
async submit(spec, styleImages, context) {
|
|
6461
6975
|
this.validate(spec, styleImages);
|
|
6462
6976
|
if (spec.revision) return this.submitRevision(spec);
|
|
6463
6977
|
if (spec.generator === "character") return this.submitCharacter(spec, styleImages, context);
|
|
6978
|
+
if (spec.generator === "objectPro") return this.submitObjectPro(spec, styleImages, context);
|
|
6464
6979
|
if (spec.generator === "pixflux") {
|
|
6465
6980
|
const swatch = spec.palette.length ? paletteSwatch(spec.palette).toString("base64") : void 0;
|
|
6466
6981
|
const { png } = await this.client.createImagePixflux({
|
|
@@ -6521,6 +7036,20 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
6521
7036
|
});
|
|
6522
7037
|
return { jobId: res2.tileset_id, metadata: { backgroundJobId: res2.background_job_id } };
|
|
6523
7038
|
}
|
|
7039
|
+
if (spec.generator === "isometricTile") {
|
|
7040
|
+
const res2 = await this.client.createIsometricTile({
|
|
7041
|
+
description: spec.prompt,
|
|
7042
|
+
imageWidth: spec.width,
|
|
7043
|
+
imageHeight: spec.height,
|
|
7044
|
+
tileSize: spec.isometricTileSize,
|
|
7045
|
+
tileShape: spec.isometricTileShape,
|
|
7046
|
+
outline: spec.outline,
|
|
7047
|
+
shading: spec.shading,
|
|
7048
|
+
detail: spec.detail,
|
|
7049
|
+
seed: spec.seed
|
|
7050
|
+
});
|
|
7051
|
+
return { jobId: res2.tile_id, metadata: { backgroundJobId: res2.background_job_id } };
|
|
7052
|
+
}
|
|
6524
7053
|
if (spec.generator === "imagePro") {
|
|
6525
7054
|
const res2 = await this.client.createImagePro({
|
|
6526
7055
|
description: spec.prompt,
|
|
@@ -6637,8 +7166,10 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
6637
7166
|
if (generator === "map") return this.pollMap(jobId, context);
|
|
6638
7167
|
if (generator === "tiles") return this.pollTiles(jobId, Boolean(context?.tileFeature), context);
|
|
6639
7168
|
if (generator === "terrain") return this.pollTerrain(jobId, context);
|
|
7169
|
+
if (generator === "isometricTile") return this.pollIsometricTile(jobId, context);
|
|
6640
7170
|
if (generator === "imagePro") return this.pollImagePro(jobId);
|
|
6641
7171
|
if (generator === "character") return this.pollCharacter(jobId, context);
|
|
7172
|
+
if (generator === "objectPro") return this.pollObjectPro(jobId, context);
|
|
6642
7173
|
const backgroundJobId = context?.metadata?.backgroundJobId;
|
|
6643
7174
|
const obj = await this.client.getObject(jobId);
|
|
6644
7175
|
if (obj.status === "review") {
|
|
@@ -6847,6 +7378,31 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
6847
7378
|
throw err;
|
|
6848
7379
|
}
|
|
6849
7380
|
}
|
|
7381
|
+
/**
|
|
7382
|
+
* `/create-isometric-tile` reports progress the same way `/create-tileset`
|
|
7383
|
+
* does (423 while drawing, 200 once finished, no `status` field to poll),
|
|
7384
|
+
* and its image also comes back embedded as base64 rather than a
|
|
7385
|
+
* `storage_urls` link. One call is one tile with nothing to review, so
|
|
7386
|
+
* this goes straight to "ready" like a connectable `tiles`/`terrain` set.
|
|
7387
|
+
*/
|
|
7388
|
+
async pollIsometricTile(tileId, context) {
|
|
7389
|
+
try {
|
|
7390
|
+
const { image, usage } = await this.client.getIsometricTile(tileId);
|
|
7391
|
+
const backgroundJobId = context?.metadata?.backgroundJobId;
|
|
7392
|
+
const billed = billedFromUsage(usage) ?? await this.billedForJob(backgroundJobId);
|
|
7393
|
+
const file = path12.join(_PixelLabProvider.cacheDir(), `${tileId}.png`);
|
|
7394
|
+
writeFileSync(file, Buffer.from(image.base64, "base64"));
|
|
7395
|
+
return {
|
|
7396
|
+
status: "ready",
|
|
7397
|
+
objectId: tileId,
|
|
7398
|
+
sourceUrl: `file://${file}`,
|
|
7399
|
+
billed
|
|
7400
|
+
};
|
|
7401
|
+
} catch (err) {
|
|
7402
|
+
if (err instanceof PixelLabError && err.status === 423) return { status: "processing" };
|
|
7403
|
+
throw err;
|
|
7404
|
+
}
|
|
7405
|
+
}
|
|
6850
7406
|
async selectCandidate(jobId, index, commonTag, generator) {
|
|
6851
7407
|
if (generator === "tiles") {
|
|
6852
7408
|
const set = await this.client.getTilesPro(jobId);
|
|
@@ -6963,9 +7519,9 @@ function remoteCharacter(character) {
|
|
|
6963
7519
|
animationCount: character.animation_count
|
|
6964
7520
|
};
|
|
6965
7521
|
}
|
|
6966
|
-
function requirePixelLabOption(name, value, allowed) {
|
|
7522
|
+
function requirePixelLabOption(generator, name, value, allowed) {
|
|
6967
7523
|
if (value != null && !allowed.includes(value)) {
|
|
6968
|
-
throw new Error(`PixelLab
|
|
7524
|
+
throw new Error(`PixelLab ${generator} ${name} must be one of: ${allowed.join(", ")}`);
|
|
6969
7525
|
}
|
|
6970
7526
|
}
|
|
6971
7527
|
function tilesInIndexOrder(urls) {
|
|
@@ -7031,6 +7587,28 @@ function findAnimation(character, name, direction, animationId) {
|
|
|
7031
7587
|
}
|
|
7032
7588
|
return null;
|
|
7033
7589
|
}
|
|
7590
|
+
function objectRotationSources(object) {
|
|
7591
|
+
const order = object.directions === 1 ? ["south"] : CHARACTER_DIRECTIONS_8;
|
|
7592
|
+
const sources = [];
|
|
7593
|
+
for (const direction of order) {
|
|
7594
|
+
const url = object.rotation_urls?.[direction];
|
|
7595
|
+
if (url) sources.push({ url, role: direction });
|
|
7596
|
+
}
|
|
7597
|
+
return sources;
|
|
7598
|
+
}
|
|
7599
|
+
function findObjectAnimation(object, name, direction, animationId) {
|
|
7600
|
+
for (const group of object.animations) {
|
|
7601
|
+
const match = group.directions.find((d) => d.direction === direction);
|
|
7602
|
+
const frames = match ? match.storage_urls.frames : void 0;
|
|
7603
|
+
if (!match || !Array.isArray(frames) || !frames.length || !frames.every((f) => typeof f === "string")) continue;
|
|
7604
|
+
const byName = group.display_name === name;
|
|
7605
|
+
const byId = animationId !== null && frames.some((url) => url.includes(`/animations/${animationId}/`));
|
|
7606
|
+
if (!byName && !byId) continue;
|
|
7607
|
+
const fromUrl = /\/animations\/([^/]+)\//.exec(frames[0])?.[1] ?? null;
|
|
7608
|
+
return { frames, groupId: group.animation_group_id, animationId: animationId ?? fromUrl };
|
|
7609
|
+
}
|
|
7610
|
+
return null;
|
|
7611
|
+
}
|
|
7034
7612
|
|
|
7035
7613
|
// src/providers/retrodiffusion.ts
|
|
7036
7614
|
var DEFAULT_BASE_URL2 = "https://api.retrodiffusion.ai/v1";
|
|
@@ -8206,14 +8784,22 @@ async function resolveSpecs(loaded, filter) {
|
|
|
8206
8784
|
size = style.terrainTileSize ?? 16;
|
|
8207
8785
|
width = size;
|
|
8208
8786
|
height = size;
|
|
8787
|
+
} else if (generator === "isometricTile") {
|
|
8788
|
+
size = asset.size ?? style.size ?? 32;
|
|
8789
|
+
width = size;
|
|
8790
|
+
height = size;
|
|
8791
|
+
} else if (generator === "objectPro") {
|
|
8792
|
+
size = asset.size ?? style.size ?? 64;
|
|
8793
|
+
width = size;
|
|
8794
|
+
height = size;
|
|
8209
8795
|
} else {
|
|
8210
8796
|
width = asset.width ?? style.size ?? 64;
|
|
8211
8797
|
height = asset.height ?? style.size ?? 64;
|
|
8212
8798
|
size = Math.max(width, height);
|
|
8213
8799
|
}
|
|
8214
|
-
if ((asset.state || asset.animation) && generator !== "character") {
|
|
8800
|
+
if ((asset.state || asset.animation) && generator !== "character" && generator !== "objectPro") {
|
|
8215
8801
|
throw new Error(
|
|
8216
|
-
`assets.${assetId}: ${asset.state ? "state" : "animation"} needs a character style; "${styleId}" generates ${generator}`
|
|
8802
|
+
`assets.${assetId}: ${asset.state ? "state" : "animation"} needs a character or objectPro style; "${styleId}" generates ${generator}`
|
|
8217
8803
|
);
|
|
8218
8804
|
}
|
|
8219
8805
|
const characterKind = asset.animation ? "animation" : asset.state ? "state" : "base";
|
|
@@ -8234,7 +8820,7 @@ async function resolveSpecs(loaded, filter) {
|
|
|
8234
8820
|
terrainUpperDescription = wrap(parsed.upper);
|
|
8235
8821
|
terrainTransitionDescription = parsed.transition ? wrap(parsed.transition) : void 0;
|
|
8236
8822
|
prompt = subject.trim();
|
|
8237
|
-
} else if (generator === "character" && characterKind !== "base") {
|
|
8823
|
+
} else if ((generator === "character" || generator === "objectPro") && characterKind !== "base") {
|
|
8238
8824
|
prompt = subject.trim();
|
|
8239
8825
|
} else {
|
|
8240
8826
|
prompt = [style.promptPrefix, subject, style.promptSuffix].map((p) => p.trim()).filter(Boolean).join(", ");
|
|
@@ -8299,8 +8885,11 @@ async function resolveSpecs(loaded, filter) {
|
|
|
8299
8885
|
terrainRaggedness: generator === "terrain" ? style.terrainRaggedness : void 0,
|
|
8300
8886
|
terrainTransitionSize: generator === "terrain" ? style.terrainTransitionSize : void 0,
|
|
8301
8887
|
terrainView: generator === "terrain" ? style.terrainView : void 0,
|
|
8888
|
+
isometricTileSize: generator === "isometricTile" ? style.isometricTileSize : void 0,
|
|
8889
|
+
isometricTileShape: generator === "isometricTile" ? style.isometricTileShape : void 0,
|
|
8302
8890
|
...generator === "character" ? { character: await resolveCharacterShape(asset, style, characterKind, { root, load: loadStyleImage }) } : {},
|
|
8303
|
-
|
|
8891
|
+
...generator === "objectPro" ? { objectPro: await resolveObjectProShape(assetId, asset, style, characterKind, { root, load: loadStyleImage }) } : {},
|
|
8892
|
+
cost: generator === "tiles" ? tilesCost(tileSize, tileVariations) : generator === "terrain" ? tilesCost(size, terrainTiles) : generator === "isometricTile" ? isometricTileCost() : generationCost(width, height, generator),
|
|
8304
8893
|
costUnit: "generations",
|
|
8305
8894
|
candidates: generator === "tiles" ? tileVariations : generator === "terrain" ? terrainTiles : generator === "1dir" || generator === "imagePro" ? candidateCount(size) : 1
|
|
8306
8895
|
};
|
|
@@ -8365,6 +8954,18 @@ async function resolveSpecs(loaded, filter) {
|
|
|
8365
8954
|
} else if (sourceSpec.character) {
|
|
8366
8955
|
resolved.character = { ...sourceSpec.character };
|
|
8367
8956
|
}
|
|
8957
|
+
if (sourceSpec.objectPro?.kind === "animation") {
|
|
8958
|
+
const facing = sourceSpec.objectPro.animation.direction;
|
|
8959
|
+
const mirrored = MIRRORED_DIRECTION[facing];
|
|
8960
|
+
if (mirrored === facing) {
|
|
8961
|
+
throw new Error(
|
|
8962
|
+
`assets.${assetId}: mirroring ${asset.mirror} gives another ${facing}-facing loop; mirror a loop facing east, west, or a diagonal`
|
|
8963
|
+
);
|
|
8964
|
+
}
|
|
8965
|
+
resolved.objectPro = { ...sourceSpec.objectPro, animation: { ...sourceSpec.objectPro.animation, direction: mirrored } };
|
|
8966
|
+
} else if (sourceSpec.objectPro) {
|
|
8967
|
+
resolved.objectPro = { ...sourceSpec.objectPro };
|
|
8968
|
+
}
|
|
8368
8969
|
resolved.mirror = { sourceAssetId: asset.mirror, sourceSpec };
|
|
8369
8970
|
resolved.generator = sourceSpec.generator;
|
|
8370
8971
|
resolved.width = sourceSpec.width;
|
|
@@ -8406,6 +9007,23 @@ async function resolveSpecs(loaded, filter) {
|
|
|
8406
9007
|
directions: resolved.character.kind === "base" ? resolved.character.directions : parentSpec.character?.directions ?? resolved.character.directions
|
|
8407
9008
|
};
|
|
8408
9009
|
}
|
|
9010
|
+
if (resolved.objectPro && characterParent) {
|
|
9011
|
+
const parentSpec = await finalize(characterParent);
|
|
9012
|
+
if (!parentSpec.objectPro) {
|
|
9013
|
+
throw new Error(`assets.${assetId}: ${characterParent} is not an objectPro asset`);
|
|
9014
|
+
}
|
|
9015
|
+
const parentDirections = parentSpec.objectPro.directions;
|
|
9016
|
+
const parentFile = memberPath(parentSpec.outFile, "south", 0, parentDirections, MediaType.PNG);
|
|
9017
|
+
resolved.objectPro = {
|
|
9018
|
+
...resolved.objectPro,
|
|
9019
|
+
parentAssetId: characterParent,
|
|
9020
|
+
parentSpec,
|
|
9021
|
+
parentFile,
|
|
9022
|
+
parentSha256: existsSync8(parentFile) ? await sha256File(parentFile) : null,
|
|
9023
|
+
// A state and its animations keep the base's own rotation count.
|
|
9024
|
+
directions: resolved.objectPro.kind === "base" ? resolved.objectPro.directions : parentSpec.objectPro.directions
|
|
9025
|
+
};
|
|
9026
|
+
}
|
|
8409
9027
|
if (asset.revision) {
|
|
8410
9028
|
if (!activeProvider.supportsRevision?.(asset.revision.mode)) {
|
|
8411
9029
|
throw new Error(
|
|
@@ -8588,6 +9206,60 @@ async function resolveCharacterShape(asset, style, kind, files) {
|
|
|
8588
9206
|
}
|
|
8589
9207
|
return shape;
|
|
8590
9208
|
}
|
|
9209
|
+
async function resolveObjectProShape(assetId, asset, style, kind, files) {
|
|
9210
|
+
const directions = style.objectDirections ?? 8;
|
|
9211
|
+
const shape = {
|
|
9212
|
+
kind,
|
|
9213
|
+
directions,
|
|
9214
|
+
...style.styleTraits ? { styleTraits: style.styleTraits } : {}
|
|
9215
|
+
};
|
|
9216
|
+
if (asset.reference) {
|
|
9217
|
+
const south = typeof asset.reference === "string" ? asset.reference : asset.reference.south;
|
|
9218
|
+
if (south) {
|
|
9219
|
+
const image = await files.load(south, "Reference sprite");
|
|
9220
|
+
shape.reference = { path: path13.resolve(files.root, south), sha256: image.hash, width: image.width, height: image.height, format: image.format };
|
|
9221
|
+
}
|
|
9222
|
+
}
|
|
9223
|
+
if (asset.state) {
|
|
9224
|
+
shape.state = {
|
|
9225
|
+
paletteFromReference: asset.state.paletteFromReference,
|
|
9226
|
+
...asset.state.canvas ? { canvas: asset.state.canvas } : {}
|
|
9227
|
+
};
|
|
9228
|
+
}
|
|
9229
|
+
if (asset.animation) {
|
|
9230
|
+
for (const [field, value] of Object.entries({
|
|
9231
|
+
template: asset.animation.template,
|
|
9232
|
+
subject: asset.animation.subject,
|
|
9233
|
+
outline: asset.animation.outline,
|
|
9234
|
+
shading: asset.animation.shading,
|
|
9235
|
+
detail: asset.animation.detail
|
|
9236
|
+
})) {
|
|
9237
|
+
if (value !== void 0) {
|
|
9238
|
+
throw new Error(
|
|
9239
|
+
`assets.${assetId}: objectPro has no skeleton/template concept, so animation.${field} does not apply; describe the motion in the asset's own prompt instead`
|
|
9240
|
+
);
|
|
9241
|
+
}
|
|
9242
|
+
}
|
|
9243
|
+
if (asset.animation.mode === "template") {
|
|
9244
|
+
throw new Error(`assets.${assetId}: objectPro animations have no "template" mode; use "v3" or "pro"`);
|
|
9245
|
+
}
|
|
9246
|
+
const pose = async (rel, what) => {
|
|
9247
|
+
const image = await files.load(rel, what);
|
|
9248
|
+
return { path: path13.resolve(files.root, rel), sha256: image.hash, width: image.width, height: image.height, format: image.format };
|
|
9249
|
+
};
|
|
9250
|
+
shape.animation = {
|
|
9251
|
+
mode: asset.animation.mode ?? "v3",
|
|
9252
|
+
direction: asset.animation.direction,
|
|
9253
|
+
frames: asset.animation.frames ?? 8,
|
|
9254
|
+
fps: asset.animation.fps,
|
|
9255
|
+
keepFirstFrame: asset.animation.keepFirstFrame,
|
|
9256
|
+
...asset.animation.startFrame ? { startFrame: await pose(asset.animation.startFrame, "Animation start frame") } : {},
|
|
9257
|
+
...asset.animation.endFrame ? { endFrame: await pose(asset.animation.endFrame, "Animation end frame") } : {},
|
|
9258
|
+
...asset.animation.enhancePrompt !== void 0 ? { enhancePrompt: asset.animation.enhancePrompt } : {}
|
|
9259
|
+
};
|
|
9260
|
+
}
|
|
9261
|
+
return shape;
|
|
9262
|
+
}
|
|
8591
9263
|
|
|
8592
9264
|
// src/pipeline/fetch.ts
|
|
8593
9265
|
import { existsSync as existsSync9, readFileSync as readFileSync3 } from "fs";
|
|
@@ -12710,7 +13382,8 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
|
|
|
12710
13382
|
lastSubmitAt = Date.now();
|
|
12711
13383
|
try {
|
|
12712
13384
|
const refs = styleImages.get(spec.styleId) ?? [];
|
|
12713
|
-
const
|
|
13385
|
+
const parentSpec = spec.character?.parentSpec ?? spec.objectPro?.parentSpec;
|
|
13386
|
+
const parentEntry = parentSpec ? lock.entries[lockKey(parentSpec.styleId, parentSpec.assetId)] : void 0;
|
|
12714
13387
|
const anchor = spec.character?.styleAnchor;
|
|
12715
13388
|
const anchorEntry = anchor ? lock.entries[lockKey(anchor.spec.styleId, anchor.spec.assetId)] : void 0;
|
|
12716
13389
|
const replacedMetadata = previousEntry?.providerMetadata?.[provider.id];
|