pixelkiln 0.55.0 → 0.57.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 CHANGED
@@ -708,7 +708,11 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
708
708
  from: spec.revision.sourceAssetId,
709
709
  sourceSha256: spec.revision.sourceSha256,
710
710
  maskSha256: spec.revision.maskSha256,
711
- strength: spec.revision.strength
711
+ strength: spec.revision.strength,
712
+ numColors: spec.revision.numColors,
713
+ paletteImageSha256: spec.revision.paletteImageSha256,
714
+ dithering: spec.revision.dithering,
715
+ ditheringStrength: spec.revision.ditheringStrength
712
716
  } : void 0
713
717
  })
714
718
  );
@@ -728,7 +732,7 @@ import { z } from "zod";
728
732
  var MediaTypeSchema = z.enum(["image/png", "image/gif"]);
729
733
  var GeneratorSchema = z.enum(["1dir", "map", "pixflux", "tiles", "animation", "frames", "character", "terrain", "imagePro", "isometricTile", "objectPro"]);
730
734
  var GridConfidenceSchema = z.enum(["low", "medium", "high"]);
731
- var RevisionModeSchema = z.enum(["image-to-image", "inpaint", "outpaint"]);
735
+ var RevisionModeSchema = z.enum(["image-to-image", "inpaint", "outpaint", "reduce-colors", "correct-pixelart"]);
732
736
  function tileVariationCount(descriptions) {
733
737
  return Math.max(1, descriptions) * 4;
734
738
  }
@@ -922,6 +926,7 @@ var CharacterAnimationSchema = z.object({
922
926
  context.addIssue({ code: z.ZodIssueCode.custom, message: "template mode needs a template", path: ["template"] });
923
927
  }
924
928
  });
929
+ var RevisionDitheringSchema = z.enum(["none", "2x2", "4x4", "8x8"]);
925
930
  var RevisionSchema = z.object({
926
931
  /** What kind of controlled change the provider workflow performs. */
927
932
  mode: RevisionModeSchema,
@@ -930,7 +935,15 @@ var RevisionSchema = z.object({
930
935
  /** Manifest-relative black/white PNG. Required only for masked inpainting. */
931
936
  mask: z.string().min(1).optional(),
932
937
  /** Provider-neutral edit strength. The active adapter must bind it explicitly. */
933
- strength: z.number().min(0).max(1).optional()
938
+ strength: z.number().min(0).max(1).optional(),
939
+ /** `reduce-colors` only: target color count. Mutually exclusive with `paletteImage`. */
940
+ numColors: z.number().int().min(2).max(256).optional(),
941
+ /** `reduce-colors` only: manifest-relative image whose colors become the palette. Mutually exclusive with `numColors`. */
942
+ paletteImage: z.string().min(1).optional(),
943
+ /** `reduce-colors` only: ordered dithering matrix size. */
944
+ dithering: RevisionDitheringSchema.optional(),
945
+ /** `reduce-colors` only: dithering intensity; ignored when `dithering` is "none" or unset. */
946
+ ditheringStrength: z.number().min(0).max(10).optional()
934
947
  }).strict().superRefine((revision, context) => {
935
948
  if (revision.mode === "inpaint" && !revision.mask) {
936
949
  context.addIssue({
@@ -946,6 +959,29 @@ var RevisionSchema = z.object({
946
959
  path: ["mask"]
947
960
  });
948
961
  }
962
+ if (revision.numColors !== void 0 && revision.paletteImage !== void 0) {
963
+ context.addIssue({
964
+ code: z.ZodIssueCode.custom,
965
+ message: "numColors and paletteImage are mutually exclusive",
966
+ path: ["numColors"]
967
+ });
968
+ }
969
+ for (const field of ["numColors", "paletteImage", "dithering", "ditheringStrength"]) {
970
+ if (revision[field] !== void 0 && revision.mode !== "reduce-colors") {
971
+ context.addIssue({
972
+ code: z.ZodIssueCode.custom,
973
+ message: `${field} applies to reduce-colors revisions only`,
974
+ path: [field]
975
+ });
976
+ }
977
+ }
978
+ if (revision.strength !== void 0 && revision.mode === "reduce-colors") {
979
+ context.addIssue({
980
+ code: z.ZodIssueCode.custom,
981
+ message: "reduce-colors revisions do not take a strength; use dithering/ditheringStrength",
982
+ path: ["strength"]
983
+ });
984
+ }
949
985
  });
950
986
  var StyleObjectSchema = z.object({
951
987
  /** Generation backend for this style. Omit to inherit the manifest default. */
@@ -1552,7 +1588,11 @@ var LockEntrySchema = z.object({
1552
1588
  sourceAssetId: z.string().min(1),
1553
1589
  sourceSha256: z.string().regex(/^[0-9a-f]{64}$/),
1554
1590
  maskSha256: z.string().regex(/^[0-9a-f]{64}$/).optional(),
1555
- strength: z.number().min(0).max(1).optional()
1591
+ strength: z.number().min(0).max(1).optional(),
1592
+ numColors: z.number().int().min(2).max(256).optional(),
1593
+ paletteImageSha256: z.string().regex(/^[0-9a-f]{64}$/).optional(),
1594
+ dithering: RevisionDitheringSchema.optional(),
1595
+ ditheringStrength: z.number().min(0).max(10).optional()
1556
1596
  }).strict().nullable().default(null),
1557
1597
  /** For a mirror: the asset it flips and a hash over that asset's output hashes when this was made. */
1558
1598
  mirror: z.object({
@@ -4690,6 +4730,11 @@ var ComfyUIProvider = class _ComfyUIProvider {
4690
4730
  if (spec.revision.strength != null && !options.bindings.strength) {
4691
4731
  throw new Error("ComfyUI revision strength requires bindings.strength");
4692
4732
  }
4733
+ if (spec.revision.numColors != null || spec.revision.paletteImageFile || spec.revision.dithering) {
4734
+ throw new Error(
4735
+ "ComfyUI has no binding for numColors/paletteImage/dithering \u2014 those are PixelLab's /reduce-colors parameters. A reduce-colors revision on ComfyUI is whatever your own workflow does with bindings.sourceImage; write that behavior into the graph directly."
4736
+ );
4737
+ }
4693
4738
  if ((!options.bindings.width || !options.bindings.height) && spec.revision.sourceWidth != null && spec.revision.sourceHeight != null && (spec.width !== spec.revision.sourceWidth || spec.height !== spec.revision.sourceHeight)) {
4694
4739
  throw new Error(
4695
4740
  "ComfyUI revisions without width/height bindings must keep the source dimensions"
@@ -5388,6 +5433,16 @@ var MapObjectSchema = z2.object({
5388
5433
  }).passthrough();
5389
5434
  var ObjectListSchema = z2.object({ objects: z2.array(PixelLabObjectSchema), total: z2.number().int().min(0) }).passthrough();
5390
5435
  var PixfluxResponseSchema = z2.object({ image: z2.object({ base64: z2.string().min(1) }).passthrough(), usage: z2.unknown().optional() }).passthrough();
5436
+ var ReduceColorsResponseSchema = z2.object({
5437
+ images: z2.array(z2.object({ base64: z2.string().min(1) }).passthrough()).min(1),
5438
+ palette: z2.object({ base64: z2.string().min(1) }).passthrough(),
5439
+ n_colors: z2.number().int(),
5440
+ usage: z2.unknown().optional()
5441
+ }).passthrough();
5442
+ var CorrectPixelartResponseSchema = z2.object({
5443
+ images: z2.array(z2.object({ base64: z2.string().min(1) }).passthrough()).min(1),
5444
+ usage: z2.unknown().optional()
5445
+ }).passthrough();
5391
5446
  var SelectFramesSchema = z2.object({ created_object_ids: z2.array(z2.string()) }).passthrough();
5392
5447
  var CharacterSubmitSchema = z2.object({
5393
5448
  background_job_id: z2.string().min(1),
@@ -6095,6 +6150,52 @@ var PixelLabClient = class {
6095
6150
  "generate-image-v2"
6096
6151
  );
6097
6152
  }
6153
+ /**
6154
+ * `/reduce-colors`, PixelLab's "Cleanup" tier: quantize one image onto a
6155
+ * smaller palette, synchronously — no `background_job_id`, the result
6156
+ * comes back in this same response, like `createImagePixflux`. Verified
6157
+ * against the live OpenAPI schema, not exercised against a live account:
6158
+ * the schema's own response example is `usage: {type: "usd", usd: 0.02}`,
6159
+ * which — going by this codebase's own repeated experience with PixelLab's
6160
+ * documented-vs-billed cost mismatches (`isometricTile`, `objectPro`) —
6161
+ * should not be trusted over a real call. `numColors` and `paletteImage`
6162
+ * are mutually exclusive upstream; the manifest schema already enforces
6163
+ * that before this is ever called.
6164
+ */
6165
+ async reduceColors(args) {
6166
+ const body = { images: [args.image] };
6167
+ if (args.numColors != null) body.num_colors = args.numColors;
6168
+ if (args.paletteImage) body.palette_image = args.paletteImage;
6169
+ if (args.dithering) body.dithering = args.dithering;
6170
+ if (args.ditheringStrength != null) body.dithering_strength = args.ditheringStrength;
6171
+ const res = validateResponse(
6172
+ ReduceColorsResponseSchema,
6173
+ await this.request("/reduce-colors", { method: "POST", body: JSON.stringify(body) }),
6174
+ "reduce-colors"
6175
+ );
6176
+ return {
6177
+ png: Buffer.from(res.images[0].base64, "base64"),
6178
+ paletteStripPng: Buffer.from(res.palette.base64, "base64"),
6179
+ nColors: res.n_colors,
6180
+ usage: res.usage
6181
+ };
6182
+ }
6183
+ /**
6184
+ * `/correct-pixelart`, PixelLab's "Cleanup" tier: sharpen edges and drop
6185
+ * stray pixels without resizing, synchronously — same shape as
6186
+ * `reduceColors` above, no background job. Cost is likewise unverified
6187
+ * against a live account (schema example: `usage: {type: "usd", usd: 0.02}`).
6188
+ */
6189
+ async correctPixelart(args) {
6190
+ const body = { images: [args.image] };
6191
+ if (args.strength != null) body.strength = args.strength;
6192
+ const res = validateResponse(
6193
+ CorrectPixelartResponseSchema,
6194
+ await this.request("/correct-pixelart", { method: "POST", body: JSON.stringify(body) }),
6195
+ "correct-pixelart"
6196
+ );
6197
+ return { png: Buffer.from(res.images[0].base64, "base64"), usage: res.usage };
6198
+ }
6098
6199
  async getBackgroundJob(jobId) {
6099
6200
  const raw = await this.request(`/background-jobs/${encodeURIComponent(jobId)}`);
6100
6201
  return validateResponse(BackgroundJobSchema, raw, "background-jobs/{id}");
@@ -6274,12 +6375,15 @@ var PixelLabProvider = class _PixelLabProvider {
6274
6375
  }
6275
6376
  /**
6276
6377
  * `inpaint` (`/inpaint-v3`, a mask) and `image-to-image` (`/edit-images-v2`,
6277
- * no mask) both exist on PixelLab. `outpaint` does not: there is no
6378
+ * no mask) both exist on PixelLab, and so do `reduce-colors`
6379
+ * (`/reduce-colors`) and `correct-pixelart` (`/correct-pixelart`) — PixelLab's
6380
+ * "Cleanup" tier, a mechanical post-process on the source's own pixels
6381
+ * rather than a described edit. `outpaint` does not: there is no
6278
6382
  * canvas-expansion endpoint in the API, matching docs/REVISIONS.md's note
6279
6383
  * that no provider ships a tested outpaint path yet.
6280
6384
  */
6281
6385
  supportsRevision(mode2) {
6282
- return mode2 === "inpaint" || mode2 === "image-to-image";
6386
+ return mode2 === "inpaint" || mode2 === "image-to-image" || mode2 === "reduce-colors" || mode2 === "correct-pixelart";
6283
6387
  }
6284
6388
  /** PixelLab's own constraints: submissions must be >2s apart, and
6285
6389
  * background jobs in flight are capped by subscription tier (Tier 1=8,
@@ -6301,6 +6405,9 @@ var PixelLabProvider = class _PixelLabProvider {
6301
6405
  return dir;
6302
6406
  }
6303
6407
  estimate(spec) {
6408
+ if (spec.revision?.mode === "reduce-colors" || spec.revision?.mode === "correct-pixelart") {
6409
+ return { unit: "generations", amount: 0.1, candidates: 1 };
6410
+ }
6304
6411
  if (spec.revision) {
6305
6412
  const width = spec.revision.sourceWidth ?? spec.width;
6306
6413
  const height = spec.revision.sourceHeight ?? spec.height;
@@ -6328,6 +6435,20 @@ var PixelLabProvider = class _PixelLabProvider {
6328
6435
  throw new Error(`PixelLab inpaint source is ${width}x${height}; the API takes 32 to 512 pixels per side`);
6329
6436
  }
6330
6437
  }
6438
+ if (spec.revision?.mode === "reduce-colors") {
6439
+ const { sourceWidth: width, sourceHeight: height } = spec.revision;
6440
+ if (width != null && height != null && width * height > 512 * 512) {
6441
+ throw new Error(
6442
+ `PixelLab reduce-colors source is ${width}x${height} (${width * height}px\xB2); the API takes at most 512x512 worth of pixels per call`
6443
+ );
6444
+ }
6445
+ }
6446
+ if (spec.revision?.mode === "correct-pixelart") {
6447
+ const { sourceWidth: width, sourceHeight: height } = spec.revision;
6448
+ if (width != null && height != null && (width > 1024 || height > 1024)) {
6449
+ throw new Error(`PixelLab correct-pixelart source is ${width}x${height}; the API takes at most 1024 pixels per side`);
6450
+ }
6451
+ }
6331
6452
  if (spec.generator === "1dir" && (spec.width < 32 || spec.width > 256)) {
6332
6453
  throw new Error("PixelLab 1dir dimensions must be between 32 and 256 pixels");
6333
6454
  }
@@ -7120,6 +7241,35 @@ var PixelLabProvider = class _PixelLabProvider {
7120
7241
  });
7121
7242
  return { jobId: res2.background_job_id };
7122
7243
  }
7244
+ if (revision.mode === "reduce-colors") {
7245
+ let paletteImage;
7246
+ if (revision.paletteImageFile) {
7247
+ if (!revision.paletteImageSha256 || !revision.paletteImageFormat) {
7248
+ throw new Error(`${spec.styleId}/${spec.assetId}: revision palette image is not ready`);
7249
+ }
7250
+ const paletteBytes = readFileSync2(revision.paletteImageFile);
7251
+ if (sha256(paletteBytes) !== revision.paletteImageSha256) {
7252
+ throw new Error(`${spec.styleId}/${spec.assetId}: revision palette image changed after the manifest was resolved`);
7253
+ }
7254
+ paletteImage = { base64: paletteBytes.toString("base64"), format: revision.paletteImageFormat };
7255
+ }
7256
+ const res2 = await this.client.reduceColors({
7257
+ image,
7258
+ numColors: revision.numColors,
7259
+ paletteImage,
7260
+ dithering: revision.dithering,
7261
+ ditheringStrength: revision.ditheringStrength
7262
+ });
7263
+ const jobId = randomUUID3();
7264
+ writeFileSync(path12.join(_PixelLabProvider.cacheDir(), `${jobId}.png`), res2.png);
7265
+ return { jobId, metadata: { revisionUsage: res2.usage } };
7266
+ }
7267
+ if (revision.mode === "correct-pixelart") {
7268
+ const res2 = await this.client.correctPixelart({ image, strength: revision.strength });
7269
+ const jobId = randomUUID3();
7270
+ writeFileSync(path12.join(_PixelLabProvider.cacheDir(), `${jobId}.png`), res2.png);
7271
+ return { jobId, metadata: { revisionUsage: res2.usage } };
7272
+ }
7123
7273
  if (revision.strength != null) {
7124
7274
  throw new Error(
7125
7275
  `${spec.styleId}/${spec.assetId}: PixelLab image-to-image revisions take no strength; edit-images-v2 always applies the full instruction`
@@ -7151,6 +7301,10 @@ var PixelLabProvider = class _PixelLabProvider {
7151
7301
  }
7152
7302
  }
7153
7303
  async poll(jobId, generator, context) {
7304
+ const revisionMode = context?.spec?.revision?.mode;
7305
+ if (revisionMode === "reduce-colors" || revisionMode === "correct-pixelart") {
7306
+ return this.pollCachedRevision(jobId, context);
7307
+ }
7154
7308
  if (context?.spec?.revision) return this.pollRevision(jobId);
7155
7309
  if (generator === "pixflux") {
7156
7310
  const file = path12.join(_PixelLabProvider.cacheDir(), `${jobId}.png`);
@@ -7186,6 +7340,27 @@ var PixelLabProvider = class _PixelLabProvider {
7186
7340
  etaSeconds: obj.eta_seconds ?? null
7187
7341
  };
7188
7342
  }
7343
+ /**
7344
+ * `reduce-colors` and `correct-pixelart` results are already on disk by
7345
+ * the time `poll` is first called — `submitRevision` wrote them
7346
+ * synchronously, the same way `pixflux`'s poll branch above checks its own
7347
+ * cache file rather than a provider resource. The billed amount, if any,
7348
+ * travels through `context.metadata.revisionUsage` since there is no
7349
+ * background job to ask afterwards the way `billedForJob` does for async
7350
+ * work.
7351
+ */
7352
+ async pollCachedRevision(jobId, context) {
7353
+ const file = path12.join(_PixelLabProvider.cacheDir(), `${jobId}.png`);
7354
+ if (!existsSync7(file)) {
7355
+ return {
7356
+ status: "failed",
7357
+ error: "revision result is no longer cached locally; re-run submit for this asset"
7358
+ };
7359
+ }
7360
+ const sourceUrl = `file://${file}`;
7361
+ const usage = context?.metadata?.revisionUsage;
7362
+ return { status: "ready", objectId: jobId, sourceUrl, sources: [{ url: sourceUrl }], billed: billedFromUsage(usage) };
7363
+ }
7189
7364
  /**
7190
7365
  * `/inpaint-v3` and `/edit-images-v2` both hand back a plain background
7191
7366
  * job with no resource of its own, polled generically at
@@ -9040,6 +9215,8 @@ async function resolveSpecs(loaded, filter) {
9040
9215
  `Revision mask for ${styleId}/${assetId} is ${maskImage.width}x${maskImage.height}; source ${asset.revision.from} is ${sourceImage.width}x${sourceImage.height}`
9041
9216
  );
9042
9217
  }
9218
+ const paletteImageFile = asset.revision.paletteImage ? path13.resolve(root, asset.revision.paletteImage) : void 0;
9219
+ const paletteImage = paletteImageFile ? await optionalRevisionImage(paletteImageFile, "revision palette image", "png") : null;
9043
9220
  resolved.revision = {
9044
9221
  mode: asset.revision.mode,
9045
9222
  sourceAssetId: asset.revision.from,
@@ -9056,7 +9233,17 @@ async function resolveSpecs(loaded, filter) {
9056
9233
  maskHeight: maskImage?.height ?? null,
9057
9234
  maskFormat: maskImage?.format ?? null
9058
9235
  } : {},
9059
- ...asset.revision.strength == null ? {} : { strength: asset.revision.strength }
9236
+ ...asset.revision.strength == null ? {} : { strength: asset.revision.strength },
9237
+ ...asset.revision.numColors == null ? {} : { numColors: asset.revision.numColors },
9238
+ ...paletteImageFile ? {
9239
+ paletteImageFile,
9240
+ paletteImageSha256: paletteImage?.hash ?? null,
9241
+ paletteImageWidth: paletteImage?.width ?? null,
9242
+ paletteImageHeight: paletteImage?.height ?? null,
9243
+ paletteImageFormat: paletteImage?.format ?? null
9244
+ } : {},
9245
+ ...asset.revision.dithering ? { dithering: asset.revision.dithering } : {},
9246
+ ...asset.revision.ditheringStrength == null ? {} : { ditheringStrength: asset.revision.ditheringStrength }
9060
9247
  };
9061
9248
  }
9062
9249
  resolved.specHash = specHash(
@@ -11426,6 +11613,25 @@ var AssetPatchSchema = z5.object({
11426
11613
  size: z5.number().int().nullable().optional(),
11427
11614
  tags: z5.array(z5.string()).optional()
11428
11615
  }).strict();
11616
+ var NewStateSchema = z5.object({
11617
+ /** Asset id of the parent this states; must already exist. */
11618
+ of: z5.string().min(1),
11619
+ paletteFromReference: z5.boolean().optional(),
11620
+ canvas: z5.object({ width: z5.number().int().min(16).max(256), height: z5.number().int().min(16).max(256) }).strict().optional()
11621
+ }).strict();
11622
+ var NewAnimationSchema = z5.object({
11623
+ /** Asset id of the parent this animates; must already exist. */
11624
+ of: z5.string().min(1),
11625
+ /** A PixelLab template id (`character` only; `objectPro` has no template concept). */
11626
+ template: z5.string().min(1).optional(),
11627
+ direction: CharacterDirectionSchema.optional(),
11628
+ frames: z5.number().int().min(4).max(16).optional(),
11629
+ fps: z5.number().int().min(1).max(60).optional(),
11630
+ mode: CharacterAnimationModeSchema.optional(),
11631
+ /** What is being animated, when the parent's own description would mislead the model (`character` only). */
11632
+ subject: z5.string().min(1).optional(),
11633
+ enhancePrompt: z5.boolean().optional()
11634
+ }).strict();
11429
11635
  var NewAssetSchema = z5.object({
11430
11636
  prompt: z5.string(),
11431
11637
  category: z5.string().optional(),
@@ -11446,8 +11652,16 @@ var NewAssetSchema = z5.object({
11446
11652
  /** Asset id of the parent this revises; must already exist. */
11447
11653
  from: z5.string().min(1),
11448
11654
  strength: z5.number().min(0).max(1).optional()
11449
- }).strict().optional()
11450
- }).strict();
11655
+ }).strict().optional(),
11656
+ /** A pose or outfit of an existing `character`/`objectPro` base or state. */
11657
+ state: NewStateSchema.optional(),
11658
+ /** A loop of an existing `character`/`objectPro` base or state. */
11659
+ animation: NewAnimationSchema.optional()
11660
+ }).strict().refine((asset) => !(asset.revision && (asset.state || asset.animation)), {
11661
+ message: "an asset is a revision, a state, or an animation \u2014 not more than one"
11662
+ }).refine((asset) => !(asset.state && asset.animation), {
11663
+ message: "an asset is a state or an animation \u2014 not both"
11664
+ });
11451
11665
  var CANDIDATE_OPTION = {
11452
11666
  retrodiffusion: "numImages",
11453
11667
  comfyui: "numImages",
@@ -11615,6 +11829,12 @@ function applyEdit(raw, edit) {
11615
11829
  if (edit.asset.revision && !Object.hasOwn(raw.assets, edit.asset.revision.from)) {
11616
11830
  throw new ManifestEditError(`revision parent "${edit.asset.revision.from}" is not declared by the manifest`);
11617
11831
  }
11832
+ if (edit.asset.state && !Object.hasOwn(raw.assets, edit.asset.state.of)) {
11833
+ throw new ManifestEditError(`state parent "${edit.asset.state.of}" is not declared by the manifest`);
11834
+ }
11835
+ if (edit.asset.animation && !Object.hasOwn(raw.assets, edit.asset.animation.of)) {
11836
+ throw new ManifestEditError(`animation parent "${edit.asset.animation.of}" is not declared by the manifest`);
11837
+ }
11618
11838
  raw.assets[edit.assetId] = asset2;
11619
11839
  return;
11620
11840
  }
@@ -12265,27 +12485,54 @@ async function samePixels(a, b) {
12265
12485
  }
12266
12486
  var metadataFps = (entry) => entry ? frameSetFps(entry) : null;
12267
12487
  function describeCharacter(spec, entry) {
12268
- const recorded = entry?.providerMetadata?.[entry.provider]?.character;
12488
+ const recordedCharacter = entry?.providerMetadata?.[entry.provider]?.character;
12269
12489
  if (spec?.character) {
12270
12490
  const character = spec.character;
12271
12491
  return {
12492
+ generator: "character",
12272
12493
  kind: character.kind,
12273
12494
  parentKey: character.parentAssetId ? lockKey(spec.styleId, character.parentAssetId) : null,
12274
12495
  mode: character.kind === "animation" ? character.animation.mode : character.mode,
12275
12496
  directions: character.directions,
12276
12497
  direction: character.animation?.direction ?? null,
12277
- characterId: recorded?.characterId ?? entry?.objectId?.split("#")[0] ?? null
12498
+ characterId: recordedCharacter?.characterId ?? entry?.objectId?.split("#")[0] ?? null
12499
+ };
12500
+ }
12501
+ if (spec?.objectPro) {
12502
+ const objectPro = spec.objectPro;
12503
+ return {
12504
+ generator: "objectPro",
12505
+ kind: objectPro.kind,
12506
+ parentKey: objectPro.parentAssetId ? lockKey(spec.styleId, objectPro.parentAssetId) : null,
12507
+ mode: objectPro.kind === "animation" ? objectPro.animation.mode : "pro-flash",
12508
+ directions: objectPro.directions,
12509
+ direction: objectPro.animation?.direction ?? null,
12510
+ characterId: entry?.objectId?.split("#")[0] ?? null
12278
12511
  };
12279
12512
  }
12280
12513
  if (entry?.generator === "character") {
12281
- const kind = recorded?.kind === "animation" || entry.outputs.some((o) => o.role?.startsWith("frame-")) ? "animation" : "base";
12514
+ const kind = recordedCharacter?.kind === "animation" || entry.outputs.some((o) => o.role?.startsWith("frame-")) ? "animation" : "base";
12282
12515
  return {
12516
+ generator: "character",
12283
12517
  kind,
12284
12518
  parentKey: null,
12285
- mode: recorded?.mode ?? "standard",
12286
- directions: recorded?.directions ?? entry.outputs.length,
12287
- direction: recorded?.direction ?? null,
12288
- characterId: recorded?.characterId ?? entry.objectId?.split("#")[0] ?? null
12519
+ mode: recordedCharacter?.mode ?? "standard",
12520
+ directions: recordedCharacter?.directions ?? entry.outputs.length,
12521
+ direction: recordedCharacter?.direction ?? null,
12522
+ characterId: recordedCharacter?.characterId ?? entry.objectId?.split("#")[0] ?? null
12523
+ };
12524
+ }
12525
+ if (entry?.generator === "objectPro") {
12526
+ const recordedObject = entry.providerMetadata?.[entry.provider]?.objectPro;
12527
+ const kind = recordedObject?.kind === "animation" || entry.outputs.some((o) => o.role?.startsWith("frame-")) ? "animation" : "base";
12528
+ return {
12529
+ generator: "objectPro",
12530
+ kind,
12531
+ parentKey: null,
12532
+ mode: "pro-flash",
12533
+ directions: recordedObject?.directions ?? entry.outputs.length,
12534
+ direction: recordedObject?.direction ?? null,
12535
+ characterId: entry.objectId?.split("#")[0] ?? null
12289
12536
  };
12290
12537
  }
12291
12538
  return null;
@@ -12621,7 +12868,7 @@ async function buildGallerySnapshot(opts) {
12621
12868
  view: style?.view ?? null,
12622
12869
  noBackground: style?.noBackground ?? true,
12623
12870
  quality: Boolean(style?.quality),
12624
- characters: style?.generator === "character" ? {
12871
+ characters: style?.generator === "character" || style?.generator === "objectPro" ? {
12625
12872
  bases: styleItems.filter((item) => item.character?.kind === "base").length,
12626
12873
  states: styleItems.filter((item) => item.character?.kind === "state").length,
12627
12874
  animations: styleItems.filter((item) => item.character?.kind === "animation").length
@@ -13352,7 +13599,11 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
13352
13599
  sourceAssetId: spec.revision.sourceAssetId,
13353
13600
  sourceSha256: spec.revision.sourceSha256,
13354
13601
  ...spec.revision.maskSha256 ? { maskSha256: spec.revision.maskSha256 } : {},
13355
- ...spec.revision.strength == null ? {} : { strength: spec.revision.strength }
13602
+ ...spec.revision.strength == null ? {} : { strength: spec.revision.strength },
13603
+ ...spec.revision.numColors == null ? {} : { numColors: spec.revision.numColors },
13604
+ ...spec.revision.paletteImageSha256 ? { paletteImageSha256: spec.revision.paletteImageSha256 } : {},
13605
+ ...spec.revision.dithering ? { dithering: spec.revision.dithering } : {},
13606
+ ...spec.revision.ditheringStrength == null ? {} : { ditheringStrength: spec.revision.ditheringStrength }
13356
13607
  } : null,
13357
13608
  status: "pending",
13358
13609
  jobId: previousJobId ?? null,