pixelkiln 0.89.1 → 0.89.3

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
@@ -772,8 +772,9 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
772
772
  uiColorPalette: spec.uiColorPalette,
773
773
  // The older terrain knobs (terrainMode, terrainTransitionSize, and the
774
774
  // rest) were never part of this hash, and adding them now would make
775
- // every existing terrain entry stale. The Pro shape controls added
776
- // after them are identity, each only when set.
775
+ // every existing terrain entry stale; terrainSettings below records
776
+ // them per entry instead. The Pro shape controls added after them are
777
+ // identity, each only when set.
777
778
  terrainRoundness: spec.terrainRoundness,
778
779
  terrainBoundaryAmplitude: spec.terrainBoundaryAmplitude,
779
780
  terrainBoundarySeed: spec.terrainBoundarySeed,
@@ -899,6 +900,22 @@ function specHash(spec, styleImageHashes, providerOptionIdentity = spec.provider
899
900
  })
900
901
  );
901
902
  }
903
+ function terrainSettings(spec) {
904
+ if (spec.generator !== "terrain") return void 0;
905
+ const settings = {
906
+ terrainMode: spec.terrainMode,
907
+ terrainShapeStyle: spec.terrainShapeStyle,
908
+ terrainSpreadX: spec.terrainSpreadX,
909
+ terrainSlopeSize: spec.terrainSlopeSize,
910
+ terrainRaggedness: spec.terrainRaggedness,
911
+ terrainTransitionSize: spec.terrainTransitionSize,
912
+ terrainView: spec.terrainView,
913
+ terrainLowerDescription: spec.terrainLowerDescription,
914
+ terrainUpperDescription: spec.terrainUpperDescription,
915
+ terrainTransitionDescription: spec.terrainTransitionDescription
916
+ };
917
+ return Object.fromEntries(Object.entries(settings).filter(([, value]) => value !== void 0));
918
+ }
902
919
 
903
920
  // src/pipeline/mirror.ts
904
921
  import { mkdir as mkdir2, readFile as readFile2, rename as rename2, writeFile as writeFile2 } from "fs/promises";
@@ -2224,6 +2241,18 @@ var LockTerrainConnectSchema = z.record(
2224
2241
  TerrainSideSchema,
2225
2242
  z.object({ sourceAssetId: z.string().min(1), sourceObjectId: z.string().min(1) }).strict()
2226
2243
  );
2244
+ var LockTerrainSettingsSchema = z.object({
2245
+ terrainMode: z.string().optional(),
2246
+ terrainShapeStyle: z.string().optional(),
2247
+ terrainSpreadX: z.number().optional(),
2248
+ terrainSlopeSize: z.number().optional(),
2249
+ terrainRaggedness: z.number().optional(),
2250
+ terrainTransitionSize: z.number().optional(),
2251
+ terrainView: z.string().optional(),
2252
+ terrainLowerDescription: z.string().optional(),
2253
+ terrainUpperDescription: z.string().optional(),
2254
+ terrainTransitionDescription: z.string().optional()
2255
+ });
2227
2256
  var LockHistoryEntrySchema = z.object({
2228
2257
  specHash: z.string(),
2229
2258
  generator: GeneratorSchema,
@@ -2248,6 +2277,8 @@ var LockHistoryEntrySchema = z.object({
2248
2277
  postprocess: PostprocessSchema.optional(),
2249
2278
  /** A connected terrain set's other sets; see LockEntrySchema.terrainConnect. */
2250
2279
  terrainConnect: LockTerrainConnectSchema.optional(),
2280
+ /** A terrain set's settings outside specHash; see LockEntrySchema.terrainSettings. */
2281
+ terrainSettings: LockTerrainSettingsSchema.optional(),
2251
2282
  /** When the replacement was submitted. */
2252
2283
  retiredAt: z.string()
2253
2284
  });
@@ -2322,6 +2353,14 @@ var LockEntrySchema = z.object({
2322
2353
  * Absent (not null) otherwise, so other entries do not grow the key.
2323
2354
  */
2324
2355
  terrainConnect: LockTerrainConnectSchema.optional(),
2356
+ /**
2357
+ * For a terrain set: the settings it was submitted with that `specHash`
2358
+ * leaves out (see LockTerrainSettingsSchema). The plan marks the entry
2359
+ * stale when the manifest's differ. Absent on entries submitted before
2360
+ * it was recorded, which are taken as current, and on every other
2361
+ * generator's entries.
2362
+ */
2363
+ terrainSettings: LockTerrainSettingsSchema.optional(),
2325
2364
  /**
2326
2365
  * Output hashes owned by the previous generation while its replacement is
2327
2366
  * pending. They authorize replacing only unchanged PixelKiln-owned files.
@@ -4905,6 +4944,15 @@ function changedTerrainConnection(spec, entry, lock) {
4905
4944
  }
4906
4945
  return null;
4907
4946
  }
4947
+ function changedTerrainSettings(spec, entry) {
4948
+ const recorded = entry.terrainSettings;
4949
+ const current = terrainSettings(spec);
4950
+ if (!recorded || !current) return null;
4951
+ const show = (value) => value === void 0 ? "unset" : JSON.stringify(value);
4952
+ const fields = /* @__PURE__ */ new Set([...Object.keys(recorded), ...Object.keys(current)]);
4953
+ const changes = [...fields].filter((field) => recorded[field] !== current[field]).map((field) => `${field} changed from ${show(recorded[field])} to ${show(current[field])}`);
4954
+ return changes.length ? changes.join("; ") : null;
4955
+ }
4908
4956
  function resumeCommandForStatus(status) {
4909
4957
  if (status === "pending" || status === "processing") return "poll";
4910
4958
  if (status === "review") return "pick";
@@ -4937,6 +4985,7 @@ async function buildPlan(specs, lock, opts = {}) {
4937
4985
  let state;
4938
4986
  let reason;
4939
4987
  const changedConnection = entry ? changedTerrainConnection(spec, entry, lock) : null;
4988
+ const changedSettings = entry ? changedTerrainSettings(spec, entry) : null;
4940
4989
  const revision = await inspectRevisionReadiness(spec, lock);
4941
4990
  if (revision && !revision.ready) {
4942
4991
  state = "blocked";
@@ -4975,6 +5024,9 @@ async function buildPlan(specs, lock, opts = {}) {
4975
5024
  } else if (changedConnection) {
4976
5025
  state = "stale";
4977
5026
  reason = `connected set ${changedConnection} was regenerated; draw this set from its new terrain`;
5027
+ } else if (changedSettings) {
5028
+ state = "stale";
5029
+ reason = changedSettings;
4978
5030
  } else if (entry.status === "download-failed") {
4979
5031
  state = "recoverable";
4980
5032
  const force = entry.error?.includes("pass --force") ? " --force" : "";
@@ -17394,6 +17446,7 @@ function retireGeneration(entry, retiredAt) {
17394
17446
  provider: entry.provider,
17395
17447
  postprocess: entry.postprocess,
17396
17448
  ...entry.terrainConnect ? { terrainConnect: entry.terrainConnect } : {},
17449
+ ...entry.terrainSettings ? { terrainSettings: entry.terrainSettings } : {},
17397
17450
  retiredAt
17398
17451
  };
17399
17452
  }
@@ -17488,6 +17541,9 @@ async function revertGeneration(provider, spec, lock, lockPath, opts) {
17488
17541
  // A connected set comes back with the connection it was drawn from, so
17489
17542
  // the plan compares the restored generation, not the one it replaced.
17490
17543
  terrainConnect: generation.terrainConnect,
17544
+ // Likewise its settings; a generation from before they were recorded
17545
+ // comes back with none and is taken as current.
17546
+ terrainSettings: generation.terrainSettings,
17491
17547
  history
17492
17548
  });
17493
17549
  await saveLock(lockPath, lock);
@@ -18889,6 +18945,9 @@ async function submit(provider, loaded, items, lock, lockPath, opts = {}) {
18889
18945
  referenceSha256: spec.character.outfit.reference.sha256
18890
18946
  } : null,
18891
18947
  terrainConnect: spec.terrainConnect ? terrainConnect : void 0,
18948
+ // The terrain knobs specHash leaves out, so the plan can tell a
18949
+ // changed one; undefined (no key) for every other generator.
18950
+ terrainSettings: terrainSettings(spec),
18892
18951
  status: "pending",
18893
18952
  jobId: previousJobId ?? null,
18894
18953
  ...previousJobId ? { submissionComplete: false } : { submissionComplete: void 0 },