@vibecook/ice 0.15.0-desk.2 → 0.15.0-desk.4

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/README.md CHANGED
@@ -102,7 +102,9 @@ export const Clock = defineObject({
102
102
  - **Being a container** — `ObjectKind.face` and `faceLaw: { radius, chips, finishes }`; a child's `chip()` names its finish.
103
103
  - **Held tools and menu acts** — a tool's `glyph` is a name of the bar's set or its own `{ path }`; `menu` acts show for a
104
104
  selection of the kind and run through `ops.runMenuAction`.
105
- - **Stills** — `handle.pinAsset(entity, asset)` pins the kind's own `ctx.asset`.
105
+ - **Stills** — `handle.pinAsset(entity, asset)` pins the kind's own `ctx.asset`; and a kind's pixel proof needs no desk layer:
106
+ `createStill({ device, format, size, dpr, objects, stage })` draws ONE frame on your device (Dawn in Node, or a browser's), no
107
+ canvas — `stage` lays its world (`engine.ops.spawnWidget`, `pinAsset`) — and hands back its RGBA to hold to a golden of your own.
106
108
 
107
109
  React and `react-dom` are **optional** peer dependencies — the core, desk and dom
108
110
  entries are React-free; `three` is imported nowhere.
@@ -18,6 +18,7 @@ import {
18
18
  HOLD,
19
19
  LAYER_IDLE_MS,
20
20
  MARKS,
21
+ MAT,
21
22
  MAT_GRID,
22
23
  MISSING,
23
24
  MatUniforms,
@@ -75,8 +76,9 @@ import {
75
76
  smoothstep,
76
77
  solveFlightStart,
77
78
  spring,
79
+ themeFrom,
78
80
  tilted
79
- } from "./chunk-K4HUZAYJ.js";
81
+ } from "./chunk-M7E6E7K5.js";
80
82
  import {
81
83
  BLEND_OVER,
82
84
  bindGroup,
@@ -85,7 +87,7 @@ import {
85
87
  renderPipeline,
86
88
  storageBuffer,
87
89
  uniformBuffer
88
- } from "./chunk-QXOCF56A.js";
90
+ } from "./chunk-RTBW5AIZ.js";
89
91
  import {
90
92
  Active,
91
93
  BoardRoot,
@@ -132,6 +134,7 @@ import {
132
134
  WidgetEquipped,
133
135
  closeTray,
134
136
  compareStackOrder,
137
+ createCanvasEngine,
135
138
  createSiblingOrderIndex,
136
139
  currentNavFrame,
137
140
  defineQuery,
@@ -3356,8 +3359,8 @@ function renderHeldFrame(device, hold, root, pool, grid, into, inputs, held, cac
3356
3359
  device.queue.submit([encoder.finish()]);
3357
3360
  return cache.stats ?? { k0: 0, fade: 0, wind: false, kinds: hand.kinds, outgoing: null, portals: 0 };
3358
3361
  }
3359
- var captureNumber = (what, v, positive) => {
3360
- if (typeof v !== "number" || !Number.isFinite(v) || positive && v <= 0) throw new Error(`capture: ${what} must be a ${positive ? "positive " : ""}finite number (got ${String(v)})`);
3362
+ var captureNumber = (what, v, positive2) => {
3363
+ if (typeof v !== "number" || !Number.isFinite(v) || positive2 && v <= 0) throw new Error(`capture: ${what} must be a ${positive2 ? "positive " : ""}finite number (got ${String(v)})`);
3361
3364
  return v;
3362
3365
  };
3363
3366
  function checkCapture(opts) {
@@ -8166,6 +8169,151 @@ async function ablateKinds(a, opts = {}) {
8166
8169
  return { frames, rounds, base: baseS, control: sampled(c), aa: median(c) - baseS.median, noise, kinds: out, samples: Object.fromEntries(ms) };
8167
8170
  }
8168
8171
 
8172
+ // ../desk/src/still.ts
8173
+ async function createStillDesk(opts) {
8174
+ const mat = await CuttingMat.create(opts.device, opts.format, matShaders(shaderText(MAT_SHADER_FILES)));
8175
+ let root;
8176
+ try {
8177
+ root = await createSlotSet(opts.device, opts.format, mat, opts.kinds, opts.onError);
8178
+ } catch (err) {
8179
+ mat.dispose();
8180
+ throw err;
8181
+ }
8182
+ mat.setNoise(blueNoise());
8183
+ const pool = new SlotPool(root);
8184
+ let disposed = false;
8185
+ return {
8186
+ mat,
8187
+ root,
8188
+ pool,
8189
+ dispose() {
8190
+ if (disposed) return;
8191
+ disposed = true;
8192
+ pool.dispose();
8193
+ root.card?.dispose();
8194
+ for (const k of [...root.kinds.values()].reverse()) k.pass.dispose();
8195
+ root.missing?.dispose();
8196
+ mat.dispose();
8197
+ }
8198
+ };
8199
+ }
8200
+ var FORMATS = ["rgba8unorm", "bgra8unorm"];
8201
+ var THEMES = ["light", "dark"];
8202
+ var STILL_PALETTE = { canvasBg: MAT.ground, select: MARKS.inks.pencil };
8203
+ var positive = (what, v) => {
8204
+ if (typeof v !== "number" || !Number.isFinite(v) || v <= 0) throw new Error(`createStill: ${what} must be a positive finite number (got ${String(v)})`);
8205
+ return v;
8206
+ };
8207
+ var finite = (what, v) => {
8208
+ if (typeof v !== "number" || !Number.isFinite(v)) throw new Error(`createStill: ${what} must be a finite number (got ${String(v)})`);
8209
+ return v;
8210
+ };
8211
+ function kindsOf2(objects) {
8212
+ const byName = /* @__PURE__ */ new Map();
8213
+ for (const t of objects) {
8214
+ const k = objectKindOf(t);
8215
+ if (k === void 0) throw new Error(`createStill: "${t.type}" is not a desk object \u2014 a still draws object types (\`defineObject\`)`);
8216
+ if (!byName.has(k.name)) byName.set(k.name, k);
8217
+ }
8218
+ return [...byName.values()];
8219
+ }
8220
+ var SCOPES2 = ["validation", "out-of-memory", "internal"];
8221
+ var watched2 = (device) => typeof device.pushErrorScope === "function";
8222
+ var pushScopes2 = (device) => {
8223
+ if (watched2(device)) for (const f of SCOPES2) device.pushErrorScope(f);
8224
+ };
8225
+ var popScopes2 = (device) => watched2(device) ? Promise.all(SCOPES2.map(() => device.popErrorScope())).then((errors) => errors.find((e) => e !== null) ?? null) : Promise.resolve(null);
8226
+ var gpuError = (e) => `${e.constructor?.name ?? "GPUError"}: ${e.message}`;
8227
+ var queues = /* @__PURE__ */ new WeakMap();
8228
+ function inTurn(device, run) {
8229
+ const turn = (queues.get(device) ?? Promise.resolve()).then(run);
8230
+ queues.set(device, turn.then(() => void 0, () => void 0));
8231
+ return turn;
8232
+ }
8233
+ function createStill(opts) {
8234
+ if (typeof opts !== "object" || opts === null) throw new Error("createStill: options are required");
8235
+ if (typeof opts.device !== "object" || opts.device === null) throw new Error("createStill: `device` is required \u2014 the caller's GPUDevice");
8236
+ if (!FORMATS.includes(opts.format)) throw new Error(`createStill: format must be ${FORMATS.map((f) => `"${f}"`).join(" or ")} (got ${String(opts.format)})`);
8237
+ const width = positive("size.width", opts.size?.width);
8238
+ const height = positive("size.height", opts.size?.height);
8239
+ const dpr = positive("dpr", opts.dpr);
8240
+ if (!Array.isArray(opts.objects)) throw new Error("createStill: `objects` must be a list of object types (`defineObject`)");
8241
+ const kinds = kindsOf2(opts.objects);
8242
+ const theme = opts.theme ?? "light";
8243
+ if (!THEMES.includes(theme)) throw new Error(`createStill: theme must be "light" or "dark" (got ${String(theme)})`);
8244
+ const cam = opts.camera === void 0 ? { x: -width / 2, y: -height / 2, zoom: 1 } : { x: finite("camera.x", opts.camera.x), y: finite("camera.y", opts.camera.y), zoom: positive("camera.zoom", opts.camera.zoom) };
8245
+ if (typeof opts.stage !== "function") throw new Error("createStill: `stage` must be a function \u2014 it lays the still's world");
8246
+ const plan = { width, height, dpr, kinds, theme, palette: opts.palette ?? STILL_PALETTE, cam };
8247
+ return inTurn(opts.device, () => drawStill(opts, plan));
8248
+ }
8249
+ async function drawStill(opts, plan) {
8250
+ const { device, format } = opts;
8251
+ const { width, height, dpr, cam } = plan;
8252
+ let unattributed2 = null;
8253
+ const desk = await createStillDesk({ device, format, kinds: plan.kinds, onError: (e) => {
8254
+ unattributed2 ??= e;
8255
+ } });
8256
+ let engine;
8257
+ let builder;
8258
+ try {
8259
+ const refused = desk.root.faults?.[0];
8260
+ if (refused !== void 0) throw new Error(`createStill: the kind "${refused.kind}" was ${refused.reason}`);
8261
+ if (unattributed2 !== null) throw new Error(`createStill: a GPU error while the kinds were made, no kind's alone \u2014 ${gpuError(unattributed2)}`);
8262
+ const theme = themeFrom(plan.theme, plan.palette);
8263
+ const looks = looksOf(plan.kinds, plan.palette, theme);
8264
+ engine = createCanvasEngine({ widgets: [...opts.objects] });
8265
+ engine.docs.create();
8266
+ const world = engine.world;
8267
+ world.setResource(Viewport, { w: width, h: height, dpr });
8268
+ world.setResource(Camera, { x: cam.x, y: cam.y, zoom: cam.zoom, gesturing: false });
8269
+ const b = createDeskBuilder(world, { objects: [...opts.objects] });
8270
+ builder = b;
8271
+ await opts.stage({ engine, world, pinAsset: (e, asset) => b.pin(e, asset), pinFlux: (e, targets) => b.pinFlux(e, targets) });
8272
+ engine.step(16);
8273
+ engine.step(32);
8274
+ b.changed();
8275
+ const built = b.build(cam, { width, height, dpr }, 0, theme, DEFAULT_GRID, looks, { now: 0 });
8276
+ const inputs = {
8277
+ view: { camX: cam.x, camY: cam.y, zoom: cam.zoom, width, height, dpr },
8278
+ grid: built.grid,
8279
+ objects: built.objects,
8280
+ ...built.portals.length > 0 ? { portals: built.portals } : {},
8281
+ ...built.lodZoom !== void 0 ? { lodZoom: built.lodZoom } : {},
8282
+ ...built.present !== void 0 ? { present: built.present } : {},
8283
+ ...built.light !== void 0 ? { light: built.light } : {},
8284
+ ...built.outgoing !== void 0 ? { outgoing: built.outgoing } : {},
8285
+ theme
8286
+ };
8287
+ const passes = { root: desk.root, pool: desk.pool, grid: built.grid, marks: null, hold: null, tray: null, traySlots: null };
8288
+ pushScopes2(device);
8289
+ let shot;
8290
+ let threw = null;
8291
+ try {
8292
+ shot = await captureFrame(device, passes, format, inputs, { stamp: null, stats: null, copies: 0 });
8293
+ } catch (err) {
8294
+ threw = { err };
8295
+ }
8296
+ const raised = await popScopes2(device);
8297
+ if (threw !== null) throw threw.err;
8298
+ if (raised !== null) throw new Error(`createStill: a GPU error while the still was drawn \u2014 ${gpuError(raised)}`);
8299
+ if (shot === void 0) throw new Error("createStill: the device was lost before the still was read back");
8300
+ const rgba = shot.bytes;
8301
+ if (format === "bgra8unorm") {
8302
+ for (let i = 0; i < rgba.length; i += 4) {
8303
+ const blue = rgba[i];
8304
+ rgba[i] = rgba[i + 2];
8305
+ rgba[i + 2] = blue;
8306
+ }
8307
+ }
8308
+ return { width: shot.width, height: shot.height, rgba, dispose() {
8309
+ } };
8310
+ } finally {
8311
+ builder?.dispose();
8312
+ engine?.dispose();
8313
+ desk.dispose();
8314
+ }
8315
+ }
8316
+
8169
8317
  export {
8170
8318
  KIND_STRIKES,
8171
8319
  KIND_MISSING,
@@ -8264,6 +8412,8 @@ export {
8264
8412
  inkRaster,
8265
8413
  kindsIn,
8266
8414
  withoutKind,
8267
- ablateKinds
8415
+ ablateKinds,
8416
+ STILL_PALETTE,
8417
+ createStill
8268
8418
  };
8269
- //# sourceMappingURL=chunk-52GMQ2BN.js.map
8419
+ //# sourceMappingURL=chunk-E5ADZNMK.js.map