pixelkiln 0.75.0 → 0.76.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.
@@ -357,6 +357,7 @@ textarea.mono { font-family:var(--mono); font-size:11.5px; }
357
357
  .studio-price-rows > div { display:flex; justify-content:space-between; gap:12px; border-bottom:1px dotted var(--line); padding:2px 0; }
358
358
  .studio-price-rows .mono { font-family:var(--mono); color:var(--dim); }
359
359
  .studio-total { font:650 12.5px/1.4 var(--mono); }
360
+ .quote-live { font:600 9.5px/1 var(--mono); color:var(--bg); background:var(--cool); padding:2px 4px; text-transform:uppercase; letter-spacing:.04em; vertical-align:1px; cursor:help; }
360
361
 
361
362
  /* ---- family view: turntable and loop grid ---- */
362
363
  .sheet.family-sheet { width:min(1180px, 96vw); overflow-y:auto; align-content:start; }
@@ -4114,10 +4114,21 @@
4114
4114
  const table = el("div", "studio-price-rows");
4115
4115
  for (const i of mine) {
4116
4116
  const row2 = el("div");
4117
- row2.append(el("span", "mono", i.key), el("span", null, i.cost ? fmtCost(i.costUnit, i.cost) : "free (mirrored)"));
4117
+ const cost = el("span", null, i.cost ? fmtCost(i.costUnit, i.cost) : i.quote === "live" ? "free" : "free (mirrored)");
4118
+ if (i.quote === "live") {
4119
+ const tag = el("span", "quote-live", "live");
4120
+ tag.title = "PixelLab's own Pro Flash quote; the offline estimate was " + fmtCost(i.costUnit, i.estimate);
4121
+ cost.append(" ", tag);
4122
+ }
4123
+ row2.append(el("span", "mono", i.key), cost);
4118
4124
  table.append(row2);
4119
4125
  }
4120
4126
  priceBox.append(table);
4127
+ if (mine.some((i) => i.quote === "live")) {
4128
+ priceBox.append(el("small", "state-dim", "Rows marked live are PixelLab's current Pro Flash quote, which it calls provisional; the lock records what the finished job billed."));
4129
+ } else if (price.quoteError) {
4130
+ priceBox.append(el("small", "state-dim", "Pro Flash is priced offline: PixelLab's quote could not be had (" + price.quoteError + ")."));
4131
+ }
4121
4132
  const totals = {};
4122
4133
  for (const i of mine) totals[i.costUnit] = (totals[i.costUnit] || 0) + i.cost;
4123
4134
  priceBox.append(el("div", "studio-total", "Total: " + (Object.entries(totals).map(([u, n]) => fmtCost(u, n)).join(" + ") || "free")));
package/dist/index.cjs CHANGED
@@ -221,6 +221,7 @@ __export(index_exports, {
221
221
  paletteDistance: () => paletteDistance,
222
222
  parentAssetId: () => parentAssetId,
223
223
  parseLock: () => parseLock,
224
+ parseProFlashQuote: () => parseProFlashQuote,
224
225
  parseTerrainDescriptions: () => parseTerrainDescriptions,
225
226
  parseWorkspace: () => parseWorkspace,
226
227
  pickHistory: () => pickHistory,
@@ -2032,6 +2033,25 @@ function cleanupResults(images, sent, operation) {
2032
2033
  }
2033
2034
  return images.map((image) => Buffer.from(image.base64, "base64"));
2034
2035
  }
2036
+ function parseProFlashQuote(raw) {
2037
+ const body = raw && typeof raw === "object" ? raw : {};
2038
+ const nested = [body, body.usage, body.cost, body.estimate].filter((v) => Boolean(v) && typeof v === "object");
2039
+ const pick = (...names) => {
2040
+ for (const obj of nested) {
2041
+ for (const name of names) {
2042
+ const value = obj[name];
2043
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) return value;
2044
+ }
2045
+ }
2046
+ return null;
2047
+ };
2048
+ const image = pick("image", "image_generations", "first_image", "first_image_generations");
2049
+ const rotations = pick("rotations", "rotation", "rotations_generations", "rotation_generations");
2050
+ const total = pick("total", "total_generations", "generations");
2051
+ if (total !== null) return { image, rotations, total };
2052
+ if (image !== null || rotations !== null) return { image, rotations, total: (image ?? 0) + (rotations ?? 0) };
2053
+ throw new Error(`GET /pro-flash/cost returned no price: ${JSON.stringify(raw).slice(0, 200)}`);
2054
+ }
2035
2055
  var PixelLabError = class extends ProviderError {
2036
2056
  constructor(message6, status, body) {
2037
2057
  super("pixellab", message6, { status });
@@ -2090,6 +2110,19 @@ var PixelLabClient = class {
2090
2110
  plan: raw.subscription?.plan ?? "unknown"
2091
2111
  };
2092
2112
  }
2113
+ /**
2114
+ * `GET /pro-flash/cost`: PixelLab's own quote for one Pro Flash job, split
2115
+ * into the first image and the v3 rotations. Free, and provisional by
2116
+ * PixelLab's own description; the billed amount on the finished job is
2117
+ * the real one. The response schema is undocumented (`{}` in the spec),
2118
+ * so this reads it leniently: a total if one is given, else the sum of
2119
+ * the parts, and refuses an answer with no number in it.
2120
+ */
2121
+ async proFlashCost(args) {
2122
+ const query = new URLSearchParams({ operation: args.operation, width: String(args.width), height: String(args.height) });
2123
+ if (args.nDirections !== void 0) query.set("n_directions", String(args.nDirections));
2124
+ return parseProFlashQuote(await this.request(`/pro-flash/cost?${query}`));
2125
+ }
2093
2126
  /**
2094
2127
  * Square objects that persist indefinitely.
2095
2128
  *
@@ -18865,6 +18898,7 @@ async function workspaceStatus(ws, dir) {
18865
18898
  paletteDistance,
18866
18899
  parentAssetId,
18867
18900
  parseLock,
18901
+ parseProFlashQuote,
18868
18902
  parseTerrainDescriptions,
18869
18903
  parseWorkspace,
18870
18904
  pickHistory,