pixelkiln 0.58.0 → 0.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +369 -22
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +375 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1265 -145
- package/dist/index.d.ts +1265 -145
- package/dist/index.js +372 -22
- package/dist/index.js.map +1 -1
- package/docs/ENDPOINTS.md +104 -15
- package/docs/GENERATORS.md +104 -0
- package/docs/MANIFEST.md +4 -1
- package/docs/REVISIONS.md +21 -21
- package/package.json +1 -1
- package/schema/manifest.schema.json +174 -2
- package/schema/recipe.schema.json +6 -1
- package/skills/pixelkiln/references/pixellab-roadmap.md +69 -29
- package/skills/pixelkiln/references/pixellab.md +65 -4
- package/skills/pixelkiln/references/revisions.md +3 -2
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
AssetBatchSchema: () => AssetBatchSchema,
|
|
33
34
|
AssetSchema: () => AssetSchema,
|
|
34
35
|
BudgetError: () => BudgetError,
|
|
35
36
|
CHARACTER_DIRECTIONS_4: () => CHARACTER_DIRECTIONS_4,
|
|
@@ -96,6 +97,8 @@ __export(index_exports, {
|
|
|
96
97
|
ScenarioProvider: () => ScenarioProvider,
|
|
97
98
|
StyleInputSchema: () => StyleInputSchema,
|
|
98
99
|
StyleSchema: () => StyleSchema,
|
|
100
|
+
UiElementSchema: () => UiElementSchema,
|
|
101
|
+
UiPieceSchema: () => UiPieceSchema,
|
|
99
102
|
UnsupportedCapabilityError: () => UnsupportedCapabilityError,
|
|
100
103
|
UsageError: () => UsageError,
|
|
101
104
|
WorkspaceProjectSchema: () => WorkspaceProjectSchema,
|
|
@@ -305,7 +308,7 @@ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
|
305
308
|
// src/types.ts
|
|
306
309
|
var import_zod = require("zod");
|
|
307
310
|
var MediaTypeSchema = import_zod.z.enum(["image/png", "image/gif"]);
|
|
308
|
-
var GeneratorSchema = import_zod.z.enum(["1dir", "map", "pixflux", "tiles", "animation", "frames", "character", "terrain", "imagePro", "isometricTile", "objectPro"]);
|
|
311
|
+
var GeneratorSchema = import_zod.z.enum(["1dir", "map", "pixflux", "tiles", "animation", "frames", "character", "terrain", "imagePro", "isometricTile", "objectPro", "uiAsset"]);
|
|
309
312
|
var GridConfidenceSchema = import_zod.z.enum(["low", "medium", "high"]);
|
|
310
313
|
var RevisionModeSchema = import_zod.z.enum([
|
|
311
314
|
"image-to-image",
|
|
@@ -427,6 +430,56 @@ var CharacterProportionsSchema = import_zod.z.union([
|
|
|
427
430
|
}).strict()
|
|
428
431
|
]);
|
|
429
432
|
var CharacterAnimationModeSchema = import_zod.z.enum(["template", "v3", "pro"]);
|
|
433
|
+
var UiPieceSchema = import_zod.z.discriminatedUnion("kind", [
|
|
434
|
+
import_zod.z.object({
|
|
435
|
+
id: import_zod.z.string().min(1),
|
|
436
|
+
kind: import_zod.z.literal("rounded_rect"),
|
|
437
|
+
label: import_zod.z.string().optional(),
|
|
438
|
+
x: import_zod.z.number(),
|
|
439
|
+
y: import_zod.z.number(),
|
|
440
|
+
w: import_zod.z.number(),
|
|
441
|
+
h: import_zod.z.number(),
|
|
442
|
+
radius: import_zod.z.number().min(0).optional()
|
|
443
|
+
}).strict(),
|
|
444
|
+
import_zod.z.object({
|
|
445
|
+
id: import_zod.z.string().min(1),
|
|
446
|
+
kind: import_zod.z.literal("circle"),
|
|
447
|
+
label: import_zod.z.string().optional(),
|
|
448
|
+
x: import_zod.z.number(),
|
|
449
|
+
y: import_zod.z.number(),
|
|
450
|
+
r: import_zod.z.number()
|
|
451
|
+
}).strict(),
|
|
452
|
+
import_zod.z.object({
|
|
453
|
+
id: import_zod.z.string().min(1),
|
|
454
|
+
kind: import_zod.z.literal("polygon"),
|
|
455
|
+
label: import_zod.z.string().optional(),
|
|
456
|
+
x: import_zod.z.number(),
|
|
457
|
+
y: import_zod.z.number(),
|
|
458
|
+
r: import_zod.z.number(),
|
|
459
|
+
sides: import_zod.z.number().int().min(3),
|
|
460
|
+
phase: import_zod.z.number().optional()
|
|
461
|
+
}).strict()
|
|
462
|
+
]);
|
|
463
|
+
var UiElementSchema = import_zod.z.enum([
|
|
464
|
+
"button",
|
|
465
|
+
"icon_button",
|
|
466
|
+
"toolbar",
|
|
467
|
+
"tab",
|
|
468
|
+
"panel",
|
|
469
|
+
"window",
|
|
470
|
+
"health_bar",
|
|
471
|
+
"avatar",
|
|
472
|
+
"triangle",
|
|
473
|
+
"pentagon",
|
|
474
|
+
"hexagon",
|
|
475
|
+
"octagon"
|
|
476
|
+
]);
|
|
477
|
+
var AssetBatchSchema = import_zod.z.object({
|
|
478
|
+
/** The batch leader: another `1dir` asset in the same style. */
|
|
479
|
+
of: import_zod.z.string().min(1),
|
|
480
|
+
/** 1-based slot in the batch (the leader is implicitly slot 0). Unique and contiguous among siblings. */
|
|
481
|
+
index: import_zod.z.number().int().min(1)
|
|
482
|
+
}).strict();
|
|
430
483
|
var CharacterStateSchema = import_zod.z.object({
|
|
431
484
|
/** The base character, or another state, in the same style. */
|
|
432
485
|
of: import_zod.z.string().min(1),
|
|
@@ -723,6 +776,12 @@ var StyleObjectSchema = import_zod.z.object({
|
|
|
723
776
|
* in place of `terrainShapeStyle`. The API default is `standard`.
|
|
724
777
|
*/
|
|
725
778
|
terrainMode: import_zod.z.enum(["standard", "pro"]).optional(),
|
|
779
|
+
/**
|
|
780
|
+
* `uiAsset` generator only. A natural-language palette hint sent as-is
|
|
781
|
+
* (e.g. "brown and gold"), distinct from `palette`'s hex-color array —
|
|
782
|
+
* this is a prompt-level steer, not a local quantization target.
|
|
783
|
+
*/
|
|
784
|
+
uiColorPalette: import_zod.z.string().max(200).optional(),
|
|
726
785
|
/**
|
|
727
786
|
* `terrain` generator only, `terrainMode: "standard"`. Procedural
|
|
728
787
|
* boundary geometry: `square` or `round`, 16px or 32px tiles only.
|
|
@@ -1000,6 +1059,16 @@ var AssetSchema = import_zod.z.object({
|
|
|
1000
1059
|
* `prompt` instead.
|
|
1001
1060
|
*/
|
|
1002
1061
|
animation: CharacterAnimationSchema.optional(),
|
|
1062
|
+
/**
|
|
1063
|
+
* `1dir` styles only: this asset rides along on another `1dir` asset's
|
|
1064
|
+
* batch submission instead of generating on its own. See
|
|
1065
|
+
* `AssetBatchSchema`.
|
|
1066
|
+
*/
|
|
1067
|
+
batch: AssetBatchSchema.optional(),
|
|
1068
|
+
/** `uiAsset` styles: precise shape regions the panel is composited from. Combine with `elements`; omit both for a default full-canvas panel. */
|
|
1069
|
+
pieces: import_zod.z.array(UiPieceSchema).min(1).optional(),
|
|
1070
|
+
/** `uiAsset` styles: named, auto-positioned UI element scaffolds. Combine with `pieces`; omit both for a default full-canvas panel. */
|
|
1071
|
+
elements: import_zod.z.array(UiElementSchema).min(1).optional(),
|
|
1003
1072
|
/** `character` styles, `standard` humanoid bases: this character's proportions, over the style's. */
|
|
1004
1073
|
proportions: CharacterProportionsSchema.optional(),
|
|
1005
1074
|
/**
|
|
@@ -1076,7 +1145,7 @@ var AssetSchema = import_zod.z.object({
|
|
|
1076
1145
|
path: ["revision"]
|
|
1077
1146
|
});
|
|
1078
1147
|
}
|
|
1079
|
-
const shapes = [asset.revision && "revision", asset.state && "state", asset.animation && "animation", asset.mirror && "mirror"].filter(Boolean);
|
|
1148
|
+
const shapes = [asset.revision && "revision", asset.state && "state", asset.animation && "animation", asset.mirror && "mirror", asset.batch && "batch"].filter(Boolean);
|
|
1080
1149
|
if (shapes.length > 1) {
|
|
1081
1150
|
context.addIssue({
|
|
1082
1151
|
code: import_zod.z.ZodIssueCode.custom,
|
|
@@ -1227,6 +1296,12 @@ var LockEntrySchema = import_zod.z.object({
|
|
|
1227
1296
|
sourceAssetId: import_zod.z.string().min(1),
|
|
1228
1297
|
sourceSha256: import_zod.z.string().regex(/^[0-9a-f]{64}$/)
|
|
1229
1298
|
}).strict().nullable().default(null),
|
|
1299
|
+
/** For a `1dir` batch member: which leader and slot it rode along on. */
|
|
1300
|
+
batch: import_zod.z.object({
|
|
1301
|
+
role: import_zod.z.enum(["leader", "member"]),
|
|
1302
|
+
leaderAssetId: import_zod.z.string().min(1).optional(),
|
|
1303
|
+
index: import_zod.z.number().int().min(1).optional()
|
|
1304
|
+
}).strict().nullable().default(null),
|
|
1230
1305
|
/**
|
|
1231
1306
|
* Output hashes owned by the previous generation while its replacement is
|
|
1232
1307
|
* pending. They authorize replacing only unchanged PixelKiln-owned files.
|
|
@@ -1511,6 +1586,19 @@ var IsometricTileGetSchema = import_zod2.z.object({
|
|
|
1511
1586
|
image: import_zod2.z.object({ base64: import_zod2.z.string().min(1), format: import_zod2.z.string().default("png") }).passthrough(),
|
|
1512
1587
|
usage: UsageSchema
|
|
1513
1588
|
}).passthrough();
|
|
1589
|
+
var UiAssetSubmitSchema = import_zod2.z.object({
|
|
1590
|
+
ui_asset_id: import_zod2.z.string().min(1),
|
|
1591
|
+
background_job_id: import_zod2.z.string().min(1),
|
|
1592
|
+
status: import_zod2.z.string().default("processing"),
|
|
1593
|
+
usage: UsageSchema
|
|
1594
|
+
}).passthrough();
|
|
1595
|
+
var UiAssetGetSchema = import_zod2.z.object({
|
|
1596
|
+
id: import_zod2.z.string().min(1),
|
|
1597
|
+
status: import_zod2.z.string().nullable().default(null),
|
|
1598
|
+
image_url: import_zod2.z.string().nullable().optional(),
|
|
1599
|
+
progress_percent: import_zod2.z.number().nullable().optional(),
|
|
1600
|
+
eta_seconds: import_zod2.z.number().nullable().optional()
|
|
1601
|
+
}).passthrough();
|
|
1514
1602
|
var RevisionJobSubmitSchema = import_zod2.z.object({
|
|
1515
1603
|
background_job_id: import_zod2.z.string().min(1),
|
|
1516
1604
|
status: import_zod2.z.string().default("processing")
|
|
@@ -1871,6 +1959,48 @@ var PixelLabClient = class {
|
|
|
1871
1959
|
);
|
|
1872
1960
|
return { image: res.image, usage: res.usage ?? null };
|
|
1873
1961
|
}
|
|
1962
|
+
/**
|
|
1963
|
+
* `/create-ui-asset`: one composited panel image from a shape template —
|
|
1964
|
+
* `pieces` (precise rect/circle/polygon regions on a virtual 0–512
|
|
1965
|
+
* editor canvas) and/or named `elements` (auto-positioned scaffolds:
|
|
1966
|
+
* button, icon_button, toolbar, tab, panel, window, health_bar, avatar,
|
|
1967
|
+
* triangle, pentagon, hexagon, octagon), or neither for a default
|
|
1968
|
+
* full-canvas rounded-rect panel. Unlike every other generator here, the
|
|
1969
|
+
* result is one flat image with no per-piece sub-regions or nine-slice
|
|
1970
|
+
* metadata returned — cropping a `pieces` layout into separate files
|
|
1971
|
+
* would be pixelkiln's own local work, not modeled yet.
|
|
1972
|
+
*/
|
|
1973
|
+
async createUiAsset(args) {
|
|
1974
|
+
const body = {
|
|
1975
|
+
description: args.description,
|
|
1976
|
+
image_size: { width: args.width, height: args.height }
|
|
1977
|
+
};
|
|
1978
|
+
if (args.pieces?.length) body.pieces = args.pieces;
|
|
1979
|
+
if (args.elements?.length) body.elements = args.elements;
|
|
1980
|
+
if (args.styleImage) body.style_image = args.styleImage;
|
|
1981
|
+
if (args.colorPalette) body.color_palette = args.colorPalette;
|
|
1982
|
+
if (args.noBackground != null) body.no_background = args.noBackground;
|
|
1983
|
+
if (args.seed != null) body.seed = args.seed;
|
|
1984
|
+
const res = await validateResponse(
|
|
1985
|
+
UiAssetSubmitSchema,
|
|
1986
|
+
await this.request("/create-ui-asset", { method: "POST", body: JSON.stringify(body) }),
|
|
1987
|
+
"create ui asset"
|
|
1988
|
+
);
|
|
1989
|
+
return { ui_asset_id: res.ui_asset_id, background_job_id: res.background_job_id, usage: res.usage };
|
|
1990
|
+
}
|
|
1991
|
+
async getUiAsset(uiAssetId) {
|
|
1992
|
+
const res = await validateResponse(
|
|
1993
|
+
UiAssetGetSchema,
|
|
1994
|
+
await this.request(`/ui-assets/${encodeURIComponent(uiAssetId)}`),
|
|
1995
|
+
"get ui asset"
|
|
1996
|
+
);
|
|
1997
|
+
return {
|
|
1998
|
+
status: res.status,
|
|
1999
|
+
imageUrl: res.image_url ?? null,
|
|
2000
|
+
progressPercent: res.progress_percent ?? null,
|
|
2001
|
+
etaSeconds: res.eta_seconds ?? null
|
|
2002
|
+
};
|
|
2003
|
+
}
|
|
1874
2004
|
/**
|
|
1875
2005
|
* Synchronous single-image generation. Returns the PNG inline rather than a
|
|
1876
2006
|
* job id, and is the only endpoint that honours a forced palette;
|
|
@@ -2277,14 +2407,15 @@ var PixelLabClient = class {
|
|
|
2277
2407
|
/**
|
|
2278
2408
|
* `/reduce-colors`, PixelLab's "Cleanup" tier: quantize one image onto a
|
|
2279
2409
|
* smaller palette, synchronously — no `background_job_id`, the result
|
|
2280
|
-
* comes back in this same response, like `createImagePixflux`.
|
|
2281
|
-
*
|
|
2282
|
-
*
|
|
2283
|
-
*
|
|
2284
|
-
*
|
|
2285
|
-
*
|
|
2286
|
-
*
|
|
2287
|
-
*
|
|
2410
|
+
* comes back in this same response, like `createImagePixflux`. The
|
|
2411
|
+
* schema's own response example is dollar-denominated (`usage: {type:
|
|
2412
|
+
* "usd", usd: 0.02}`), which — matching this codebase's repeated
|
|
2413
|
+
* experience with PixelLab's documented-vs-billed cost mismatches
|
|
2414
|
+
* (`isometricTile`, `objectPro`) — did not hold: confirmed live against a
|
|
2415
|
+
* Tier 2 account at exactly 0.1 generations for a 32x32 source
|
|
2416
|
+
* (docs/REVISIONS.md). `numColors` and `paletteImage` are mutually
|
|
2417
|
+
* exclusive upstream; the manifest schema already enforces that before
|
|
2418
|
+
* this is ever called.
|
|
2288
2419
|
*/
|
|
2289
2420
|
async reduceColors(args) {
|
|
2290
2421
|
const body = { images: [args.image] };
|
|
@@ -2307,8 +2438,9 @@ var PixelLabClient = class {
|
|
|
2307
2438
|
/**
|
|
2308
2439
|
* `/correct-pixelart`, PixelLab's "Cleanup" tier: sharpen edges and drop
|
|
2309
2440
|
* stray pixels without resizing, synchronously — same shape as
|
|
2310
|
-
* `reduceColors` above, no background job. Cost is likewise
|
|
2311
|
-
*
|
|
2441
|
+
* `reduceColors` above, no background job. Cost is likewise confirmed
|
|
2442
|
+
* live at 0.1 generations for a 32x32 source, not the schema's own
|
|
2443
|
+
* dollar-denominated example (`usage: {type: "usd", usd: 0.02}`).
|
|
2312
2444
|
*/
|
|
2313
2445
|
async correctPixelart(args) {
|
|
2314
2446
|
const body = { images: [args.image] };
|
|
@@ -3006,16 +3138,20 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
|
|
|
3006
3138
|
detail: spec.detail ?? null,
|
|
3007
3139
|
seed: spec.seed ?? null,
|
|
3008
3140
|
palette: spec.palette,
|
|
3009
|
-
// `noBackground` only reaches the wire for pixflux; the
|
|
3010
|
-
// undefined for every other generator. `tileSize` is
|
|
3011
|
-
// absent; width/height are derived from it, so it is
|
|
3012
|
-
|
|
3141
|
+
// `noBackground` only reaches the wire for pixflux and uiAsset; the
|
|
3142
|
+
// tile fields are undefined for every other generator. `tileSize` is
|
|
3143
|
+
// intentionally absent; width/height are derived from it, so it is
|
|
3144
|
+
// already covered.
|
|
3145
|
+
noBackground: spec.generator === "pixflux" || spec.generator === "uiAsset" || spec.provider !== "pixellab" ? spec.noBackground : void 0,
|
|
3013
3146
|
tileType: spec.tileType,
|
|
3014
3147
|
tileView: spec.tileView,
|
|
3015
3148
|
tileFeature: spec.tileFeature,
|
|
3016
3149
|
outlineMode: spec.outlineMode,
|
|
3017
3150
|
isometricTileSize: spec.isometricTileSize,
|
|
3018
3151
|
isometricTileShape: spec.isometricTileShape,
|
|
3152
|
+
uiPieces: spec.uiPieces,
|
|
3153
|
+
uiElements: spec.uiElements,
|
|
3154
|
+
uiColorPalette: spec.uiColorPalette,
|
|
3019
3155
|
styleImages: styleImageHashes,
|
|
3020
3156
|
// A character family: the engine and rotations of a base, the parent's
|
|
3021
3157
|
// generated bytes for a state or animation (so a regenerated parent
|
|
@@ -3064,6 +3200,11 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
|
|
|
3064
3200
|
// A mirror's bytes come from its source's recorded outputs; the plan
|
|
3065
3201
|
// compares those directly, so only the choice of source is identity.
|
|
3066
3202
|
mirror: spec.mirror ? { of: spec.mirror.sourceAssetId } : void 0,
|
|
3203
|
+
// The whole group's ordered descriptions, not just this asset's own —
|
|
3204
|
+
// any sibling's prompt changing, or a member being added or removed,
|
|
3205
|
+
// must mark every member stale together, since they all ride on one
|
|
3206
|
+
// submitted job with no way to amend it after the fact.
|
|
3207
|
+
batch: spec.batch ? { role: spec.batch.role, itemDescriptions: spec.batch.itemDescriptions, index: spec.batch.index } : void 0,
|
|
3067
3208
|
revision: spec.revision ? {
|
|
3068
3209
|
mode: spec.revision.mode,
|
|
3069
3210
|
from: spec.revision.sourceAssetId,
|
|
@@ -3208,7 +3349,7 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
3208
3349
|
return new _PixelLabProvider(new PixelLabClient("download-only"));
|
|
3209
3350
|
}
|
|
3210
3351
|
supports(generator) {
|
|
3211
|
-
return generator === "1dir" || generator === "map" || generator === "pixflux" || generator === "tiles" || generator === "terrain" || generator === "imagePro" || generator === "character" || generator === "isometricTile" || generator === "objectPro";
|
|
3352
|
+
return generator === "1dir" || generator === "map" || generator === "pixflux" || generator === "tiles" || generator === "terrain" || generator === "imagePro" || generator === "character" || generator === "isometricTile" || generator === "objectPro" || generator === "uiAsset";
|
|
3212
3353
|
}
|
|
3213
3354
|
/**
|
|
3214
3355
|
* `inpaint` (`/inpaint-v3`, a mask) and `image-to-image` (`/edit-images-v2`,
|
|
@@ -3245,6 +3386,9 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
3245
3386
|
return dir;
|
|
3246
3387
|
}
|
|
3247
3388
|
estimate(spec) {
|
|
3389
|
+
if (spec.batch?.role === "member") {
|
|
3390
|
+
return { unit: "generations", amount: 0, candidates: 1 };
|
|
3391
|
+
}
|
|
3248
3392
|
if (spec.revision?.mode === "reduce-colors" || spec.revision?.mode === "correct-pixelart") {
|
|
3249
3393
|
return { unit: "generations", amount: 0.1, candidates: 1 };
|
|
3250
3394
|
}
|
|
@@ -3321,6 +3465,11 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
3321
3465
|
`PixelLab imagePro is ${spec.width}x${spec.height}; the API takes 16 to 792 wide and 16 to 688 tall (the exact ceiling also depends on aspect ratio)`
|
|
3322
3466
|
);
|
|
3323
3467
|
}
|
|
3468
|
+
if (spec.generator === "uiAsset" && (spec.width < 192 || spec.height < 192 || spec.width > 688 || spec.height > 688)) {
|
|
3469
|
+
throw new Error(
|
|
3470
|
+
`PixelLab uiAsset is ${spec.width}x${spec.height}; the API takes 192 to 688 pixels per side (the exact ceiling also depends on aspect ratio \u2014 square tops out at 512, 16:9 at 688x384, 9:16 at 384x688, 4:3 at 600x448, 3:4 at 448x600)`
|
|
3471
|
+
);
|
|
3472
|
+
}
|
|
3324
3473
|
if (spec.generator === "map") {
|
|
3325
3474
|
requirePixelLabOption("map", "view", spec.view, ["low top-down", "high top-down", "side"]);
|
|
3326
3475
|
requirePixelLabOption("map", "outline", spec.outline, [
|
|
@@ -3958,6 +4107,20 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
3958
4107
|
if (spec.revision) return this.submitRevision(spec);
|
|
3959
4108
|
if (spec.generator === "character") return this.submitCharacter(spec, styleImages, context);
|
|
3960
4109
|
if (spec.generator === "objectPro") return this.submitObjectPro(spec, styleImages, context);
|
|
4110
|
+
if (spec.generator === "uiAsset") {
|
|
4111
|
+
const res2 = await this.client.createUiAsset({
|
|
4112
|
+
description: spec.prompt,
|
|
4113
|
+
width: spec.width,
|
|
4114
|
+
height: spec.height,
|
|
4115
|
+
pieces: spec.uiPieces,
|
|
4116
|
+
elements: spec.uiElements,
|
|
4117
|
+
styleImage: styleImages[0] ? { base64: styleImages[0].base64, format: styleImages[0].format } : void 0,
|
|
4118
|
+
colorPalette: spec.uiColorPalette,
|
|
4119
|
+
noBackground: spec.noBackground,
|
|
4120
|
+
seed: spec.seed
|
|
4121
|
+
});
|
|
4122
|
+
return { jobId: res2.ui_asset_id, metadata: { backgroundJobId: res2.background_job_id } };
|
|
4123
|
+
}
|
|
3961
4124
|
if (spec.generator === "pixflux") {
|
|
3962
4125
|
const swatch = spec.palette.length ? paletteSwatch(spec.palette).toString("base64") : void 0;
|
|
3963
4126
|
const { png } = await this.client.createImagePixflux({
|
|
@@ -4043,11 +4206,21 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
4043
4206
|
return { jobId: res2.background_job_id };
|
|
4044
4207
|
}
|
|
4045
4208
|
if (spec.generator === "1dir") {
|
|
4209
|
+
if (spec.batch?.role === "member") {
|
|
4210
|
+
throw new Error(`${spec.styleId}/${spec.assetId}: a batch member cannot submit on its own`);
|
|
4211
|
+
}
|
|
4046
4212
|
const res2 = await this.client.create1Direction({
|
|
4047
4213
|
description: spec.prompt,
|
|
4048
4214
|
size: spec.size,
|
|
4049
4215
|
view: spec.view === "sidescroller" ? "sidescroller" : "top-down",
|
|
4050
|
-
styleImages
|
|
4216
|
+
styleImages,
|
|
4217
|
+
// The leader's own subject is item_descriptions[0], so `description`
|
|
4218
|
+
// matches it exactly. Confirmed live: item_descriptions[0] owns
|
|
4219
|
+
// candidate slot 0 (a 3-item batch returned exactly the declared
|
|
4220
|
+
// items at frames 0/1/2, in order; docs/GENERATORS.md#1dir), so
|
|
4221
|
+
// sending the same text as `description` too is redundant but
|
|
4222
|
+
// harmless, not a hedge against ambiguity.
|
|
4223
|
+
itemDescriptions: spec.batch?.role === "leader" ? spec.batch.itemDescriptions : void 0
|
|
4051
4224
|
});
|
|
4052
4225
|
return { jobId: res2.object_id, metadata: { backgroundJobId: res2.background_job_id } };
|
|
4053
4226
|
}
|
|
@@ -4224,6 +4397,7 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
4224
4397
|
if (generator === "imagePro") return this.pollImagePro(jobId);
|
|
4225
4398
|
if (generator === "character") return this.pollCharacter(jobId, context);
|
|
4226
4399
|
if (generator === "objectPro") return this.pollObjectPro(jobId, context);
|
|
4400
|
+
if (generator === "uiAsset") return this.pollUiAsset(jobId, context);
|
|
4227
4401
|
const backgroundJobId = context?.metadata?.backgroundJobId;
|
|
4228
4402
|
const obj = await this.client.getObject(jobId);
|
|
4229
4403
|
if (obj.status === "review") {
|
|
@@ -4543,6 +4717,29 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
4543
4717
|
throw err;
|
|
4544
4718
|
}
|
|
4545
4719
|
}
|
|
4720
|
+
/**
|
|
4721
|
+
* `/ui-assets/{id}` reports progress with its own `status` field
|
|
4722
|
+
* ("processing"/"completed"/"failed") rather than a 423, and its result is
|
|
4723
|
+
* one flat composited image at a hosted `image_url` — no per-piece
|
|
4724
|
+
* sub-images, no nine-slice metadata, confirmed absent from the response
|
|
4725
|
+
* schema. There is nothing to review: one job is one ready image, like
|
|
4726
|
+
* `isometricTile`.
|
|
4727
|
+
*/
|
|
4728
|
+
async pollUiAsset(uiAssetId, context) {
|
|
4729
|
+
const asset = await this.client.getUiAsset(uiAssetId);
|
|
4730
|
+
if (asset.status === "failed") return { status: "failed", error: "UI asset generation failed upstream" };
|
|
4731
|
+
if (asset.status !== "completed" || !asset.imageUrl) {
|
|
4732
|
+
return { status: "processing", progressPercent: asset.progressPercent, etaSeconds: asset.etaSeconds };
|
|
4733
|
+
}
|
|
4734
|
+
const backgroundJobId = context?.metadata?.backgroundJobId;
|
|
4735
|
+
return {
|
|
4736
|
+
status: "ready",
|
|
4737
|
+
objectId: uiAssetId,
|
|
4738
|
+
sourceUrl: asset.imageUrl,
|
|
4739
|
+
sources: [{ url: asset.imageUrl }],
|
|
4740
|
+
billed: await this.billedForJob(backgroundJobId)
|
|
4741
|
+
};
|
|
4742
|
+
}
|
|
4546
4743
|
async selectCandidate(jobId, index, commonTag, generator) {
|
|
4547
4744
|
if (generator === "tiles") {
|
|
4548
4745
|
const set = await this.client.getTilesPro(jobId);
|
|
@@ -7323,6 +7520,10 @@ async function resolveSpecs(loaded, filter) {
|
|
|
7323
7520
|
size = asset.size ?? style.size ?? 64;
|
|
7324
7521
|
width = size;
|
|
7325
7522
|
height = size;
|
|
7523
|
+
} else if (generator === "uiAsset") {
|
|
7524
|
+
width = asset.width ?? style.size ?? 256;
|
|
7525
|
+
height = asset.height ?? style.size ?? 256;
|
|
7526
|
+
size = Math.max(width, height);
|
|
7326
7527
|
} else {
|
|
7327
7528
|
width = asset.width ?? style.size ?? 64;
|
|
7328
7529
|
height = asset.height ?? style.size ?? 64;
|
|
@@ -7333,6 +7534,11 @@ async function resolveSpecs(loaded, filter) {
|
|
|
7333
7534
|
`assets.${assetId}: ${asset.state ? "state" : "animation"} needs a character or objectPro style; "${styleId}" generates ${generator}`
|
|
7334
7535
|
);
|
|
7335
7536
|
}
|
|
7537
|
+
if ((asset.pieces || asset.elements) && generator !== "uiAsset") {
|
|
7538
|
+
throw new Error(
|
|
7539
|
+
`assets.${assetId}: ${asset.pieces ? "pieces" : "elements"} needs a uiAsset style; "${styleId}" generates ${generator}`
|
|
7540
|
+
);
|
|
7541
|
+
}
|
|
7336
7542
|
const characterKind = asset.animation ? "animation" : asset.state ? "state" : "base";
|
|
7337
7543
|
const subject = asset.promptByStyle[styleId] ?? asset.prompt ?? "";
|
|
7338
7544
|
let terrainLowerDescription;
|
|
@@ -7379,6 +7585,9 @@ async function resolveSpecs(loaded, filter) {
|
|
|
7379
7585
|
shading: style.shading,
|
|
7380
7586
|
detail: style.detail,
|
|
7381
7587
|
seed: style.seed,
|
|
7588
|
+
uiPieces: generator === "uiAsset" ? asset.pieces : void 0,
|
|
7589
|
+
uiElements: generator === "uiAsset" ? asset.elements : void 0,
|
|
7590
|
+
uiColorPalette: generator === "uiAsset" ? style.uiColorPalette : void 0,
|
|
7382
7591
|
palette: style.palette,
|
|
7383
7592
|
enforcePalette: style.enforcePalette,
|
|
7384
7593
|
noBackground: style.noBackground,
|
|
@@ -7457,6 +7666,14 @@ async function resolveSpecs(loaded, filter) {
|
|
|
7457
7666
|
};
|
|
7458
7667
|
styleSpecs.set(assetId, resolved);
|
|
7459
7668
|
}
|
|
7669
|
+
const batchMembersByLeader = /* @__PURE__ */ new Map();
|
|
7670
|
+
for (const id of styleSpecs.keys()) {
|
|
7671
|
+
const batch = manifest.assets[id].batch;
|
|
7672
|
+
if (!batch) continue;
|
|
7673
|
+
const members = batchMembersByLeader.get(batch.of) ?? [];
|
|
7674
|
+
members.push({ id, index: batch.index });
|
|
7675
|
+
batchMembersByLeader.set(batch.of, members);
|
|
7676
|
+
}
|
|
7460
7677
|
const finalized = /* @__PURE__ */ new Set();
|
|
7461
7678
|
const finalize = async (assetId) => {
|
|
7462
7679
|
const resolved = styleSpecs.get(assetId);
|
|
@@ -7615,6 +7832,56 @@ async function resolveSpecs(loaded, filter) {
|
|
|
7615
7832
|
...asset.revision.enhancePrompt == null ? {} : { enhancePrompt: asset.revision.enhancePrompt }
|
|
7616
7833
|
};
|
|
7617
7834
|
}
|
|
7835
|
+
if (asset.batch) {
|
|
7836
|
+
if (asset.batch.of === assetId) throw new Error(`assets.${assetId}: a batch cannot name itself as its own leader`);
|
|
7837
|
+
const leaderSpec = await finalize(asset.batch.of);
|
|
7838
|
+
if (!leaderSpec.batch || leaderSpec.batch.role !== "leader") {
|
|
7839
|
+
throw new Error(`assets.${assetId}: ${asset.batch.of} is not a 1dir batch leader`);
|
|
7840
|
+
}
|
|
7841
|
+
if (resolved.size !== leaderSpec.size) {
|
|
7842
|
+
throw new Error(
|
|
7843
|
+
`assets.${assetId}: batch members share the leader's canvas size; ${asset.batch.of} is ${leaderSpec.size}px, this is ${resolved.size}px`
|
|
7844
|
+
);
|
|
7845
|
+
}
|
|
7846
|
+
resolved.batch = {
|
|
7847
|
+
role: "member",
|
|
7848
|
+
itemDescriptions: leaderSpec.batch.itemDescriptions,
|
|
7849
|
+
index: asset.batch.index,
|
|
7850
|
+
leaderAssetId: asset.batch.of,
|
|
7851
|
+
leaderSpec
|
|
7852
|
+
};
|
|
7853
|
+
} else {
|
|
7854
|
+
const members = batchMembersByLeader.get(assetId);
|
|
7855
|
+
if (members) {
|
|
7856
|
+
if (style.generator !== "1dir") {
|
|
7857
|
+
throw new Error(`assets.${assetId}: only a 1dir asset can lead a batch`);
|
|
7858
|
+
}
|
|
7859
|
+
const sorted = [...members].sort((a, b) => a.index - b.index);
|
|
7860
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7861
|
+
for (const { index } of sorted) {
|
|
7862
|
+
if (seen.has(index)) throw new Error(`assets.${assetId}: two batch members both claim index ${index}`);
|
|
7863
|
+
seen.add(index);
|
|
7864
|
+
}
|
|
7865
|
+
const expectedIndices = sorted.map((_, i) => i + 1).join(",");
|
|
7866
|
+
if (sorted.map((m) => m.index).join(",") !== expectedIndices) {
|
|
7867
|
+
throw new Error(
|
|
7868
|
+
`assets.${assetId}: batch member indices must be 1..${sorted.length} with no gaps; got ${sorted.map((m) => m.index).join(",")}`
|
|
7869
|
+
);
|
|
7870
|
+
}
|
|
7871
|
+
const total = 1 + sorted.length;
|
|
7872
|
+
const limit = candidateCount(resolved.size);
|
|
7873
|
+
if (total > limit) {
|
|
7874
|
+
throw new Error(
|
|
7875
|
+
`assets.${assetId}: a ${resolved.size}px batch holds at most ${limit} items (1 leader + ${limit - 1} members); this one declares ${total}`
|
|
7876
|
+
);
|
|
7877
|
+
}
|
|
7878
|
+
resolved.batch = {
|
|
7879
|
+
role: "leader",
|
|
7880
|
+
itemDescriptions: [resolved.prompt, ...sorted.map(({ id }) => styleSpecs.get(id).prompt)],
|
|
7881
|
+
memberAssetIds: sorted.map((m) => m.id)
|
|
7882
|
+
};
|
|
7883
|
+
}
|
|
7884
|
+
}
|
|
7618
7885
|
resolved.specHash = specHash(
|
|
7619
7886
|
resolved,
|
|
7620
7887
|
styleImageHashes,
|
|
@@ -12000,6 +12267,7 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
|
|
|
12000
12267
|
for (const styleId of new Set(items.map((i) => i.spec.styleId))) {
|
|
12001
12268
|
styleImages.set(styleId, await resolveStyleImages(loaded, styleId));
|
|
12002
12269
|
}
|
|
12270
|
+
const byKey = new Map(items.map((item) => [item.key, item]));
|
|
12003
12271
|
let submitted = 0;
|
|
12004
12272
|
let failed = 0;
|
|
12005
12273
|
let spent = 0;
|
|
@@ -12029,8 +12297,62 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
|
|
|
12029
12297
|
await pruneInFlight();
|
|
12030
12298
|
}
|
|
12031
12299
|
}
|
|
12300
|
+
function writeBatchMembers(leaderSpec, leaderKey, error) {
|
|
12301
|
+
if (leaderSpec.batch?.role !== "leader") return;
|
|
12302
|
+
const leaderEntry = lock.entries[leaderKey];
|
|
12303
|
+
for (const memberAssetId of leaderSpec.batch.memberAssetIds ?? []) {
|
|
12304
|
+
const memberKey = lockKey(leaderSpec.styleId, memberAssetId);
|
|
12305
|
+
const memberItem = byKey.get(memberKey);
|
|
12306
|
+
if (!memberItem) {
|
|
12307
|
+
throw new Error(
|
|
12308
|
+
`${leaderKey}: batch member "${memberAssetId}" must be submitted in the same run as its leader; run without --only, or include every sibling`
|
|
12309
|
+
);
|
|
12310
|
+
}
|
|
12311
|
+
const memberSpec = memberItem.spec;
|
|
12312
|
+
const memberEstimate = estimates.get(memberKey);
|
|
12313
|
+
const previousMemberEntry = lock.entries[memberKey];
|
|
12314
|
+
const memberSubmittedAt = leaderEntry.submittedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
12315
|
+
upsert(lock, memberKey, {
|
|
12316
|
+
styleId: memberSpec.styleId,
|
|
12317
|
+
assetId: memberSpec.assetId,
|
|
12318
|
+
specHash: memberSpec.specHash,
|
|
12319
|
+
generator: memberSpec.generator,
|
|
12320
|
+
prompt: memberSpec.prompt,
|
|
12321
|
+
width: memberSpec.width,
|
|
12322
|
+
height: memberSpec.height,
|
|
12323
|
+
batch: { role: "member", leaderAssetId: leaderSpec.assetId, index: memberSpec.batch.index },
|
|
12324
|
+
status: error ? "failed" : "processing",
|
|
12325
|
+
error,
|
|
12326
|
+
jobId: error ? null : leaderEntry.jobId,
|
|
12327
|
+
submissionComplete: error ? void 0 : true,
|
|
12328
|
+
reviewObjectId: error ? null : leaderEntry.reviewObjectId,
|
|
12329
|
+
objectId: null,
|
|
12330
|
+
candidateIndex: null,
|
|
12331
|
+
outputs: [],
|
|
12332
|
+
supersededOutputs: previousMemberEntry?.outputs.length ? previousMemberEntry.outputs : previousMemberEntry?.supersededOutputs ?? [],
|
|
12333
|
+
providerMetadata: leaderEntry.providerMetadata,
|
|
12334
|
+
sourceUrl: null,
|
|
12335
|
+
sourceUrls: [],
|
|
12336
|
+
submittedAt: memberSubmittedAt,
|
|
12337
|
+
history: historyAfterReplacing(previousMemberEntry, historyLimit(loaded.manifest), memberSubmittedAt),
|
|
12338
|
+
cost: memberEstimate.amount,
|
|
12339
|
+
costUnit: memberEstimate.unit,
|
|
12340
|
+
provider: provider.id,
|
|
12341
|
+
downloadedAt: null
|
|
12342
|
+
});
|
|
12343
|
+
if (error) failed++;
|
|
12344
|
+
else {
|
|
12345
|
+
submitted++;
|
|
12346
|
+
spent += memberEstimate.amount;
|
|
12347
|
+
log(` ${memberKey} \u2192 ${leaderEntry.jobId} (rides on ${leaderKey}'s batch)`);
|
|
12348
|
+
}
|
|
12349
|
+
}
|
|
12350
|
+
}
|
|
12032
12351
|
for (const { spec, key } of items) {
|
|
12033
12352
|
const estimate = estimates.get(key);
|
|
12353
|
+
if (spec.batch?.role === "member") {
|
|
12354
|
+
continue;
|
|
12355
|
+
}
|
|
12034
12356
|
if (spec.mirror) {
|
|
12035
12357
|
try {
|
|
12036
12358
|
await requireRevisionReady(spec, lock);
|
|
@@ -12063,6 +12385,14 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
|
|
|
12063
12385
|
const since = Date.now() - lastSubmitAt;
|
|
12064
12386
|
if (since < spacing) await sleep(spacing - since);
|
|
12065
12387
|
await requireRevisionReady(spec, lock);
|
|
12388
|
+
if (spec.batch?.role === "leader") {
|
|
12389
|
+
const missing = (spec.batch.memberAssetIds ?? []).filter((id) => !byKey.has(lockKey(spec.styleId, id)));
|
|
12390
|
+
if (missing.length) {
|
|
12391
|
+
throw new Error(
|
|
12392
|
+
`${key}: batch member(s) ${missing.join(", ")} must be submitted in the same run as this leader; run without --only, or include every sibling`
|
|
12393
|
+
);
|
|
12394
|
+
}
|
|
12395
|
+
}
|
|
12066
12396
|
const previousEntry = lock.entries[key];
|
|
12067
12397
|
const resumesCheckpoint = Boolean(
|
|
12068
12398
|
previousEntry?.specHash === spec.specHash && previousEntry.provider === provider.id && previousEntry.generator === spec.generator && previousEntry.submissionComplete === false && previousEntry.jobId
|
|
@@ -12168,6 +12498,7 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
|
|
|
12168
12498
|
inFlight.set(jobId, spec);
|
|
12169
12499
|
submitted++;
|
|
12170
12500
|
spent += estimate.amount;
|
|
12501
|
+
writeBatchMembers(spec, key, null);
|
|
12171
12502
|
log(
|
|
12172
12503
|
` ${key} \u2192 ${jobId} (${spec.width}x${spec.height}` + (estimate.candidates > 1 ? `, ${estimate.candidates} ${spec.tileFeature ? "outputs" : "candidates"}` : "") + `, ${estimate.amount})`
|
|
12173
12504
|
);
|
|
@@ -12179,6 +12510,7 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
|
|
|
12179
12510
|
inFlight.set(entry.jobId, spec);
|
|
12180
12511
|
submitted++;
|
|
12181
12512
|
spent += estimate.amount;
|
|
12513
|
+
writeBatchMembers(spec, key, null);
|
|
12182
12514
|
log(` ${key} \u2192 ${entry.jobId} (recovered from completed checkpoint)`);
|
|
12183
12515
|
} else {
|
|
12184
12516
|
failed++;
|
|
@@ -12187,6 +12519,7 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
|
|
|
12187
12519
|
error: message6,
|
|
12188
12520
|
cost: entry.jobId ? estimate.amount : 0
|
|
12189
12521
|
});
|
|
12522
|
+
writeBatchMembers(spec, key, message6);
|
|
12190
12523
|
log(` FAILED ${key}: ${message6}`);
|
|
12191
12524
|
}
|
|
12192
12525
|
}
|
|
@@ -12778,6 +13111,9 @@ function renderSheet(groups, options = {}) {
|
|
|
12778
13111
|
.cand.active { border-color:var(--dim); box-shadow:0 0 0 2px color-mix(in srgb, var(--dim) 20%, transparent); }
|
|
12779
13112
|
.cand.sel { border-color:var(--ok); box-shadow:0 0 0 3px color-mix(in srgb, var(--ok) 22%, transparent); }
|
|
12780
13113
|
.cand.set-frame { cursor:pointer; }
|
|
13114
|
+
.cand.recommended:not(.sel) { border-color:var(--accent); }
|
|
13115
|
+
.cand.recommended .badge { position:absolute; top:-1px; left:-1px; font-size:9.5px; font-weight:650;
|
|
13116
|
+
text-transform:uppercase; letter-spacing:.03em; color:var(--bg); background:var(--accent); padding:1px 5px; }
|
|
12781
13117
|
.cand img { image-rendering:pixelated; display:block;
|
|
12782
13118
|
background-image:
|
|
12783
13119
|
linear-gradient(45deg,#0000 25%,#7f7f7f22 25%,#7f7f7f22 75%,#0000 75%),
|
|
@@ -12935,14 +13271,17 @@ GROUPS.forEach((g, gi) => {
|
|
|
12935
13271
|
}).observe(loop);
|
|
12936
13272
|
}
|
|
12937
13273
|
}
|
|
13274
|
+
const recommended = g.recommendedIndex ?? 0;
|
|
12938
13275
|
g.frameUrls.forEach((url, i) => {
|
|
12939
13276
|
const c = document.createElement('button');
|
|
12940
13277
|
c.type = 'button';
|
|
12941
|
-
c.className = 'cand' + (g.mode === 'frame-set' ? ' set-frame' : '')
|
|
13278
|
+
c.className = 'cand' + (g.mode === 'frame-set' ? ' set-frame' : '') +
|
|
13279
|
+
(g.recommendedIndex != null && i === recommended ? ' recommended' : '');
|
|
12942
13280
|
c.tabIndex = -1;
|
|
12943
|
-
c.setAttribute('aria-label', g.mode === 'frame-set'
|
|
13281
|
+
c.setAttribute('aria-label', (g.mode === 'frame-set'
|
|
12944
13282
|
? 'Accept ordered frame set from frame ' + (i + 1)
|
|
12945
|
-
: 'Choose candidate ' + (i + 1) + ' of ' + g.frameUrls.length)
|
|
13283
|
+
: 'Choose candidate ' + (i + 1) + ' of ' + g.frameUrls.length) +
|
|
13284
|
+
(g.recommendedIndex != null && i === recommended ? ' (this asset\u2019s declared slot)' : ''));
|
|
12946
13285
|
|
|
12947
13286
|
const preview = document.createElement('img');
|
|
12948
13287
|
preview.className = 'preview';
|
|
@@ -12955,6 +13294,12 @@ GROUPS.forEach((g, gi) => {
|
|
|
12955
13294
|
index.textContent = (g.frameLabels?.[i] || String(i + 1)) + ' \xB7 ' +
|
|
12956
13295
|
g.width + '\xD7' + g.height + ' \xB7 ' + scaleLabel;
|
|
12957
13296
|
c.append(preview, index);
|
|
13297
|
+
if (g.recommendedIndex != null && i === recommended) {
|
|
13298
|
+
const badge = document.createElement('span');
|
|
13299
|
+
badge.className = 'badge';
|
|
13300
|
+
badge.textContent = 'declared';
|
|
13301
|
+
c.append(badge);
|
|
13302
|
+
}
|
|
12958
13303
|
if (displayScale > 1 && largestSide <= 96) {
|
|
12959
13304
|
const actual = document.createElement('img');
|
|
12960
13305
|
actual.className = 'actual';
|
|
@@ -12979,6 +13324,7 @@ GROUPS.forEach((g, gi) => {
|
|
|
12979
13324
|
});
|
|
12980
13325
|
frames.dataset.active = '0';
|
|
12981
13326
|
frames.querySelector('.cand')?.classList.add('active');
|
|
13327
|
+
if (recommended > 0) activate(recommended, frames);
|
|
12982
13328
|
root.appendChild(el);
|
|
12983
13329
|
});
|
|
12984
13330
|
|
|
@@ -13234,7 +13580,11 @@ async function prepareReview(provider, lock, opts = {}) {
|
|
|
13234
13580
|
height: spec.revision.sourceHeight
|
|
13235
13581
|
}
|
|
13236
13582
|
} : {},
|
|
13237
|
-
...current && currentRoute ? { current: { url: currentRoute, width: current.width, height: current.height } } : {}
|
|
13583
|
+
...current && currentRoute ? { current: { url: currentRoute, width: current.width, height: current.height } } : {},
|
|
13584
|
+
// A batch member's declared slot in the shared candidate set — the
|
|
13585
|
+
// leader's own slot 0 is already the sheet's ordinary default, so
|
|
13586
|
+
// only a member (whose slot is 1+) needs to say so explicitly.
|
|
13587
|
+
...entry.batch?.index != null ? { recommendedIndex: entry.batch.index } : {}
|
|
13238
13588
|
});
|
|
13239
13589
|
} catch (err) {
|
|
13240
13590
|
log(` could not load candidates for ${key}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -16282,6 +16632,7 @@ async function workspaceStatus(ws, dir) {
|
|
|
16282
16632
|
}
|
|
16283
16633
|
// Annotate the CommonJS export names for ESM import in node:
|
|
16284
16634
|
0 && (module.exports = {
|
|
16635
|
+
AssetBatchSchema,
|
|
16285
16636
|
AssetSchema,
|
|
16286
16637
|
BudgetError,
|
|
16287
16638
|
CHARACTER_DIRECTIONS_4,
|
|
@@ -16348,6 +16699,8 @@ async function workspaceStatus(ws, dir) {
|
|
|
16348
16699
|
ScenarioProvider,
|
|
16349
16700
|
StyleInputSchema,
|
|
16350
16701
|
StyleSchema,
|
|
16702
|
+
UiElementSchema,
|
|
16703
|
+
UiPieceSchema,
|
|
16351
16704
|
UnsupportedCapabilityError,
|
|
16352
16705
|
UsageError,
|
|
16353
16706
|
WorkspaceProjectSchema,
|