@vibecook/ice 0.14.0 → 0.15.0-desk.2

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.
Files changed (62) hide show
  1. package/dist/{chunk-ZPUPBULB.js → chunk-52GMQ2BN.js} +1320 -450
  2. package/dist/chunk-52GMQ2BN.js.map +1 -0
  3. package/dist/{chunk-BA7XJC27.js → chunk-K4HUZAYJ.js} +49 -34
  4. package/dist/chunk-K4HUZAYJ.js.map +1 -0
  5. package/dist/{chunk-DYK6KNLG.js → chunk-PFJN3XN2.js} +39 -14
  6. package/dist/chunk-PFJN3XN2.js.map +1 -0
  7. package/dist/{chunk-IBWWLA6D.js → chunk-U4YMA4TJ.js} +6 -5
  8. package/dist/chunk-U4YMA4TJ.js.map +1 -0
  9. package/dist/desk-kit.js +2 -2
  10. package/dist/desk-objects.js +15 -9
  11. package/dist/desk-objects.js.map +1 -1
  12. package/dist/desk.js +31 -3
  13. package/dist/devtools.js +1 -1
  14. package/dist/dom.js +2 -2
  15. package/dist/index.js +1 -1
  16. package/dist/react.js +9 -7
  17. package/dist/react.js.map +1 -1
  18. package/dist/types/packages/core/src/index.d.ts +1 -1
  19. package/dist/types/packages/core/src/interaction/install.d.ts +7 -1
  20. package/dist/types/packages/core/src/systems/held.d.ts +3 -1
  21. package/dist/types/packages/core/src/systems/l1-pick.d.ts +16 -0
  22. package/dist/types/packages/core/src/systems/l2-recognize.d.ts +3 -1
  23. package/dist/types/packages/core/src/systems/press-button.d.ts +25 -0
  24. package/dist/types/packages/core/src/systems/tray.d.ts +3 -0
  25. package/dist/types/packages/desk/src/compose/builder.d.ts +22 -1
  26. package/dist/types/packages/desk/src/compose/marks.d.ts +7 -0
  27. package/dist/types/packages/desk/src/compose/pick.d.ts +1 -1
  28. package/dist/types/packages/desk/src/compose/reflector.d.ts +9 -0
  29. package/dist/types/packages/desk/src/engine/budget.d.ts +5 -0
  30. package/dist/types/packages/desk/src/faults.d.ts +38 -0
  31. package/dist/types/packages/desk/src/ground.d.ts +158 -9
  32. package/dist/types/packages/desk/src/hold/focus.d.ts +1 -1
  33. package/dist/types/packages/desk/src/hold/pose.d.ts +27 -7
  34. package/dist/types/packages/desk/src/host/index.d.ts +1 -1
  35. package/dist/types/packages/desk/src/host/layer.d.ts +110 -4
  36. package/dist/types/packages/desk/src/index.d.ts +5 -2
  37. package/dist/types/packages/desk/src/kind.d.ts +7 -5
  38. package/dist/types/packages/desk/src/kinds/world.d.ts +20 -2
  39. package/dist/types/packages/desk/src/kit/editor.d.ts +3 -1
  40. package/dist/types/packages/desk/src/kit/hold.d.ts +1 -1
  41. package/dist/types/packages/desk/src/missing/layout.d.ts +27 -0
  42. package/dist/types/packages/desk/src/missing/object.d.ts +6 -0
  43. package/dist/types/packages/desk/src/missing/pass.d.ts +39 -0
  44. package/dist/types/packages/desk/src/missing/shaders.d.ts +7 -0
  45. package/dist/types/packages/desk/src/missing/slots.d.ts +8 -0
  46. package/dist/types/packages/desk/src/shaders.gen.d.ts +5 -4
  47. package/dist/types/packages/desk/src/theme.d.ts +22 -0
  48. package/dist/types/packages/desk/src/tray/drawer.d.ts +16 -5
  49. package/dist/types/packages/desk/src/tray/flux.d.ts +4 -1
  50. package/dist/types/packages/desk/src/tray/index.d.ts +1 -1
  51. package/dist/types/packages/desk/src/tray/pass.d.ts +7 -2
  52. package/dist/types/packages/desk/src/tray/specimens.d.ts +21 -6
  53. package/dist/types/packages/dom/src/desk-host.d.ts +15 -2
  54. package/dist/types/packages/dom/src/pointer-adapter.d.ts +6 -0
  55. package/dist/types/packages/objects/src/board/layout.d.ts +1 -1
  56. package/dist/types/packages/objects/src/shaders.gen.d.ts +1 -1
  57. package/dist/types/packages/react/src/selection-menu.d.ts +5 -2
  58. package/package.json +1 -1
  59. package/dist/chunk-BA7XJC27.js.map +0 -1
  60. package/dist/chunk-DYK6KNLG.js.map +0 -1
  61. package/dist/chunk-IBWWLA6D.js.map +0 -1
  62. package/dist/chunk-ZPUPBULB.js.map +0 -1
@@ -74,7 +74,7 @@ export { createPresentationTransitionCoordinator, type FrameSwitchDescriptor, ty
74
74
  export { canvasRuntimeExtensions, defineCanvasRuntimeExtension, defineFrameBehavior, frameBehaviorPackId, frameBehaviors, type CanvasReadFacet, type CanvasRuntimeChanges, type CanvasRuntimeExtension, type CanvasRuntimeExtensionContext, type CanvasRuntimeExtensionDef, type CanvasScopedReader, type CanvasSemanticMigration, type CanvasSemanticMigrationContext, type ExistingPlacementPosture, type FrameBehavior, type FrameBehaviorDef, type FrameBehaviorWriteContext, } from "./canvas/extensions";
75
75
  export { createWidgetEquipSystem } from "./widget/equip";
76
76
  export { attachSpawnBehaviors, attachSpawnParent, spawnWidget, widgetSpawnInits, type SpawnWidgetOpts } from "./widget/spawn";
77
- export type { FramePickSlot, FramePickSource } from "./systems/l1-pick";
77
+ export type { FramePickSlot, FramePickSource, PointPick } from "./systems/l1-pick";
78
78
  export { setWidgetProps } from "./widget/set-props";
79
79
  export { createWidgetRuntime, installWidgetRuntime, type WidgetRuntime } from "./widget/mount-store";
80
80
  export * from "./settings/defaults";
@@ -17,7 +17,7 @@ import { SpatialIndex } from "../../../kernel/src/index";
17
17
  import { type CommitSink } from "../engine/commit-sink";
18
18
  import type { Engine } from "../engine/engine";
19
19
  import type { InputQueue } from "../input/queue";
20
- import { type FramePickSlot } from "../systems/l1-pick";
20
+ import { type FramePickSlot, type PointPick } from "../systems/l1-pick";
21
21
  import { type SpawnProfiles } from "../systems/l2-recognize";
22
22
  import { type WirePreviewBuffer } from "../systems/l3-connect";
23
23
  import { type DropPlacementPolicy } from "../systems/l3-drop";
@@ -55,6 +55,12 @@ export interface InteractionStack extends InteractionCore {
55
55
  readonly wirePreview: WirePreviewBuffer;
56
56
  /** The frame pick source's slot (design-014, B3b): the ground layer sets `current` at mount, clears it at dispose. */
57
57
  readonly framePick: FramePickSlot;
58
+ /**
59
+ * The exact pick OUT of the tick (petition I27; l1-pick `pickAt`): what a press at a SCREEN point (CSS px of the view) would touch
60
+ * now — `picking`'s own `TouchesExact` body on the world as the last tick left it, through the frame source above; `undefined` over
61
+ * the bare canvas and while the pegboard drawer is out. Read-only. The desk's `handle.pick` reaches it through its mount context.
62
+ */
63
+ pickAt(sx: number, sy: number): PointPick | undefined;
58
64
  /**
59
65
  * The nav geometry seam (design-015 §9; D2b) beside `framePick`: the renderer's word on a
60
66
  * container's DRAWN face, the inside's arrival and the exact cut camera — core's nav, the
@@ -26,7 +26,9 @@
26
26
  * drawing surface with a mode in hand (D3t-a): that press is the TOOL's (`HeldPress` `tool` — the
27
27
  * board's stroke; a double-click there is two dots, never a way back) — or on one of the kind's named
28
28
  * PARTS (D3t-b: any part but `content` and `frame`, the object itself): that press is the KIND's
29
- * (`HeldPress` `part`, with the part it began on — a notebook's turn: two clicks there turn two pages).
29
+ * (`HeldPress` `part`, with the part it began on — a notebook's turn: two clicks there turn two pages);
30
+ * - every route above is the PRIMARY button's (petition I28, press-button.ts): a secondary press — and a middle one with nothing
31
+ * to pan — is a point in the hand as on the desk: it keeps no `HeldPress`, so it puts nothing down, works no part, lays no stroke.
30
32
  * The ways back are OPS (`ops.putDown`, structural) and a system may not run them mid-tick: it writes
31
33
  * the one-tick `HeldIntent` and the facade applies it after the step (D2b's `NavIntent`, same shape).
32
34
  * Nothing here reads a kind: the seam gives a frame and (D3t-a) the part under a point; the tool in
@@ -42,6 +42,11 @@
42
42
  * pickTopAt, so pick can never diverge from paint), then the CanvasSurface
43
43
  * entity as the guaranteed fallback (design-001 §4). Relations are written
44
44
  * change-only (design-002 §4 hygiene). Wires/ports are M8 — skipped.
45
+ *
46
+ * `pickAt` is the exact pick OUT of the tick (petition I27 — the desk's
47
+ * `handle.pick`, a host's right-click): the `TouchesExact` body above run on
48
+ * the world as the last tick left it, read-only — one body, so a host's pick
49
+ * and a press can never disagree.
45
50
  */
46
51
  import type { Entity, System, TickSystem, World } from "@vibecook/strata-ecs";
47
52
  import { SpatialIndex } from "../../../kernel/src/index";
@@ -82,11 +87,22 @@ export interface FramePickSource {
82
87
  export interface FramePickSlot {
83
88
  current: FramePickSource | null;
84
89
  }
90
+ /** What a press at a point would touch (`TouchesExact`): the entity, and the PART under it (`""` the thing itself — design-014). */
91
+ export interface PointPick {
92
+ readonly entity: Entity;
93
+ readonly part: string;
94
+ }
85
95
  export interface PickingSystems {
86
96
  spatialSync: TickSystem;
87
97
  picking: System;
88
98
  /** The shared spatial index — snap/drop/marquee consume the SAME instance. */
89
99
  index: SpatialIndex<Entity>;
100
+ /**
101
+ * The exact pick OUT of the tick (petition I27): what a press at a SCREEN point (CSS px of the view) would touch now — the
102
+ * `TouchesExact` body `picking` runs, on the world as the last tick left it and the camera as it stands; `undefined` over the bare
103
+ * canvas, and everywhere while the pegboard drawer is out (the desk inert, design-017 §4). Read-only: no relation, no tag, no stamp.
104
+ */
105
+ pickAt(sx: number, sy: number): PointPick | undefined;
90
106
  /** Nav-op seam (design-004 §7): forget every last-known AABB so the next
91
107
  * spatialSync pass repopulates the cleared index from the new Active set. */
92
108
  clearCaches(): void;
@@ -3,7 +3,9 @@
3
3
  *
4
4
  * `ctl:spawn` — cancelSweep (consumes the one-tick CancelRequest) →
5
5
  * recognizerSpawn (tool profiles; pinch on 2nd touch suspends
6
- * singles; wheel recognizers are `Simultaneous`) →
6
+ * singles; wheel recognizers are `Simultaneous`; a primary
7
+ * press only — a secondary one is a point, a middle one its
8
+ * pan's drag: petition I28, press-button.ts) →
7
9
  * recognizerIntegrity (requiredWatches counts, capture death,
8
10
  * suspension lift). Newborns are identity-only until the
9
11
  * ctl:spawn flush, so integrity naturally skips them until the
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The button a press began with (petition I28) — read off the pointer's `PointerButtons.buttons` on its `WentDown` tick, which is
3
+ * the down fact's own PointerEvent `buttons` mask (ingest's fold ends at the transition, l0-input.ts). A pointerdown carries the
4
+ * one button that pressed: a second button joining a held press arrives as a pointermove (the Pointer Events spec's chorded
5
+ * buttons), never as a down.
6
+ *
7
+ * - PRIMARY — bit 1 (a mouse's left button, a touch, a pen's tip) or 32 (a pen's eraser end: the board's eraser reads it), or
8
+ * no bit at all (a synthetic down that names no button — every press, as before I28). The recognizers take it, and the hand.
9
+ * - MIDDLE — bit 4: a drag only where its drag pans — `dragRoute`'s device convention on the bare canvas (design-003 §4.4), the
10
+ * hand's pan brought close (held.ts) — and never a tap, a long press, or a grab of what is under it.
11
+ * - SECONDARY — anything else (2: the right button, a pen's barrel; 8/16: back and forward): a POINT. Its point, its pick and
12
+ * its hover move with it (ingest, `picking`); nothing selects, drags, enters or works a part. Its `contextmenu` is the host's.
13
+ *
14
+ * Pure over the ctx: no writes.
15
+ */
16
+ import type { Entity, SystemCtx } from "@vibecook/strata-ecs";
17
+ export type PressButton = "primary" | "middle" | "secondary";
18
+ /** The button a press began with, from the `buttons` mask its down carried. */
19
+ export declare function pressButton(buttons: number): PressButton;
20
+ /**
21
+ * Whether a MIDDLE press on `captured` is a drag: where `dragRoute` sends a drag to its CANVAS branch, whose device convention pans
22
+ * — the bare canvas, or a capture that is no handle, no port and no object (a wire). Over a handle, a port or an object it is a
23
+ * point: before I28 it resized, connected or MOVED what it landed on.
24
+ */
25
+ export declare function middlePans(ctx: Pick<SystemCtx, "has" | "hasTag">, captured: Entity | undefined): boolean;
@@ -44,6 +44,8 @@ import { type WidgetType } from "../widget/define-widget";
44
44
  * `trayScrollMax`), so the lay can clamp the scroll in the tick a new foot or face moves the range; absent, nothing clamps it.
45
45
  * design-018 R4: the `head`, how far under its top nothing of the board's content shows (the renderer's clear HEADER, where a host lays
46
46
  * the category chips) — a specimen scrolled under it is never hovered or taken there: a press there is the board's; absent, 0.
47
+ * Petition I21: the `foot`, how far above its bottom edge nothing of the content shows (a host's foot inset — its line floats over the
48
+ * board there): the same at the foot, and the range is the content's foot plus a pitch less the face ABOVE it; absent, 0.
47
49
  */
48
50
  export interface TrayScreenFrame {
49
51
  readonly x: number;
@@ -56,6 +58,7 @@ export interface TrayScreenFrame {
56
58
  readonly scroll: number;
57
59
  readonly face?: number;
58
60
  readonly head?: number;
61
+ readonly foot?: number;
59
62
  }
60
63
  /** The pose seam: the renderer's word on where the drawer is — `undefined` before its first frame. */
61
64
  export interface TrayPoseSource {
@@ -1,5 +1,7 @@
1
1
  import { type MarqueeBuffer, type NavFace, type Entity, type WidgetType, type World } from "../../../core/src/index";
2
+ import type { KindFaults } from "../faults";
2
3
  import type { HeldFrameInputs, OutgoingInputs, PortalInputs, SlotObject } from "../ground";
4
+ import { type HoldReserves } from "../hold/pose";
3
5
  import { FLUX_REST, type KindLocal, type ObjectFlux, type ObjectKind } from "../kinds/world";
4
6
  import type { MarksInput } from "../marks/layout";
5
7
  import { type SelectionAnchor } from "./marks";
@@ -20,7 +22,9 @@ export interface DeskBuilderOptions {
20
22
  /**
21
23
  * The OBJECT widget types this desk may hold: their prop groups join the change journal and
22
24
  * their kinds' `reach` the pick pad. An object of a type not listed still draws (its kind is found
23
- * off the widget type) but its props are refreshed only on a Position/Size/tag change.
25
+ * off the widget type) but its props are refreshed only on a Position/Size/tag change — unless the
26
+ * boundary is on (`faults`, the layer's): then its kind must be one the listed types name, the set
27
+ * the desk was MOUNTED with and its ground compiled (petition I25), or it wears the missing face.
24
28
  */
25
29
  readonly objects: readonly WidgetType[];
26
30
  /** The objects' springs (springs.ts `SPRINGS`). */
@@ -45,6 +49,17 @@ export interface DeskBuilderOptions {
45
49
  * host), every member is tested.
46
50
  */
47
51
  readonly spatial?: SpatialSource;
52
+ /** The hand's reserves (petition I20 — `deskLayer({ hold })`): where the reading fit keeps clear of the host's chrome; `HOLD`'s by default. */
53
+ readonly hold?: HoldReserves;
54
+ /**
55
+ * THE KIND BOUNDARY (petition I24, faults.ts): every call into a kind's world half — `resolve`, `record` (and what a remake asks with
56
+ * them: `face`, `frame`, `rung`, its desk state's `landed`), `chip`, `hit`, the hand's, a ghost's, `lifted` and `veils` — is caught at the
57
+ * kind: the throw is a strike, the object it was made for is left out of this frame (its record reset, made afresh next time), and
58
+ * the frame goes on. A MISSING kind's objects — and an object whose type has no kind — are drawn by the missing face
59
+ * (`MISSING_OBJECT`) under their kind's name; nothing of a missing kind is called again. Absent (a unit, a bare host): as before — a
60
+ * throw is the caller's, and an object whose type has no kind is not drawn.
61
+ */
62
+ readonly faults?: KindFaults;
48
63
  }
49
64
  /**
50
65
  * What the builder asks of the spatial index: the entries whose rects meet a world AABB (kernel's `SpatialIndex.search`), and how
@@ -258,6 +273,12 @@ export interface DeskBuilder {
258
273
  heldPoint(e: Entity, wx: number, wy: number): readonly [number, number] | undefined;
259
274
  /** The held kind's part under such a point — its `hit` on the geometry it was drawn with in hand; null over nothing (the pose seam's `part`, D3t-a). */
260
275
  heldPart(e: Entity, x: number, y: number): string | null;
276
+ /**
277
+ * What is under a world point of `e` as the last build drew it (the pick source's mirror, design-015 §4.5): its kind's `hit` on that
278
+ * geometry — or, drawn as the MISSING face (petition I24), its box. `null`: a miss (a kind whose `hit` throws takes a strike and misses
279
+ * — the pick goes on to the next object down); `undefined`: not drawn (unseen, culled, its kind gone missing since).
280
+ */
281
+ hitAt(e: Entity, wx: number, wy: number): string | null | undefined;
261
282
  /** The objects the last build painted LIFTED by their kind's own state (D3t-a — a print carried or in the air): above their siblings, asked first by the pick. */
262
283
  lifted(): readonly Entity[];
263
284
  /** An entity a kind's state veiled in the last build (D3t-c — a note gone with its month): not drawn, never picked. */
@@ -55,6 +55,13 @@ export interface HeldAnchor {
55
55
  readonly active: string;
56
56
  readonly landing: boolean;
57
57
  readonly settled: boolean;
58
+ /**
59
+ * The held bar's TRAVEL (M1), ms, as the host set it (`deskLayer({ hold: { travelMs } })`, petition I20): the bar travels to the
60
+ * foot over it and back. Absent — the host set none — the bar keeps its own.
61
+ */
62
+ readonly travelMs?: number;
63
+ /** The kind's WORD in hand (petition I22 — its `open.readout`): "Page 2", "September". Absent — the kind says none (or none now). */
64
+ readonly readout?: string;
58
65
  }
59
66
  /** A kind's act as the selection menu shows it (K8a): plain data — the op stays on the widget type (`ops.runMenuAction`). */
60
67
  export interface MenuSlot {
@@ -1,5 +1,5 @@
1
1
  import type { FramePickSource } from "../../../core/src/index";
2
2
  import type { DeskBuilder } from "./builder";
3
- export declare function createPickSource(builder: Pick<DeskBuilder, "geometryOf" | "kindOf" | "live" | "reach" | "lifted"> & Partial<Pick<DeskBuilder, "veiled" | "heldPoint" | "hand">>, opts?: {
3
+ export declare function createPickSource(builder: Pick<DeskBuilder, "geometryOf" | "kindOf" | "live" | "reach" | "lifted"> & Partial<Pick<DeskBuilder, "veiled" | "heldPoint" | "hand" | "hitAt">>, opts?: {
4
4
  readonly moving?: () => boolean;
5
5
  }): FramePickSource;
@@ -1,8 +1,10 @@
1
1
  import { type Entity, type ReflectorDef, type World } from "../../../core/src/index";
2
+ import type { KindFaults } from "../faults";
2
3
  import type { Ground, GroundFrameInputs, GroundStats } from "../ground";
3
4
  import { type KindLocal, type ObjectKind } from "../kinds/world";
4
5
  import { type GridConfig } from "../mat/grid";
5
6
  import type { GroundTheme, Palette } from "../theme";
7
+ import { type TrayOptions } from "../tray/drawer";
6
8
  import { type TrayFlux } from "../tray/flux";
7
9
  import { type TrayCarry } from "../tray/carry";
8
10
  import { type TrayCarriedFrame, type TraySpecimenFrame } from "../tray/specimens";
@@ -32,6 +34,13 @@ export interface DeskReflectorOptions {
32
34
  readonly onFrame?: () => void;
33
35
  /** The kinds' own state on this desk (`KindLocal`, by kind name): what the tray's carried objects are drawn with, as the builder's are (K5b). */
34
36
  readonly locals?: ReadonlyMap<string, KindLocal>;
37
+ /** The drawer as the host set it (petition I21 — `deskLayer({ tray })`): its foot inset. */
38
+ readonly tray?: TrayOptions;
39
+ /**
40
+ * THE KIND BOUNDARY (petition I24, faults.ts): the tray's specimens and carried copies are recorded through it (a missing kind's are the
41
+ * missing face; a throw is a strike), and a missing kind is never asked for its look again.
42
+ */
43
+ readonly faults?: KindFaults;
35
44
  /**
36
45
  * Called when a frame is asked for from OUTSIDE the flush — a wake (a pin, an ink landing, the ambient's policy), the ground
37
46
  * arriving, a theme, a grid, a harness's pin (K7a): the host wakes a sleeping loop with it.
@@ -26,6 +26,11 @@ export interface RasterBudget {
26
26
  touch(owner: string, key: string): void;
27
27
  /** The owner let the raster (or the resident array) go itself (no eviction). */
28
28
  release(owner: string, key: string): void;
29
+ /**
30
+ * Every raster `owner` holds, let go WITHOUT its eviction (petition I24 — a kind gone MISSING: its caches went with its passes,
31
+ * and nothing of it is called again). Returns the bytes forgotten.
32
+ */
33
+ forget(owner: string): number;
29
34
  /**
30
35
  * Evict least-recently-used caches until they are within their room — never one `keep` says to hold (an owner's word on
31
36
  * what is on screen this frame; absent, every cache may go), never a resident charge. Returns the bytes freed.
@@ -0,0 +1,38 @@
1
+ import type { Entity } from "../../core/src/index";
2
+ /** A kind the desk draws as MISSING, and why — what `DeskLayerStatus.faults` lists, in the order the kinds went missing. */
3
+ export interface KindFault {
4
+ readonly kind: string;
5
+ readonly reason: string;
6
+ }
7
+ /** The strikes a kind is allowed: the third throw makes it missing (design-009 §16's ladder — three strikes, then quarantine). */
8
+ export declare const KIND_STRIKES = 3;
9
+ /**
10
+ * A missing kind's row in the layer's `due().kinds` (host/layer.ts): no time — a time there is ≥ 0, `Infinity` never on its own —
11
+ * so −1 reads as what it is: this kind is never due again, nothing of it runs.
12
+ */
13
+ export declare const KIND_MISSING = -1;
14
+ export interface KindFaults {
15
+ /** How many kinds are missing — 0 while none is (every call site's first and, then, only test). */
16
+ readonly size: number;
17
+ /** Is `kind` missing (refused at create, or quarantined)? Nothing of it is called again. */
18
+ missing(kind: string): boolean;
19
+ /** `kind` was REFUSED at create (ground.ts — `reason` says so): missing from the start. Said once; a second word for it is ignored. */
20
+ refuse(kind: string, reason: string): void;
21
+ /**
22
+ * One of `kind`'s calls threw: a strike, said on the console with the call and the entity. The third makes it MISSING (said once,
23
+ * the listeners told) — returns true then. A strike for a kind already missing is ignored (its last call's throw, still unwinding).
24
+ */
25
+ strike(kind: string, call: string, err: unknown, entity?: Entity): boolean;
26
+ /** The strikes `kind` has taken (0 … `KIND_STRIKES`). */
27
+ strikes(kind: string): number;
28
+ /** The missing kinds, in the order they went — one array until the next goes (a host may hold it). */
29
+ list(): readonly KindFault[];
30
+ /** Told once for each kind that goes missing, after it is in `list()`. Returns the unsubscribe. */
31
+ subscribe(listener: (fault: KindFault) => void): () => void;
32
+ }
33
+ /**
34
+ * The first line of a fault worth a host's reading: an error's message, its first non-blank line — for the engine's WGSL refusal
35
+ * (`WGSL <label>:` then the compiler's lines, engine/shader.ts `compile`) the compiler's first, with its place.
36
+ */
37
+ export declare function faultText(err: unknown): string;
38
+ export declare function createKindFaults(): KindFaults;
@@ -1,5 +1,7 @@
1
1
  import type { Surface } from "./engine/device";
2
2
  import { CardPass } from "./card/card";
3
+ import { type KindFault } from "./faults";
4
+ import { type MissingFaces } from "./missing/pass";
3
5
  import { HoldPass } from "./hold/focus";
4
6
  import type { HoldShaders } from "./hold/shaders";
5
7
  import { type KindPass, type KindProgram, type RenderTarget, type StratumName } from "./kind";
@@ -59,6 +61,11 @@ export interface GroundOptions {
59
61
  * `tray`. Absent = a ground with no tray.
60
62
  */
61
63
  readonly tray?: TrayShaders;
64
+ /**
65
+ * A GPU error the kinds' creation window caught that no kind raises alone (petition I24, `createKindPasses`): not a kind's — told
66
+ * here as the device would have told its uncaptured-error handler (the layer's `onError`: `degraded`). Absent: it is said on the console.
67
+ */
68
+ readonly onError?: (error: GPUError) => void;
62
69
  }
63
70
  /** A layer a host rendered first, laid on the mat inside the ground's pass (it sets its own scissor; the ground restores the slot's). */
64
71
  export interface Underlay {
@@ -176,9 +183,22 @@ export interface SlotKind {
176
183
  /** The passes one slot owns: the mat's, and every registered kind's by name, in registration order. The root's are the ground's; the pool spawns the rest on the same pipelines. */
177
184
  export interface SlotSet {
178
185
  readonly mat: CuttingMat;
186
+ /**
187
+ * Every registered kind's entry — a MISSING kind's (petition I24) drawn by a spawn of the slot's missing face under the kind's own
188
+ * name and stratum — and, made on the first object that names it, `MISSING_KIND`'s (an object whose type has no kind). The ground
189
+ * swaps an entry when a kind goes missing (`Ground.quarantine`); nothing else writes the map.
190
+ */
179
191
  readonly kinds: ReadonlyMap<string, SlotKind>;
180
192
  /** The slot's FLAT-CARD pass (K7b, card/card.ts) over its kinds — absent when no registered kind declares a card material. */
181
193
  readonly card?: CardPass;
194
+ /**
195
+ * The desk's MISSING FACES (petition I24, missing/ — the root's alone): what draws an object nothing can draw, spawned on a slot's
196
+ * mat into a kind's entry when the kind goes missing; nothing of them is made until the first is asked for. Absent on a slot set
197
+ * made by hand (a unit's): such an object is then refused as an unregistered kind is.
198
+ */
199
+ readonly missing?: MissingFaces;
200
+ /** The kinds REFUSED at create (the root's alone; petition I24): each its name and why — their entries draw the missing face from the start. */
201
+ readonly faults?: readonly KindFault[];
182
202
  }
183
203
  /** One slot's passes for `drawFrame` — the ground's own, or the oracle's. A parent carries its nested slots. */
184
204
  export interface DrawSlot {
@@ -212,21 +232,43 @@ export interface DrawSlot {
212
232
  }
213
233
  /** A slot at opacity 0 draws nothing: "source over" with alpha 0 leaves every pixel as it was. */
214
234
  export declare const visible: (p: Presentation | undefined) => boolean;
235
+ /** What `createKindPasses` made: each program's root pass in registration order (undefined: refused), and the refusals. */
236
+ export interface KindPasses {
237
+ readonly passes: readonly (KindPass | undefined)[];
238
+ readonly faults: readonly KindFault[];
239
+ }
215
240
  /**
216
- * The root slot's passes: the mat's (made first — every kind's pass is made on it) and every registered
217
- * kind's, made in parallel on the root's mat, in registration order. Names must be unique and strata known.
218
- * Shared by the ground and the Node oracle.
241
+ * EVERY KIND'S PASS, EACH IN ITS OWN ERROR SCOPE (petition I24 — one kind's create is that kind's): made in parallel on the root's mat,
242
+ * as before, each settled alone — a create that rejects (a WGSL that does not compile — the engine's `compile` throws — a pipeline the
243
+ * device refused) or whose own scope caught an error is REFUSED, and the rest stand. A kind's scope is pushed before its `create` is
244
+ * called and popped as it returns, so it holds what the kind does before its first await (its layouts, its shader modules — where a
245
+ * compile error is raised); a WINDOW scope around them all holds whatever any kind does after (a pipeline made synchronously after an
246
+ * await). When the window caught something, whose it was is found by making each kind that stood ALONE again, wholly in a scope of its
247
+ * own, one after another (its first pass let go) — a fault's cost; a desk with none pays three scopes a kind. An error the window
248
+ * caught that no kind raises alone is not a kind's: `onError` hears it (the device's handler would have — the layer's `degraded`).
249
+ * Nothing else may run on the device while the window is open: the card, the missing face and the chrome are made after it.
219
250
  */
220
- export declare function createSlotSet(device: GPUDevice, format: GPUTextureFormat, mat: CuttingMat, programs: readonly KindProgram[]): Promise<SlotSet>;
251
+ export declare function createKindPasses(device: GPUDevice, format: GPUTextureFormat, mat: CuttingMat, programs: readonly KindProgram[], onError?: (error: GPUError) => void): Promise<KindPasses>;
252
+ /**
253
+ * The root slot's passes: the mat's (made first — every kind's pass is made on it), every registered kind's — made in parallel on the
254
+ * root's mat, in registration order, each in its own error scope (`createKindPasses`: a kind refused there is MISSING, petition I24) —
255
+ * then the card over the kinds that stood, and the desk's missing faces (made only if a kind was refused). Names must be unique and
256
+ * strata known. Shared by the ground and the Node oracle (whose `onError` is its probe's: it watches the creation in scopes of its own).
257
+ */
258
+ export declare function createSlotSet(device: GPUDevice, format: GPUTextureFormat, mat: CuttingMat, programs: readonly KindProgram[], onError?: (error: GPUError) => void): Promise<SlotSet>;
221
259
  /** Slots beyond the root, spawned on first use and reused every frame: `reset()` then `acquire()` per slot the frame needs. */
222
260
  export declare class SlotPool {
223
261
  private readonly slots;
224
262
  private used;
225
263
  private readonly root;
264
+ /** False once a kind the card composes went missing (petition I24 — `Ground.quarantine`): every slot's card, spawned or yet to be, is off. */
265
+ cardOn: boolean;
226
266
  constructor(root: SlotSet);
227
267
  reset(): void;
228
268
  /** The next slot: on first use its mat spawned from the root's, then every registered kind's pass on that mat, in registration order. */
229
269
  acquire(): SlotSet;
270
+ /** Every slot spawned so far (a quarantine walks them). */
271
+ each(fn: (s: SlotSet) => void): void;
230
272
  /** Slots spawned so far — the churn instrument. */
231
273
  get size(): number;
232
274
  dispose(): void;
@@ -303,6 +345,36 @@ export declare function drawTray(pass: GPURenderPassEncoder, size: {
303
345
  readonly w: number;
304
346
  readonly h: number;
305
347
  }, dpr: number, tray: TrayPass, slots: readonly DrawSlot[] | undefined, marks: MarksPass | null, carried?: readonly DrawSlot[]): void;
348
+ /**
349
+ * The passes one frame draws with — a `Ground`'s own (it IS one), or the Node oracle's: the root slot and the pool, the root's
350
+ * grid, and the three chrome passes (null where a host made none). What `encodeFrame` and `captureFrame` take, so the swap chain's
351
+ * frame, a capture's still and the oracle's render go through ONE encoding of a frame (petition I23: the capture is the frame path
352
+ * drawn once more into another target — never a second copy of it).
353
+ */
354
+ export interface FramePasses {
355
+ readonly root: SlotSet;
356
+ readonly pool: SlotPool;
357
+ readonly grid: GridConfig;
358
+ readonly marks: MarksPass | null;
359
+ readonly hold: HoldPass | null;
360
+ readonly tray: TrayPass | null;
361
+ readonly traySlots: TraySlotSource | null;
362
+ }
363
+ /** What `encodeFrame` recorded: the frame's stats (`Ground.render`'s answer), the prepared tree, and the marks it laid (0: none). */
364
+ export interface EncodedFrame {
365
+ readonly stats: GroundStats;
366
+ readonly prepared: PreparedFrame;
367
+ readonly marked: number;
368
+ }
369
+ /**
370
+ * Record one REST frame (no object in hand — the held frame is `renderHeldFrame`'s) into `encoder`, drawn into `into` — the swap
371
+ * chain (`Ground.render`), a capture's readable still (`captureFrame`), the oracle's target: the drawer's prepare (shut, p 0, it
372
+ * lays no quad; what the tray carries draws whatever the slide — a ghost grows, or flies home, past it), the slot tree, the marks,
373
+ * then ONE render pass — the frame stratum by stratum, the marks over every slot and stratum (stratum 5; `drawFrame` left the
374
+ * scissor on the whole view), the pegboard drawer over them (design-017: the dim, its shadow, the board, the specimens — K5a).
375
+ * The caller submits. `target` marks what the slots are prepared for (D7, `RenderTarget`); absent, the frame's.
376
+ */
377
+ export declare function encodeFrame(encoder: GPUCommandEncoder, passes: FramePasses, inputs: GroundFrameInputs, into: HeldInto, target?: RenderTarget): EncodedFrame;
306
378
  export declare class Ground {
307
379
  readonly device: GPUDevice;
308
380
  readonly surface: Surface;
@@ -328,10 +400,33 @@ export declare class Ground {
328
400
  heldCopies(): number;
329
401
  /** The drops last said, by kind (D7). */
330
402
  private readonly dropSaid;
403
+ /** The kinds MISSING on this ground (petition I24): refused at create, or quarantined since — their entries draw the missing face. */
404
+ private readonly missing;
331
405
  private constructor();
406
+ /**
407
+ * The mat first (every kind's pass is made on it), then every kind's pass in its own error scope (`createKindPasses` — a kind refused
408
+ * there is MISSING from the start: `faults()`, petition I24), then — once the kinds' window has closed, so nothing of theirs is taken
409
+ * for a kind's — the card over the kinds that stood, the marks, the hand and the tray, together. The missing faces are made only when
410
+ * one is asked for (a kind refused here, a quarantine, an object with no kind).
411
+ */
332
412
  static create(opts: GroundOptions): Promise<Ground>;
333
- /** The root's pass of the kind registered as `name` (undefined if none) — a host reaches its kind's own API through it: the note's ink pages, the whiteboard's rasters. */
413
+ /**
414
+ * The root's pass of the kind registered as `name` (undefined if none, or MISSING — petition I24: nothing of a missing kind is reached
415
+ * again) — a host reaches its kind's own API through it: the note's ink pages, the whiteboard's rasters.
416
+ */
334
417
  pass(name: string): KindPass | undefined;
418
+ /** The kinds REFUSED when this ground was made (petition I24): each its name and why. Quarantines since are the layer's word. */
419
+ faults(): readonly KindFault[];
420
+ /**
421
+ * THE QUARANTINE (petition I24): kind `name` is MISSING from now on — its entry in every slot this ground holds (the root, the pool's,
422
+ * the tray's) becomes a spawn of that slot's missing face, under its name and in its stratum, and the passes it had are disposed (the
423
+ * pool's and the tray's first; the root's — whose shared resources go with it — last; a dispose that throws is said and passed). A
424
+ * kind the flat card composes turns the card off in every slot, for good (K7b's own A/B door: each material kind then draws its own
425
+ * runs — the card's pixels, more draws): its resources leave the card's bind group, and no draw may reach them. The next frame draws
426
+ * the kind's objects in the missing face (the builder makes their records so); one already made for its own pass is drawn as nothing.
427
+ * A kind already missing, or none of that name: nothing.
428
+ */
429
+ quarantine(name: string): void;
335
430
  /** Size the canvas to its CSS box; returns the CSS size and dpr the frame should use. (A host may size the canvas itself instead.) */
336
431
  fit(maxDpr?: number): {
337
432
  changed: boolean;
@@ -343,9 +438,16 @@ export declare class Ground {
343
438
  };
344
439
  /** Render one frame now. Synchronous submit; the caller owns the cadence. */
345
440
  render(inputs: GroundFrameInputs): GroundStats;
441
+ /**
442
+ * THE CAPTURE DOOR's GPU half (petition I23; `captureFrame`): `inputs` — the last frame's, as `render` took them — drawn once
443
+ * more into a readable texture at the view's dpr × `scale`, `rect` (CSS px of the view) read back. The swap chain is not touched,
444
+ * no frame is drawn, nothing is kept: the still and its readback buffer are made for the call and destroyed after it. Undefined
445
+ * when the device is lost mid-copy, or the rect lies off the view.
446
+ */
447
+ capture(inputs: GroundFrameInputs, opts?: CaptureOptions): Promise<CaptureBytes | undefined>;
346
448
  /** A kind whose cap turns objects away is an ERROR on the host's console (D7): said when the drop begins and again if it grows. */
347
449
  private said;
348
- /** The pool's slots, then the root's kinds in reverse registration order, then the mat. */
450
+ /** The pool's slots, then the root's kinds in reverse registration order (a missing kind's face among them), its missing face, then the mat. */
349
451
  dispose(): void;
350
452
  /** Make the passes the tray's composite specimens need (K5a — `TraySlots.warm`); `onReady` asks for the frame that shows them. */
351
453
  warmTray(wanted: readonly (readonly [string, string])[], onReady: () => void): void;
@@ -366,7 +468,7 @@ export interface HeldCache {
366
468
  stats: GroundStats | null;
367
469
  copies: number;
368
470
  }
369
- /** Where a held frame goes: the swap chain's view and size (the ground's surface, or the oracle's target dressed as one). */
471
+ /** Where a frame goes: the swap chain's view and size (the ground's surface), the oracle's target dressed as one, or a capture's still. */
370
472
  export interface HeldInto {
371
473
  view(): GPUTextureView;
372
474
  size(): {
@@ -383,6 +485,53 @@ export interface HeldInto {
383
485
  * slot), the hand's light (the day's by night, as the carry rises);
384
486
  * 3. the FRAME: the desk out of focus (the sharp copy mixed toward the blurred one by the carry, dimmed), then the hand over it
385
487
  * through the reading light. Two fullscreen draws; the marks stay quiet (the builder hands none).
386
- * Returns the desk copy's stats (the frame's objects behind the hand).
488
+ * Returns the desk copy's stats (the frame's objects behind the hand). `label` names the encoders (`hold`; a capture's
489
+ * `capture` — the pass instrument closes a profiler frame at the `hold` encoder's submit, and a capture is not a frame).
490
+ */
491
+ export declare function renderHeldFrame(device: GPUDevice, hold: HoldPass, root: SlotSet, pool: SlotPool, grid: GridConfig, into: HeldInto, inputs: GroundFrameInputs, held: HeldFrameInputs, cache: HeldCache, label?: string): GroundStats;
492
+ /** A rect of the view, CSS px: `x`, `y` its top-left, `width` × `height` its size. */
493
+ export interface CaptureRect {
494
+ readonly x: number;
495
+ readonly y: number;
496
+ readonly width: number;
497
+ readonly height: number;
498
+ }
499
+ export interface CaptureOptions {
500
+ /** The part of the view to return, CSS px (the whole view when absent); what lies off the view is cut off. */
501
+ readonly rect?: CaptureRect;
502
+ /** A multiplier on the view's dpr (1 — the still at device pixels; a thumbnail asks 0.25). */
503
+ readonly scale?: number;
504
+ }
505
+ /**
506
+ * A captured still's bytes: `width` × `height` device px, rows tightly packed, four bytes a pixel in `format`'s own channel
507
+ * order and alpha AS DRAWN (`bgra8unorm` on most swap chains; `rgba8unorm` the oracle's) — the texture's texels, exactly:
508
+ * the host turns them into an `ImageBitmap` (host/layer.ts), the oracle hashes them beside its own render.
509
+ */
510
+ export interface CaptureBytes {
511
+ readonly width: number;
512
+ readonly height: number;
513
+ readonly format: GPUTextureFormat;
514
+ readonly bytes: Uint8Array<ArrayBuffer>;
515
+ }
516
+ /** A malformed capture option is a THROW at the call (a programming error, never an honest undefined): the scale positive and finite, the rect's four numbers finite. */
517
+ export declare function checkCapture(opts: CaptureOptions): void;
518
+ /**
519
+ * The frame's inputs with EVERY view's dpr × `scale` — the root's, each live inside's (recursively: a nested slot carries a view of
520
+ * its own, and its mat prepares its lattice against it), the departed desk's and its insides', the hand's, the marks' (their
521
+ * hairlines are 1/dpr), the tray's specimens' and carried copies' — so every slot names the capture's attachment (kit/layer.ts
522
+ * `attachmentOf`), exactly as the reflector's inputs would at that dpr. At scale 1, the inputs themselves.
523
+ */
524
+ export declare function scaledInputs(inputs: GroundFrameInputs, scale: number): GroundFrameInputs;
525
+ /**
526
+ * THE CAPTURE (petition I23 — the covers' still, the thumbnails, "Send to…"): `inputs` — the last PRESENTED frame's, as
527
+ * `Ground.render` took them (the same camera, theme, marks, hand and tray) — drawn ONCE MORE, outside any frame, into a readable
528
+ * texture at the view's size × its dpr × `scale`, through the same encoding the swap chain's frame went through (`encodeFrame`;
529
+ * the held frame through `renderHeldFrame` — at the view's own size the standing desk copy is reused, the hand alone redrawn);
530
+ * then `rect` (CSS px of the view; the whole view when absent), in device px of the still, copied to a buffer and MAPPED. The
531
+ * slots are prepared for the `capture` target (D7, `RenderTarget`: a kind re-asks the residency the frame holds, as under the
532
+ * hand — a thumbnail's coarser asks evict nothing). The per-frame path gains nothing: no copy of any frame is kept, the swap chain
533
+ * is not touched, and the still and the readback buffer — `capture/still`, `capture/readback`, the ledger's own `capture` line
534
+ * while they live — are destroyed before the promise settles. Undefined, never a throw, when the device is lost before the bytes
535
+ * are mapped (the map rejects) or the rect lies wholly off the view; a malformed option throws at the call.
387
536
  */
388
- export declare function renderHeldFrame(device: GPUDevice, hold: HoldPass, root: SlotSet, pool: SlotPool, grid: GridConfig, into: HeldInto, inputs: GroundFrameInputs, held: HeldFrameInputs, cache: HeldCache): GroundStats;
537
+ export declare function captureFrame(device: GPUDevice, passes: FramePasses, format: GPUTextureFormat, inputs: GroundFrameInputs, cache: HeldCache, opts?: CaptureOptions): Promise<CaptureBytes | undefined>;
@@ -1,7 +1,7 @@
1
1
  import { Target } from "../engine/target";
2
2
  import type { HoldShaders } from "./shaders";
3
3
  /** The pass's knobs: `texel` = (1/w, 1/h of the SOURCE, the Kawase offset in half-texels, 0); `mix` = (the carry e, the dim, the saturate, the brightness). */
4
- export declare const HoldUniformsStruct: import("../engine").StructDef<"texel" | "mix">;
4
+ export declare const HoldUniformsStruct: import("../engine").StructDef<"mix" | "texel">;
5
5
  /** The dual-Kawase chain's shape: two levels below the half-size copy; the offset per texel of radius. */
6
6
  export declare const KAWASE: {
7
7
  readonly levels: 2;
@@ -29,12 +29,30 @@ export declare const HOLD: {
29
29
  readonly hz: 1.4;
30
30
  readonly zeta: 0.78;
31
31
  };
32
- /** The held bar's travel (M1 morph) and its return after the landing. */
32
+ /** The held bar's travel (M1 morph) and its return after the landing — `travelMs` the default of a host's `hold.travelMs` (I20). */
33
33
  readonly bar: {
34
34
  readonly travelMs: 340;
35
35
  readonly backMs: 200;
36
36
  };
37
37
  };
38
+ /**
39
+ * THE HAND'S RESERVES (petition I20): how far the reading fit keeps from the view's top (`top`) and from its foot (`band` — the held
40
+ * bar's band), CSS px. `HOLD`'s are the default; a host's chrome moves them (`deskLayer({ hold })`). A phone keeps its own top.
41
+ */
42
+ export interface HoldReserves {
43
+ readonly top: number;
44
+ readonly band: number;
45
+ }
46
+ /**
47
+ * The hand as a HOST sets it (petition I20 — `deskLayer({ hold })`, read at the mount): the reserves above and below the reading fit,
48
+ * CSS px, and the held bar's travel (M1), ms — each `HOLD`'s when absent (`top` 56, `band` 72, `travelMs` 340). The bar is the host's:
49
+ * the desk publishes the travel on the selection's anchor (`HeldAnchor.travelMs`) for whichever bar draws it.
50
+ */
51
+ export interface HoldOptions {
52
+ readonly top?: number;
53
+ readonly band?: number;
54
+ readonly travelMs?: number;
55
+ }
38
56
  export interface HeldViewport {
39
57
  readonly width: number;
40
58
  readonly height: number;
@@ -49,13 +67,15 @@ export interface ReadingTarget {
49
67
  /** A phone's width (desk.js `narrow`). */
50
68
  export declare const isNarrow: (vp: HeldViewport) => boolean;
51
69
  /**
52
- * The reading size (desk.js `heldTarget`): the extent fits the view inside the margins and above the bar's band, never past 3×.
53
- * `spread`: the extent is a two-page spread that may open one page at a time — on a portrait phone (W < 0.85·H) it does when
54
- * one page reads at least 1.3× larger than the whole spread would; then the RIGHT page is centred (the extent's centre sits a
55
- * quarter of its width left of the middle) — or, `face` → 1, the LEFT (D3t-b: the notebook reads page by page; between, the view
56
- * glides).
70
+ * The reading size (desk.js `heldTarget`): the extent fits the view inside the margins, `reserves.top` under its top and the bar's
71
+ * band (`reserves.band`) above its foot, never past 3× — the host's reserves (I20), `HOLD`'s by default; a phone keeps its own top
72
+ * and margins. `spread`: the extent is a two-page spread that may open one page at a time — on a portrait phone (W < 0.85·H) it
73
+ * does when one page reads at least 1.3× larger than the whole spread would; then the RIGHT page is centred (the extent's centre sits
74
+ * a quarter of its width left of the middle) — or, `face` → 1, the LEFT (D3t-b: the notebook reads page by page; between, the view
75
+ * glides). That decision is the PHONE'S, on `HOLD`'s band whatever the host reserves, so a kind that asks it alone
76
+ * (`readingTarget(extent, vp, true).single`) agrees with the hand; the reserves size the page it shows.
57
77
  */
58
- export declare function readingTarget(extent: ObjectRect, vp: HeldViewport, spread?: boolean, face?: number): ReadingTarget;
78
+ export declare function readingTarget(extent: ObjectRect, vp: HeldViewport, spread?: boolean, face?: number, reserves?: HoldReserves): ReadingTarget;
59
79
  /** Where the extent lies on screen at rest, under the desk's camera: its centre (CSS px), the scale (the zoom) and the object's turn. */
60
80
  export interface HomePose {
61
81
  readonly cx: number;
@@ -1,4 +1,4 @@
1
- export { deskLayer, type DeskLayerContext, type DeskLayerFactory, type DeskLayerHandle, type DeskLayerOptions, type DeskLayerPerf, type DeskLayerStatus, type DeskTrayDoor, type MatPin, type SelectionSource, type TrayAnchor } from "./layer";
1
+ export { deskLayer, type DeskLayerBoot, type DeskLayerContext, type DeskLayerFactory, type DeskLayerHandle, type DeskLayerOptions, type DeskLayerPerf, type DeskLayerStatus, type DeskTrayDoor, type MatPin, type PickResult, type SelectionSource, type TrayAnchor } from "./layer";
2
2
  export type { SelectionAnchor } from "../compose/marks";
3
3
  export { surface, type Surface } from "./surface";
4
4
  export { createDeskEditor, type DeskEditorOptions, EDITOR_ATTR, KEYBOARD_CLAIM_ATTR } from "./editor";