@vibecook/ice 0.15.0 → 0.16.0-portals.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.
- package/dist/{chunk-644DO6PN.js → chunk-K4SKRVLK.js} +2 -2
- package/dist/chunk-K4SKRVLK.js.map +1 -0
- package/dist/{chunk-A6PUTIL7.js → chunk-KV6RMWG4.js} +142 -8
- package/dist/chunk-KV6RMWG4.js.map +1 -0
- package/dist/{chunk-US7WJNR6.js → chunk-NB4ALBIY.js} +349 -66
- package/dist/chunk-NB4ALBIY.js.map +1 -0
- package/dist/{chunk-SPG45AVV.js → chunk-SHU3HVMF.js} +72 -12
- package/dist/chunk-SHU3HVMF.js.map +1 -0
- package/dist/desk-kit.js +14 -2
- package/dist/desk-objects.js +3 -3
- package/dist/desk.js +15 -3
- package/dist/devtools.js +1 -1
- package/dist/dom.js +2 -2
- package/dist/index.js +5 -3
- package/dist/react.js +20 -5
- package/dist/react.js.map +1 -1
- package/dist/types/packages/core/src/catalog/desk.d.ts +36 -1
- package/dist/types/packages/core/src/catalog/selection-presence.d.ts +0 -10
- package/dist/types/packages/core/src/systems/held.d.ts +10 -3
- package/dist/types/packages/core/src/systems/l4-cursor.d.ts +9 -2
- package/dist/types/packages/core/src/widget/define-widget.d.ts +16 -22
- package/dist/types/packages/desk/src/hold/told.d.ts +15 -0
- package/dist/types/packages/desk/src/index.d.ts +1 -1
- package/dist/types/packages/desk/src/kind.d.ts +5 -1
- package/dist/types/packages/desk/src/kinds/world.d.ts +93 -2
- package/dist/types/packages/desk/src/kit/editor.d.ts +9 -1
- package/dist/types/packages/desk/src/kit/index.d.ts +1 -0
- package/dist/types/packages/desk/src/kit/live.d.ts +260 -0
- package/dist/types/packages/desk/src/kit/mips.d.ts +11 -0
- package/dist/types/packages/desk/src/kit/wgsl.d.ts +5 -2
- package/dist/types/packages/desk/src/object.d.ts +1 -1
- package/dist/types/packages/desk/src/still.d.ts +3 -1
- package/dist/types/packages/dom/src/input-ownership.d.ts +5 -3
- package/dist/types/packages/dom/src/widget-focus.d.ts +2 -2
- package/dist/types/packages/objects/src/minimat/layout.d.ts +1 -1
- package/dist/types/packages/react/src/keymap.d.ts +9 -3
- package/package.json +1 -1
- package/dist/chunk-644DO6PN.js.map +0 -1
- package/dist/chunk-A6PUTIL7.js.map +0 -1
- package/dist/chunk-SPG45AVV.js.map +0 -1
- package/dist/chunk-US7WJNR6.js.map +0 -1
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import type { Entity } from "../../../core/src/index";
|
|
2
|
+
import type { SlotContext } from "../kind";
|
|
3
|
+
import type { ObjectRect } from "../kinds/world";
|
|
4
|
+
import { type ServiceKey } from "./services";
|
|
5
|
+
/** A rectangle of a source in its own pixels — a `VideoFrame`'s `visibleRect`: `x`, `y` its top-left. */
|
|
6
|
+
export interface LiveRect {
|
|
7
|
+
readonly x: number;
|
|
8
|
+
readonly y: number;
|
|
9
|
+
readonly width: number;
|
|
10
|
+
readonly height: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* What a host presents to a face: a source the device copies from (`copyExternalImageToTexture`'s). A `VideoFrame` is CLOSED by the
|
|
14
|
+
* writer once its copy is made — the host hands it over and never closes it itself; any other source stays the host's.
|
|
15
|
+
*/
|
|
16
|
+
export type LiveImage = VideoFrame | ImageBitmap | OffscreenCanvas | HTMLCanvasElement | ImageData;
|
|
17
|
+
/** The formats a live texture is made in: `rgba8unorm-srgb` (the default — filtered in linear light, as the photo's chain is) or `rgba8unorm`. */
|
|
18
|
+
export type LiveFormat = "rgba8unorm-srgb" | "rgba8unorm";
|
|
19
|
+
/** What a kind reads (design-019 §3.1): a stable texture, its size, and the counters that say when to rebind or make its mips again. */
|
|
20
|
+
export interface LiveTexture {
|
|
21
|
+
/** Texels of level 0 — the source's size as last presented (or as made, before the first). */
|
|
22
|
+
readonly width: number;
|
|
23
|
+
readonly height: number;
|
|
24
|
+
readonly format: LiveFormat;
|
|
25
|
+
/** Moves on every present: the content changed, so the mips are owed again (0 — nothing presented yet). */
|
|
26
|
+
readonly revision: number;
|
|
27
|
+
/** Moves when the GPU texture itself is replaced (a resize): a kind rebinds every group that holds `view()`. */
|
|
28
|
+
readonly epoch: number;
|
|
29
|
+
/**
|
|
30
|
+
* The whole chain's view — sampled through a trilinear sampler. Levels past `prepare`'s reach hold the mips last made there (a
|
|
31
|
+
* texture of a new epoch: nothing until they are made).
|
|
32
|
+
*/
|
|
33
|
+
view(): GPUTextureView;
|
|
34
|
+
/**
|
|
35
|
+
* Make the mips down to level `upTo` into `encoder` — the frame's, from the kind's `prepare`: never a submit of its own. Once per
|
|
36
|
+
* revision: a second call at the same revision and depth records nothing, a deeper one only the levels still missing. `upTo` is
|
|
37
|
+
* clamped to the chain's last level; nothing is made before the first present, nor for a texture made without mips.
|
|
38
|
+
*/
|
|
39
|
+
prepare(encoder: GPUCommandEncoder, upTo: number): void;
|
|
40
|
+
/** Bytes held on the device, every level counted (the kit's `chainBytes`) — what a kind charges its budget. */
|
|
41
|
+
readonly bytes: number;
|
|
42
|
+
/**
|
|
43
|
+
* The LOGICAL size of the frame last presented (design-019 §5, M24 LT2 — a page's CSS viewport, as its host said with the present):
|
|
44
|
+
* `[width, height]`, the texel size when the present gave none (and after a resize, before the next). Texels are not a source's
|
|
45
|
+
* coordinates: a kind maps the hand's point into the frame it DISPLAYED through this, and the host stamps that frame's geometry.
|
|
46
|
+
*/
|
|
47
|
+
readonly logical: readonly [number, number];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* What a HOST holds (design-019 §3.1): the face's texture and the doors that fill it. Every present is ONE copy into level 0 and moves
|
|
51
|
+
* `revision`; a source whose size differs from the texture's makes a new texture first (`resize` — `epoch` moves), so the texture is
|
|
52
|
+
* always the source's size. After `destroy` every present is ignored (a `VideoFrame` is still closed).
|
|
53
|
+
*/
|
|
54
|
+
export interface LiveWriter {
|
|
55
|
+
readonly face: LiveTexture;
|
|
56
|
+
/**
|
|
57
|
+
* One copy from a browser source (`copyExternalImageToTexture`), from `rect` when given; a `VideoFrame` is closed after the copy.
|
|
58
|
+
* `logical`: the frame's LOGICAL size (`LiveTexture.logical` — a page's CSS viewport), each side finite and > 0; absent, its texels.
|
|
59
|
+
*/
|
|
60
|
+
present(source: LiveImage, rect?: LiveRect, logical?: readonly [number, number]): void;
|
|
61
|
+
/** Raw RGBA8 rows, row-major, no header, `width` × `height` (`writeTexture`) — Node, Dawn, tests and stills; `logical` as `present`'s. */
|
|
62
|
+
presentBytes(bytes: Uint8Array, width: number, height: number, logical?: readonly [number, number]): void;
|
|
63
|
+
/** A texture already on this device (`copyTextureToTexture`, in a submit of its own), from `rect` when given; its format the face's or its `-srgb` twin; `logical` as `present`'s. */
|
|
64
|
+
presentTexture(texture: GPUTexture, rect?: LiveRect, logical?: readonly [number, number]): void;
|
|
65
|
+
/** A new texture of `width` × `height` — `epoch` moves, the old one is destroyed, the content is gone until the next present. The same size: nothing. */
|
|
66
|
+
resize(width: number, height: number): void;
|
|
67
|
+
destroy(): void;
|
|
68
|
+
}
|
|
69
|
+
/** What a live texture is made with. */
|
|
70
|
+
export interface LiveTextureOptions {
|
|
71
|
+
/** The texture's label — the CALLER's, `<kind>/live <key>` by convention, so the memory ledger (`gpuLedger`) groups it under the kind. */
|
|
72
|
+
readonly label: string;
|
|
73
|
+
readonly width: number;
|
|
74
|
+
readonly height: number;
|
|
75
|
+
/** `rgba8unorm-srgb` (the default) or `rgba8unorm`. */
|
|
76
|
+
readonly format?: LiveFormat;
|
|
77
|
+
/** A mip chain beside level 0 (the default) — made by `prepare` as deep as the kind reads it; `false`, level 0 alone. */
|
|
78
|
+
readonly mips?: boolean;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* A LIVE TEXTURE on `device` (design-019 §3.1) — what a host's `LiveSources` makes per face, on the desk's device (`deskLayer({
|
|
82
|
+
* onDevice })`): `rgba8unorm-srgb` with a mip chain by default, usable as a binding, a copy's source and target, and a level's
|
|
83
|
+
* attachment (the mips are drawn).
|
|
84
|
+
*/
|
|
85
|
+
export declare function createLiveTexture(device: GPUDevice, o: LiveTextureOptions): LiveWriter;
|
|
86
|
+
/**
|
|
87
|
+
* How deep a face is READ (design-019 §3.1 — "a mip chain made only as deep as it is read"): drawn at `px` device px, a texture of
|
|
88
|
+
* `face`'s size is minified by its larger ratio, and a trilinear sampler reads the two levels about log2 of it — so the levels down
|
|
89
|
+
* to ⌈log2 ratio⌉ are what `prepare` must make; 0 when the face is drawn at its size or larger. A kind passes it to `prepare`.
|
|
90
|
+
*/
|
|
91
|
+
export declare function liveDepth(face: Pick<LiveTexture, "width" | "height">, px: readonly [number, number]): number;
|
|
92
|
+
/**
|
|
93
|
+
* The source's state (design-019 §3.2): `starting` (no frame yet — the kind draws its still or its film), `live`, `paused` (the last
|
|
94
|
+
* frame holds), `closed`; `failed` with its reason in words and, when the host can, a way to try again.
|
|
95
|
+
*/
|
|
96
|
+
export type LiveState = {
|
|
97
|
+
readonly is: "starting" | "live" | "paused" | "closed";
|
|
98
|
+
} | {
|
|
99
|
+
readonly is: "failed";
|
|
100
|
+
readonly reason: string;
|
|
101
|
+
readonly retry?: () => void;
|
|
102
|
+
};
|
|
103
|
+
/** The source's words for the face — all optional; `extra` carries a host's own (the kind knows its host's contract). */
|
|
104
|
+
export interface LiveInfo {
|
|
105
|
+
readonly title?: string;
|
|
106
|
+
readonly address?: string;
|
|
107
|
+
/** 0 … 1 while loading; undefined when not. */
|
|
108
|
+
readonly loading?: number;
|
|
109
|
+
readonly back?: boolean;
|
|
110
|
+
readonly forward?: boolean;
|
|
111
|
+
/** The CSS cursor the source shows at the last point it was told of. */
|
|
112
|
+
readonly cursor?: string;
|
|
113
|
+
/** The source runs degraded — `"cpu-bitmap"` (LSF's fallback): the kind may badge it. */
|
|
114
|
+
readonly degraded?: string;
|
|
115
|
+
readonly extra?: Readonly<Record<string, unknown>>;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* What the desk asks of a face, in the KIND's law over what it is seen (design-019 §4) — handed to `LiveFace.demand` change-only; the
|
|
119
|
+
* host maps it to its producer and folds several holders of one key. `mode`: live or paused (a face unseen, behind the hand, evicted);
|
|
120
|
+
* `fps`: the rate it wants (the host buckets it — VibeField's 0/2/5/10/15/30/60); `raster`: the device px it wants delivered (a rung,
|
|
121
|
+
* held through a wobbling zoom); `viewport`: the source's logical layout size (a page's CSS viewport — absent: unchanged);
|
|
122
|
+
* `interactive`: in use, at the native rate and urgent.
|
|
123
|
+
*/
|
|
124
|
+
export interface LiveDemand {
|
|
125
|
+
readonly mode: "live" | "paused";
|
|
126
|
+
readonly fps: number;
|
|
127
|
+
readonly raster: readonly [number, number];
|
|
128
|
+
readonly viewport?: readonly [number, number];
|
|
129
|
+
readonly interactive: boolean;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Input to a face, in the coordinates of the frame it DISPLAYED — its LOGICAL size (`LiveTexture.logical`, design-019 §5; M24 LT2): a
|
|
133
|
+
* pointer (`button` the one pressed or released — −1 on a move, `buttons` the mask held, `count` the click count the hand told the kind:
|
|
134
|
+
* 1, 2, 3… — `HeldEvent`'s), a wheel (CSS px of the hand), a key (`mods` Chrome DevTools' modifier mask — Alt 1, Ctrl 2, Meta 4,
|
|
135
|
+
* Shift 8), committed text, an IME composition (its text and its caret within it — `EditorLease.compose`). A kind maps what the hand
|
|
136
|
+
* told it (`KindLocal.held`, the editor's lease) and sends it; it carries no geometry revision: the host stamps the frame it displayed.
|
|
137
|
+
*/
|
|
138
|
+
export type LiveInput = {
|
|
139
|
+
readonly kind: "pointer";
|
|
140
|
+
readonly x: number;
|
|
141
|
+
readonly y: number;
|
|
142
|
+
readonly button: number;
|
|
143
|
+
readonly buttons: number;
|
|
144
|
+
readonly count: number;
|
|
145
|
+
readonly phase: "down" | "move" | "up";
|
|
146
|
+
} | {
|
|
147
|
+
readonly kind: "wheel";
|
|
148
|
+
readonly x: number;
|
|
149
|
+
readonly y: number;
|
|
150
|
+
readonly dx: number;
|
|
151
|
+
readonly dy: number;
|
|
152
|
+
} | {
|
|
153
|
+
readonly kind: "key";
|
|
154
|
+
readonly key: string;
|
|
155
|
+
readonly code: string;
|
|
156
|
+
readonly phase: "down" | "up";
|
|
157
|
+
readonly mods: number;
|
|
158
|
+
readonly repeat: boolean;
|
|
159
|
+
} | {
|
|
160
|
+
readonly kind: "text";
|
|
161
|
+
readonly text: string;
|
|
162
|
+
} | {
|
|
163
|
+
readonly kind: "compose";
|
|
164
|
+
readonly text: string;
|
|
165
|
+
readonly caret: number;
|
|
166
|
+
};
|
|
167
|
+
/**
|
|
168
|
+
* ONE FACE a host opened for an object (design-019 §3.2). The kind holds it — a view and words, never a frame. A kind TAKES once right
|
|
169
|
+
* after it opens a face (what the host already holds — a page re-attached, a still's committed frame — lands in the frame that opens
|
|
170
|
+
* it), and then whenever `arrived` woke it.
|
|
171
|
+
*/
|
|
172
|
+
export interface LiveFace {
|
|
173
|
+
readonly key: string;
|
|
174
|
+
/** The face's texture, or undefined before the first frame and after an eviction — the kind draws its still or its film. */
|
|
175
|
+
texture(): LiveTexture | undefined;
|
|
176
|
+
/**
|
|
177
|
+
* In the kind's tick (and once at its open): the host copies the NEWEST arrival once into the texture and closes it — older ones
|
|
178
|
+
* were closed on arrival. True when the look changed: a frame landed, the state or the info moved.
|
|
179
|
+
*/
|
|
180
|
+
take(): boolean;
|
|
181
|
+
state(): LiveState;
|
|
182
|
+
info(): LiveInfo;
|
|
183
|
+
/** What the desk sees of the face, in the kind's law — change-only; the host maps it to its producer. */
|
|
184
|
+
demand(d: LiveDemand): void;
|
|
185
|
+
/** Input in the displayed frame's logical coordinates (§5 — a kind sends what the hand told it, M24 LT2). Absent: a view-only source (a captured window, a simulator without a control lane). */
|
|
186
|
+
readonly input?: ((e: LiveInput) => void) | undefined;
|
|
187
|
+
close(): void;
|
|
188
|
+
}
|
|
189
|
+
/** What a HOST lends (design-019 §3.2) under `LIVE`: its sources, as faces by key. */
|
|
190
|
+
export interface LiveSources {
|
|
191
|
+
/**
|
|
192
|
+
* Open a face for `key` — the object's DURABLE key (`KindHost.keyOf`) — on a source the HOST understands: `spec` is the host's
|
|
193
|
+
* contract (a page's address, a window's id), the desk never reads it. `arrived` is called — outside a frame, any number of times —
|
|
194
|
+
* when something new is ready: a frame, a state, an info change. One face per key per desk: a key opened twice is a host error.
|
|
195
|
+
* Open on the kind's first `record` of the object; `close` on its `forget`, the kind's `dispose`, or its eviction.
|
|
196
|
+
*/
|
|
197
|
+
open(key: string, spec: Readonly<Record<string, unknown>>, arrived: () => void): LiveFace;
|
|
198
|
+
}
|
|
199
|
+
/** The host's live sources as a desk SERVICE (design-019 §3.2 — `deskLayer({ services: [service(LIVE, sources)] })`): `host.use?.(LIVE)`. */
|
|
200
|
+
export declare const LIVE: ServiceKey<LiveSources>;
|
|
201
|
+
/**
|
|
202
|
+
* What a kind is told of one face (design-019 §3.4): `seen` — drawn in the last frame, in any slot but the desk copy behind the hand;
|
|
203
|
+
* `px` — the LARGEST device-px extent it was drawn at there (`[0, 0]` unseen); `held` — drawn in the hand.
|
|
204
|
+
*/
|
|
205
|
+
export interface Seen {
|
|
206
|
+
readonly seen: boolean;
|
|
207
|
+
readonly px: readonly [number, number];
|
|
208
|
+
readonly held: boolean;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* THE SIGHT (design-019 §3.4): where a kind's faces are drawn, folded into one fact per entity per frame. The kind's pass tells it what
|
|
212
|
+
* each slot drew (`saw`, in `prepare`); its tick asks what moved (`step`) and hands its demand law the facts.
|
|
213
|
+
*
|
|
214
|
+
* WHICH RENDERS COUNT: the frame's (`target` absent or `frame`) and the hand's (`held`). Not the desk COPY behind a carried object —
|
|
215
|
+
* prepared once per stamp, it is what the eye sees out of focus: a face behind the hand reads `seen: false` and rests on its last
|
|
216
|
+
* frame (§6, a policy) — and not a CAPTURE, which draws the last frame's inputs once more, outside any frame. So the photo's
|
|
217
|
+
* `keepSlotted` re-ask (K9 R4 — the hand's prepare the only prepare of a carried frame) is not needed here: under a carry the hand's
|
|
218
|
+
* prepare tells the held face, and every other face reads unseen BY THE POLICY, not by the gap.
|
|
219
|
+
*
|
|
220
|
+
* THE FRAME BOUNDARY: a kind with no records in a slot is never prepared, so "not asked" means "not drawn" only when a frame WAS drawn —
|
|
221
|
+
* and a kind is ticked on steps that draw nothing (any input step: a pointer moving over the bare mat ticks every kind and draws no
|
|
222
|
+
* frame). `frames` — the desk's count of frames drawn (`KindHost.frames`) — is that boundary: a step after no new frame says nothing
|
|
223
|
+
* moved; one after a frame folds what THAT frame's prepares saw, and an entity they did not see reads `seen: false`; prepares made
|
|
224
|
+
* outside the count (an instrument's render — the cost rig's batches, the profiler's ablation) are no frame's, dropped at the next
|
|
225
|
+
* step. Absent (a bare host, a still): every step is taken as a frame.
|
|
226
|
+
*/
|
|
227
|
+
export interface Sight {
|
|
228
|
+
/** In `prepare`, per slot, per record: the face's rect (world units — its `w`, `h` read) → device px through the slot's view. */
|
|
229
|
+
saw(e: Entity, slot: SlotContext, face: ObjectRect): void;
|
|
230
|
+
/** In the tick: the entities whose fact moved since the last step, each with its fact now. Empty when no frame was drawn since. */
|
|
231
|
+
step(): ReadonlyMap<Entity, Seen>;
|
|
232
|
+
/** `e`'s fact as of the last step; undefined before its first. */
|
|
233
|
+
of(e: Entity): Seen | undefined;
|
|
234
|
+
/** A frame was drawn since the last step — the kind is due NOW (its `KindLocal.due`), or a face culled by it reads seen until a later tick. */
|
|
235
|
+
owed(): boolean;
|
|
236
|
+
/** The entity left the desk: nothing of it is kept. */
|
|
237
|
+
forget(e: Entity): void;
|
|
238
|
+
}
|
|
239
|
+
/** A kind's SIGHT over its faces (design-019 §3.4) — `frames` the desk's count of frames drawn (`KindHost.frames`); see `Sight`. */
|
|
240
|
+
export declare function createSight(frames?: () => number): Sight;
|
|
241
|
+
/** A face's committed STILL (`stillLive`): RGBA8 rows, row-major, no header — `presentBytes`' shape — its logical size, and the words the face says. */
|
|
242
|
+
export interface LiveStill {
|
|
243
|
+
readonly width: number;
|
|
244
|
+
readonly height: number;
|
|
245
|
+
readonly bytes: Uint8Array;
|
|
246
|
+
/** The frame's LOGICAL size (`LiveTexture.logical` — a page's CSS viewport); absent, its texels. */
|
|
247
|
+
readonly logical?: readonly [number, number];
|
|
248
|
+
readonly info?: LiveInfo;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* A `LiveSources` of COMMITTED BYTES (design-019 §9) — lend it to a still (`createStill({ services: [service(LIVE, stillLive(device,
|
|
252
|
+
* …))] })`) or the oracle, and a live kind draws deterministically through the product's own path: the texture, its sampling, its mips,
|
|
253
|
+
* from owned fixtures (never a third party's pixels; a `VideoFrame` never enters Node). Every `open` answers a face whose FIRST `take()`
|
|
254
|
+
* presents its still through `presentBytes` — on `device`, labelled `live/still <key>` — and answers true once; then nothing ever
|
|
255
|
+
* arrives. Its state is `live` (`starting` for a key with no still — the kind draws its film), its info the still's. `frames` is read
|
|
256
|
+
* when a face OPENS — by key, or a function of the key and the host's spec — so a stage may fill a record with the keys its spawns were
|
|
257
|
+
* given (`engine.docs.current()?.store.keyOf(e)`: a still's keys are minted by its own document). A key opened twice while open throws;
|
|
258
|
+
* `close` destroys the face's texture.
|
|
259
|
+
*/
|
|
260
|
+
export declare function stillLive(device: GPUDevice, frames: Readonly<Record<string, LiveStill>> | ((key: string, spec: Readonly<Record<string, unknown>>) => LiveStill | undefined)): LiveSources;
|
|
@@ -1,2 +1,13 @@
|
|
|
1
1
|
export declare const mipCount: (w: number, h: number) => number;
|
|
2
2
|
export declare function generateMips(device: GPUDevice, texture: GPUTexture): void;
|
|
3
|
+
/**
|
|
4
|
+
* A chain's levels `from` … `to` made INTO `encoder` (design-019 §3.1, M24 LT1 — a live face's chain, made only as deep as it is read,
|
|
5
|
+
* in the frame's own encoder: no submit of its own), each level drawn from the one above it as `generateMips` draws it (a 2×2 box
|
|
6
|
+
* through a linear sampler; from an `-srgb` texture, in linear light). The pipeline is the device's per format (`generateMips`' own);
|
|
7
|
+
* each level's view and the bind group that reads the level above it are kept per TEXTURE, made the first time that level is asked —
|
|
8
|
+
* a face made again at a producer's 60 a second makes no view or group per call. `from` is at least 1; `to` is clamped to the chain's
|
|
9
|
+
* last level; `to` below `from` records nothing. The texture needs RENDER_ATTACHMENT (a level is drawn) and TEXTURE_BINDING (the one
|
|
10
|
+
* above is read). The DEVICE comes first, as `generateMips`' does: a WebGPU encoder and texture carry none, and the views and groups
|
|
11
|
+
* are made on it.
|
|
12
|
+
*/
|
|
13
|
+
export declare function mipsInto(device: GPUDevice, encoder: GPUCommandEncoder, texture: GPUTexture, from: number, to: number): void;
|
|
@@ -21,8 +21,11 @@ export { type ShaderText, shaderText } from "../shaders";
|
|
|
21
21
|
* desk in metres, y up (`desk_of`); colours 0..1, sRGB-encoded unless a parameter says LINEAR.
|
|
22
22
|
*/
|
|
23
23
|
export type KitWgslPiece = "view" | "portal" | "sdf" | "light" | "ruler" | "book";
|
|
24
|
-
/**
|
|
25
|
-
|
|
24
|
+
/**
|
|
25
|
+
* A name of the kit's WGSL: a piece, or another name for one (`lamp`, `gobo`, `night`, `noise` and `mat` are `light` — `mat` since
|
|
26
|
+
* M24 LT1: the mat's module IS the light piece, and a port that names the mat's module by its own name composes).
|
|
27
|
+
*/
|
|
28
|
+
export type KitWgslName = KitWgslPiece | "lamp" | "gobo" | "night" | "noise" | "mat";
|
|
26
29
|
/**
|
|
27
30
|
* The .wgsl file behind each of the kit's module pieces — what a host's `ShaderText` is asked for. `shaderText(KIT_WGSL_FILES)`
|
|
28
31
|
* is the pieces' text, their `///` blocks with it: what a consumer's docs generator quotes (petition I31).
|
|
@@ -12,7 +12,7 @@ export declare const PINNABLE = "desk.pinnable";
|
|
|
12
12
|
/** A kind's drivers on one desk, made by the host from what it lends (D7 #5, D-D7-A.3). Undefined: the kind has none. */
|
|
13
13
|
export type DriverFactory = (host: KindDriverHost) => KindDriver | undefined;
|
|
14
14
|
/** What an object declares: a widget definition without a view, plus its KIND. */
|
|
15
|
-
export interface ObjectDef extends Omit<WidgetDef, "object" | "stratum" | "openable" | "defaultSize" | "container" | "heldTools" | "heldTool"> {
|
|
15
|
+
export interface ObjectDef extends Omit<WidgetDef, "object" | "stratum" | "openable" | "defaultSize" | "container" | "heldTools" | "heldTool" | "heldWheel" | "heldEscape"> {
|
|
16
16
|
/** The kind: its program (the ground registers it) and its world half (the builder and the pick source drive it). */
|
|
17
17
|
readonly kind: ObjectKind;
|
|
18
18
|
/** The size a new one spawns at, world units (`defaultSize`). */
|
|
@@ -99,7 +99,9 @@ export interface StillOptions {
|
|
|
99
99
|
* then draws the hour the still is drawn at, so a still that must hold still pins its asset (`stage.pinAsset` — the desk
|
|
100
100
|
* clock's `{ at }`), as the oracle does. Absent: no desk state (a kind draws its facts and its pins alone), as before. ICE lends
|
|
101
101
|
* no text raster of its own in Node — `inkRaster` is a browser's Canvas 2D; a Node caller lends its own (a native canvas, or
|
|
102
|
-
* its test's).
|
|
102
|
+
* its test's). A LIVE kind (design-019 §9, M24) is lent committed bytes for its faces — `service(LIVE, stillLive(device, …))`:
|
|
103
|
+
* its desk state opens each face by the object's durable key (`KindHost.keyOf` — the still's own document mints them) and its
|
|
104
|
+
* first take presents the bytes through the product's path.
|
|
103
105
|
*/
|
|
104
106
|
readonly services?: readonly Lent[];
|
|
105
107
|
}
|
|
@@ -6,9 +6,11 @@
|
|
|
6
6
|
* - `isEditableTarget` — the 4-class editable gate (keymap default + the
|
|
7
7
|
* adapter's Space-types fix). Kept narrow so UNDECLARED widgets behave
|
|
8
8
|
* exactly as before (design-007 F5).
|
|
9
|
-
* - `keyboardClaimOf` — the `data-canvas-keyboard` claim marker
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* - `keyboardClaimOf` — the `data-canvas-keyboard` claim marker on a focused
|
|
10
|
+
* screen-space host: the desk's one editor carries it while a kind leases it
|
|
11
|
+
* (M24 LT2: the lease IS the claim — the `interaction.keyboard` declaration
|
|
12
|
+
* and its `KeyboardExclusive` tag, unread since D5b, are retired), and an
|
|
13
|
+
* app's own chrome may. Read UNBOUNDED (page-global) on purpose: `document.activeElement` is one
|
|
12
14
|
* per page, so engine B's keymap must stand down while you type into engine
|
|
13
15
|
* A's claiming widget (design-007 F2 — only the DOM is page-global).
|
|
14
16
|
* - `wheelCede` — the NARROW, DYNAMIC scroll opt-out (design-007 §3.4/§4):
|
|
@@ -50,8 +50,8 @@ export interface WidgetFocusHandle {
|
|
|
50
50
|
/**
|
|
51
51
|
* Programmatically focus a claiming widget (its proxy, else its host).
|
|
52
52
|
* Returns false when the entity has no host in the lookup (always, without
|
|
53
|
-
* one) or no keyboard claim
|
|
54
|
-
*
|
|
53
|
+
* one) or the host carries no keyboard claim (the `data-canvas-keyboard`
|
|
54
|
+
* marker — the claim drives focusability; design-007 §3.1).
|
|
55
55
|
*/
|
|
56
56
|
focusWidget(entity: Entity): boolean;
|
|
57
57
|
/** Blur whatever claim currently holds focus. False when none does. */
|
|
@@ -3,7 +3,7 @@ import type { RGB } from "../../../desk/src/index";
|
|
|
3
3
|
import { type Chip, type FaceLattice, type MiniMatGeometry, type MiniMatLaw, type Numerals } from "./minimat";
|
|
4
4
|
export declare const MiniMat: import("../../../desk/src/engine/index").StructDef<"ground" | "ring" | "lamp" | "inside" | "name" | "live" | "rungs" | "centre" | "half" | "lift" | "alpha" | "margin" | "slope" | "radius" | "thick" | "lampZ" | "chipFirst" | "chipCount" | "nameLen" | "pad0" | "weights" | "widths" | "numerals">;
|
|
5
5
|
export declare const ChipRecord: import("../../../desk/src/engine/index").StructDef<"kind" | "frame" | "ink" | "centre" | "half" | "colour" | "radius" | "rot" | "lines">;
|
|
6
|
-
export declare const MiniMatUniforms: import("../../../desk/src/engine/index").StructDef<"shadow" | "shadow2" | "select" | "edge" | "ticks" | "atlas" | "
|
|
6
|
+
export declare const MiniMatUniforms: import("../../../desk/src/engine/index").StructDef<"shadow" | "shadow2" | "select" | "edge" | "ticks" | "atlas" | "text" | "print" | "cream" | "castTint" | "digits" | "atlas2">;
|
|
7
7
|
export declare const MAX_MINIMATS = 256;
|
|
8
8
|
export declare const MAX_CHIPS = 4096;
|
|
9
9
|
/** One mini mat for the pass: its geometry, its vinyl, its inside's embedding and lattice, the live inside's presence, its chips. */
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
* ⇧⌘Z / ⇧Ctrl-Z → docs.redo
|
|
9
9
|
* ⌘D → ops.duplicateSelection
|
|
10
10
|
* ⌘A → ops.selectAll
|
|
11
|
-
* Esc → closes the pegboard tray first (design-017 §4),
|
|
11
|
+
* Esc → closes the pegboard tray first (design-017 §4), puts the object in hand down — unless its kind owns Esc
|
|
12
|
+
* (design-019 §5, M24 LT2: `open.escape: "kind"`, its type's `heldEscape`) — else ops.cancelActiveGestures
|
|
12
13
|
* Arrows → nudge the selection ±1px (⇧ = ±10px) — ONE tx per press; a TAPED widget
|
|
13
14
|
* never moves (design-015 §5.1 — `Locked`, D4a), its untaped companions do
|
|
14
15
|
* ⇧⌘L / ⇧Ctrl-L → tape the selection down, or lift the tape when all of it is taped
|
|
@@ -33,8 +34,13 @@
|
|
|
33
34
|
* This gate runs BEFORE the editable gate on purpose (2026-08-09 review):
|
|
34
35
|
* a claim whose focus node is an editable proxy (the Ghosttea-style
|
|
35
36
|
* hidden textarea, design-007 §6) must still get the Escape release —
|
|
36
|
-
* editable-first would shadow it and silently
|
|
37
|
-
*
|
|
37
|
+
* editable-first would shadow it and silently make every such claim
|
|
38
|
+
* one that owns Escape. With an object IN HAND (design-019 §5, M24
|
|
39
|
+
* LT2) the claim is its kind's lease on the desk's one editor — the
|
|
40
|
+
* hand's keyboard: an Escape the lease declined releases it AND puts
|
|
41
|
+
* the object down in the same press (a blur alone would leave it in
|
|
42
|
+
* hand, deaf); a kind that owns Escape (`heldEscape: "kind"`) has it
|
|
43
|
+
* left to its lease — no blur, no put-down.
|
|
38
44
|
* 3. Editable target (input/textarea/select/contentEditable — the shared
|
|
39
45
|
* 4-class predicate from @ice/dom) → ignored, so typing never triggers a
|
|
40
46
|
* shortcut. Kept narrow so UNDECLARED widgets behave exactly as before.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibecook/ice",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0-portals.2",
|
|
4
4
|
"description": "ICE — infinite canvas engine. The desk: every object under the camera drawn by one WebGPU renderer from a CRDT-synced ECS world, the DOM in screen space. defineObject turns a GPU kind into a canvas citizen — selectable, movable, nested in mini mats, undoable per gesture, collaborative.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "James Yong <me@jamesyong42.com>",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../dom/src/host.ts","../../dom/src/loop.ts","../../dom/src/input-ownership.ts","../../dom/src/pointer-adapter.ts","../../dom/src/reflectors/cursor.ts","../../dom/src/reflectors/remote-cursors.ts","../../dom/src/widget-focus.ts","../../dom/src/desk-host.ts","../../dom/src/index.ts"],"sourcesContent":["/**\n * The canvas host (design-004 §1, host pipeline; SCREEN SPACE ONLY since design-015 D5b).\n *\n * The container is styled to be a stable, gesture-clean viewport: `relative` so\n * absolutely-positioned children (the desk's canvas, the one focused editor, the\n * remote-cursor plane, the app's screen-space chrome) anchor to it,\n * `overflow:hidden` to clip them, `touch-action:none` + `user-select:none` so\n * browser scroll/zoom and text selection never fight the interaction stack.\n *\n * THE CONTENT PLANE IS GONE (design-015 §2 law 2). Until D5b the host also made\n * one camera-transformed `<div>` — M3's \"content plane\", the P1 every DOM\n * widget mounted in, carrying the camera as ONE CSS transform written by the\n * plane-transform reflector (kernel `planeCssTransform`, gone since D7). No DOM element\n * carries a camera transform any more: everything under the camera is the\n * desk renderer's, and the DOM that remains is screen-space.\n */\n\nexport interface CanvasHost {\n /** The app-provided viewport element (styled, not created, by the host). */\n readonly container: HTMLElement;\n /** Clear the inline styles the host wrote. */\n dispose(): void;\n}\n\n/** The container styles the host owns unconditionally (cleared on dispose). */\nconst CONTAINER_STYLE: Readonly<Record<string, string>> = {\n overflow: \"hidden\",\n touchAction: \"none\",\n userSelect: \"none\",\n};\n\nexport function createCanvasHost(container: HTMLElement): CanvasHost {\n // The children need the container to be a POSITIONED containing block — but\n // `absolute`/`fixed` already qualify, so only promote a `static` container\n // to `relative`. Stomping an app's `position: absolute` with inline\n // `relative` collapses the common `#app { position: absolute; inset: 0 }`\n // sizing pattern to zero height (inset offsets a relative box, it does not\n // size it), and the host's own overflow:hidden then clips everything.\n // Inline style first (authoritative and environment-independent), computed\n // second (catches stylesheet rules like `#app { position: absolute }`).\n const view = container.ownerDocument.defaultView;\n const position =\n container.style.position !== \"\"\n ? container.style.position\n : (view?.getComputedStyle(container).position ?? \"\");\n const promoteToRelative = position === \"\" || position === \"static\";\n if (promoteToRelative) container.style.position = \"relative\";\n // Record each host-owned property's prior inline value BEFORE overwriting it,\n // so dispose restores what the caller set (e.g. an inline overflow:auto) rather\n // than blanket-clearing it. Empty string = the caller had no inline value here.\n const priorContainerStyle = Object.keys(CONTAINER_STYLE).map((prop) => {\n // camelCase → kebab-case for the CSSOM property API (touchAction → touch-action).\n const cssName = prop.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);\n return [cssName, container.style.getPropertyValue(cssName)] as const;\n });\n Object.assign(container.style, CONTAINER_STYLE);\n\n return {\n container,\n dispose() {\n if (promoteToRelative) container.style.removeProperty(\"position\");\n for (const [cssName, prior] of priorContainerStyle) {\n // Restore the caller's prior inline value, or clear the host's if the\n // caller had none.\n if (prior !== \"\") container.style.setProperty(cssName, prior);\n else container.style.removeProperty(cssName);\n }\n },\n };\n}\n","/**\n * The rAF frame loop (design-002 §1: core is scheduler-free — the DOM package\n * owns the loop; core never touches the platform).\n *\n * Drives `engine.step(now)` once per animation frame, threading the rAF\n * timestamp straight into the frame clock (so FrameInfo.dt is real wall time,\n * clamped in core). The returned stop fn cancels the pending frame AND latches\n * so a frame already in flight cannot re-schedule — calling it is idempotent.\n *\n * THE FREEZE GATE (2026-08-04). Before each frame the loop claims a step from\n * `engine.frame`. While a freeze is held the gate walks its settle and then\n * refuses, at which point the loop PARKS: it stops scheduling rAF at all\n * rather than spinning a callback that does nothing — \"the engine is stopped\"\n * should mean the browser has no work queued for it either. Thaw arrives\n * through the gate's change subscription, which schedules the next frame.\n *\n * The dt clamp is what makes resume safe: a freeze that lasted an hour hands\n * `step` an enormous `now`, and core clamps the delta (frame-info.ts), so\n * tweens advance by one ordinary frame instead of teleporting to their end.\n *\n * THE LOOP OUTLIVES A THROWING STEP (2026-08-15, petition I14). Until this fix\n * the reschedule sat on the line AFTER `engine.step(now)`, so ONE throw from a\n * publish hook (which propagate loudly by design — engine.ts) escaped the rAF\n * callback, left `handle` un-renewed, and killed the canvas PERMANENTLY: with\n * `parked` still false, `wake` early-returns forever, so even freeze/thaw could\n * not restart it. Recovery needed a fresh `startRafLoop`. The next frame is now\n * scheduled in a `finally`, so a faulting frame is loud but survivable — the\n * throw still reaches the browser's error handler; only the death is gone.\n *\n * BEFORE EACH STEP (2026-09-27, ICE M21 K1): an optional `beforeStep` — where a host reads what the\n * platform changes without an event it can hear (the device's ratio: `createDeskHost`). It runs\n * only for a step that runs (never while parked), and inside the same `try`, so a throwing hook is\n * as survivable as a throwing step.\n *\n * THE SLEEP (2026-09-27, ICE M21 K7a — `@ice/core`'s frame-control.ts): with `sleep`, the loop asks\n * the frame gate after every step when the next one is due, and SLEEPS when it is not the next\n * frame — no rAF queued, and a timer only when a registered wake names a time (a flight, a caret's\n * blink, a layer let go). A wake (an input, a write from outside a step, a document's or a room's\n * arrival, a desk's flux) restarts it in the next frame; so does a freeze, whose settle walk needs\n * steps. A desk at rest then costs the main thread nothing at all (design-015 §11.4). Without\n * `sleep` the loop steps every frame, as it always did.\n */\nimport type { Engine } from \"@ice/core\";\n\nexport interface RafLoopOptions {\n /** Sleep when the engine is quiet (K7a): no frame scheduled until a wake or a registered time. Default false. */\n readonly sleep?: boolean;\n}\n\nexport function startRafLoop(engine: Engine, beforeStep?: (now: number) => void, opts: RafLoopOptions = {}): () => void {\n const sleepy = opts.sleep === true;\n const frame = engine.frame;\n let handle = 0;\n let stopped = false;\n let parked = false;\n let asleep = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const tick = (now: number): void => {\n if (stopped) return;\n if (!frame.claimStep()) {\n // Park: no step, and deliberately no reschedule. `wake` owns the restart.\n parked = true;\n handle = 0;\n return;\n }\n try {\n beforeStep?.(now);\n engine.step(now);\n } finally {\n // Re-checked AFTER the body: a `stop()` called from inside the step\n // (a publish hook, a reflector) must still win, or the loop would\n // resurrect itself past its own stopper.\n if (!stopped) schedule();\n }\n };\n\n /** After a step: the next frame, or sleep — until a wake, or the soonest registered time. */\n const schedule = (): void => {\n if (!sleepy) {\n handle = requestAnimationFrame(tick);\n return;\n }\n const t = performance.now();\n const due = frame.nextStep(t);\n if (due <= t) {\n handle = requestAnimationFrame(tick);\n return;\n }\n handle = 0;\n asleep = true;\n frame.sleeping(true);\n if (due !== Number.POSITIVE_INFINITY) timer = setTimeout(() => rise(true), Math.max(0, due - t));\n };\n\n /** Out of the sleep: the next frame steps (`timed`: for a registered time alone). */\n const rise = (timed: boolean): void => {\n if (stopped || !asleep) return;\n asleep = false;\n if (timer !== undefined) clearTimeout(timer);\n timer = undefined;\n frame.sleeping(false, timed);\n handle = requestAnimationFrame(tick);\n };\n\n // (a freeze taken while the loop sleeps rises it through the world's doors — the freeze writes its `FrameMode` mirror — so its\n // settle walk has the steps it needs; the sleep test pins it)\n const wake = (): void => {\n if (stopped || !parked || frame.isFrozen()) return;\n parked = false;\n handle = requestAnimationFrame(tick);\n };\n const unsubscribe = frame.onChange(wake);\n const unsubscribeWake = sleepy ? frame.onWake(() => rise(false)) : undefined;\n\n handle = requestAnimationFrame(tick);\n\n return () => {\n stopped = true;\n unsubscribe();\n unsubscribeWake?.();\n if (timer !== undefined) clearTimeout(timer);\n if (asleep) frame.sleeping(false);\n cancelAnimationFrame(handle);\n };\n}\n","/**\n * Input-ownership predicates (design-007 §4 — one predicate FAMILY, not one\n * predicate; petitions I1/I4). \"Does the widget own input here\" is answered\n * per-surface at the right scope, sharing the DOM-walk mechanics:\n *\n * - `isEditableTarget` — the 4-class editable gate (keymap default + the\n * adapter's Space-types fix). Kept narrow so UNDECLARED widgets behave\n * exactly as before (design-007 F5).\n * - `keyboardClaimOf` — the `data-canvas-keyboard` claim marker written by the\n * dom-widgets reflector for `interaction.keyboard: \"exclusive\"` widgets.\n * Read UNBOUNDED (page-global) on purpose: `document.activeElement` is one\n * per page, so engine B's keymap must stand down while you type into engine\n * A's claiming widget (design-007 F2 — only the DOM is page-global).\n * - `wheelCede` — the NARROW, DYNAMIC scroll opt-out (design-007 §3.4/§4):\n * plain wheel cedes to native scroll only when a scroller WITH ROOM in the\n * wheel's dominant axis sits inside a claim-marked / `data-canvas-\n * interactive` / editable subtree. At the scroll bounds the predicate turns\n * false and the wheel falls through to canvas pan/zoom — the familiar\n * nested-scroll feel — which also keeps scroll-chaining from ever reaching\n * the page. Deliberately NOT the full `crossesInteractive` set: hovering a\n * bare `<button>` must not kill canvas zoom (design-007 F4).\n *\n * The keymap (`@ice/react`) imports these — the two historically disagreeing\n * guards now share one implementation (the I1 unification).\n */\n\n/** The claim marker the dom-widgets reflector writes on an exclusive widget's host. */\nexport const KEYBOARD_CLAIM_ATTR = \"data-canvas-keyboard\";\n\n/** Marker value for `keyboardEscape: \"widget\"` (the widget receives even Escape). */\nexport const CLAIM_OWNS_ESCAPE = \"escape\";\n\nexport interface KeyboardClaim {\n /** The event-target chain crosses a claim marker (⇒ engine keyboard stands down). */\n readonly claimed: boolean;\n /** The claiming widget owns Escape too (`keyboardEscape: \"widget\"`). */\n readonly ownsEscape: boolean;\n}\n\nconst NO_CLAIM: KeyboardClaim = { claimed: false, ownsEscape: false };\n\n/** The keymap's editable gate — exactly INPUT/TEXTAREA/SELECT/contentEditable. */\nexport function isEditableTarget(target: EventTarget | null): boolean {\n if (target === null || !(target instanceof HTMLElement)) return false;\n const tag = target.tagName;\n return tag === \"INPUT\" || tag === \"TEXTAREA\" || tag === \"SELECT\" || target.isContentEditable;\n}\n\n/**\n * Resolve the keyboard claim for an event target (keydown targets ARE\n * `document.activeElement`, so this reads browser focus — the single source of\n * truth, design-007 §2.1). Unbounded `closest` by design (see header).\n */\nexport function keyboardClaimOf(target: EventTarget | null): KeyboardClaim {\n if (!(target instanceof Element)) return NO_CLAIM;\n const marked = target.closest(`[${KEYBOARD_CLAIM_ATTR}]`);\n if (marked === null) return NO_CLAIM;\n return { claimed: true, ownsEscape: marked.getAttribute(KEYBOARD_CLAIM_ATTR) === CLAIM_OWNS_ESCAPE };\n}\n\n/**\n * The scroll-opt-out subtree roots (design-007 §4 `crossesScrollable` — the\n * narrow set): the claim marker, the explicit interactive opt-in, and real\n * editable scrollers. NOT bare button/a/media.\n */\nconst SCROLL_OPT_OUT_SELECTOR =\n `[${KEYBOARD_CLAIM_ATTR}], [data-canvas-interactive], input, textarea, select, [contenteditable=\"\"], [contenteditable=\"true\"]`;\n\n/** Sub-pixel tolerance for fractional scroll positions. */\nconst SCROLL_EPS = 1;\n\n/** The element can consume this wheel's dominant-axis delta RIGHT NOW (overflow + room). */\nfunction hasScrollRoom(el: HTMLElement, dx: number, dy: number): boolean {\n const style = el.ownerDocument.defaultView?.getComputedStyle(el);\n if (style === undefined || style === null) return false;\n if (Math.abs(dy) >= Math.abs(dx)) {\n if (dy === 0) return false;\n const oy = style.overflowY;\n if (oy !== \"auto\" && oy !== \"scroll\") return false;\n if (el.scrollHeight <= el.clientHeight + SCROLL_EPS) return false;\n return dy > 0 ? el.scrollTop + el.clientHeight < el.scrollHeight - SCROLL_EPS : el.scrollTop > SCROLL_EPS;\n }\n if (dx === 0) return false;\n const ox = style.overflowX;\n if (ox !== \"auto\" && ox !== \"scroll\") return false;\n if (el.scrollWidth <= el.clientWidth + SCROLL_EPS) return false;\n return dx > 0 ? el.scrollLeft + el.clientWidth < el.scrollWidth - SCROLL_EPS : el.scrollLeft > SCROLL_EPS;\n}\n\n/**\n * Should this plain (non-pinch) wheel be ceded to native widget scroll?\n * Walks target → container: remembers any scroller-with-room, cedes on the\n * first opt-out root at or above it. Walking up meets scrollers BEFORE their\n * widget's marker (the marker sits on the host), so \"a scroller inside an\n * opt-out subtree\" is exactly \"saw a scroller by the time the marker matches\".\n * ctrl/pinch wheels never reach this — they are ALWAYS the canvas's zoom.\n */\nexport function wheelCede(\n target: EventTarget | null,\n container: HTMLElement,\n dx: number,\n dy: number,\n): boolean {\n if (!(target instanceof Element)) return false;\n if (dx === 0 && dy === 0) return false;\n let sawScrollableRoom = false;\n for (let el: Element | null = target; el !== null && el !== container; el = el.parentElement) {\n if (el instanceof HTMLElement && hasScrollRoom(el, dx, dy)) sawScrollableRoom = true;\n if (sawScrollableRoom && el.matches(SCROLL_OPT_OUT_SELECTOR)) return true;\n }\n return false;\n}\n","/**\n * The real L0 pointer adapter (design-003 §2–§3; replaces the M3 demo-input scaffold).\n *\n * Translates DOM pointer / wheel / keyboard events into normalized `InputEvent`s\n * and ONLY enqueues them (Law 2: adapters enqueue, they never touch the world —\n * this file imports no ECS state and writes nothing). `pointerIngest` drains the\n * queue once per tick and turns the facts into world state.\n *\n * Details that matter:\n * - stable pointer ids: `\"mouse\"` for the one mouse, `\"touch:<pointerId>\"` per\n * contact, `\"pen\"` — ingest keys pointer entities off these (design-003 §2);\n * - container-relative CSS px (the kernel screen space): the client point mapped\n * into the container's LAYOUT space by kernel `clientToScreen` (petition I39) —\n * its offset from the bounding rect's top-left divided by the scale a CSS\n * transform draws the container at (`rect.width / clientWidth`), so a press\n * lands on what the desk draws under it at any transform, in the space the\n * viewport is measured in (the layout size, desk-host.ts);\n * - the button: every pointer fact carries PointerEvent `buttons`, and a down's\n * mask IS the button that pressed (a second button joins a held press as a\n * pointermove, never a down). Which press is a gesture is the stack's call,\n * not this file's — only the primary's (petition I28, core press-button.ts):\n * a secondary press is enqueued like any other, a point. `contextmenu` is\n * never listened to here — it is the host's, untouched;\n * - `setPointerCapture` on down so a drag keeps delivering outside the element;\n * - Space is the pan modifier (design-003 §4.4): tracked here and threaded onto\n * every event's `mods.space`; keydown/keyup emit a `key` fact only when the\n * modifier tuple actually changes. Space's preventDefault/latch is OWNERSHIP-\n * AWARE (design-007 §3.4, petition I1): into an editable it neither latches\n * nor preventDefaults (Space must TYPE — the §1.6 live-bug fix); into a\n * `data-canvas-keyboard` claim it cedes the pan SEMANTICS but keeps killing\n * the page-scroll default (the key still reaches the widget's own listener —\n * preventDefault is not stopPropagation); elsewhere it pans, as ever;\n * - wheel: ctrl-wheel / trackpad pinch is ALWAYS `preventDefault` + the canvas\n * zoom signal (`wheel.pinch`) — a browser page-zoom over a widget is\n * catastrophic, claimed or not. A PLAIN wheel over a scroller-with-room\n * inside a claim/`data-canvas-interactive`/editable subtree (`wheelCede`,\n * design-007 §3.4/§3.5) is ceded: NO preventDefault (native scroll\n * proceeds), the fact still lands flagged `wheelHandled` so ingest stamps\n * the one-tick `WheelHandled` and the wheel recognizers skip the tick. At\n * the scroll bounds the predicate flips and the wheel falls through to\n * canvas pan/zoom. Everything else: `preventDefault`, dx/dy (deltaMode\n * lines → px ×16) — unchanged;\n * - window blur enqueues a synthetic `cancel` for every pointer still down\n * (design-003 §8) — the browser will not send the pointerups;\n * - a NO-OP move is dropped (petition I33): a `pointermove` with no button down\n * and no press of ours live, whose position, target, hover verdict and\n * modifiers are those of the last move this adapter enqueued for that pointer,\n * says nothing the world does not hold — so it enqueues nothing: no wake, no\n * step, no `PointerVersion` bump, no touch of the desk's wind. Chromium\n * re-sends a resting pointer's move on a focus or visibility change, and other\n * windows' input raises trusted moves with no motion (VibeField DK-10b); each\n * held a still desk out of rest. Every press is untouched — a down, an up, a\n * cancel, and every move while a button is down or a press of ours is live\n * (a held drag's same-position moves included) are enqueued as before — and a\n * transition or a wheel starts the comparison afresh, so the first move after\n * one always lands. A `pointerleave`/`pointerenter` pair at one position is a\n * no-op (the leave below is answered by its enter and says nothing), and the\n * move that follows it is the same move;\n * - the pointer OUT OF THE HOST (petitions I42, I44): the mouse leaving the\n * WINDOW, no press of ours live, wherever it is in the host's document — off\n * the container itself (its `pointerleave`, no related target), or off a\n * host's chrome BESIDE it (a `pointerout` with no related target, heard on the\n * document in the capture phase: a page's handler cannot stop it) — is said\n * once, as a `leave` fact at the pointer's LAST POINT over the host: off the\n * container, the point it left; off the chrome, the point of the last fact\n * this adapter enqueued for the mouse — the desk never saw it over the chrome\n * (no such fact: nothing is said). Ingest tags the pointer `PointerOutside`;\n * presence publishes the peer `away`. An exit off the container is heard\n * twice (its pointerout reaches the document, then its pointerleave): one\n * leave, never two. It is deferred one task, so a leave answered at once —\n * by the container's enter, or the document's `pointerover` with no related\n * target (Chromium's pair at a resting point, above, at either) — enqueues\n * nothing. Once said, only the pointer back over the HOST clears it: an\n * enter after a said leave is a move at its point, and any fact of its own\n * clears it too, so a pointer back at rest is never left away; back in the\n * window over the chrome the adapter hears no fact, and it stays away.\n *\n * Widget opt-out (the pinned widget-event contract, design-002 §8 / design-004 §4):\n * on `pointerdown` ONLY, if the event's target chain (up to the container) crosses\n * a native interactive (input/textarea/select/button/a[href]/contenteditable/\n * media[controls]) or an explicit `[data-canvas-interactive]`, the down fact is\n * flagged `surfaceHandled: true` — the fact still lands (the one input path is\n * absolute), but recognizer-spawn/arbitration skip it (`HandledByWidget` flows\n * through L0). `stopPropagation()` inside widget content is the OTHER boundary:\n * a stopped event never reaches the container listener, so it never becomes a\n * canvas fact at all — that is the design, not a gap.\n *\n * Hover-time amendment (design-002 §8, 2026-07-18): down/move facts ALSO carry\n * `overInteractive` — the same `crossesInteractive` check, run at hover time —\n * so the world can telegraph the opt-out BEFORE a down (cursor affordances).\n * (Until design-015 D5b the GL router's hover verdict was OR'd in; the GL\n * route left with the islands it picked.) It never gates gestures:\n * `surfaceHandled` stays down-only and keeps its exact meaning. During a\n * container-captured drag every event retargets to the container, so\n * `overInteractive` reads false mid-gesture — correct: the gesture owns the\n * pointer, not the widget under it.\n */\nimport { NO_MODS, type InputEvent, type InputMods, type InputQueue } from \"@ice/core\";\nimport { clientToScreen } from \"@ice/kernel\";\nimport type { CanvasHost } from \"./host\";\nimport { isEditableTarget, keyboardClaimOf, wheelCede } from \"./input-ownership\";\n\n/** DOM_DELTA_LINE → px (one wheel \"line\" ≈ 16px; matches typical browser mapping). */\nconst WHEEL_LINE_PX = 16;\n\n/**\n * Elements whose `pointerdown` is the widget's, not the canvas's (design-002 §8).\n * `contenteditable=\"\"` (bare attribute) and `=\"true\"` are editable; `=\"false\"` is\n * not. Media only opts out WITH visible controls.\n */\nconst INTERACTIVE_SELECTOR =\n 'input, textarea, select, button, a[href], [contenteditable=\"\"], [contenteditable=\"true\"], audio[controls], video[controls], [data-canvas-interactive]';\n\n/** True if `target`'s ancestor chain, up to but excluding `container`, holds an interactive surface. */\nfunction crossesInteractive(target: EventTarget | null, container: HTMLElement): boolean {\n if (!(target instanceof Element)) return false;\n const match = target.closest(INTERACTIVE_SELECTOR);\n // `closest` walks to the document root; bound it to inside the canvas so an\n // interactive ANCESTOR of the container (unusual, but legal) does not count.\n return match !== null && container.contains(match);\n}\n\nfunction pointerIdOf(e: PointerEvent): string {\n if (e.pointerType === \"touch\") return `touch:${e.pointerId}`;\n if (e.pointerType === \"pen\") return \"pen\";\n return \"mouse\";\n}\n\nfunction deviceOf(e: PointerEvent): InputEvent[\"device\"] {\n if (e.pointerType === \"touch\") return \"touch\";\n if (e.pointerType === \"pen\") return \"pen\";\n return \"mouse\";\n}\n\nexport function attachPointerAdapter(host: CanvasHost, queue: InputQueue): () => void {\n const { container } = host;\n const doc = container.ownerDocument;\n const view = doc.defaultView;\n\n let spaceHeld = false;\n // Pointers currently down — blur-cancel sweep + DEFERRED-CAPTURE state.\n // Capture timing (field report 2026-07-12, v1's click-vs-drag discrimination):\n // capturing on DOWN retargets the derived `click`/`dblclick` to the container,\n // killing every widget-body handler (todo rows, folder double-click). So:\n // - GL-claimed downs capture IMMEDIATELY (the router synthesizes its own\n // clicks; island drags must keep delivering outside the element);\n // - native-interactive downs NEVER capture (the engine ignores them);\n // - everything else captures on the FIRST held-button move past a 4px slop\n // — below every recognizer dead zone, so a drag is always captured\n // before it can activate, while a clean tap never captures at all.\n const live = new Map<\n string,\n {\n device: InputEvent[\"device\"];\n x: number;\n y: number;\n downX: number;\n downY: number;\n native: boolean;\n captured: boolean;\n }\n >();\n const CAPTURE_SLOP_PX = 4;\n\n const capture = (pointerId: number): void => {\n if (typeof container.setPointerCapture !== \"function\") return;\n try {\n container.setPointerCapture(pointerId);\n } catch {\n // capture is best-effort — a detached or unsupported target must not throw here.\n }\n };\n // Last emitted keyboard tuple — emit a `key` fact only when it actually changes.\n let lastMods = { shift: false, ctrl: false, alt: false, meta: false, space: false };\n // THE LAST MOVE AT REST (petition I33): per pointer, the last move enqueued with no button down and no press of ours live —\n // what a no-op move is measured against. A press or a wheel deletes it (the first move after one always lands); the window's\n // blur keeps it (a resting pointer's re-dispatch after the focus returns is exactly the no-op to drop); detach clears it.\n const atRest = new Map<string, { x: number; y: number; target: EventTarget | null; over: boolean; mods: InputMods }>();\n const sameMods = (a: InputMods, b: InputMods): boolean =>\n a.shift === b.shift && a.ctrl === b.ctrl && a.alt === b.alt && a.meta === b.meta && a.space === b.space;\n\n // THE POINTER OUT OF THE HOST (petitions I42, I44): a leave waiting out its task (an answer cancels it), whether one was said, and\n // the mouse's point in the last fact enqueued for it — where a leave off the host's chrome is said (the desk never saw it there)\n let leaving: ReturnType<typeof setTimeout> | undefined;\n let left = false;\n let lastMouse: { x: number; y: number } | undefined;\n /** The mouse is over the host (any fact of its own): a leave waiting is no leave, and one said is answered by this fact. */\n const mouseBack = (): void => {\n if (leaving !== undefined) clearTimeout(leaving);\n leaving = undefined;\n left = false;\n };\n /** Every fact this adapter says is enqueued here; the mouse's keeps its point (petition I44 — a leave off the chrome is said at it). */\n const enqueue = (ev: InputEvent): void => {\n if (ev.pointerId === \"mouse\") lastMouse = { x: ev.screenX, y: ev.screenY };\n queue.enqueue(ev);\n };\n\n // the client point in the container's LAYOUT space (petition I39): a transform scales its bounding rect, never the space the desk draws in\n const relative = (clientX: number, clientY: number): { x: number; y: number } => clientToScreen(clientX, clientY, container.getBoundingClientRect(), container);\n\n const pointerMods = (e: PointerEvent | WheelEvent): InputMods => ({\n shift: e.shiftKey,\n ctrl: e.ctrlKey,\n alt: e.altKey,\n meta: e.metaKey,\n space: spaceHeld,\n });\n\n const onPointerDown = (e: PointerEvent): void => {\n const id = pointerIdOf(e);\n const device = deviceOf(e);\n const { x, y } = relative(e.clientX, e.clientY);\n // Widget opt-out: down on a native interactive / [data-canvas-interactive]\n // flags the fact so recognizers skip it (design-002 §8).\n const native = crossesInteractive(e.target, container);\n live.set(id, { device, x, y, downX: x, downY: y, native, captured: false });\n atRest.delete(id);\n if (id === \"mouse\") mouseBack();\n const surfaceHandled = native;\n enqueue({\n kind: \"down\",\n pointerId: id,\n device,\n screenX: x,\n screenY: y,\n buttons: e.buttons,\n mods: pointerMods(e),\n tMs: e.timeStamp,\n ...(surfaceHandled ? { surfaceHandled: true } : {}),\n // The down IS a hover-bearing fact: a touch's first event must seed the\n // pointer's OverInteractive truth (no prior moves exist to).\n overInteractive: surfaceHandled,\n });\n };\n\n const onPointerMove = (e: PointerEvent): void => {\n const id = pointerIdOf(e);\n if (id === \"mouse\") mouseBack();\n const device = deviceOf(e);\n const { x, y } = relative(e.clientX, e.clientY);\n const over = crossesInteractive(e.target, container);\n const mods = pointerMods(e);\n let seen = live.get(id);\n // ADOPTED-gesture hardening (ops.insertByDrag, 2026-07-19): a held-button\n // move for a pointer whose down we never saw means widget content started\n // the gesture and handed it to the engine (the tray tile stopPropagation'd\n // its down — the sanctioned boundary). Track it from HERE so the deferred\n // capture below still takes the pointer before it can leave the window\n // mid-drag, and the blur sweep knows to cancel it. Gated off interactive\n // targets: a move over opted-out content (text selection in a widget's\n // input, the tray panel itself) must never trigger a container capture.\n if (seen === undefined && e.buttons !== 0 && !over) {\n seen = { device, x, y, downX: x, downY: y, native: false, captured: false };\n live.set(id, seen);\n }\n if (seen !== undefined) {\n seen.x = x;\n seen.y = y;\n // Deferred capture: a real drag is forming — take it before any\n // recognizer dead zone (>=8px) can activate.\n if (\n !seen.captured &&\n !seen.native &&\n e.buttons !== 0 &&\n Math.hypot(x - seen.downX, y - seen.downY) > CAPTURE_SLOP_PX\n ) {\n capture(e.pointerId);\n seen.captured = true;\n }\n }\n // A NO-OP move (petition I33): at rest — no button down, no press of ours live — and the last move enqueued for this pointer\n // in every fact it carries but the time. Dropped: the world already holds all of it, and an enqueue would wake the loop.\n if (seen === undefined && e.buttons === 0) {\n const last = atRest.get(id);\n if (last !== undefined && last.x === x && last.y === y && last.target === e.target && last.over === over && sameMods(last.mods, mods)) return;\n atRest.set(id, { x, y, target: e.target, over, mods });\n } else atRest.delete(id);\n enqueue({\n kind: \"move\",\n pointerId: id,\n device,\n screenX: x,\n screenY: y,\n buttons: e.buttons,\n mods,\n tMs: e.timeStamp,\n // Hover fact, every move: the DOM chain check.\n overInteractive: over,\n });\n };\n\n const endPointer =\n (kind: \"up\" | \"cancel\") =>\n (e: PointerEvent): void => {\n const id = pointerIdOf(e);\n const device = deviceOf(e);\n const { x, y } = relative(e.clientX, e.clientY);\n live.delete(id);\n atRest.delete(id);\n if (typeof container.releasePointerCapture === \"function\") {\n try {\n container.releasePointerCapture(e.pointerId);\n } catch {\n // release is best-effort (capture may already be gone).\n }\n }\n enqueue({\n kind,\n pointerId: id,\n device,\n screenX: x,\n screenY: y,\n buttons: e.buttons,\n mods: pointerMods(e),\n // No overInteractive stamp: a capture-retargeted up reads the container,\n // not the content under the pointer — the next real move re-truths it.\n });\n };\n const onPointerUp = endPointer(\"up\");\n const onPointerCancel = endPointer(\"cancel\");\n\n const onWheel = (e: WheelEvent): void => {\n // a wheel moves the camera under the pointer: the next move is measured afresh (petition I33), never dropped as a repeat\n atRest.delete(\"mouse\");\n mouseBack();\n const { x, y } = relative(e.clientX, e.clientY);\n const scale = e.deltaMode === 1 ? WHEEL_LINE_PX : 1; // DOM_DELTA_LINE → px\n if (e.ctrlKey) {\n // Pinch/ctrl-wheel: ALWAYS the canvas's zoom (design-007 F4) — the\n // browser's page pinch-zoom must never fire, claimed content or not.\n e.preventDefault();\n enqueue({ kind: \"wheel\", pointerId: \"mouse\", device: \"mouse\", screenX: x, screenY: y, buttons: e.buttons, mods: pointerMods(e), wheel: { dx: 0, dy: 0, pinch: e.deltaY } });\n return;\n }\n const wheel = { dx: e.deltaX * scale, dy: e.deltaY * scale, pinch: 0 };\n if (wheelCede(e.target, container, e.deltaX, e.deltaY)) {\n // Ceded to widget scroll (I4): no preventDefault — native scroll consumes\n // the deltas. The fact still lands (the one input path is absolute),\n // flagged so recognizers treat the tick as wheel silence. The room-check\n // in wheelCede means an at-bounds scroller falls through to the branch\n // below — and scroll-chaining can never escape to the page.\n enqueue({ kind: \"wheel\", pointerId: \"mouse\", device: \"mouse\", screenX: x, screenY: y, buttons: e.buttons, mods: pointerMods(e), wheel, wheelHandled: true });\n return;\n }\n e.preventDefault(); // own zoom/pan — never let the page scroll\n enqueue({ kind: \"wheel\", pointerId: \"mouse\", device: \"mouse\", screenX: x, screenY: y, buttons: e.buttons, mods: pointerMods(e), wheel });\n };\n\n /** Emit a `key` fact iff the modifier tuple changed (design-003 §2 minimal keyboard). */\n const emitKeyIfChanged = (e: KeyboardEvent): void => {\n const mods: InputMods = { shift: e.shiftKey, ctrl: e.ctrlKey, alt: e.altKey, meta: e.metaKey, space: spaceHeld };\n if (\n mods.shift === lastMods.shift &&\n mods.ctrl === lastMods.ctrl &&\n mods.alt === lastMods.alt &&\n mods.meta === lastMods.meta &&\n mods.space === lastMods.space\n ) {\n return;\n }\n lastMods = { ...mods };\n enqueue({ kind: \"key\", pointerId: \"\", device: \"mouse\", screenX: 0, screenY: 0, buttons: 0, mods });\n };\n\n const isSpace = (e: KeyboardEvent): boolean => e.key === \" \" || e.code === \"Space\";\n\n const onKeyDown = (e: KeyboardEvent): void => {\n if (isSpace(e) && !e.defaultPrevented) {\n // Ownership-aware Space (design-007 §3.4; the keydown target IS the\n // focused node, so this reads browser focus — the one truth).\n if (isEditableTarget(e.target)) {\n // Space must TYPE into widget fields (§1.6 live-bug fix): no\n // preventDefault, no pan latch.\n } else if (keyboardClaimOf(e.target).claimed) {\n // Cede the pan SEMANTICS to the claiming widget; keep suppressing the\n // page-scroll default (§3.3 residual — preventDefault still lets the\n // key reach the widget's own keydown listener).\n e.preventDefault();\n } else {\n e.preventDefault(); // Space would otherwise page-scroll\n spaceHeld = true;\n }\n }\n emitKeyIfChanged(e);\n };\n const onKeyUp = (e: KeyboardEvent): void => {\n if (isSpace(e)) {\n if (!e.defaultPrevented && !isEditableTarget(e.target) && !keyboardClaimOf(e.target).claimed) {\n e.preventDefault();\n }\n // Clear UNCONDITIONALLY: the down may have latched over canvas and the\n // up can land after focus moved into a widget/editable — a skipped clear\n // here would strand the pan modifier held.\n spaceHeld = false;\n }\n emitKeyIfChanged(e);\n };\n\n /**\n * Drain every latched input to a clean slate (Law 2: enqueue only). Called on\n * window blur AND on detach: the browser delivers no more pointerups/keyups\n * once we blur, and detaching mid-gesture (unmounting the canvas while the\n * engine outlives it) must not strand an Active drag / held buttons / latched\n * modifiers in the world for a remount of the same engine to resurrect. Also\n * clears the deferred-capture `live` map so no per-pointer tracking survives.\n */\n const cancelAllInput = (): void => {\n // The browser will not deliver pointerups once we lose focus — cancel every\n // still-down pointer so no gesture is stranded Active (design-003 §8).\n for (const [id, s] of live) {\n enqueue({ kind: \"cancel\", pointerId: id, device: s.device, screenX: s.x, screenY: s.y, buttons: 0, mods: { ...NO_MODS, space: spaceHeld } });\n }\n live.clear();\n // Modifier keyups are also lost on blur — ENQUEUE the clearing fact, or\n // the world's Keyboard resource stays latched (space-stuck: every canvas\n // drag pans forever). Clearing only local state was the bug: lastMods\n // already saying \"space up\" suppressed the next emitKeyIfChanged too.\n spaceHeld = false;\n if (lastMods.space || lastMods.shift || lastMods.ctrl || lastMods.alt || lastMods.meta) {\n lastMods = { ...NO_MODS };\n enqueue({\n kind: \"key\",\n pointerId: \"\",\n device: \"mouse\",\n screenX: 0,\n screenY: 0,\n buttons: 0,\n mods: { ...NO_MODS },\n });\n }\n };\n\n const onBlur = (): void => {\n cancelAllInput();\n };\n\n // THE POINTER OUT OF THE HOST (petitions I42, I44; module doc): said a task later at (x, y), unless an answer comes first — and\n // once: a leave waiting or said is not said again (an exit off the container is heard twice, its pointerout and its pointerleave)\n const leaveAt = (x: number, y: number, mods: InputMods): void => {\n if (leaving !== undefined || left) return;\n leaving = setTimeout(() => {\n leaving = undefined;\n left = true;\n atRest.delete(\"mouse\"); // the move that brings it back always lands (petition I33's comparison starts afresh)\n enqueue({ kind: \"leave\", pointerId: \"mouse\", device: \"mouse\", screenX: x, screenY: y, buttons: 0, mods });\n }, 0);\n };\n const onPointerLeave = (e: PointerEvent): void => {\n if (e.target !== container || e.relatedTarget != null || pointerIdOf(e) !== \"mouse\" || live.has(\"mouse\")) return;\n const { x, y } = relative(e.clientX, e.clientY);\n leaveAt(x, y, pointerMods(e));\n };\n // …out of the WINDOW from anywhere in the document (I44): off the container at the point it left, off the chrome beside it at the\n // last point said for the mouse (none said: nothing to say)\n const onWindowOut = (e: PointerEvent): void => {\n if (e.relatedTarget != null || pointerIdOf(e) !== \"mouse\" || live.has(\"mouse\")) return;\n const at = e.target instanceof Node && container.contains(e.target) ? relative(e.clientX, e.clientY) : lastMouse;\n if (at !== undefined) leaveAt(at.x, at.y, pointerMods(e));\n };\n // …back in the window: an answer to a leave WAITING (the resting pair, at the document), never to one said — over the chrome the\n // desk hears nothing, so the pointer stays away until it is back over the host\n const onWindowOver = (e: PointerEvent): void => {\n if (e.relatedTarget != null || pointerIdOf(e) !== \"mouse\" || leaving === undefined) return;\n clearTimeout(leaving);\n leaving = undefined;\n };\n const onPointerEnter = (e: PointerEvent): void => {\n if (e.target !== container || pointerIdOf(e) !== \"mouse\") return;\n if (leaving !== undefined) {\n mouseBack(); // the leave answered at once: none was said\n return;\n }\n if (!left) return;\n mouseBack();\n const { x, y } = relative(e.clientX, e.clientY);\n // back over the host — at rest or not, the pointer is here (no hover verdict: the enter's target is the container; the next move's is the truth)\n enqueue({ kind: \"move\", pointerId: \"mouse\", device: \"mouse\", screenX: x, screenY: y, buttons: e.buttons, mods: pointerMods(e), tMs: e.timeStamp });\n };\n\n container.addEventListener(\"pointerdown\", onPointerDown);\n container.addEventListener(\"pointermove\", onPointerMove);\n container.addEventListener(\"pointerup\", onPointerUp);\n container.addEventListener(\"pointercancel\", onPointerCancel);\n container.addEventListener(\"wheel\", onWheel, { passive: false });\n container.addEventListener(\"pointerleave\", onPointerLeave);\n container.addEventListener(\"pointerenter\", onPointerEnter);\n // Capture phase (I44): the window's exit and return are the document's facts — no page handler's stopPropagation can hide them.\n doc.addEventListener(\"pointerout\", onWindowOut, true);\n doc.addEventListener(\"pointerover\", onWindowOver, true);\n view?.addEventListener(\"keydown\", onKeyDown);\n view?.addEventListener(\"keyup\", onKeyUp);\n view?.addEventListener(\"blur\", onBlur);\n\n return () => {\n // Sweep first (a detach can land mid-gesture) — same clean-slate cancel as\n // blur; facts drain on the engine's next step. Then unwire the listeners.\n cancelAllInput();\n atRest.clear();\n if (leaving !== undefined) clearTimeout(leaving);\n leaving = undefined;\n container.removeEventListener(\"pointerdown\", onPointerDown);\n container.removeEventListener(\"pointermove\", onPointerMove);\n container.removeEventListener(\"pointerup\", onPointerUp);\n container.removeEventListener(\"pointercancel\", onPointerCancel);\n container.removeEventListener(\"wheel\", onWheel);\n container.removeEventListener(\"pointerleave\", onPointerLeave);\n container.removeEventListener(\"pointerenter\", onPointerEnter);\n doc.removeEventListener(\"pointerout\", onWindowOut, true);\n doc.removeEventListener(\"pointerover\", onWindowOver, true);\n view?.removeEventListener(\"keydown\", onKeyDown);\n view?.removeEventListener(\"keyup\", onKeyUp);\n view?.removeEventListener(\"blur\", onBlur);\n };\n}\n","/**\n * The cursor reflector (design-003 §7 L4 cursor projection; v1 approach:\n * local cursor = OS cursor via one `style.cursor` write).\n *\n * `createCursorSync` resolves the cursor string into closure state every\n * `derive` tick with no observable ECS stamp (a plain string, not a\n * component/resource write), so there is nothing for `observe` to watch —\n * hence `always: true` with an internal last-value cache, mirroring the\n * \"cheap-overlay style\" carve-out `ReflectorDef.always` exists for. The cache\n * is what keeps the write ONE-per-change: `flush` runs every frame, but\n * `host.container.style.cursor` is only touched when `readCursor()` differs\n * from the last-applied value.\n *\n * Law 10: reflectors run post-notify, write output only, never read layout\n * or write ECS — this flush touches only `host.container.style`.\n */\nimport type { ReflectorDef, World } from \"@ice/core\";\nimport type { CanvasHost } from \"../host\";\n\nexport function createCursorReflector(\n host: CanvasHost,\n readCursor: () => string,\n): ReflectorDef & { writes(): number } {\n let writes = 0;\n let last: string | undefined;\n return {\n name: \"cursor\",\n always: true,\n flush(_world: World) {\n const next = readCursor();\n if (next === last) return;\n last = next;\n host.container.style.cursor = next;\n writes++;\n },\n writes: () => writes,\n };\n}\n","/**\n * P5 — remote cursors (design-004 §1: screen-space DOM, \"remote cursors are\n * always custom nodes\"; design-003 §7).\n *\n * Renders one pooled node per REMOTE cursor entity (`CursorVisual` kind\n * \"remote\" + world `Position`, derived by core's remote-cursors system from\n * presence peers; `Follows` names the peer whose `PresenceInfo` supplies\n * color + label). Screen-space by design: an acknowledged small-N\n * camera→position consumer (design-002 §5 rescope), recomputed on camera or\n * cursor dirt only. The plane mounts LAST in the container, above chrome;\n * `pointer-events: none` throughout — cursors are never hit-testable.\n */\nimport {\n Camera,\n CursorVisual,\n Follows,\n Position,\n PresenceInfo,\n defineQuery,\n type Entity,\n type ReflectorDef,\n type World,\n} from \"@ice/core\";\nimport { worldToScreen } from \"@ice/kernel\";\nimport type { CanvasHost } from \"../host\";\n\nconst remoteCursorQ = defineQuery([Position, CursorVisual]);\n\nexport interface RemoteCursorsReflector {\n readonly reflector: ReflectorDef;\n /** Remove the plane + pool (doc close / HMR). */\n destroy(): void;\n}\n\nexport function createRemoteCursorsReflector(host: CanvasHost, world: World): RemoteCursorsReflector {\n const doc = host.container.ownerDocument;\n const plane = doc.createElement(\"div\");\n Object.assign(plane.style, {\n position: \"absolute\",\n inset: \"0\",\n overflow: \"hidden\",\n pointerEvents: \"none\",\n } as Partial<CSSStyleDeclaration>);\n host.container.appendChild(plane); // last child ⇒ topmost (P5 over P4)\n\n interface Node {\n root: HTMLDivElement;\n label: HTMLDivElement;\n color: string;\n name: string;\n x: number;\n y: number;\n }\n const pool = new Map<Entity, Node>();\n\n const ensure = (e: Entity): Node => {\n let n = pool.get(e);\n if (n !== undefined) return n;\n const root = doc.createElement(\"div\");\n Object.assign(root.style, {\n position: \"absolute\",\n left: \"0\",\n top: \"0\",\n willChange: \"transform\",\n pointerEvents: \"none\",\n } as Partial<CSSStyleDeclaration>);\n // Arrow glyph + name chip (color applied on first style pass).\n const arrow = doc.createElement(\"div\");\n Object.assign(arrow.style, {\n width: \"0\",\n height: \"0\",\n borderLeft: \"6px solid transparent\",\n borderRight: \"6px solid transparent\",\n borderBottom: \"14px solid currentColor\",\n transform: \"rotate(-32deg)\",\n } as Partial<CSSStyleDeclaration>);\n const label = doc.createElement(\"div\");\n Object.assign(label.style, {\n marginTop: \"2px\",\n marginLeft: \"8px\",\n padding: \"1px 6px\",\n borderRadius: \"8px\",\n font: \"11px system-ui, sans-serif\",\n color: \"#fff\",\n whiteSpace: \"nowrap\",\n } as Partial<CSSStyleDeclaration>);\n root.appendChild(arrow);\n root.appendChild(label);\n plane.appendChild(root);\n n = { root, label, color: \"\", name: \"\", x: Number.NaN, y: Number.NaN };\n pool.set(e, n);\n return n;\n };\n\n const reflector: ReflectorDef = {\n name: \"remoteCursors\",\n observe: {\n resources: [Camera],\n queries: [{ q: remoteCursorQ, cols: [Position, CursorVisual] }],\n },\n flush: (w) => {\n const cam = w.getResource(Camera) ?? { x: 0, y: 0, zoom: 1 };\n const seen = new Set<Entity>();\n w.query(remoteCursorQ).each((b) => {\n for (const r of b) {\n const e = b.entity(r);\n if (w.get(e, CursorVisual)?.kind !== \"remote\") continue;\n seen.add(e);\n const p = w.read(e, Position);\n const s = worldToScreen(p.x, p.y, cam);\n const n = ensure(e);\n if (n.x !== s.x || n.y !== s.y) {\n n.root.style.transform = `translate(${s.x}px, ${s.y}px)`;\n n.x = s.x;\n n.y = s.y;\n }\n const peer = w.getRelation(e, Follows);\n const info = peer !== undefined ? w.get(peer, PresenceInfo) : undefined;\n const color = info?.color ?? \"#7a8aa0\";\n const name = info?.name ?? \"\";\n if (n.color !== color) {\n n.root.style.color = color;\n n.label.style.background = color;\n n.color = color;\n }\n if (n.name !== name) {\n n.label.textContent = name;\n n.label.style.display = name === \"\" ? \"none\" : \"block\";\n n.name = name;\n }\n }\n });\n for (const [e, n] of pool) {\n if (!seen.has(e)) {\n n.root.remove();\n pool.delete(e);\n }\n }\n },\n };\n\n return {\n reflector,\n destroy() {\n pool.clear();\n plane.remove();\n },\n };\n}\n","/**\n * The focus driver — acquisition + programmatic focus for keyboard-claiming\n * widgets (design-007 §2.3–§2.5, petition I1).\n *\n * The single source of truth is BROWSER focus: `document.activeElement` inside\n * a `data-canvas-keyboard` subtree is what makes every engine keyboard surface\n * stand down (input-ownership.ts). This driver's whole job is to make that\n * state reachable — it drives `.focus()`, it never mirrors focus into the\n * world (there is no Focus resource; design-007 §2.2 demoted it, and despawn\n * integrity is free: removing the node resets `activeElement`).\n *\n * Acquisition is DOM-at-event-time, not a world system: the browser's own event\n * targeting IS the pick (the down target sits inside the claiming host — since\n * design-015 D5b that host is screen-space chrome, never a widget under the\n * camera; the desk's one focused editor acquires focus on its own, the same\n * principle). A capture-phase listener keeps ordering deterministic across\n * browsers (Safari's click-focus quirks):\n *\n * - down on a natively-focusable node inside the claim (textarea, canvas\n * with tabindex, the declared proxy itself) → the browser's own mousedown\n * focus is correct; the driver does nothing.\n * - down anywhere else inside the claim → the driver focuses the claim's\n * declared `[data-canvas-focus]` proxy when one exists, else the host\n * (which carries `tabindex=\"-1\"`), with `preventScroll` — and\n * `preventDefault()`s the pointerdown so the native focus fixup cannot\n * fight the choice. Canceling a pointerdown suppresses only the compat\n * mouse events (focus fixup + text selection — the container is already\n * `user-select:none`); `click` still derives and the adapter's container\n * listener has already enqueued the down fact (one input path intact).\n * - down outside any claim → untouched; the browser blurs to body/other\n * content on its own (click-away release, design-007 §2.4).\n *\n * Release stays where the design put it: Escape in the keymap (blur), lifecycle\n * via node removal, window blur deliberately does NOT force-blur (the browser\n * restores focus on refocus — adapter's cancelAllInput keeps its hands off).\n */\nimport type { Entity } from \"@ice/core\";\nimport type { CanvasHost } from \"./host\";\nimport { KEYBOARD_CLAIM_ATTR } from \"./input-ownership\";\n\n/** The widget-declared focus target (a proxy descendant): `<div data-canvas-focus tabIndex={-1}>`. */\nexport const FOCUS_PROXY_ATTR = \"data-canvas-focus\";\n\n/**\n * Natively click-focusable content — where the browser's own mousedown focus\n * already lands inside the widget (the driver must not override it).\n */\nconst FOCUSABLE_SELECTOR =\n 'input, textarea, select, button, a[href], [contenteditable=\"\"], [contenteditable=\"true\"], [tabindex], audio[controls], video[controls], iframe';\n\n/**\n * Resolves an entity to a claiming host's content element — what `focusWidget` walks from. The\n * dom-widgets reflector was the one lookup until design-015 D5b; a host that keeps per-entity\n * DOM (none in the engine today) may still hand one in.\n */\nexport interface FocusHostLookup {\n hostFor(entity: Entity): HTMLElement | undefined;\n}\n\nexport interface WidgetFocusHandle {\n /**\n * Programmatically focus a claiming widget (its proxy, else its host).\n * Returns false when the entity has no host in the lookup (always, without\n * one) or no keyboard claim — only `keyboard: \"exclusive\"` widgets are\n * focusable (declaration drives focusability; design-007 §3.1).\n */\n focusWidget(entity: Entity): boolean;\n /** Blur whatever claim currently holds focus. False when none does. */\n blurFocus(): boolean;\n /** Remove the acquisition listener. */\n detach(): void;\n}\n\n/** The claiming host's focus node: the declared proxy, else the (tabindexed) host. */\nfunction focusNodeOf(claimHost: HTMLElement): HTMLElement {\n const proxy = claimHost.querySelector<HTMLElement>(`[${FOCUS_PROXY_ATTR}]`);\n return proxy ?? claimHost;\n}\n\nexport function attachWidgetFocus(host: CanvasHost, lookup?: FocusHostLookup): WidgetFocusHandle {\n const { container } = host;\n const doc = container.ownerDocument;\n\n const onPointerDown = (e: PointerEvent): void => {\n if (!(e.target instanceof Element)) return;\n const marked = e.target.closest(`[${KEYBOARD_CLAIM_ATTR}]`);\n if (!(marked instanceof HTMLElement) || !container.contains(marked)) return;\n // A natively-focusable node under the pointer (deeper than the host)\n // takes browser focus on its own — the common editable/terminal case.\n const focusable = e.target.closest(FOCUSABLE_SELECTOR);\n if (focusable !== null && focusable !== marked && marked.contains(focusable)) return;\n const node = focusNodeOf(marked);\n if (doc.activeElement === node) return; // already held — never disturb mid-gesture\n e.preventDefault(); // suppress the native focus fixup; the driver owns the choice\n node.focus({ preventScroll: true });\n };\n\n // Capture phase: runs before widget content's own handlers and before the\n // adapter's bubble listener — deterministic acquisition, no races.\n container.addEventListener(\"pointerdown\", onPointerDown, true);\n\n return {\n focusWidget(entity) {\n const content = lookup?.hostFor(entity);\n const hostEl = content?.parentElement;\n if (!(hostEl instanceof HTMLElement) || !hostEl.hasAttribute(KEYBOARD_CLAIM_ATTR)) return false;\n const node = focusNodeOf(hostEl);\n node.focus({ preventScroll: true });\n return doc.activeElement === node;\n },\n blurFocus() {\n const active = doc.activeElement;\n // SVG focus targets included — same rule as the keymap's Escape release.\n if (!(active instanceof HTMLElement || active instanceof SVGElement)) return false;\n if (active.closest(`[${KEYBOARD_CLAIM_ATTR}]`) === null) return false;\n active.blur();\n return true;\n },\n detach() {\n container.removeEventListener(\"pointerdown\", onPointerDown, true);\n },\n };\n}\n","/**\n * `createDeskHost` — the vanilla mount (design-015 §3, D-D15; D5b). What `<InfiniteCanvas>` did\n * for the hybrid, with the hybrid gone: ONE container, the desk's layer in it, and this package's\n * screen-space half around it — the pointer adapter (L0's one producer), the rAF loop, the OS\n * cursor, the room's other people (the remote cursors), the focus driver, the viewport sync.\n * `@ice/react`'s `<Desk>` wraps it; an app without React mounts it directly.\n *\n * THE DESK ARRIVES AS AN OPAQUE LAYER FACTORY. This package never imports `@ice/desk` (the wall\n * `nobody-imports-desk`): the factory's context and its handle are typed STRUCTURALLY here —\n * exactly as `<InfiniteCanvas ground={groundCompose(…)}>` received the ground — and\n * `@ice/desk`'s `deskLayer(opts)` (its src/host/) returns a function assignable to {@link LayerFactory}.\n * The context carries the interaction stack's seams the renderer fills (the frame pick, the nav\n * geometry, the held pose — design-014 B3b, design-015 §8–§9), the marquee buffer it draws, and the\n * stack's exact pick, which the layer's handle answers a host's screen-point pick with (petition I27).\n *\n * Boot order (registration order = reflector flush order): host → layer → reflectors [ layer ·\n * cursor · remoteCursors — unless the layer's handle says `cursors: false` (petition I26: its host\n * draws the room's people itself) ] → pointer adapter → focus → viewport (one layout read, then a\n * ResizeObserver, and the device's ratio read before every step) → rAF loop. `dispose` undoes it in reverse. The engine is NOT owned here — it\n * outlives the mount (the app disposes it). A layer factory that throws leaves nothing claimed on\n * the engine: it runs before any registration, and only the local host is undone.\n */\nimport {\n Viewport,\n writeRuntimeResource,\n type CanvasEngine,\n type EngineGpu,\n type InteractionStack,\n type ReflectorDef,\n type World,\n} from \"@ice/core\";\nimport { screenSizeOf } from \"@ice/kernel\";\nimport { createCanvasHost, type CanvasHost } from \"./host\";\nimport { startRafLoop } from \"./loop\";\nimport { attachPointerAdapter } from \"./pointer-adapter\";\nimport { createCursorReflector } from \"./reflectors/cursor\";\nimport { createRemoteCursorsReflector } from \"./reflectors/remote-cursors\";\nimport { attachWidgetFocus, type WidgetFocusHandle } from \"./widget-focus\";\n\n/**\n * What the host needs of a mounted layer: its drawing reflector and its teardown. The desk's own\n * handle (`@ice/desk`'s `DeskLayerHandle`) carries far more; an app keeps that through its\n * own factory wrapper, or reads it off {@link DeskHost.layer} typed as the factory returned it.\n */\nexport interface LayerHandle {\n readonly reflector: ReflectorDef & { available(): boolean };\n dispose(): void;\n /**\n * The room's other people (petition I26): `false` — the layer's host draws its peers itself (VibeField draws each by face, from\n * `usePresencePeers`), so this host mounts no remote cursors: no plane, no reflector. Absent or `true`, it mounts them, as ever.\n * Read once, when the factory returns; the presence session, the roster and the OS cursor are the same either way.\n */\n readonly cursors?: boolean;\n}\n\n/** The mount context a layer factory receives: the host, the world and the seams the renderer fills. */\nexport interface LayerContext {\n readonly host: CanvasHost;\n readonly world: World;\n /** The frame pick slot (design-014, B3b): the renderer sets its hit test here at mount, clears it at dispose. */\n readonly framePick: InteractionStack[\"framePick\"];\n /**\n * The stack's exact pick out of the tick (petition I27; `InteractionStack.pickAt`): what a press at a screen point (CSS px of the\n * view) would touch now, through the frame pick above — the layer answers a host's screen-point pick (`handle.pick`) with it.\n */\n readonly pickAt: InteractionStack[\"pickAt\"];\n /** The nav geometry slot (design-015 §9, D2b): the renderer's word on its containers' drawn faces. */\n readonly navGeometry: InteractionStack[\"navGeometry\"];\n /** The held pose slot (design-015 §8, D4b): where the object in hand is on screen, as drawn. */\n readonly heldPose: InteractionStack[\"heldPose\"];\n /** The tray pose slot (design-017 §4, K3): where the pegboard drawer is on screen, as drawn. */\n readonly trayPose: InteractionStack[\"trayPose\"];\n /** The presentation transition coordinator: the layer registers the `ground` plane so a nav flight can prepare. */\n readonly transitions: CanvasEngine[\"transitions\"];\n /** The engine's catalog: the object types whose kinds the layer registers. */\n readonly catalog: CanvasEngine[\"catalog\"];\n /** The marquee's preview, out of the ECS (design-003 §5.7): the vellum the marks draw (design-015 D4a). */\n readonly readMarquee: () => InteractionStack[\"marqueeBuffer\"];\n /** The one spatial index (design-015 §2.5; D6): the renderer's cull rides it with a hysteresis margin — no per-entity rect test on a pan. */\n readonly spatial: InteractionStack[\"index\"];\n /** The engine's device (`engine.compositorDevice`), when the app passed one (design-015 D7): the layer draws with it — ONE device per engine. */\n readonly gpu?: EngineGpu;\n /**\n * The frame gate's sleep (K7a, `@ice/core` frame-control.ts): the layer registers when it is next due (`wakeWhen`), wakes a\n * sleeping loop for what it hears outside a step (`wake` — an asset landing, a theme, a pin), and asks whether the step in\n * progress is a registered time's alone (`settled` — then it ticks only what is due).\n */\n readonly frame?: Pick<CanvasEngine[\"engine\"][\"frame\"], \"wake\" | \"wakeWhen\" | \"settled\">;\n /**\n * The engine's ops a layer runs on its host's word (petition I37): the tray's keyboard lay — `ops.layFromTray`, behind the desk\n * handle's `tray.lay`. Absent (a bare host), that door throws.\n */\n readonly ops?: Pick<CanvasEngine[\"ops\"], \"layFromTray\">;\n}\n\nexport type LayerFactory<H extends LayerHandle = LayerHandle> = (ctx: LayerContext) => H;\n\nexport interface DeskHostOptions<H extends LayerHandle = LayerHandle> {\n /** The viewport element; styled, not created, by the host. */\n readonly container: HTMLElement;\n /** A constructed engine (`createCanvasEngine(...)`); NOT disposed by the host. */\n readonly engine: CanvasEngine;\n /** The desk — `deskLayer({ … })` from `@vibecook/ice/desk` (`@ice/desk`'s src/host/), received opaquely. */\n readonly layer: LayerFactory<H>;\n /**\n * The loop SLEEPS when the engine is quiet (K7a — design-015 §11.4: a desk at rest costs the main thread nothing): no frame\n * until an input, a write, an arrival or a registered time wakes it. Default true; false steps every frame, as before K7a.\n */\n readonly sleep?: boolean;\n}\n\nexport interface DeskHost<H extends LayerHandle = LayerHandle> {\n readonly engine: CanvasEngine;\n readonly host: CanvasHost;\n /** The mounted layer's handle, as the factory returned it. */\n readonly layer: H;\n /**\n * The focus driver (design-007 §2.3): `blurFocus()` releases whatever keyboard claim holds\n * focus. Focus is VIEW state (design-007 §2.6) — it rides the mount, not `engine.ops`.\n */\n readonly focus: WidgetFocusHandle;\n /** Stop the loop, detach every adapter, unregister every reflector, dispose the layer and the host. Idempotent. */\n dispose(): void;\n}\n\nexport function createDeskHost<H extends LayerHandle>(opts: DeskHostOptions<H>): DeskHost<H> {\n const { container, engine } = opts;\n const { world, engine: core, stack } = engine;\n const host = createCanvasHost(container);\n\n let layer: H;\n try {\n layer = opts.layer({\n host,\n world,\n framePick: stack.framePick,\n pickAt: stack.pickAt,\n navGeometry: stack.navGeometry,\n heldPose: stack.heldPose,\n trayPose: stack.trayPose,\n transitions: engine.transitions,\n catalog: engine.catalog,\n readMarquee: () => stack.marqueeBuffer,\n spatial: stack.index,\n ...(engine.compositorDevice !== undefined ? { gpu: engine.compositorDevice } : {}),\n frame: core.frame,\n ops: engine.ops,\n });\n } catch (err) {\n host.dispose();\n throw err;\n }\n\n // Registration order = flush order: the desk draws, then the OS cursor, then\n // the room's other people on top (their plane is the container's last child) —\n // none when the layer's host draws them itself (`cursors: false`, petition I26).\n const remoteCursors = layer.cursors === false ? undefined : createRemoteCursorsReflector(host, world);\n const unregister = [\n core.registerReflector(layer.reflector),\n core.registerReflector(createCursorReflector(host, stack.readCursor)),\n ...(remoteCursors !== undefined ? [core.registerReflector(remoteCursors.reflector)] : []),\n ];\n\n // The OS's reduced-motion preference, into the transition coordinator (a nav flight snaps).\n const motionQuery = container.ownerDocument.defaultView?.matchMedia(\"(prefers-reduced-motion: reduce)\");\n const syncReducedMotion = (): void => {\n engine.transitions.setReducedMotion(motionQuery?.matches === true);\n };\n syncReducedMotion();\n motionQuery?.addEventListener(\"change\", syncReducedMotion);\n\n const detachPointer = attachPointerAdapter(host, stack.queue);\n const focus = attachWidgetFocus(host);\n\n const ratio = (): number => (typeof window !== \"undefined\" ? window.devicePixelRatio : 1);\n let synced = 0;\n // THE VIEWPORT IS THE CONTAINER'S LAYOUT SIZE (petition I39): the box the canvas fills, which no CSS transform changes — never its\n // bounding rect, which a transform on it or an ancestor scales and which re-syncs on nothing (no resize, no ratio): a desk mounted\n // under VibeField's recede transform read 1158.85 × 734.60 for a 1180 × 748 container and drew ~2 % small until something resized\n const syncViewport = (): void => {\n const size = screenSizeOf(container.getBoundingClientRect(), container);\n synced = ratio();\n writeRuntimeResource(world, Viewport, { w: size.width, h: size.height, dpr: synced });\n };\n syncViewport();\n let resizeObserver: ResizeObserver | undefined;\n if (typeof ResizeObserver !== \"undefined\") {\n resizeObserver = new ResizeObserver(syncViewport);\n resizeObserver.observe(container);\n }\n\n // THE RATIO (ICE M21 K1): a change of the device's ratio alone — another display, the browser's zoom, an emulated ratio — resizes\n // nothing, so the observer never hears it and the desk kept drawing at the old ratio; an emulated one (DevTools' device mode, CDP)\n // fires not even the `(resolution)` media query (measured: `.matches` flips, no `change` over three rendered frames, no\n // device-pixel box moves). So the ratio is READ before every step — a property read — and the viewport re-synced when it moved.\n const stopLoop = startRafLoop(\n core,\n () => {\n if (ratio() !== synced) syncViewport();\n },\n { sleep: opts.sleep !== false },\n );\n // …and while the loop SLEEPS (K7a) no step reads it: the ratio is read twice a second instead — a property read, and the\n // viewport write wakes the loop through the world's doors when it moved\n const ratioPoll = opts.sleep !== false ? setInterval(() => { if (ratio() !== synced) syncViewport(); }, 500) : undefined;\n\n let disposed = false;\n return {\n engine,\n host,\n layer,\n focus,\n dispose() {\n if (disposed) return;\n disposed = true;\n stopLoop();\n if (ratioPoll !== undefined) clearInterval(ratioPoll);\n resizeObserver?.disconnect();\n focus.detach();\n detachPointer();\n motionQuery?.removeEventListener(\"change\", syncReducedMotion);\n engine.transitions.setReducedMotion(false);\n for (const unreg of unregister) unreg();\n remoteCursors?.destroy();\n layer.dispose();\n host.dispose();\n },\n };\n}\n","/**\n * @ice/dom — SCREEN SPACE ONLY (design-015 §3, D-D15; D5b): the canvas host, the pointer\n * adapter (events → queue ONLY), the rAF loop, input ownership + the editor's focus, the OS\n * cursor + the room's remote cursors, and `createDeskHost` — the vanilla mount that takes the\n * desk as an opaque layer factory.\n * Import wall: @ice/core + @ice/kernel down only — never react/three/desk (enforced).\n *\n * GONE at D5b — the WORLD-SPACE half (design-015 §1): the content and lifted planes, the\n * plane-transform reflector, the DOM widget hosts + writeback, the L1 source canvas\n * (`layoutsubtree`), the Widget Surface contract, measurement, the graybox and the P4 chrome\n * reflectors, and the pointer adapter's GL route. No DOM under the camera (DK-D2).\n */\nexport const DOM_VERSION = \"0.0.0\";\n\n// The canvas host (design-004 §1): the styled container — no planes.\nexport { createCanvasHost, type CanvasHost } from \"./host\";\n\n// The vanilla mount (design-015 §3): the desk's layer, the adapters, the reflectors, the loop.\nexport {\n createDeskHost,\n type DeskHost,\n type DeskHostOptions,\n type LayerContext,\n type LayerFactory,\n type LayerHandle,\n} from \"./desk-host\";\n\n// The rAF frame loop (design-002 §1: the platform owns the loop).\nexport { startRafLoop } from \"./loop\";\n\n// The L0 pointer adapter (design-003 §2–§3): DOM events → InputQueue, nothing more.\nexport { attachPointerAdapter } from \"./pointer-adapter\";\n\n// Input-ownership predicates (design-007 §4, petitions I1/I4): the shared\n// per-surface guard family — the keymap (@ice/react) reads these too.\nexport {\n CLAIM_OWNS_ESCAPE,\n KEYBOARD_CLAIM_ATTR,\n isEditableTarget,\n keyboardClaimOf,\n wheelCede,\n type KeyboardClaim,\n} from \"./input-ownership\";\n\n// The focus driver (design-007 §2.3–§2.5): click-to-focus acquisition for a\n// keyboard-claiming host + the programmatic blurFocus/focusWidget handle.\n// Focus is VIEW state (design-007 §2.6) — it lives here, not in core.\nexport {\n FOCUS_PROXY_ATTR,\n attachWidgetFocus,\n type FocusHostLookup,\n type WidgetFocusHandle,\n} from \"./widget-focus\";\n\n// L4 cursor projection output (design-003 §7: local cursor = OS cursor, one write).\nexport { createCursorReflector } from \"./reflectors/cursor\";\n// P5 remote cursors (design-004 §1: screen-space pooled nodes; M9 presence) — a room's other people.\nexport { createRemoteCursorsReflector, type RemoteCursorsReflector } from \"./reflectors/remote-cursors\";\n"],"mappings":"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","names":[]}
|