@weasel-js/routing 1.10.1 → 1.11.1
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-VCEIQ64H.js → chunk-KLBH7VFF.js} +740 -87
- package/dist/chunk-KLBH7VFF.js.map +1 -0
- package/dist/{index-DkOqUxow.d.ts → index-Cb7ehEks.d.ts} +1689 -1177
- package/dist/index.d.ts +6 -3
- package/dist/index.js +85 -17
- package/dist/index.js.map +1 -1
- package/dist/{internal-Boc2I3zn.d.ts → internal-Dcyz_cJv.d.ts} +31 -1
- package/dist/internal.d.ts +8 -5
- package/dist/react.d.ts +61 -29
- package/dist/react.js +467 -232
- package/dist/react.js.map +1 -1
- package/package.json +13 -9
- package/dist/chunk-VCEIQ64H.js.map +0 -1
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ModifierKey, ClaimableGesture, GestureSpec, InputEvent, TargetSpec, IngestItem, BodyTarget, KeyHeldSpec, TargetPredicate, KeySpec, ParsedModifiers, GestureName } from '@weasel-js/gestures';
|
|
2
|
+
import { FramedBox, View, Point } from '@weasel-js/geom';
|
|
3
|
+
import { CursorSpec } from '@weasel-js/cursor';
|
|
4
|
+
import { MenuEntry, Rect, MenuShortcut } from '@weasel-js/menu';
|
|
5
|
+
import { PrefGroup } from '@weasel-js/prefs';
|
|
6
|
+
import { CapabilityTag, ActiveMode, ModeRegistry } from '@weasel-js/modes';
|
|
7
|
+
import * as _weasel_js_icons from '@weasel-js/icons';
|
|
8
|
+
import { IconRef } from '@weasel-js/icons';
|
|
2
9
|
import * as react from 'react';
|
|
3
10
|
import { ReactNode } from 'react';
|
|
4
|
-
import { Emitter } from '@weasel-js/registry';
|
|
5
|
-
import { ClaimableGesture, GestureSpec, IngestItem, InputEvent, TargetSpec, KeyHeldSpec, TargetPredicate, KeySpec, ParsedModifiers, GestureName } from '@weasel-js/gestures';
|
|
6
|
-
import { CapabilityTag, ActiveMode, ModeRegistry } from '@weasel-js/modes';
|
|
7
|
-
import { CursorSpec } from '@weasel-js/cursor';
|
|
11
|
+
import { Emitter, QueryMatch } from '@weasel-js/registry';
|
|
8
12
|
import '@weasel-js/select';
|
|
9
13
|
|
|
10
14
|
/**
|
|
@@ -22,20 +26,9 @@ type NodeId = string & {
|
|
|
22
26
|
readonly __brand: 'NodeId';
|
|
23
27
|
};
|
|
24
28
|
|
|
25
|
-
/**
|
|
26
|
-
*
|
|
27
|
-
|
|
28
|
-
* let chrome carry the frame a box is drawn in without a parallel type;
|
|
29
|
-
* `poseFrameOf` composes the two.
|
|
30
|
-
*/
|
|
31
|
-
interface Bounds {
|
|
32
|
-
x: number;
|
|
33
|
-
y: number;
|
|
34
|
-
width: number;
|
|
35
|
-
height: number;
|
|
36
|
-
rotation?: number;
|
|
37
|
-
transform?: PoseLinear;
|
|
38
|
-
}
|
|
29
|
+
/** Axis-aligned rectangle in world space, with the frame it is drawn in;
|
|
30
|
+
* geom's `FramedBox`. */
|
|
31
|
+
type Bounds = FramedBox;
|
|
39
32
|
/** The selection as routing reads and writes it. */
|
|
40
33
|
interface SelectionApi {
|
|
41
34
|
/** Current selection. Re-renders trigger when this reference changes. */
|
|
@@ -176,126 +169,815 @@ type ResizeAnchor = {
|
|
|
176
169
|
y: 'min' | 'max' | 'free';
|
|
177
170
|
};
|
|
178
171
|
|
|
172
|
+
/** The chrome an in-flight gesture paints beside the scene. */
|
|
173
|
+
|
|
179
174
|
/**
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
175
|
+
* What a `polyline` overlay is, in a word — the narrowest thing an action can
|
|
176
|
+
* say about how its run should look without naming a paint.
|
|
177
|
+
*
|
|
178
|
+
* The named roles are the kit's; a painter maps each to a style and falls back
|
|
179
|
+
* to plain chrome for one it does not know, so a package outside core can coin
|
|
180
|
+
* its own and still be drawn.
|
|
184
181
|
*/
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
constructor(parent: N | null, descends: boolean);
|
|
201
|
-
/** Bump the tree's version and tell every listener in it. */
|
|
202
|
-
changed(): void;
|
|
203
|
-
/** Join the parent's children, newest last; returns the leave. */
|
|
204
|
-
mount(): () => void;
|
|
205
|
-
/** Make this node the one its ancestors resolve through. */
|
|
206
|
-
activate(): void;
|
|
207
|
-
/** The node a lookup made here starts from: down the active children, or the
|
|
208
|
-
* newest where nothing has been activated yet. A yoke with no members yet
|
|
209
|
-
* is passed over — there is no canvas behind it to answer. */
|
|
210
|
-
leaf(): N;
|
|
211
|
-
private newestMember;
|
|
212
|
-
/** Where a read made through this node starts. */
|
|
213
|
-
start(): N;
|
|
214
|
-
/** Whether this node is on the path a read from the root takes. */
|
|
215
|
-
isActive(): boolean;
|
|
216
|
-
/** This node, then each ancestor up to the root. */
|
|
217
|
-
chain(): Generator<N>;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/** Which tool is active, plus the stack of tools temporarily held active by a
|
|
221
|
-
* hotkey (space-for-hand and the like). The dispatcher reads this to decide
|
|
222
|
-
* whose bindings are in scope. */
|
|
223
|
-
interface ActiveToolContextValue {
|
|
224
|
-
/** The focused tool's id, or `null` when no tool holds the active slot —
|
|
225
|
-
* a canvas can run on ambient bindings alone. */
|
|
226
|
-
active: string | null;
|
|
227
|
-
hotkeyStack: string[];
|
|
228
|
-
setActive(id: string | null): void;
|
|
229
|
-
pushHotkey(id: string): void;
|
|
230
|
-
/**
|
|
231
|
-
* Disengage a hotkey-held tool. With `id`, removes that tool's own entry
|
|
232
|
-
* wherever it now sits — holds do not have to come up in the order they went
|
|
233
|
-
* down, and popping the top would disengage a tool whose key is still held.
|
|
234
|
-
* With no id, pops the top.
|
|
235
|
-
*/
|
|
236
|
-
popHotkey(id?: string): void;
|
|
237
|
-
}
|
|
182
|
+
type OverlayRole =
|
|
183
|
+
/** Generic in-flight chrome — the marquee/lasso paint. */
|
|
184
|
+
'chrome'
|
|
185
|
+
/** A cut the gesture would make. */
|
|
186
|
+
| 'cut'
|
|
187
|
+
/** An edge the gesture would author between two points. */
|
|
188
|
+
| 'connector'
|
|
189
|
+
/** The stretch of a path a soft-selection drag reaches. */
|
|
190
|
+
| 'falloff'
|
|
191
|
+
/** What a lock holds a dragged point to — a line, a circle. */
|
|
192
|
+
| 'lock'
|
|
193
|
+
/** Where a dragged item would land: a reorder's insertion bar. */
|
|
194
|
+
| 'insert'
|
|
195
|
+
/** Where a dragged item is over and cannot land. */
|
|
196
|
+
| 'refused' | (string & {});
|
|
238
197
|
/**
|
|
239
|
-
*
|
|
240
|
-
*
|
|
198
|
+
* What an in-flight gesture proposes to paint *beside* the scene, returned by
|
|
199
|
+
* `OngoingHandle.overlay()` — a marquee rect, a lasso trail, the outline of a
|
|
200
|
+
* shape being dragged out, the line a slice would cut along.
|
|
201
|
+
*
|
|
202
|
+
* Every variant is world geometry and nothing else — no `DrawCommand`, no
|
|
203
|
+
* color, no coordinate space to choose — so a surface rendering the same
|
|
204
|
+
* gesture some other way can still read it. The painter owns every style
|
|
205
|
+
* decision.
|
|
241
206
|
*/
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
207
|
+
type OngoingOverlay = {
|
|
208
|
+
kind: 'marquee';
|
|
209
|
+
start: {
|
|
210
|
+
x: number;
|
|
211
|
+
y: number;
|
|
212
|
+
};
|
|
213
|
+
current: {
|
|
214
|
+
x: number;
|
|
215
|
+
y: number;
|
|
216
|
+
};
|
|
217
|
+
shiftHeld: boolean;
|
|
218
|
+
} | {
|
|
219
|
+
kind: 'lasso';
|
|
220
|
+
vertices: ReadonlyArray<{
|
|
221
|
+
x: number;
|
|
222
|
+
y: number;
|
|
223
|
+
}>;
|
|
224
|
+
current: {
|
|
225
|
+
x: number;
|
|
226
|
+
y: number;
|
|
227
|
+
};
|
|
228
|
+
shiftHeld: boolean;
|
|
229
|
+
} | {
|
|
230
|
+
/**
|
|
231
|
+
* An open run of world points the gesture is proposing — a cut line, a
|
|
232
|
+
* routed connector. `role` is the whole of what an action says about
|
|
233
|
+
* appearance.
|
|
234
|
+
*/
|
|
235
|
+
kind: 'polyline';
|
|
236
|
+
/** At least two points. Anything shorter is dropped in resolution. */
|
|
237
|
+
points: ReadonlyArray<{
|
|
238
|
+
x: number;
|
|
239
|
+
y: number;
|
|
240
|
+
}>;
|
|
241
|
+
/** What the run means. Omitted reads as {@link OverlayRole} `'chrome'`. */
|
|
242
|
+
role?: OverlayRole;
|
|
243
|
+
} | {
|
|
244
|
+
/**
|
|
245
|
+
* A round brush dragged through world points: the area it has swept so
|
|
246
|
+
* far, and its footprint at the last point. The radius is in screen
|
|
247
|
+
* pixels, so the footprint keeps its size at every zoom.
|
|
248
|
+
*/
|
|
249
|
+
kind: 'brush';
|
|
250
|
+
/** The brush's path so far, at least one point; the last is where it is now. */
|
|
251
|
+
points: ReadonlyArray<{
|
|
252
|
+
x: number;
|
|
253
|
+
y: number;
|
|
254
|
+
}>;
|
|
255
|
+
radiusPx: number;
|
|
256
|
+
} | {
|
|
257
|
+
/**
|
|
258
|
+
* Live insert-drag preview. Pre-commit there is no scene node to ghost
|
|
259
|
+
* via `previewIds()`/`previewPose()`, so insert paints its preview
|
|
260
|
+
* through the overlay instead.
|
|
261
|
+
*
|
|
262
|
+
* `bounds` is the AABB of the current drag (start/current normalized).
|
|
263
|
+
* `extras` is the per-kind extras the action already collected; the
|
|
264
|
+
* overlay renderer narrows on `shape` and casts the field shape it
|
|
265
|
+
* expects.
|
|
266
|
+
*/
|
|
267
|
+
kind: 'insertPreview';
|
|
268
|
+
/** Which preview the dispatcher's overlay draws. The kit's own shape
|
|
269
|
+
* kinds are the ones it knows how to render; a consumer-defined kind
|
|
270
|
+
* is legal and simply gets no live preview. */
|
|
271
|
+
shape: string;
|
|
272
|
+
bounds: {
|
|
273
|
+
x: number;
|
|
274
|
+
y: number;
|
|
275
|
+
width: number;
|
|
276
|
+
height: number;
|
|
277
|
+
};
|
|
278
|
+
extras: unknown;
|
|
279
|
+
/** World-space point to paint a small "anchor" dot at, marking the
|
|
280
|
+
* click point — for radial shapes (polygon/star), where no vertex
|
|
281
|
+
* sits on it, and for any shape in center mode. */
|
|
282
|
+
anchorPoint?: {
|
|
283
|
+
x: number;
|
|
284
|
+
y: number;
|
|
285
|
+
};
|
|
286
|
+
/** World affines the shape will also be drawn under once it lands, by
|
|
287
|
+
* the container it is drawn into: the preview is painted under each. */
|
|
288
|
+
copies?: readonly (readonly [number, number, number, number, number, number])[];
|
|
289
|
+
};
|
|
290
|
+
/** A handle's overlays as a list: empty when it has none. */
|
|
291
|
+
declare function overlaysOf(handle: OngoingHandle): readonly OngoingOverlay[];
|
|
292
|
+
|
|
266
293
|
/**
|
|
267
|
-
*
|
|
268
|
-
*
|
|
294
|
+
* Modifier roles: the modifiers an ongoing action reads while it runs, named
|
|
295
|
+
* by what they do rather than which key they are, so a binding or a tuning can
|
|
296
|
+
* move one to another key without forking the action.
|
|
269
297
|
*/
|
|
270
|
-
|
|
298
|
+
|
|
299
|
+
/** The keys that hold a role: any one of them held holds it. `'mod'` is Cmd
|
|
300
|
+
* on a Mac and Ctrl elsewhere. An empty list never holds it. */
|
|
301
|
+
type RoleKeys = readonly ModifierKey[];
|
|
302
|
+
/** One modifier an action reads mid-gesture. */
|
|
303
|
+
interface ModifierRole {
|
|
304
|
+
label: string;
|
|
305
|
+
/** The keys that hold it unless a tuning or a binding says otherwise. */
|
|
306
|
+
keys: RoleKeys;
|
|
307
|
+
}
|
|
308
|
+
/** An action's roles by id, e.g. `'handle.holdAngle'`. */
|
|
309
|
+
type ModifierRoles = Readonly<Record<string, ModifierRole>>;
|
|
310
|
+
/** Keys a tuning or a binding gives some of an action's roles. */
|
|
311
|
+
type RoleRebinds = Readonly<Record<string, RoleKeys>>;
|
|
312
|
+
/** Whether any of `keys` is held. */
|
|
313
|
+
declare function keysHeld(keys: RoleKeys, modifiers: ModifierState, isMac: boolean): boolean;
|
|
314
|
+
/**
|
|
315
|
+
* A reader for an action's own roles. It answers from `ctx.roles`, which the
|
|
316
|
+
* dispatcher resolves on every event, and falls back to the role's default
|
|
317
|
+
* keys where nothing resolved them: an invoker called directly, or a context
|
|
318
|
+
* a cancel-all built with no event.
|
|
319
|
+
*/
|
|
320
|
+
declare function roleReader<R extends ModifierRoles>(roles: R): (ctx: {
|
|
321
|
+
roles?: Readonly<Record<string, boolean>>;
|
|
322
|
+
modifiers: ModifierState;
|
|
323
|
+
}, id: keyof R & string) => boolean;
|
|
324
|
+
|
|
271
325
|
/**
|
|
272
|
-
*
|
|
273
|
-
*
|
|
326
|
+
* Invoker — pluggable invocation strategy for an Action.
|
|
327
|
+
*
|
|
328
|
+
* An Action's `invoker` field describes how it's run. `immediate` invokers
|
|
329
|
+
* fire once (delete, align, undo). `ongoing` invokers open a phase machine
|
|
330
|
+
* driven by the dispatcher (move, resize, pinch-zoom).
|
|
274
331
|
*/
|
|
275
|
-
declare function ActiveToolContextProviderIfRoot({ children, }: {
|
|
276
|
-
children: ReactNode;
|
|
277
|
-
}): react.JSX.Element;
|
|
278
332
|
|
|
333
|
+
/** A 2D point in either world or screen coordinates; geom's `Point`. */
|
|
334
|
+
type Point2 = Point;
|
|
279
335
|
/**
|
|
280
|
-
*
|
|
281
|
-
*
|
|
282
|
-
* eligibility filter — and discarded.
|
|
336
|
+
* Which UI affordance was hit at pointerdown. Populated by the dispatcher when
|
|
337
|
+
* the `affordanceAt` thunk is provided to `useGestureDispatcher`.
|
|
283
338
|
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
339
|
+
* `kind` is a discriminator string:
|
|
340
|
+
* - `'handle:top-left'` / `'handle:top-right'` / `'handle:bottom-left'` /
|
|
341
|
+
* `'handle:bottom-right'` — corner resize handles.
|
|
342
|
+
* - `'handle:top'` / `'handle:right'` / `'handle:bottom'` / `'handle:left'` —
|
|
343
|
+
* edge resize handles.
|
|
344
|
+
* - `'rotate-handle'` — the rotation affordance.
|
|
345
|
+
* - `'anchor:N'` — a path anchor at index N.
|
|
286
346
|
*/
|
|
287
|
-
interface
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
347
|
+
interface AffordanceHit {
|
|
348
|
+
/** Discriminator string, e.g. `'handle:bottom-right'`. */
|
|
349
|
+
kind: string;
|
|
350
|
+
/** Id of whatever produced this hit — a kit affordance's `id`, or the
|
|
351
|
+
* registered layer's id. */
|
|
352
|
+
owner?: string;
|
|
353
|
+
/** `'exclusive'` means no binding may act on this point unless its target
|
|
354
|
+
* consults the affordance. `'shared'` (the default) competes on scope and
|
|
355
|
+
* specificity as bindings always have. */
|
|
356
|
+
strength?: 'exclusive' | 'shared';
|
|
357
|
+
/** Which gestures an exclusive claim bars. Omitted bars all of them. */
|
|
358
|
+
claimedKinds?: readonly ClaimableGesture[];
|
|
359
|
+
/** World-space fixed/pivot point. For resize: opposite corner. For rotate: pivot. */
|
|
360
|
+
fixedPoint?: {
|
|
361
|
+
x: number;
|
|
362
|
+
y: number;
|
|
295
363
|
};
|
|
296
|
-
|
|
364
|
+
/** Which nodes this affordance belongs to. */
|
|
365
|
+
targetIds?: string[];
|
|
366
|
+
/** Set only when `kind` matches `'handle:*'`: which corner or edge stays
|
|
367
|
+
* fixed during a resize; `'free'` on the axis an edge handle leaves alone. */
|
|
368
|
+
anchor?: ResizeAnchor;
|
|
369
|
+
/** Cursor to show while the pointer hovers this affordance (no gesture in
|
|
370
|
+
* flight). Unset falls through to action-cursor prediction, then to the
|
|
371
|
+
* active tool's cursor. */
|
|
372
|
+
cursor?: CursorSpec;
|
|
297
373
|
/**
|
|
298
|
-
*
|
|
374
|
+
* Free-form payload from whatever produced the hit, carried through to the
|
|
375
|
+
* matching action untouched. Kit affordances leave it unset; it exists for
|
|
376
|
+
* a registered layer's own chrome, where the hit-test already resolved
|
|
377
|
+
* *which* of its pieces was hit.
|
|
378
|
+
*/
|
|
379
|
+
payload?: unknown;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* One accumulated point on a drag trail: world-space position plus whatever
|
|
383
|
+
* stylus state the originating `PointerEvent` carried.
|
|
384
|
+
*
|
|
385
|
+
* The stylus fields are absent for mouse/touch on browsers that don't report
|
|
386
|
+
* them, and for synthetic events.
|
|
387
|
+
*/
|
|
388
|
+
interface DragSample extends Point2 {
|
|
389
|
+
/** 0..1. Mouse/touch report 0.5 while a button is held, per the spec. */
|
|
390
|
+
pressure?: number;
|
|
391
|
+
/** Degrees, ±90. Zero for mouse/touch. */
|
|
392
|
+
tiltX?: number;
|
|
393
|
+
/** Degrees, ±90. Zero for mouse/touch. */
|
|
394
|
+
tiltY?: number;
|
|
395
|
+
}
|
|
396
|
+
/** Per-invocation runtime context the dispatcher hands to an Invoker.
|
|
397
|
+
* Gesture-kind-specific fields (`drag`, `wheel`, `multiTouch`, `key`) are
|
|
398
|
+
* populated only for matching gesture kinds. */
|
|
399
|
+
interface InvocationCtx {
|
|
400
|
+
world: Point2;
|
|
401
|
+
/**
|
|
402
|
+
* The pointer in client/screen pixels, for an action that has to place DOM
|
|
403
|
+
* at the pointer. Absent on every invocation whose event carries no client
|
|
404
|
+
* coordinates — a keystroke, a UI-driven trigger, a synthetic probe — so
|
|
405
|
+
* read it with a fallback rather than assuming a point.
|
|
406
|
+
*
|
|
407
|
+
* A view-mutating drag wants `drag.screenDelta`, not the difference of two
|
|
408
|
+
* of these: world deltas are self-referential mid-pan, and so is any delta
|
|
409
|
+
* taken across a view change.
|
|
410
|
+
*/
|
|
411
|
+
screen?: Point2;
|
|
412
|
+
modifiers: ModifierState;
|
|
413
|
+
deps: ActionDeps;
|
|
414
|
+
drag?: {
|
|
415
|
+
start: Point2;
|
|
416
|
+
current: Point2;
|
|
417
|
+
delta: Point2;
|
|
418
|
+
/**
|
|
419
|
+
* Drag delta in client/screen coordinates (CSS pixels from the drag
|
|
420
|
+
* origin). Use this — never `delta` — for any action whose effect
|
|
421
|
+
* mutates the viewport itself (pan, view-zoom), because world-space
|
|
422
|
+
* deltas become self-referential as the view shifts mid-drag.
|
|
423
|
+
*
|
|
424
|
+
* Absent when the underlying pointer events carried no
|
|
425
|
+
* `clientX`/`clientY`.
|
|
426
|
+
*/
|
|
427
|
+
screenDelta?: Point2;
|
|
428
|
+
affordance?: AffordanceHit;
|
|
429
|
+
/**
|
|
430
|
+
* Full pointermove history for the current drag, in world space, with
|
|
431
|
+
* per-sample stylus state when the browser reported it.
|
|
432
|
+
* Available only during `onMove` and `onEnd` calls (not on `start`).
|
|
433
|
+
*
|
|
434
|
+
* During `onMove` this is the dispatcher's live accumulator, and it grows
|
|
435
|
+
* under you — read it, don't retain it. On `onEnd` it is a snapshot, so
|
|
436
|
+
* that is where an action that keeps the trail should take it.
|
|
437
|
+
*/
|
|
438
|
+
points?: DragSample[];
|
|
439
|
+
};
|
|
440
|
+
wheel?: {
|
|
441
|
+
deltaX: number;
|
|
442
|
+
deltaY: number;
|
|
443
|
+
deltaZ: number;
|
|
444
|
+
};
|
|
445
|
+
/** One `pinch` sample: `scale` is a multiplier since the previous sample,
|
|
446
|
+
* `rotation` degrees since it. See `PinchEvent`. */
|
|
447
|
+
pinch?: {
|
|
448
|
+
scale: number;
|
|
449
|
+
rotation: number;
|
|
450
|
+
};
|
|
451
|
+
multiTouch?: {
|
|
452
|
+
/** Canvas-local CSS pixels — see `MultiTouchEvent.centroid`. Neither
|
|
453
|
+
* `world` nor `screen`: it is the space `zoomAt` anchors in, so passing
|
|
454
|
+
* it through `clientToWorld` removes the canvas origin twice. */
|
|
455
|
+
centroid: Point2;
|
|
456
|
+
/** Distance between the two primary pointers, in CSS pixels. */
|
|
457
|
+
spread: number;
|
|
458
|
+
/** Degrees the line between the two primary pointers has turned since
|
|
459
|
+
* the gesture began, clockwise positive. It keeps counting past a half
|
|
460
|
+
* turn. Zero where the events carry no `MultiTouchEvent.angle`. */
|
|
461
|
+
rotation: number;
|
|
462
|
+
/**
|
|
463
|
+
* Pinch-zoom geometry, populated while a multitouch handle is in flight.
|
|
464
|
+
* `startSpread` is the spread at the moment the gesture began.
|
|
465
|
+
*/
|
|
466
|
+
pinch?: {
|
|
467
|
+
startSpread: number;
|
|
468
|
+
currentSpread: number;
|
|
469
|
+
/** Canvas-local, same as the enclosing `centroid`. */
|
|
470
|
+
centroid: Point2;
|
|
471
|
+
};
|
|
472
|
+
};
|
|
473
|
+
key?: {
|
|
474
|
+
key: string;
|
|
475
|
+
repeat: boolean;
|
|
476
|
+
};
|
|
477
|
+
/** What a carry over the surface brings: its payload, as `startCarry` was
|
|
478
|
+
* handed it. Present for every event of a `carry` binding's gesture. */
|
|
479
|
+
carry?: {
|
|
480
|
+
type: string;
|
|
481
|
+
value: unknown;
|
|
482
|
+
};
|
|
483
|
+
/**
|
|
484
|
+
* The view the input landed in, as the surface's view resolver assigned it:
|
|
485
|
+
* a view id, or `null` for the surface's own camera. Absent where no
|
|
486
|
+
* surface routed the input — a UI-driven trigger, a bare dispatcher.
|
|
487
|
+
*/
|
|
488
|
+
viewId?: string | null;
|
|
489
|
+
/**
|
|
490
|
+
* Per-invocation parameters. Populated by `ActionsRegistry.begin()` for
|
|
491
|
+
* UI-driven ongoing actions (color picker, opacity slider). The gesture
|
|
492
|
+
* dispatcher does not populate this field; gesture-driven actions receive
|
|
493
|
+
* params via `BindingOpts.params` on `start` (the `opts` arg).
|
|
494
|
+
*/
|
|
495
|
+
params?: Record<string, unknown>;
|
|
496
|
+
/**
|
|
497
|
+
* Which of the action's `modifierRoles` are held for this event, by role
|
|
498
|
+
* id. The dispatcher fills it on every event of a gesture whose action
|
|
499
|
+
* declares roles; absent otherwise. Read it through `roleReader`, which
|
|
500
|
+
* falls back to the roles' default keys where it is absent.
|
|
501
|
+
*/
|
|
502
|
+
roles?: Readonly<Record<string, boolean>>;
|
|
503
|
+
}
|
|
504
|
+
/** Per-invocation options the dispatcher reads from a `GestureBinding`'s
|
|
505
|
+
* `opts` field and passes to `OngoingInvoker.start`. */
|
|
506
|
+
interface BindingOpts {
|
|
507
|
+
/** Gesture behaviors the action's own invoker runs. Routing carries the
|
|
508
|
+
* array to the action and never reads an element, so the element type is
|
|
509
|
+
* open here; `@weasel-js/core` narrows it. */
|
|
510
|
+
behaviors?: unknown[];
|
|
511
|
+
/** Per-binding action parameters, which an immediate invoker reads via the
|
|
512
|
+
* second arg to `run`.
|
|
513
|
+
*
|
|
514
|
+
* May also be a thunk evaluated each time the value is needed, so a tool
|
|
515
|
+
* can close over refs that mutate during a gesture (e.g. polygon `sides`
|
|
516
|
+
* adjusted mid-drag via ArrowUp). An ongoing invoker that wants the latest
|
|
517
|
+
* values at commit re-calls it inside `onEnd` via
|
|
518
|
+
* `resolveParams(opts?.params)`. */
|
|
519
|
+
params?: Record<string, unknown> | (() => Record<string, unknown>);
|
|
520
|
+
/**
|
|
521
|
+
* The views this binding is live in, by id, where `null` is the surface's
|
|
522
|
+
* own camera. Omitted means every view.
|
|
523
|
+
*
|
|
524
|
+
* A binding that names the view an input landed in outranks every binding
|
|
525
|
+
* that does not, whatever their scope tiers: naming a view is the most
|
|
526
|
+
* specific thing a binding can say about where it applies. Without that, a
|
|
527
|
+
* drag bound inside a minimap view loses to the active tool's marquee.
|
|
528
|
+
*/
|
|
529
|
+
views?: readonly (string | null)[];
|
|
530
|
+
/**
|
|
531
|
+
* Says this binding is declared ahead of another on the same input on
|
|
532
|
+
* purpose: its action declines through `enabled()` when the input is not its
|
|
533
|
+
* to take, and the next binding fires. The dispatcher does that for any
|
|
534
|
+
* binding; this tells the route-conflict check the order is the design.
|
|
535
|
+
*/
|
|
536
|
+
yields?: boolean;
|
|
537
|
+
/**
|
|
538
|
+
* For a key binding: live while the pointer is over the surface, whichever
|
|
539
|
+
* canvas holds the keys, and only then. For a key that means "here" — a
|
|
540
|
+
* magnifier's peek — on a page with more than one surface that hears it.
|
|
541
|
+
*/
|
|
542
|
+
over?: boolean;
|
|
543
|
+
/**
|
|
544
|
+
* Keys for some of the action's `modifierRoles` on this binding, by role
|
|
545
|
+
* id, over the action's defaults and any tuning's.
|
|
546
|
+
*/
|
|
547
|
+
roles?: RoleRebinds;
|
|
548
|
+
}
|
|
549
|
+
/** Resolve `BindingOpts.params` to a concrete record (calling the thunk if
|
|
550
|
+
* needed). Returns `undefined` for absent params. */
|
|
551
|
+
declare function resolveParams(params: BindingOpts['params']): Record<string, unknown> | undefined;
|
|
552
|
+
/** The deps an action receives, composed per call from what it `requires`.
|
|
553
|
+
* Each name resolves to its {@link DepSchema} entry, and is absent when no
|
|
554
|
+
* source registered it; a name nothing has declared passes through as
|
|
555
|
+
* `unknown`. */
|
|
556
|
+
interface ActionDeps extends Partial<DepSchema> {
|
|
557
|
+
[k: string]: unknown;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/** Handle returned from an `OngoingInvoker.start`. The dispatcher pumps
|
|
561
|
+
* `onMove` on subsequent input events of the same gesture and calls
|
|
562
|
+
* `onEnd` exactly once (with `'commit'` on natural completion or `'cancel'`
|
|
563
|
+
* on pointercancel / blur / escape). */
|
|
564
|
+
interface OngoingHandle {
|
|
565
|
+
/**
|
|
566
|
+
* Optional logical action kind — a stable, human-readable tag the
|
|
567
|
+
* dispatcher exposes via `getActiveAction()`, e.g. `'marquee'`, `'move'`,
|
|
568
|
+
* `'resize'`, `'pan'`.
|
|
569
|
+
*
|
|
570
|
+
* When omitted, `getActiveAction().kind` reports `null` even though a
|
|
571
|
+
* handle is in flight.
|
|
572
|
+
*/
|
|
573
|
+
kind?: string;
|
|
574
|
+
/**
|
|
575
|
+
* The cursor this gesture shows now, read after every move. Wins over the
|
|
576
|
+
* action's `activeCursor`, for a cursor that follows the gesture — the
|
|
577
|
+
* rotate glyph turning about its pivot. `undefined` defers to the action.
|
|
578
|
+
*/
|
|
579
|
+
cursor?(): CursorSpec | undefined;
|
|
580
|
+
onMove?(ctx: InvocationCtx): void;
|
|
581
|
+
onEnd?(ctx: InvocationCtx, reason: 'commit' | 'cancel'): void;
|
|
582
|
+
/**
|
|
583
|
+
* Optional preview surface: the in-flight state the canvas's preview-ghost
|
|
584
|
+
* layer renders on top of the committed scene during the gesture.
|
|
585
|
+
*
|
|
586
|
+
* `previewIds()` enumerates the displaced node ids; `previewPose(id)`
|
|
587
|
+
* returns the interim pose for one of them (shape opaque — the canvas
|
|
588
|
+
* casts to its `TPose` parameter). `previewIds` returning `null`, or
|
|
589
|
+
* omitted, means "no preview this gesture". An id with no `previewPose`
|
|
590
|
+
* is shown at its effective pose: an action that publishes its poses to
|
|
591
|
+
* the scene's pose overrides, as move, resize and rotate do, leaves
|
|
592
|
+
* `previewPose` out rather than answering the same pose twice.
|
|
593
|
+
*/
|
|
594
|
+
previewIds?(): Iterable<string> | null;
|
|
595
|
+
previewPose?(id: string): unknown | null;
|
|
596
|
+
/**
|
|
597
|
+
* Subset of `previewIds()` the ghost layer paints at full opacity. The
|
|
598
|
+
* ghost alpha says "this is in flight under the pointer"; a node the
|
|
599
|
+
* gesture merely displaces — a layout sibling reflowing into its
|
|
600
|
+
* destination slot — is not, and reads better settled. Honored at
|
|
601
|
+
* subtree-root granularity.
|
|
602
|
+
*/
|
|
603
|
+
previewOpaqueIds?(): Iterable<string> | null;
|
|
604
|
+
/**
|
|
605
|
+
* When `false`, the preview-ghost layer paints the ghost AND the source
|
|
606
|
+
* node stays visible at its committed pose, as a clone wants. Defaults to
|
|
607
|
+
* `true`: the ghost replaces the source during the gesture.
|
|
608
|
+
*/
|
|
609
|
+
previewHidesSource?: boolean;
|
|
610
|
+
/**
|
|
611
|
+
* Optional per-id preview *data*, for a gesture that mutates `node.data`
|
|
612
|
+
* (e.g. anchor-edit on nodes that store the polygon on `data.path`) rather
|
|
613
|
+
* than, or in addition to, the pose. Falls back to the committed
|
|
614
|
+
* `node.data` when null/absent.
|
|
615
|
+
*/
|
|
616
|
+
previewData?(id: string): unknown | null;
|
|
617
|
+
/**
|
|
618
|
+
* Optional chrome surface: a non-ghost visual (marquee rectangle, lasso
|
|
619
|
+
* polyline) painted while the gesture is in flight — one, or a list for a
|
|
620
|
+
* gesture showing several runs at once. Returning `null`, or omitting the
|
|
621
|
+
* method, means "no overlay this gesture". Read it through
|
|
622
|
+
* {@link overlaysOf}, which answers a list either way.
|
|
623
|
+
*/
|
|
624
|
+
overlay?(): OngoingOverlay | readonly OngoingOverlay[] | null;
|
|
625
|
+
/**
|
|
626
|
+
* A wheel turned while this gesture is in flight — scrubbing a value the
|
|
627
|
+
* drag is using, say. Return `true` to claim the wheel, so no wheel binding
|
|
628
|
+
* (the viewport's zoom or pan) sees it; anything else lets it through.
|
|
629
|
+
* Omit it and the wheel goes to the bindings as usual.
|
|
630
|
+
*/
|
|
631
|
+
onWheel?(ctx: InvocationCtx): boolean | void;
|
|
632
|
+
}
|
|
633
|
+
/** Fire-once invocation. Runs to completion synchronously (or fires off an
|
|
634
|
+
* async side-effect; the registry doesn't wait). */
|
|
635
|
+
interface ImmediateInvoker {
|
|
636
|
+
timing: 'immediate';
|
|
637
|
+
/** `params` carries the matched binding's opts.params. Invoked from the
|
|
638
|
+
* command palette, or anywhere else with no per-binding context, `params`
|
|
639
|
+
* is undefined; descriptors should default to a sensible variant. */
|
|
640
|
+
run(deps: ActionDeps, params?: Record<string, unknown>): void;
|
|
641
|
+
}
|
|
642
|
+
/** Phase-machine invocation. `start` opens the phase and returns the handle
|
|
643
|
+
* the dispatcher pumps. */
|
|
644
|
+
interface OngoingInvoker {
|
|
645
|
+
timing: 'ongoing';
|
|
646
|
+
start(ctx: InvocationCtx, opts?: BindingOpts): OngoingHandle;
|
|
647
|
+
}
|
|
648
|
+
/** Pluggable invocation strategy for an Action. */
|
|
649
|
+
type Invoker = ImmediateInvoker | OngoingInvoker;
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* GestureBinding — connects a GestureSpec to an Action id (with per-binding
|
|
653
|
+
* options). Tools own arrays of these on their `bindings` field; ambient
|
|
654
|
+
* gesture-bindings are registered globally.
|
|
655
|
+
*/
|
|
656
|
+
|
|
657
|
+
/** An interaction: a gesture spec composed with the id of the action it
|
|
658
|
+
* invokes. Tools declare arrays of these; the dispatcher matches an incoming
|
|
659
|
+
* input event against them and runs the winner's action. */
|
|
660
|
+
interface GestureBinding {
|
|
661
|
+
spec: GestureSpec;
|
|
662
|
+
actionId: string;
|
|
663
|
+
opts?: BindingOpts;
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* A single entry in `Action.defaultBinding[]`. Either a bare `GestureSpec`
|
|
667
|
+
* (no per-binding opts) or an object form that pairs a spec with
|
|
668
|
+
* `BindingOpts` for parametric actions (e.g. `{ params: { axis: 'x' } }`).
|
|
669
|
+
* Use the object form when two bindings for the same action differ only in
|
|
670
|
+
* a runtime parameter — the dispatcher extracts `opts.params` and passes
|
|
671
|
+
* them to `ImmediateInvoker.run` as its second argument.
|
|
672
|
+
*/
|
|
673
|
+
type BoundGesture = GestureSpec | {
|
|
674
|
+
spec: GestureSpec;
|
|
675
|
+
opts: BindingOpts;
|
|
676
|
+
};
|
|
677
|
+
/**
|
|
678
|
+
* The whole of an action `actionBindings` reads. Routing needs this much and
|
|
679
|
+
* none of `Action`'s palette fields, so it is the narrowest type the
|
|
680
|
+
* dispatcher's binding walk can be written against.
|
|
681
|
+
*/
|
|
682
|
+
interface BindingSource {
|
|
683
|
+
id: string;
|
|
684
|
+
/** The gesture-spec form of the binding, read by the gesture dispatcher.
|
|
685
|
+
* May be a single `GestureSpec`, a bare `GestureSpec[]` (any-of semantics),
|
|
686
|
+
* or a `BoundGesture[]` where each entry is either a bare `GestureSpec` or
|
|
687
|
+
* `{ spec, opts }` — use the object form for parametric actions where two
|
|
688
|
+
* bindings for the same action differ only by `opts.params` (e.g. `flip`
|
|
689
|
+
* with `axis: 'x'` vs `'y'`). The dispatcher extracts `opts.params` and
|
|
690
|
+
* passes them to `ImmediateInvoker.run` as its second argument. */
|
|
691
|
+
defaultBinding?: GestureSpec | BoundGesture[];
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Flatten an action's `defaultBinding` into `GestureBinding`s. A bare
|
|
695
|
+
* `GestureSpec` has `kind` at top level; the object form has `spec`.
|
|
696
|
+
*/
|
|
697
|
+
declare function actionBindings(action: BindingSource): GestureBinding[];
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Re-exports the pure matcher primitives from `@weasel-js/gestures` and layers
|
|
701
|
+
* the actions-layer binding-scope / matchBest logic on top.
|
|
702
|
+
*/
|
|
703
|
+
|
|
704
|
+
/** Where a binding came from, which is also its priority: a held hotkey beats
|
|
705
|
+
* the active tool, which beats bindings that are always in scope. */
|
|
706
|
+
type BindingScope = 'ambient' | 'active' | 'hotkey';
|
|
707
|
+
/** A binding paired with where it came from, ready to be matched against an
|
|
708
|
+
* event. */
|
|
709
|
+
interface ScopedBinding {
|
|
710
|
+
binding: GestureBinding;
|
|
711
|
+
scope: BindingScope;
|
|
712
|
+
/** Id of the tool or contribution that declared this binding —
|
|
713
|
+
* `'&'`-channel phase atoms resolve to it. `null` for a registered
|
|
714
|
+
* action's own binding. */
|
|
715
|
+
ownerId: string | null;
|
|
716
|
+
}
|
|
717
|
+
/** The binding that won a match. */
|
|
718
|
+
interface MatchResult {
|
|
719
|
+
binding: GestureBinding;
|
|
720
|
+
scope: BindingScope;
|
|
721
|
+
/** Id of the entry that declared the binding — propagated from
|
|
722
|
+
* `ScopedBinding` so the dispatcher can record it as the handle owner. */
|
|
723
|
+
ownerId: string | null;
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* True when a spec's target actually consults the affordance hit rather than
|
|
727
|
+
* only the body classification. `kindOf` predicates are handed the hit;
|
|
728
|
+
* `affordance:<k>` matches on its `kind`. The body-class strings (`'empty'`,
|
|
729
|
+
* `'selected-body'`, `'unselected-body'`) and the `kind:` forms resolve from
|
|
730
|
+
* `bodyTarget` / `bodyKind` and never see it.
|
|
731
|
+
*
|
|
732
|
+
* A predicate declares which it is with `readsAffordance`; one that declares
|
|
733
|
+
* nothing, which only untyped code can write, counts as not reading the hit.
|
|
734
|
+
*/
|
|
735
|
+
declare function targetConsultsAffordance(specTarget: TargetSpec | undefined): boolean;
|
|
736
|
+
/** The first of {@link matchSorted}'s matches, or null when nothing matches. */
|
|
737
|
+
declare function matchBest(e: InputEvent, bindings: readonly ScopedBinding[], isMac: boolean, engagedChannels?: ReadonlySet<string>): MatchResult | null;
|
|
738
|
+
/**
|
|
739
|
+
* Find every binding that matches the event, sorted by precedence
|
|
740
|
+
* (best → worst). Ordering rules:
|
|
741
|
+
*
|
|
742
|
+
* - Scopes by priority: hotkey > active > ambient.
|
|
743
|
+
* - Within a scope, more-specific bindings beat less-specific (see
|
|
744
|
+
* `specificity()`).
|
|
745
|
+
* - Among same-specificity bindings, first-declared wins.
|
|
746
|
+
*/
|
|
747
|
+
declare function matchSorted(e: InputEvent, bindings: readonly ScopedBinding[], isMac: boolean, engagedChannels?: ReadonlySet<string>, warn?: (message: string) => void): MatchResult[];
|
|
748
|
+
/** {@link matchSorted}, plus what an exclusive claim on the event barred. */
|
|
749
|
+
interface MatchOutcome {
|
|
750
|
+
matches: MatchResult[];
|
|
751
|
+
/** Bindings whose spec matched the event but which the claim barred, in
|
|
752
|
+
* the same order `matches` uses. Filled only when `collectBarred` is set. */
|
|
753
|
+
barred: MatchResult[];
|
|
754
|
+
/** Owner of the exclusive claim, when one applied. */
|
|
755
|
+
claimOwner?: string;
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* {@link matchSorted} that can also report the bindings an exclusive claim
|
|
759
|
+
* kept out. Collecting them costs a spec match per barred binding, so it is
|
|
760
|
+
* opt-in — the dispatcher asks only when it is recording.
|
|
761
|
+
*/
|
|
762
|
+
declare function matchSortedWithBarred(e: InputEvent, bindings: readonly ScopedBinding[], isMac: boolean, engagedChannels?: ReadonlySet<string>, opts?: {
|
|
763
|
+
collectBarred?: boolean;
|
|
764
|
+
warn?: (message: string) => void;
|
|
765
|
+
}): MatchOutcome;
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* How one input was resolved: every binding that matched it, what each filter
|
|
769
|
+
* dropped, the order the rest were ranked in and why, and what the walk down
|
|
770
|
+
* that order did. The dispatcher fills one per input in dev builds, and
|
|
771
|
+
* `Dispatcher.explain` fills one on request without invoking anything.
|
|
772
|
+
*
|
|
773
|
+
* Plain data — no functions, no live objects — so it can be kept in a trace
|
|
774
|
+
* buffer, serialized, or built by hand as a fixture.
|
|
775
|
+
*/
|
|
776
|
+
interface DispatchRecord {
|
|
777
|
+
kind: 'dispatch';
|
|
778
|
+
ts: number;
|
|
779
|
+
input: DispatchRecordInput;
|
|
780
|
+
/** Every binding whose spec matched the input, in matcher order, including
|
|
781
|
+
* those a filter then dropped. */
|
|
782
|
+
matched: readonly RecordCandidate[];
|
|
783
|
+
dropped: readonly DroppedCandidate[];
|
|
784
|
+
/** The candidates that survived both filters, best first. */
|
|
785
|
+
ranked: readonly RankedCandidate[];
|
|
786
|
+
/** True when nothing was invoked: the record answers "what would happen". */
|
|
787
|
+
predicted: boolean;
|
|
788
|
+
/** Action id of the candidate that fired (or would), else `null`. */
|
|
789
|
+
fired: string | null;
|
|
790
|
+
outcome: 'handled' | 'unhandled';
|
|
791
|
+
}
|
|
792
|
+
/** The input a {@link DispatchRecord} describes, reduced to plain data. */
|
|
793
|
+
interface DispatchRecordInput {
|
|
794
|
+
/** `InputEvent.kind`: `'key'`, `'pointerdown'`, `'wheel'`, … */
|
|
795
|
+
eventKind: string;
|
|
796
|
+
/** For `key` / `key-held` events, the key id (`'Escape'`, `' '`, `'a'`). */
|
|
797
|
+
key?: string;
|
|
798
|
+
modifiers: {
|
|
799
|
+
alt: boolean;
|
|
800
|
+
ctrl: boolean;
|
|
801
|
+
meta: boolean;
|
|
802
|
+
shift: boolean;
|
|
803
|
+
};
|
|
804
|
+
/** The view the input was routed to; `null` for the surface's own camera. */
|
|
805
|
+
viewId: string | null;
|
|
806
|
+
/** World point, for events that carry one. */
|
|
807
|
+
world?: {
|
|
808
|
+
x: number;
|
|
809
|
+
y: number;
|
|
810
|
+
};
|
|
811
|
+
/** Active mode id, when the host supplies a rule context. */
|
|
812
|
+
mode?: string;
|
|
813
|
+
}
|
|
814
|
+
/** One matched binding. */
|
|
815
|
+
interface RecordCandidate {
|
|
816
|
+
actionId: string;
|
|
817
|
+
/** The binding's spec in route grammar — one per arg alternative. */
|
|
818
|
+
routes: readonly string[];
|
|
819
|
+
scope: BindingScope;
|
|
820
|
+
/** The tool or contribution that declared the binding; `null` for an action's own binding. */
|
|
821
|
+
ownerId: string | null;
|
|
822
|
+
/** Whether the binding's `views` names the routed view. */
|
|
823
|
+
namesView: boolean;
|
|
824
|
+
/** `specificity(spec)`: target, modifiers, phase, exact. */
|
|
825
|
+
specificity: readonly [number, number, number, number];
|
|
826
|
+
/** The action's `eligible` rule as text, when it declares one. */
|
|
827
|
+
eligible?: string;
|
|
828
|
+
}
|
|
829
|
+
/** A matched candidate a filter removed before ranking, and which filter. */
|
|
830
|
+
type DroppedCandidate =
|
|
831
|
+
/** An exclusive claim on the input barred bindings that do not read the
|
|
832
|
+
* claim's affordance. */
|
|
833
|
+
{
|
|
834
|
+
candidate: RecordCandidate;
|
|
835
|
+
filter: 'claim';
|
|
836
|
+
owner?: string;
|
|
837
|
+
}
|
|
838
|
+
/** The action's `eligible` rule does not hold. */
|
|
839
|
+
| {
|
|
840
|
+
candidate: RecordCandidate;
|
|
841
|
+
filter: 'ineligible';
|
|
842
|
+
rule: string;
|
|
843
|
+
};
|
|
844
|
+
/** One field of the `specificity` tuple, in the order it is compared. */
|
|
845
|
+
type SpecificityPart = 'target' | 'mods' | 'phase' | 'exact';
|
|
846
|
+
/** The ranking step that put a candidate below the one before it. */
|
|
847
|
+
type PlacedBy = {
|
|
848
|
+
step: 'first';
|
|
849
|
+
}
|
|
850
|
+
/** The one above names the routed view; this one does not. */
|
|
851
|
+
| {
|
|
852
|
+
step: 'view';
|
|
853
|
+
}
|
|
854
|
+
/** The one above sits in a higher scope tier. */
|
|
855
|
+
| {
|
|
856
|
+
step: 'tier';
|
|
857
|
+
}
|
|
858
|
+
/** Same tier; the one above is more specific in `part`. */
|
|
859
|
+
| {
|
|
860
|
+
step: 'specificity';
|
|
861
|
+
part: SpecificityPart;
|
|
862
|
+
}
|
|
863
|
+
/** Tied; the one above is gated by an `eligible` rule that holds. */
|
|
864
|
+
| {
|
|
865
|
+
step: 'context';
|
|
866
|
+
}
|
|
867
|
+
/** Tied on everything; registration order. */
|
|
868
|
+
| {
|
|
869
|
+
step: 'order';
|
|
870
|
+
};
|
|
871
|
+
/** What the walk did with one ranked candidate. */
|
|
872
|
+
type WalkStep = {
|
|
873
|
+
kind: 'fired';
|
|
874
|
+
}
|
|
875
|
+
/** Predicted records only: this candidate would fire. Nothing was started,
|
|
876
|
+
* so an ongoing action here may still bail at `start()` for real. */
|
|
877
|
+
| {
|
|
878
|
+
kind: 'would-fire';
|
|
879
|
+
}
|
|
880
|
+
/** `enabled()` returned this reason. */
|
|
881
|
+
| {
|
|
882
|
+
kind: 'declined';
|
|
883
|
+
reason: string;
|
|
884
|
+
}
|
|
885
|
+
/** An ongoing action's `start()` returned an empty handle. */
|
|
886
|
+
| {
|
|
887
|
+
kind: 'empty-handle';
|
|
888
|
+
}
|
|
889
|
+
/** An ongoing action bound to a press-time `pointerDown` spec; refused. */
|
|
890
|
+
| {
|
|
891
|
+
kind: 'misbound';
|
|
892
|
+
}
|
|
893
|
+
/** Its action was already tried higher up; each runs at most once. */
|
|
894
|
+
| {
|
|
895
|
+
kind: 'duplicate';
|
|
896
|
+
} | {
|
|
897
|
+
kind: 'no-such-action';
|
|
898
|
+
}
|
|
899
|
+
/** Below the winner; never asked. */
|
|
900
|
+
| {
|
|
901
|
+
kind: 'not-asked';
|
|
902
|
+
}
|
|
903
|
+
/** Below the winner, asked anyway (`evaluateShadowed`), and it passed. */
|
|
904
|
+
| {
|
|
905
|
+
kind: 'outranked';
|
|
906
|
+
};
|
|
907
|
+
/** A candidate that survived the filters: why it sits where it does, and
|
|
908
|
+
* what the walk did with it. */
|
|
909
|
+
interface RankedCandidate {
|
|
910
|
+
candidate: RecordCandidate;
|
|
911
|
+
placedBy: PlacedBy;
|
|
912
|
+
walk: WalkStep;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
/** One entry per "mode change" — a kit state transition that isn't a
|
|
916
|
+
* dispatched input event but explains why an input was (or wasn't) handled
|
|
917
|
+
* later. Pushed via {@link DispatchTrace.recordModeSwitch}. */
|
|
918
|
+
interface ModeSwitchLogEntry {
|
|
919
|
+
kind: 'mode';
|
|
920
|
+
ts: number;
|
|
921
|
+
/** Logical mode name, e.g. `'editAnchors.editingId'`. */
|
|
922
|
+
mode: string;
|
|
923
|
+
from: string | null;
|
|
924
|
+
to: string | null;
|
|
925
|
+
/** Optional free-form context — `'enterPathEdit'`, `'exitPathEdit'`,
|
|
926
|
+
* `'selection-dropped target'`, etc. */
|
|
927
|
+
detail?: string;
|
|
928
|
+
}
|
|
929
|
+
/** A trace entry: one {@link DispatchRecord} per resolved input, or a mode switch. */
|
|
930
|
+
type TraceLogEntry = DispatchRecord | ModeSwitchLogEntry;
|
|
931
|
+
/** A rolling buffer that records in a dev build, and in any build while something watches it. */
|
|
932
|
+
interface TraceRing<T> {
|
|
933
|
+
/** Whether a push now keeps its entry. A caller building an entry checks first. */
|
|
934
|
+
recording(): boolean;
|
|
935
|
+
push(entry: T): void;
|
|
936
|
+
/** The entries pushed after `cursor`, oldest first, and the cursor to pass
|
|
937
|
+
* next time. Start from 0. Entries the buffer has dropped are skipped. */
|
|
938
|
+
since(cursor: number): {
|
|
939
|
+
entries: T[];
|
|
940
|
+
cursor: number;
|
|
941
|
+
};
|
|
942
|
+
/** Turn recording on until the returned release is called. */
|
|
943
|
+
watch(): () => void;
|
|
944
|
+
/** Hear each kept push. Returns the unsubscribe. */
|
|
945
|
+
subscribe(listener: () => void): () => void;
|
|
946
|
+
}
|
|
947
|
+
/** A {@link TraceRing} keeping the last `limit` entries. `dev` defaults to the build's. */
|
|
948
|
+
declare function createTraceRing<T>(limit?: number, dev?: boolean): TraceRing<T>;
|
|
949
|
+
/** One canvas's dispatch trace. `useGestureDispatcher` makes one, puts it on
|
|
950
|
+
* every `DispatcherContext` it builds, and publishes it as the `dispatchTrace` dep. */
|
|
951
|
+
interface DispatchTrace extends TraceRing<TraceLogEntry> {
|
|
952
|
+
recordModeSwitch(mode: string, from: string | null, to: string | null, detail?: string): void;
|
|
953
|
+
/** Offer the record for what a press at the pointer would do, built only
|
|
954
|
+
* when {@link DispatchTrace.live} reads it. `null` withdraws it. */
|
|
955
|
+
setLive(explain: (() => DispatchRecord) | null): void;
|
|
956
|
+
live(): DispatchRecord | null;
|
|
957
|
+
}
|
|
958
|
+
/** A {@link DispatchTrace} keeping the last `limit` entries. `dev` defaults to the build's. */
|
|
959
|
+
declare function createDispatchTrace(limit?: number, dev?: boolean): DispatchTrace;
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* Live state read by rule evaluation. Built once per frame on the consuming
|
|
963
|
+
* surface — chrome-caps, the affordance pipeline, the dispatcher's
|
|
964
|
+
* eligibility filter — and discarded.
|
|
965
|
+
*
|
|
966
|
+
* Adding a new field is additive: existing rules don't change, new
|
|
967
|
+
* selector atoms can read it.
|
|
968
|
+
*/
|
|
969
|
+
interface RuleCtx {
|
|
970
|
+
readonly focused: boolean;
|
|
971
|
+
readonly selection: readonly NodeId[];
|
|
972
|
+
readonly multiActive: boolean;
|
|
973
|
+
readonly modifiers: ModifierState;
|
|
974
|
+
readonly action: {
|
|
975
|
+
readonly kind: string | null;
|
|
976
|
+
readonly id: string | null;
|
|
977
|
+
};
|
|
978
|
+
readonly hover: NodeId | null;
|
|
979
|
+
/**
|
|
980
|
+
* How far the viewport is zoomed in, as one number — `1` at native scale.
|
|
299
981
|
*
|
|
300
982
|
* A scalar rather than the `View` this used to carry, because `zoomAtLeast`
|
|
301
983
|
* is the only selector that ever read one and a number is all it needs. A
|
|
@@ -354,612 +1036,219 @@ interface BuildRuleCtxArgs {
|
|
|
354
1036
|
zoom?: number;
|
|
355
1037
|
mode: string;
|
|
356
1038
|
allowedCapabilities: ReadonlySet<CapabilityTag>;
|
|
357
|
-
/** Optional — omitted means "resizable" (handles show). See {@link RuleCtx}. */
|
|
358
|
-
selectionResizable?: boolean;
|
|
359
|
-
/** Optional — omitted means "no path is being anchor-edited". */
|
|
360
|
-
editingAnchors?: boolean;
|
|
361
|
-
/** Optional — omitted means {@link DEFAULT_DEVICE_PROFILE}. */
|
|
362
|
-
device?: DeviceProfile;
|
|
363
|
-
}
|
|
364
|
-
/**
|
|
365
|
-
* Capability set to assume when no mode registry is wired.
|
|
366
|
-
*
|
|
367
|
-
* "No modes configured" means *everything the default mode permits*, not
|
|
368
|
-
* *nothing permitted*. The empty set is the wrong default: it makes every
|
|
369
|
-
* `capability:` rule evaluate false, so a kit default written the
|
|
370
|
-
* recommended way (capability rules outlive new modes; mode rules have to
|
|
371
|
-
* be edited) would silently hide its chrome for every consumer that hasn't
|
|
372
|
-
* opted into modality. `NORMAL.allows` plus the implicit tags is what a
|
|
373
|
-
* mode-less canvas actually behaves like.
|
|
374
|
-
*/
|
|
375
|
-
declare const DEFAULT_ALLOWED_CAPABILITIES: ReadonlySet<CapabilityTag>;
|
|
376
|
-
/** Gather the current canvas and mode state into the context that action
|
|
377
|
-
* eligibility and chrome-visibility rules are evaluated against. */
|
|
378
|
-
declare function buildRuleCtx(args: BuildRuleCtxArgs): RuleCtx;
|
|
379
|
-
|
|
380
|
-
/**
|
|
381
|
-
* A selector is a conjunction of key/value tests. Multiple keys at the same
|
|
382
|
-
* level AND together. Each key maps to a selector primitive in the evaluator.
|
|
383
|
-
*/
|
|
384
|
-
interface Selector {
|
|
385
|
-
selection?: {
|
|
386
|
-
is?: number;
|
|
387
|
-
atLeast?: number;
|
|
388
|
-
empty?: boolean;
|
|
389
|
-
};
|
|
390
|
-
mode?: string | {
|
|
391
|
-
not: string;
|
|
392
|
-
} | {
|
|
393
|
-
in: readonly string[];
|
|
394
|
-
};
|
|
395
|
-
capability?: CapabilityTag | readonly CapabilityTag[] | {
|
|
396
|
-
in: readonly CapabilityTag[];
|
|
397
|
-
} | {
|
|
398
|
-
not: CapabilityTag;
|
|
399
|
-
};
|
|
400
|
-
gesturing?: boolean;
|
|
401
|
-
actionIs?: string;
|
|
402
|
-
modifierHeld?: keyof ModifierState;
|
|
403
|
-
focused?: boolean;
|
|
404
|
-
hovering?: boolean;
|
|
405
|
-
hoveringSelected?: boolean;
|
|
406
|
-
zoomAtLeast?: number;
|
|
407
|
-
/** Matches `ctx.editingAnchors` — true while a path is in anchor-edit
|
|
408
|
-
* mode. Absent flag is treated as `false`. */
|
|
409
|
-
editingAnchors?: boolean;
|
|
410
|
-
/** Matches `ctx.selectionResizable`. Absent flag is treated as `true`
|
|
411
|
-
* (resizable), so `{ resizable: true }` passes for legacy ctx builders
|
|
412
|
-
* that don't compute it. */
|
|
413
|
-
resizable?: boolean;
|
|
414
|
-
/** Matches `ctx.device.coarsePointer` — the primary pointer is imprecise
|
|
415
|
-
* (touch, most styluses). Absent device is treated as `false`. */
|
|
416
|
-
coarsePointer?: boolean;
|
|
417
|
-
/** Matches `ctx.device.canHover` — the primary pointer can hover. Absent
|
|
418
|
-
* device is treated as `true`. */
|
|
419
|
-
canHover?: boolean;
|
|
420
|
-
}
|
|
421
|
-
/**
|
|
422
|
-
* Composable visibility/eligibility rule. Trees of `all`/`any`/`not` nodes
|
|
423
|
-
* over `Selector` leaves. `when` is the escape hatch — its closure is
|
|
424
|
-
* opaque to introspection and should be avoided when a declarative form
|
|
425
|
-
* exists. Empty `all` is true; empty `any` is false.
|
|
426
|
-
*/
|
|
427
|
-
type Rule = Selector | {
|
|
428
|
-
all: readonly Rule[];
|
|
429
|
-
} | {
|
|
430
|
-
any: readonly Rule[];
|
|
431
|
-
} | {
|
|
432
|
-
not: Rule;
|
|
433
|
-
} | {
|
|
434
|
-
when: (ctx: RuleCtx) => boolean;
|
|
435
|
-
};
|
|
436
|
-
/** Constant rules. Kept here so they have a single source. */
|
|
437
|
-
declare const ALWAYS: Rule;
|
|
438
|
-
/** A rule that never passes. `any` of nothing is false. */
|
|
439
|
-
declare const NEVER: Rule;
|
|
440
|
-
/**
|
|
441
|
-
* Render a {@link Rule} as a short human-readable string, for inspectors and
|
|
442
|
-
* diagnostics that need to say WHICH rule decided something.
|
|
443
|
-
*
|
|
444
|
-
* ```
|
|
445
|
-
* { capability: 'edits-page' } → capability:edits-page
|
|
446
|
-
* { all: [{ mode: 'normal' }, { focused: true }] } → all(mode:normal, focused:true)
|
|
447
|
-
* { not: { selection: { empty: true } } } → not(selection:empty=true)
|
|
448
|
-
* { when: function hasPath() { … } } → when(hasPath)
|
|
449
|
-
* ```
|
|
450
|
-
*
|
|
451
|
-
* Never throws and always terminates.
|
|
452
|
-
*/
|
|
453
|
-
declare function describeRule(rule: Rule): string;
|
|
454
|
-
/** Evaluate a rule against a context. Pure, and cheap enough to run per
|
|
455
|
-
* frame for every piece of chrome. */
|
|
456
|
-
declare function evaluate(rule: Rule, ctx: RuleCtx): boolean;
|
|
457
|
-
/**
|
|
458
|
-
* Composable eligibility predicate with a fluent surface. `cond(ctx)`
|
|
459
|
-
* evaluates its `Rule` tree, carried at `.rule`; the fluent methods return new
|
|
460
|
-
* Conditions wrapping new trees.
|
|
461
|
-
*
|
|
462
|
-
* **Chain semantics: strict left-to-right, no precedence.** `a.and(b).or(c)` is
|
|
463
|
-
* `(a && b) || c`; `a.or(b).and(c)` is `(a || b) && c`. For grouped
|
|
464
|
-
* disjunction, name the subexpression or use the top-level `or(...)`.
|
|
465
|
-
*/
|
|
466
|
-
interface Condition {
|
|
467
|
-
(ctx: RuleCtx): boolean;
|
|
468
|
-
readonly rule: Rule;
|
|
469
|
-
/** `this && other` */
|
|
470
|
-
and(other: Condition | Rule): Condition;
|
|
471
|
-
/** `this || other` */
|
|
472
|
-
or(other: Condition | Rule): Condition;
|
|
473
|
-
/** `this && !other` */
|
|
474
|
-
andNot(other: Condition | Rule): Condition;
|
|
475
|
-
/** `this || !other` */
|
|
476
|
-
orNot(other: Condition | Rule): Condition;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
/**
|
|
480
|
-
* Whether two rules can never hold at once — no `RuleCtx` passes both.
|
|
481
|
-
*
|
|
482
|
-
* Conservative: `true` is a proof, `false` means "may overlap". It reasons
|
|
483
|
-
* about the selector values themselves (two different `mode`s, an empty
|
|
484
|
-
* selection against a non-empty one, `focused: true` against `false`) and
|
|
485
|
-
* through `all` / `any` / `not`; a `when` closure is opaque, so it overlaps
|
|
486
|
-
* everything. The route-conflict check uses it to stay quiet about bindings
|
|
487
|
-
* that share a tuple but are never eligible together.
|
|
488
|
-
*/
|
|
489
|
-
declare function rulesExclusive(a: Rule, b: Rule): boolean;
|
|
490
|
-
/**
|
|
491
|
-
* Whether `rule` can hold while `mode` is active. Only its `mode` and
|
|
492
|
-
* `capability` tests are settled by the mode; every other test could go either
|
|
493
|
-
* way, so `false` is a proof and `true` means "may hold".
|
|
494
|
-
*/
|
|
495
|
-
declare function ruleCanHoldIn(rule: Rule, mode: ActiveMode): boolean;
|
|
496
|
-
|
|
497
|
-
/** The detected half of a profile — everything except the derived scale. */
|
|
498
|
-
type DetectedDeviceFacts = Omit<DeviceProfile, 'targetScale'>;
|
|
499
|
-
/**
|
|
500
|
-
* Handle/hit multiplier applied on a coarse pointer.
|
|
501
|
-
*
|
|
502
|
-
* 8px handle → 14px; 24px rotation distance → 42px. Counting the
|
|
503
|
-
* surrounding grab zone, that lands in the Apple HIG 44pt / Material 48dp
|
|
504
|
-
* minimum-touch-target band. One constant so it is tunable in one place.
|
|
505
|
-
*/
|
|
506
|
-
declare const COARSE_TARGET_SCALE = 1.75;
|
|
507
|
-
/**
|
|
508
|
-
* Fold consumer overrides over detected facts and derive `targetScale`.
|
|
509
|
-
*
|
|
510
|
-
* `targetScale` is re-derived AFTER the merge, so an override of
|
|
511
|
-
* `coarsePointer` alone scales the chrome as expected. An explicit
|
|
512
|
-
* `targetScale` override wins over the derivation.
|
|
513
|
-
*/
|
|
514
|
-
declare function resolveDeviceProfile(detected: DetectedDeviceFacts, overrides?: Partial<DeviceProfile>): DeviceProfile;
|
|
515
|
-
/**
|
|
516
|
-
* Assumed when `matchMedia` is unavailable (SSR, jsdom) and used as the
|
|
517
|
-
* absent-means value for `RuleCtx.device`.
|
|
518
|
-
*
|
|
519
|
-
* Derived from `resolveDeviceProfile` rather than hand-written, so it can
|
|
520
|
-
* never drift from what the function would compute for the same facts. A
|
|
521
|
-
* mouse-like device is the safe default: it is what the kit has always
|
|
522
|
-
* assumed, so an absent profile changes nothing for existing consumers.
|
|
523
|
-
*/
|
|
524
|
-
declare const DEFAULT_DEVICE_PROFILE: DeviceProfile;
|
|
525
|
-
|
|
526
|
-
/**
|
|
527
|
-
* Invoker — pluggable invocation strategy for an Action.
|
|
528
|
-
*
|
|
529
|
-
* An Action's `invoker` field describes how it's run. `immediate` invokers
|
|
530
|
-
* fire once (delete, align, undo). `ongoing` invokers open a phase machine
|
|
531
|
-
* driven by the dispatcher (move, resize, pinch-zoom).
|
|
532
|
-
*/
|
|
533
|
-
|
|
534
|
-
/** A 2D point in either world or screen coordinates. */
|
|
535
|
-
interface Point2 {
|
|
536
|
-
x: number;
|
|
537
|
-
y: number;
|
|
1039
|
+
/** Optional — omitted means "resizable" (handles show). See {@link RuleCtx}. */
|
|
1040
|
+
selectionResizable?: boolean;
|
|
1041
|
+
/** Optional — omitted means "no path is being anchor-edited". */
|
|
1042
|
+
editingAnchors?: boolean;
|
|
1043
|
+
/** Optional — omitted means {@link DEFAULT_DEVICE_PROFILE}. */
|
|
1044
|
+
device?: DeviceProfile;
|
|
538
1045
|
}
|
|
539
1046
|
/**
|
|
540
|
-
*
|
|
541
|
-
* the `affordanceAt` thunk is provided to `useGestureDispatcher`.
|
|
1047
|
+
* Capability set to assume when no mode registry is wired.
|
|
542
1048
|
*
|
|
543
|
-
*
|
|
544
|
-
*
|
|
545
|
-
*
|
|
546
|
-
*
|
|
547
|
-
*
|
|
1049
|
+
* "No modes configured" means *everything the default mode permits*, not
|
|
1050
|
+
* *nothing permitted*. The empty set is the wrong default: it makes every
|
|
1051
|
+
* `capability:` rule evaluate false, so a kit default written the
|
|
1052
|
+
* recommended way (capability rules outlive new modes; mode rules have to
|
|
1053
|
+
* be edited) would silently hide its chrome for every consumer that hasn't
|
|
1054
|
+
* opted into modality. `NORMAL.allows` plus the implicit tags is what a
|
|
1055
|
+
* mode-less canvas actually behaves like.
|
|
548
1056
|
*/
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
owner?: string;
|
|
555
|
-
/** `'exclusive'` means no binding may act on this point unless its target
|
|
556
|
-
* consults the affordance. `'shared'` (the default) competes on scope and
|
|
557
|
-
* specificity as bindings always have. */
|
|
558
|
-
strength?: 'exclusive' | 'shared';
|
|
559
|
-
/** Which gestures an exclusive claim bars. Omitted bars all of them. */
|
|
560
|
-
claimedKinds?: readonly ClaimableGesture[];
|
|
561
|
-
/** World-space fixed/pivot point. For resize: opposite corner. For rotate: pivot. */
|
|
562
|
-
fixedPoint?: {
|
|
563
|
-
x: number;
|
|
564
|
-
y: number;
|
|
565
|
-
};
|
|
566
|
-
/** Which nodes this affordance belongs to. */
|
|
567
|
-
targetIds?: string[];
|
|
568
|
-
/** Set only when `kind` matches `'handle:*'`: which corner stays fixed
|
|
569
|
-
* during a resize. */
|
|
570
|
-
anchor?: ResizeAnchor;
|
|
571
|
-
/** Cursor to show while the pointer hovers this affordance (no gesture in
|
|
572
|
-
* flight). Unset falls through to action-cursor prediction, then to the
|
|
573
|
-
* active tool's cursor. */
|
|
574
|
-
cursor?: CursorSpec;
|
|
575
|
-
/**
|
|
576
|
-
* Free-form payload from whatever produced the hit, carried through to the
|
|
577
|
-
* matching action untouched. Kit affordances leave it unset; it exists for
|
|
578
|
-
* a registered layer's own chrome, where the hit-test already resolved
|
|
579
|
-
* *which* of its pieces was hit.
|
|
580
|
-
*/
|
|
581
|
-
payload?: unknown;
|
|
582
|
-
}
|
|
1057
|
+
declare const DEFAULT_ALLOWED_CAPABILITIES: ReadonlySet<CapabilityTag>;
|
|
1058
|
+
/** Gather the current canvas and mode state into the context that action
|
|
1059
|
+
* eligibility and chrome-visibility rules are evaluated against. */
|
|
1060
|
+
declare function buildRuleCtx(args: BuildRuleCtxArgs): RuleCtx;
|
|
1061
|
+
|
|
583
1062
|
/**
|
|
584
|
-
*
|
|
585
|
-
*
|
|
586
|
-
*
|
|
587
|
-
* The stylus fields are absent for mouse/touch on browsers that don't report
|
|
588
|
-
* them, and for synthetic events.
|
|
1063
|
+
* A selector is a conjunction of key/value tests. Multiple keys at the same
|
|
1064
|
+
* level AND together. Each key maps to a selector primitive in the evaluator.
|
|
589
1065
|
*/
|
|
590
|
-
interface
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
/** Degrees, ±90. Zero for mouse/touch. */
|
|
596
|
-
tiltY?: number;
|
|
597
|
-
}
|
|
598
|
-
/** Per-invocation runtime context the dispatcher hands to an Invoker.
|
|
599
|
-
* Gesture-kind-specific fields (`drag`, `wheel`, `multiTouch`, `key`) are
|
|
600
|
-
* populated only for matching gesture kinds. */
|
|
601
|
-
interface InvocationCtx {
|
|
602
|
-
world: Point2;
|
|
603
|
-
/**
|
|
604
|
-
* The pointer in client/screen pixels, for an action that has to place DOM
|
|
605
|
-
* at the pointer. Absent on every invocation whose event carries no client
|
|
606
|
-
* coordinates — a keystroke, a UI-driven trigger, a synthetic probe — so
|
|
607
|
-
* read it with a fallback rather than assuming a point.
|
|
608
|
-
*
|
|
609
|
-
* A view-mutating drag wants `drag.screenDelta`, not the difference of two
|
|
610
|
-
* of these: world deltas are self-referential mid-pan, and so is any delta
|
|
611
|
-
* taken across a view change.
|
|
612
|
-
*/
|
|
613
|
-
screen?: Point2;
|
|
614
|
-
modifiers: ModifierState;
|
|
615
|
-
deps: ActionDeps;
|
|
616
|
-
drag?: {
|
|
617
|
-
start: Point2;
|
|
618
|
-
current: Point2;
|
|
619
|
-
delta: Point2;
|
|
620
|
-
/**
|
|
621
|
-
* Drag delta in client/screen coordinates (CSS pixels from the drag
|
|
622
|
-
* origin). Use this — never `delta` — for any action whose effect
|
|
623
|
-
* mutates the viewport itself (pan, view-zoom), because world-space
|
|
624
|
-
* deltas become self-referential as the view shifts mid-drag.
|
|
625
|
-
*
|
|
626
|
-
* Absent when the underlying pointer events carried no
|
|
627
|
-
* `clientX`/`clientY`.
|
|
628
|
-
*/
|
|
629
|
-
screenDelta?: Point2;
|
|
630
|
-
affordance?: AffordanceHit;
|
|
631
|
-
/**
|
|
632
|
-
* Full pointermove history for the current drag, in world space, with
|
|
633
|
-
* per-sample stylus state when the browser reported it.
|
|
634
|
-
* Available only during `onMove` and `onEnd` calls (not on `start`).
|
|
635
|
-
*
|
|
636
|
-
* During `onMove` this is the dispatcher's live accumulator, and it grows
|
|
637
|
-
* under you — read it, don't retain it. On `onEnd` it is a snapshot, so
|
|
638
|
-
* that is where an action that keeps the trail should take it.
|
|
639
|
-
*/
|
|
640
|
-
points?: DragSample[];
|
|
641
|
-
};
|
|
642
|
-
wheel?: {
|
|
643
|
-
deltaX: number;
|
|
644
|
-
deltaY: number;
|
|
645
|
-
deltaZ: number;
|
|
646
|
-
};
|
|
647
|
-
/** One `pinch` sample: `scale` is a multiplier since the previous sample,
|
|
648
|
-
* `rotation` degrees since it. See `PinchEvent`. */
|
|
649
|
-
pinch?: {
|
|
650
|
-
scale: number;
|
|
651
|
-
rotation: number;
|
|
1066
|
+
interface Selector {
|
|
1067
|
+
selection?: {
|
|
1068
|
+
is?: number;
|
|
1069
|
+
atLeast?: number;
|
|
1070
|
+
empty?: boolean;
|
|
652
1071
|
};
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
centroid: Point2;
|
|
658
|
-
/** Distance between the two primary pointers, in CSS pixels. */
|
|
659
|
-
spread: number;
|
|
660
|
-
/** Degrees the line between the two primary pointers has turned since
|
|
661
|
-
* the gesture began, clockwise positive. It keeps counting past a half
|
|
662
|
-
* turn. Zero where the events carry no `MultiTouchEvent.angle`. */
|
|
663
|
-
rotation: number;
|
|
664
|
-
/**
|
|
665
|
-
* Pinch-zoom geometry, populated while a multitouch handle is in flight.
|
|
666
|
-
* `startSpread` is the spread at the moment the gesture began.
|
|
667
|
-
*/
|
|
668
|
-
pinch?: {
|
|
669
|
-
startSpread: number;
|
|
670
|
-
currentSpread: number;
|
|
671
|
-
/** Canvas-local, same as the enclosing `centroid`. */
|
|
672
|
-
centroid: Point2;
|
|
673
|
-
};
|
|
1072
|
+
mode?: string | {
|
|
1073
|
+
not: string;
|
|
1074
|
+
} | {
|
|
1075
|
+
in: readonly string[];
|
|
674
1076
|
};
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
1077
|
+
capability?: CapabilityTag | readonly CapabilityTag[] | {
|
|
1078
|
+
in: readonly CapabilityTag[];
|
|
1079
|
+
} | {
|
|
1080
|
+
not: CapabilityTag;
|
|
678
1081
|
};
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
*
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
/**
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
/**
|
|
697
|
-
*
|
|
698
|
-
|
|
699
|
-
behaviors?: unknown[];
|
|
700
|
-
/** Per-binding action parameters, which an immediate invoker reads via the
|
|
701
|
-
* second arg to `run`.
|
|
702
|
-
*
|
|
703
|
-
* May also be a thunk evaluated each time the value is needed, so a tool
|
|
704
|
-
* can close over refs that mutate during a gesture (e.g. polygon `sides`
|
|
705
|
-
* adjusted mid-drag via ArrowUp). An ongoing invoker that wants the latest
|
|
706
|
-
* values at commit re-calls it inside `onEnd` via
|
|
707
|
-
* `resolveParams(opts?.params)`. */
|
|
708
|
-
params?: Record<string, unknown> | (() => Record<string, unknown>);
|
|
709
|
-
/**
|
|
710
|
-
* The views this binding is live in, by id, where `null` is the surface's
|
|
711
|
-
* own camera. Omitted means every view.
|
|
712
|
-
*
|
|
713
|
-
* A binding that names the view an input landed in outranks every binding
|
|
714
|
-
* that does not, whatever their scope tiers: naming a view is the most
|
|
715
|
-
* specific thing a binding can say about where it applies. Without that, a
|
|
716
|
-
* drag bound inside a minimap view loses to the active tool's marquee.
|
|
717
|
-
*/
|
|
718
|
-
views?: readonly (string | null)[];
|
|
719
|
-
/**
|
|
720
|
-
* Says this binding is declared ahead of another on the same input on
|
|
721
|
-
* purpose: its action declines through `enabled()` when the input is not its
|
|
722
|
-
* to take, and the next binding fires. The dispatcher does that for any
|
|
723
|
-
* binding; this tells the route-conflict check the order is the design.
|
|
724
|
-
*/
|
|
725
|
-
yields?: boolean;
|
|
726
|
-
}
|
|
727
|
-
/** Resolve `BindingOpts.params` to a concrete record (calling the thunk if
|
|
728
|
-
* needed). Returns `undefined` for absent params. */
|
|
729
|
-
declare function resolveParams(params: BindingOpts['params']): Record<string, unknown> | undefined;
|
|
730
|
-
/** The deps an action receives, composed per call from what it `requires`.
|
|
731
|
-
* Each name resolves to its {@link DepSchema} entry, and is absent when no
|
|
732
|
-
* source registered it; a name nothing has declared passes through as
|
|
733
|
-
* `unknown`. */
|
|
734
|
-
interface ActionDeps extends Partial<DepSchema> {
|
|
735
|
-
[k: string]: unknown;
|
|
1082
|
+
gesturing?: boolean;
|
|
1083
|
+
actionIs?: string;
|
|
1084
|
+
modifierHeld?: keyof ModifierState;
|
|
1085
|
+
focused?: boolean;
|
|
1086
|
+
hovering?: boolean;
|
|
1087
|
+
hoveringSelected?: boolean;
|
|
1088
|
+
zoomAtLeast?: number;
|
|
1089
|
+
/** Matches `ctx.editingAnchors` — true while a path is in anchor-edit
|
|
1090
|
+
* mode. Absent flag is treated as `false`. */
|
|
1091
|
+
editingAnchors?: boolean;
|
|
1092
|
+
/** Matches `ctx.selectionResizable`. Absent flag is treated as `true`
|
|
1093
|
+
* (resizable), so `{ resizable: true }` passes for legacy ctx builders
|
|
1094
|
+
* that don't compute it. */
|
|
1095
|
+
resizable?: boolean;
|
|
1096
|
+
/** Matches `ctx.device.coarsePointer` — the primary pointer is imprecise
|
|
1097
|
+
* (touch, most styluses). Absent device is treated as `false`. */
|
|
1098
|
+
coarsePointer?: boolean;
|
|
1099
|
+
/** Matches `ctx.device.canHover` — the primary pointer can hover. Absent
|
|
1100
|
+
* device is treated as `true`. */
|
|
1101
|
+
canHover?: boolean;
|
|
736
1102
|
}
|
|
737
1103
|
/**
|
|
738
|
-
*
|
|
739
|
-
*
|
|
1104
|
+
* Composable visibility/eligibility rule. Trees of `all`/`any`/`not` nodes
|
|
1105
|
+
* over `Selector` leaves. `when` is the escape hatch — its closure is
|
|
1106
|
+
* opaque to introspection and should be avoided when a declarative form
|
|
1107
|
+
* exists. Empty `all` is true; empty `any` is false.
|
|
1108
|
+
*/
|
|
1109
|
+
type Rule = Selector | {
|
|
1110
|
+
all: readonly Rule[];
|
|
1111
|
+
} | {
|
|
1112
|
+
any: readonly Rule[];
|
|
1113
|
+
} | {
|
|
1114
|
+
not: Rule;
|
|
1115
|
+
} | {
|
|
1116
|
+
when: (ctx: RuleCtx) => boolean;
|
|
1117
|
+
};
|
|
1118
|
+
/** Constant rules. Kept here so they have a single source. */
|
|
1119
|
+
declare const ALWAYS: Rule;
|
|
1120
|
+
/** A rule that never passes. `any` of nothing is false. */
|
|
1121
|
+
declare const NEVER: Rule;
|
|
1122
|
+
/**
|
|
1123
|
+
* Render a {@link Rule} as a short human-readable string, for inspectors and
|
|
1124
|
+
* diagnostics that need to say WHICH rule decided something.
|
|
740
1125
|
*
|
|
741
|
-
*
|
|
742
|
-
*
|
|
743
|
-
*
|
|
1126
|
+
* ```
|
|
1127
|
+
* { capability: 'edits-page' } → capability:edits-page
|
|
1128
|
+
* { all: [{ mode: 'normal' }, { focused: true }] } → all(mode:normal, focused:true)
|
|
1129
|
+
* { not: { selection: { empty: true } } } → not(selection:empty=true)
|
|
1130
|
+
* { when: function hasPath() { … } } → when(hasPath)
|
|
1131
|
+
* ```
|
|
1132
|
+
*
|
|
1133
|
+
* Never throws and always terminates.
|
|
744
1134
|
*/
|
|
745
|
-
|
|
746
|
-
/**
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
| 'cut'
|
|
750
|
-
/** An edge the gesture would author between two points. */
|
|
751
|
-
| 'connector' | (string & {});
|
|
1135
|
+
declare function describeRule(rule: Rule): string;
|
|
1136
|
+
/** Evaluate a rule against a context. Pure, and cheap enough to run per
|
|
1137
|
+
* frame for every piece of chrome. */
|
|
1138
|
+
declare function evaluate(rule: Rule, ctx: RuleCtx): boolean;
|
|
752
1139
|
/**
|
|
753
|
-
*
|
|
754
|
-
* `
|
|
755
|
-
*
|
|
1140
|
+
* Composable eligibility predicate with a fluent surface. `cond(ctx)`
|
|
1141
|
+
* evaluates its `Rule` tree, carried at `.rule`; the fluent methods return new
|
|
1142
|
+
* Conditions wrapping new trees.
|
|
756
1143
|
*
|
|
757
|
-
*
|
|
758
|
-
*
|
|
759
|
-
*
|
|
760
|
-
* decision.
|
|
1144
|
+
* **Chain semantics: strict left-to-right, no precedence.** `a.and(b).or(c)` is
|
|
1145
|
+
* `(a && b) || c`; `a.or(b).and(c)` is `(a || b) && c`. For grouped
|
|
1146
|
+
* disjunction, name the subexpression or use the top-level `or(...)`.
|
|
761
1147
|
*/
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
} | {
|
|
774
|
-
kind: 'lasso';
|
|
775
|
-
vertices: ReadonlyArray<{
|
|
776
|
-
x: number;
|
|
777
|
-
y: number;
|
|
778
|
-
}>;
|
|
779
|
-
current: {
|
|
780
|
-
x: number;
|
|
781
|
-
y: number;
|
|
782
|
-
};
|
|
783
|
-
shiftHeld: boolean;
|
|
784
|
-
} | {
|
|
785
|
-
/**
|
|
786
|
-
* An open run of world points the gesture is proposing — a cut line, a
|
|
787
|
-
* routed connector. `role` is the whole of what an action says about
|
|
788
|
-
* appearance.
|
|
789
|
-
*/
|
|
790
|
-
kind: 'polyline';
|
|
791
|
-
/** At least two points. Anything shorter is dropped in resolution. */
|
|
792
|
-
points: ReadonlyArray<{
|
|
793
|
-
x: number;
|
|
794
|
-
y: number;
|
|
795
|
-
}>;
|
|
796
|
-
/** What the run means. Omitted reads as {@link OverlayRole} `'chrome'`. */
|
|
797
|
-
role?: OverlayRole;
|
|
798
|
-
} | {
|
|
799
|
-
/**
|
|
800
|
-
* Live insert-drag preview. Pre-commit there is no scene node to ghost
|
|
801
|
-
* via `previewIds()`/`previewPose()`, so insert paints its preview
|
|
802
|
-
* through the overlay instead.
|
|
803
|
-
*
|
|
804
|
-
* `bounds` is the AABB of the current drag (start/current normalized).
|
|
805
|
-
* `extras` is the per-kind extras the action already collected; the
|
|
806
|
-
* overlay renderer narrows on `shape` and casts the field shape it
|
|
807
|
-
* expects.
|
|
808
|
-
*/
|
|
809
|
-
kind: 'insertPreview';
|
|
810
|
-
/** Which preview the dispatcher's overlay draws. The kit's own shape
|
|
811
|
-
* kinds are the ones it knows how to render; a consumer-defined kind
|
|
812
|
-
* is legal and simply gets no live preview. */
|
|
813
|
-
shape: string;
|
|
814
|
-
bounds: {
|
|
815
|
-
x: number;
|
|
816
|
-
y: number;
|
|
817
|
-
width: number;
|
|
818
|
-
height: number;
|
|
819
|
-
};
|
|
820
|
-
extras: unknown;
|
|
821
|
-
/** World-space point to paint a small "anchor" dot at, marking the
|
|
822
|
-
* click point — for radial shapes (polygon/star), where no vertex
|
|
823
|
-
* sits on it, and for any shape in center mode. */
|
|
824
|
-
anchorPoint?: {
|
|
825
|
-
x: number;
|
|
826
|
-
y: number;
|
|
827
|
-
};
|
|
828
|
-
/** World affines the shape will also be drawn under once it lands, by
|
|
829
|
-
* the container it is drawn into: the preview is painted under each. */
|
|
830
|
-
copies?: readonly (readonly [number, number, number, number, number, number])[];
|
|
831
|
-
};
|
|
832
|
-
/** Handle returned from an `OngoingInvoker.start`. The dispatcher pumps
|
|
833
|
-
* `onMove` on subsequent input events of the same gesture and calls
|
|
834
|
-
* `onEnd` exactly once (with `'commit'` on natural completion or `'cancel'`
|
|
835
|
-
* on pointercancel / blur / escape). */
|
|
836
|
-
interface OngoingHandle {
|
|
837
|
-
/**
|
|
838
|
-
* Optional logical action kind — a stable, human-readable tag the
|
|
839
|
-
* dispatcher exposes via `getActiveAction()`, e.g. `'marquee'`, `'move'`,
|
|
840
|
-
* `'resize'`, `'pan'`.
|
|
841
|
-
*
|
|
842
|
-
* When omitted, `getActiveAction().kind` reports `null` even though a
|
|
843
|
-
* handle is in flight.
|
|
844
|
-
*/
|
|
845
|
-
kind?: string;
|
|
846
|
-
/**
|
|
847
|
-
* The cursor this gesture shows now, read after every move. Wins over the
|
|
848
|
-
* action's `activeCursor`, for a cursor that follows the gesture — the
|
|
849
|
-
* rotate glyph turning about its pivot. `undefined` defers to the action.
|
|
850
|
-
*/
|
|
851
|
-
cursor?(): CursorSpec | undefined;
|
|
852
|
-
onMove?(ctx: InvocationCtx): void;
|
|
853
|
-
onEnd?(ctx: InvocationCtx, reason: 'commit' | 'cancel'): void;
|
|
854
|
-
/**
|
|
855
|
-
* Optional preview surface: the in-flight state the canvas's preview-ghost
|
|
856
|
-
* layer renders on top of the committed scene during the gesture.
|
|
857
|
-
*
|
|
858
|
-
* `previewIds()` enumerates the displaced node ids; `previewPose(id)`
|
|
859
|
-
* returns the interim pose for one of them (shape opaque — the canvas
|
|
860
|
-
* casts to its `TPose` parameter). Returning `null`, or omitting the
|
|
861
|
-
* method, means "no preview this gesture".
|
|
862
|
-
*/
|
|
863
|
-
previewIds?(): Iterable<string> | null;
|
|
864
|
-
previewPose?(id: string): unknown | null;
|
|
865
|
-
/**
|
|
866
|
-
* Subset of `previewIds()` the ghost layer paints at full opacity. The
|
|
867
|
-
* ghost alpha says "this is in flight under the pointer"; a node the
|
|
868
|
-
* gesture merely displaces — a layout sibling reflowing into its
|
|
869
|
-
* destination slot — is not, and reads better settled. Honored at
|
|
870
|
-
* subtree-root granularity.
|
|
871
|
-
*/
|
|
872
|
-
previewOpaqueIds?(): Iterable<string> | null;
|
|
873
|
-
/**
|
|
874
|
-
* When `false`, the preview-ghost layer paints the ghost AND the source
|
|
875
|
-
* node stays visible at its committed pose, as a clone wants. Defaults to
|
|
876
|
-
* `true`: the ghost replaces the source during the gesture.
|
|
877
|
-
*/
|
|
878
|
-
previewHidesSource?: boolean;
|
|
879
|
-
/**
|
|
880
|
-
* Optional per-id preview *data*, for a gesture that mutates `node.data`
|
|
881
|
-
* (e.g. anchor-edit on nodes that store the polygon on `data.path`) rather
|
|
882
|
-
* than, or in addition to, the pose. Falls back to the committed
|
|
883
|
-
* `node.data` when null/absent.
|
|
884
|
-
*/
|
|
885
|
-
previewData?(id: string): unknown | null;
|
|
886
|
-
/**
|
|
887
|
-
* Optional chrome surface: a non-ghost visual (marquee rectangle, lasso
|
|
888
|
-
* polyline) painted while the gesture is in flight. Returning `null`, or
|
|
889
|
-
* omitting the method, means "no overlay this gesture".
|
|
890
|
-
*/
|
|
891
|
-
overlay?(): OngoingOverlay | null;
|
|
892
|
-
}
|
|
893
|
-
/** Fire-once invocation. Runs to completion synchronously (or fires off an
|
|
894
|
-
* async side-effect; the registry doesn't wait). */
|
|
895
|
-
interface ImmediateInvoker {
|
|
896
|
-
timing: 'immediate';
|
|
897
|
-
/** `params` carries the matched binding's opts.params. Invoked from the
|
|
898
|
-
* command palette, or anywhere else with no per-binding context, `params`
|
|
899
|
-
* is undefined; descriptors should default to a sensible variant. */
|
|
900
|
-
run(deps: ActionDeps, params?: Record<string, unknown>): void;
|
|
901
|
-
}
|
|
902
|
-
/** Phase-machine invocation. `start` opens the phase and returns the handle
|
|
903
|
-
* the dispatcher pumps. */
|
|
904
|
-
interface OngoingInvoker {
|
|
905
|
-
timing: 'ongoing';
|
|
906
|
-
start(ctx: InvocationCtx, opts?: BindingOpts): OngoingHandle;
|
|
1148
|
+
interface Condition {
|
|
1149
|
+
(ctx: RuleCtx): boolean;
|
|
1150
|
+
readonly rule: Rule;
|
|
1151
|
+
/** `this && other` */
|
|
1152
|
+
and(other: Condition | Rule): Condition;
|
|
1153
|
+
/** `this || other` */
|
|
1154
|
+
or(other: Condition | Rule): Condition;
|
|
1155
|
+
/** `this && !other` */
|
|
1156
|
+
andNot(other: Condition | Rule): Condition;
|
|
1157
|
+
/** `this || !other` */
|
|
1158
|
+
orNot(other: Condition | Rule): Condition;
|
|
907
1159
|
}
|
|
908
|
-
/** Pluggable invocation strategy for an Action. */
|
|
909
|
-
type Invoker = ImmediateInvoker | OngoingInvoker;
|
|
910
1160
|
|
|
911
1161
|
/**
|
|
912
|
-
*
|
|
913
|
-
*
|
|
914
|
-
*
|
|
1162
|
+
* Whether two rules can never hold at once — no `RuleCtx` passes both.
|
|
1163
|
+
*
|
|
1164
|
+
* Conservative: `true` is a proof, `false` means "may overlap". It reasons
|
|
1165
|
+
* about the selector values themselves (two different `mode`s, an empty
|
|
1166
|
+
* selection against a non-empty one, `focused: true` against `false`) and
|
|
1167
|
+
* through `all` / `any` / `not`; a `when` closure is opaque, so it overlaps
|
|
1168
|
+
* everything. The route-conflict check uses it to stay quiet about bindings
|
|
1169
|
+
* that share a tuple but are never eligible together.
|
|
1170
|
+
*/
|
|
1171
|
+
declare function rulesExclusive(a: Rule, b: Rule): boolean;
|
|
1172
|
+
/**
|
|
1173
|
+
* Whether `rule` can hold while `mode` is active. Only its `mode` and
|
|
1174
|
+
* `capability` tests are settled by the mode; every other test could go either
|
|
1175
|
+
* way, so `false` is a proof and `true` means "may hold".
|
|
915
1176
|
*/
|
|
1177
|
+
declare function ruleCanHoldIn(rule: Rule, mode: ActiveMode): boolean;
|
|
916
1178
|
|
|
917
|
-
/**
|
|
918
|
-
|
|
919
|
-
* input event against them and runs the winner's action. */
|
|
920
|
-
interface GestureBinding {
|
|
921
|
-
spec: GestureSpec;
|
|
922
|
-
actionId: string;
|
|
923
|
-
opts?: BindingOpts;
|
|
924
|
-
}
|
|
1179
|
+
/** The detected half of a profile — everything except the derived scale. */
|
|
1180
|
+
type DetectedDeviceFacts = Omit<DeviceProfile, 'targetScale'>;
|
|
925
1181
|
/**
|
|
926
|
-
*
|
|
927
|
-
*
|
|
928
|
-
*
|
|
929
|
-
*
|
|
930
|
-
*
|
|
931
|
-
* them to `ImmediateInvoker.run` as its second argument.
|
|
1182
|
+
* Handle/hit multiplier applied on a coarse pointer.
|
|
1183
|
+
*
|
|
1184
|
+
* 8px handle → 14px; 24px rotation distance → 42px. Counting the
|
|
1185
|
+
* surrounding grab zone, that lands in the Apple HIG 44pt / Material 48dp
|
|
1186
|
+
* minimum-touch-target band. One constant so it is tunable in one place.
|
|
932
1187
|
*/
|
|
933
|
-
|
|
934
|
-
spec: GestureSpec;
|
|
935
|
-
opts: BindingOpts;
|
|
936
|
-
};
|
|
1188
|
+
declare const COARSE_TARGET_SCALE = 1.75;
|
|
937
1189
|
/**
|
|
938
|
-
*
|
|
939
|
-
*
|
|
940
|
-
*
|
|
941
|
-
*
|
|
1190
|
+
* Fold consumer overrides over detected facts and derive `targetScale`.
|
|
1191
|
+
*
|
|
1192
|
+
* `targetScale` is re-derived AFTER the merge, so an override of
|
|
1193
|
+
* `coarsePointer` alone scales the chrome as expected. An explicit
|
|
1194
|
+
* `targetScale` override wins over the derivation.
|
|
1195
|
+
*/
|
|
1196
|
+
declare function resolveDeviceProfile(detected: DetectedDeviceFacts, overrides?: Partial<DeviceProfile>): DeviceProfile;
|
|
1197
|
+
/**
|
|
1198
|
+
* Assumed when `matchMedia` is unavailable (SSR, jsdom) and used as the
|
|
1199
|
+
* absent-means value for `RuleCtx.device`.
|
|
1200
|
+
*
|
|
1201
|
+
* Derived from `resolveDeviceProfile` rather than hand-written, so it can
|
|
1202
|
+
* never drift from what the function would compute for the same facts. A
|
|
1203
|
+
* mouse-like device is the safe default: it is what the kit has always
|
|
1204
|
+
* assumed, so an absent profile changes nothing for existing consumers.
|
|
942
1205
|
*/
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
/** The gesture-spec form of the binding, read by the gesture dispatcher.
|
|
946
|
-
* May be a single `GestureSpec`, a bare `GestureSpec[]` (any-of semantics),
|
|
947
|
-
* or a `BoundGesture[]` where each entry is either a bare `GestureSpec` or
|
|
948
|
-
* `{ spec, opts }` — use the object form for parametric actions where two
|
|
949
|
-
* bindings for the same action differ only by `opts.params` (e.g. `flip`
|
|
950
|
-
* with `axis: 'x'` vs `'y'`). The dispatcher extracts `opts.params` and
|
|
951
|
-
* passes them to `ImmediateInvoker.run` as its second argument. */
|
|
952
|
-
defaultBinding?: GestureSpec | BoundGesture[];
|
|
953
|
-
}
|
|
1206
|
+
declare const DEFAULT_DEVICE_PROFILE: DeviceProfile;
|
|
1207
|
+
|
|
954
1208
|
/**
|
|
955
|
-
*
|
|
956
|
-
*
|
|
957
|
-
* `GestureSpec` has `kind` at top level; the object form has `spec`.
|
|
1209
|
+
* A settings form an action carries: what a surface draws in place of a bare
|
|
1210
|
+
* trigger for an action whose job is to change settings on the selection.
|
|
958
1211
|
*/
|
|
959
|
-
declare function actionBindings(action: BindingSource): GestureBinding[];
|
|
960
1212
|
|
|
1213
|
+
/** What a settings form shows for the selection right now. */
|
|
1214
|
+
interface ActionSettingsState {
|
|
1215
|
+
/** The values, nested as the schema's groups nest. */
|
|
1216
|
+
values: Record<string, unknown>;
|
|
1217
|
+
/** The leaf paths where the selected items disagree. */
|
|
1218
|
+
mixed?: readonly string[];
|
|
1219
|
+
}
|
|
1220
|
+
/**
|
|
1221
|
+
* The settings form an action carries. A bar or a panel draws the action as a
|
|
1222
|
+
* button opening it, a menu as a submenu of its fields, and a palette as a
|
|
1223
|
+
* dialog; each change triggers the action with the params it makes.
|
|
1224
|
+
*/
|
|
1225
|
+
interface ActionSettings {
|
|
1226
|
+
/** The form. Its `name` titles the popover, submenu or dialog. */
|
|
1227
|
+
schema: PrefGroup;
|
|
1228
|
+
/** The selection's current values, from the deps `enabled` is given. Under
|
|
1229
|
+
* `enabled`'s contract: pure, fast, and it must not throw. */
|
|
1230
|
+
read(deps: ActionDeps): ActionSettingsState;
|
|
1231
|
+
/** The params one field's change triggers the action with, or `null` for
|
|
1232
|
+
* none. Default: `{ [path]: value }`. */
|
|
1233
|
+
params?(path: string, value: unknown): Record<string, unknown> | null;
|
|
1234
|
+
}
|
|
1235
|
+
/** `action`'s settings as they stand under `deps`, or `null` for an action
|
|
1236
|
+
* with no settings. A `read` that throws shows nothing rather than breaking
|
|
1237
|
+
* the surface. */
|
|
1238
|
+
declare function readActionSettings(action: Action, deps: ActionDeps | undefined): ActionSettingsState | null;
|
|
1239
|
+
/** The params a change of the field at `path` to `value` triggers the action with. */
|
|
1240
|
+
declare function actionSettingsParams(settings: ActionSettings, path: string, value: unknown): Record<string, unknown> | null;
|
|
1241
|
+
|
|
1242
|
+
/** What content being routed brings to `Action.enabled`: a drop's or a
|
|
1243
|
+
* paste's items, or a carry's payload with no items. */
|
|
1244
|
+
interface Carried {
|
|
1245
|
+
items: readonly IngestItem[];
|
|
1246
|
+
carry?: {
|
|
1247
|
+
type: string;
|
|
1248
|
+
value: unknown;
|
|
1249
|
+
};
|
|
1250
|
+
}
|
|
961
1251
|
/**
|
|
962
|
-
* @experimental
|
|
963
1252
|
* Closed enum of reasons an action might report itself as disabled. The
|
|
964
1253
|
* consumer (palette, menu, etc.) maps these symbolic values to display
|
|
965
1254
|
* strings via its own label map.
|
|
@@ -974,20 +1263,23 @@ declare const ActionDisabledReason: {
|
|
|
974
1263
|
/** Why an action is unavailable right now. */
|
|
975
1264
|
type ActionDisabledReason = (typeof ActionDisabledReason)[keyof typeof ActionDisabledReason];
|
|
976
1265
|
/**
|
|
977
|
-
* @experimental
|
|
978
1266
|
* What a palette, menu or toolbar renders for an action. No part of this is
|
|
979
1267
|
* read during dispatch.
|
|
980
1268
|
*/
|
|
981
1269
|
interface ActionPresentation {
|
|
982
1270
|
label: string;
|
|
983
|
-
/**
|
|
984
|
-
|
|
985
|
-
* "lock" toggle). */
|
|
986
|
-
icon?: ReactNode | (() => ReactNode);
|
|
1271
|
+
/** The action's icon, by qualified name: `icon('group')` in the package that draws it. */
|
|
1272
|
+
icon?: IconRef;
|
|
987
1273
|
/** Grouping key for palette/menu surfaces. Free-form string; the kit
|
|
988
1274
|
* ships defaults for `'align'` (six edges/centers), `'distribute'`
|
|
989
1275
|
* (two axes), and recommends `'pathfinder'` for boolean ops. */
|
|
990
1276
|
group?: string;
|
|
1277
|
+
/** Words the action is also found by and gathered under. Where `group` is
|
|
1278
|
+
* the one place an action sits, an action carries any number of tags: a
|
|
1279
|
+
* palette search matches them as it matches the label, and
|
|
1280
|
+
* `actionsTagged` / `taggedActionItems` gather every action or entry
|
|
1281
|
+
* carrying one, for a bar or a menu built from a tag. */
|
|
1282
|
+
tags?: readonly string[];
|
|
991
1283
|
/** Display override for the keyboard shortcut. When omitted, palette
|
|
992
1284
|
* surfaces derive a label from `defaultBinding` via their own
|
|
993
1285
|
* formatter. */
|
|
@@ -998,9 +1290,13 @@ interface ActionPresentation {
|
|
|
998
1290
|
* triggers the action with that variant's `params`. Omitted = the action
|
|
999
1291
|
* is one entry. See `actionItems`. */
|
|
1000
1292
|
variants?: readonly ActionVariant[];
|
|
1293
|
+
/** A form of the settings the action changes on the selection. A surface
|
|
1294
|
+
* draws the action as that form instead of a bare trigger, and each change
|
|
1295
|
+
* triggers it with the params `settings.params` makes. Not combined with
|
|
1296
|
+
* `variants`. */
|
|
1297
|
+
settings?: ActionSettings;
|
|
1001
1298
|
}
|
|
1002
1299
|
/**
|
|
1003
|
-
* @experimental
|
|
1004
1300
|
* One entry a parametric action contributes to a bar, menu or palette.
|
|
1005
1301
|
*/
|
|
1006
1302
|
interface ActionVariant {
|
|
@@ -1008,13 +1304,14 @@ interface ActionVariant {
|
|
|
1008
1304
|
key: string;
|
|
1009
1305
|
label: string;
|
|
1010
1306
|
/** Falls back to the action's own `icon`. */
|
|
1011
|
-
icon?:
|
|
1307
|
+
icon?: IconRef;
|
|
1308
|
+
/** Tags this variant carries besides its action's, which it carries too. */
|
|
1309
|
+
tags?: readonly string[];
|
|
1012
1310
|
/** Passed to `trigger`, and matched against each binding's
|
|
1013
1311
|
* `opts.params` to find the variant's shortcuts. */
|
|
1014
1312
|
params: Readonly<Record<string, unknown>>;
|
|
1015
1313
|
}
|
|
1016
1314
|
/**
|
|
1017
|
-
* @experimental
|
|
1018
1315
|
* What routing consults: the bindings that reach the action, the deps its
|
|
1019
1316
|
* invoker reads, the gates it passes, and the cursors it claims.
|
|
1020
1317
|
*/
|
|
@@ -1036,14 +1333,14 @@ interface ActionDispatch extends BindingSource {
|
|
|
1036
1333
|
* shortcuts and global held-key triggers. Default: ambient. */
|
|
1037
1334
|
scope?: 'hotkey';
|
|
1038
1335
|
/**
|
|
1039
|
-
* @experimental
|
|
1040
1336
|
* Optional predicate saying whether the action can run right now. Return
|
|
1041
1337
|
* `true` when it can, or an `ActionDisabledReason` when it cannot. The
|
|
1042
1338
|
* command palette greys out a disabled row, skips it in keyboard nav,
|
|
1043
1339
|
* ignores clicks, and shows the reason next to the label. The dispatcher
|
|
1044
1340
|
* skips a disabled action too, on a key as on a pointer, and falls through
|
|
1045
|
-
* to the next binding that matches. `ActionsRegistry.trigger`
|
|
1046
|
-
*
|
|
1341
|
+
* to the next binding that matches. `ActionsRegistry.trigger` asks too, but
|
|
1342
|
+
* with no point, so a `run` whose answer depends on `at` should still
|
|
1343
|
+
* self-guard.
|
|
1047
1344
|
*
|
|
1048
1345
|
* **Contract:** must be pure (no side effects), fast (< 4ms in dev), and
|
|
1049
1346
|
* must not throw. A throw is caught and treated as disabled with reason
|
|
@@ -1058,23 +1355,23 @@ interface ActionDispatch extends BindingSource {
|
|
|
1058
1355
|
* click, a press, a long-press. An action that only applies somewhere
|
|
1059
1356
|
* (on a path segment, say) refuses other points here, so the dispatcher
|
|
1060
1357
|
* falls through to the next binding and hover prediction shows the
|
|
1061
|
-
* action's `cursor` only where it would act.
|
|
1062
|
-
*
|
|
1358
|
+
* action's `cursor` only where it would act. `trigger` passes the
|
|
1359
|
+
* `worldX`/`worldY` its params carry, so a menu row is run under the
|
|
1360
|
+
* question it was shown enabled for. Palettes and keystrokes pass no
|
|
1361
|
+
* point; treat absent as "anywhere".
|
|
1063
1362
|
*
|
|
1064
|
-
* `carried` is what a drop or a
|
|
1065
|
-
*
|
|
1066
|
-
* browser withholds contents until the drop.
|
|
1067
|
-
*
|
|
1068
|
-
*
|
|
1363
|
+
* `carried` is what a drop, a paste or a carry being routed brings. For
|
|
1364
|
+
* an OS drag still in flight its items hold a MIME and nothing else, since
|
|
1365
|
+
* a browser withholds contents until the drop. A carry has no items and
|
|
1366
|
+
* hands over its payload whole. An action that takes only some content
|
|
1367
|
+
* refuses the rest here, so a drag it would not take is not accepted and a
|
|
1368
|
+
* paste it would not take stays the page's.
|
|
1069
1369
|
*/
|
|
1070
1370
|
enabled?: (deps?: ActionDeps, at?: {
|
|
1071
1371
|
x: number;
|
|
1072
1372
|
y: number;
|
|
1073
|
-
}, carried?:
|
|
1074
|
-
items: readonly IngestItem[];
|
|
1075
|
-
}) => true | ActionDisabledReason;
|
|
1373
|
+
}, carried?: Carried) => true | ActionDisabledReason;
|
|
1076
1374
|
/**
|
|
1077
|
-
* @experimental
|
|
1078
1375
|
* Whether the action reads as held down right now: bold on the selected
|
|
1079
1376
|
* text, snapping switched on. A toolbar button draws it as pressed.
|
|
1080
1377
|
* Omitted = the action has no pressed state, which is not the same as
|
|
@@ -1084,6 +1381,15 @@ interface ActionDispatch extends BindingSource {
|
|
|
1084
1381
|
* throw. `deps` is the bag `enabled` is given.
|
|
1085
1382
|
*/
|
|
1086
1383
|
active?: (deps?: ActionDeps) => boolean;
|
|
1384
|
+
/**
|
|
1385
|
+
* Show what running the action now would do, and change nothing: the
|
|
1386
|
+
* result an operation would make, drawn over the canvas. A toolbar asks
|
|
1387
|
+
* while its button is hovered or focused, through
|
|
1388
|
+
* `ActionsRegistry.preview`, and calls what this returns when that ends.
|
|
1389
|
+
* Asked only while `enabled` says yes, with the deps and params `run`
|
|
1390
|
+
* would get. Omitted = the action has nothing to show.
|
|
1391
|
+
*/
|
|
1392
|
+
preview?: (deps: ActionDeps, params?: Record<string, unknown>) => (() => void) | void;
|
|
1087
1393
|
/**
|
|
1088
1394
|
* Declarative eligibility rule, evaluated against the current
|
|
1089
1395
|
* `RuleCtx` by the dispatcher before invoking `start()`. Omitted =
|
|
@@ -1106,12 +1412,14 @@ interface ActionDispatch extends BindingSource {
|
|
|
1106
1412
|
* resolved earlier in the pump via `AffordanceRegion.cursor` and never
|
|
1107
1413
|
* reach this field.
|
|
1108
1414
|
*
|
|
1109
|
-
*
|
|
1110
|
-
*
|
|
1111
|
-
*
|
|
1112
|
-
*
|
|
1415
|
+
* A function is asked each time with the params the winning binding would
|
|
1416
|
+
* start the action with, tunings included, so a cursor can follow them — a
|
|
1417
|
+
* brush ring as wide as the brush. Read it through {@link actionCursor}.
|
|
1418
|
+
* Prediction runs `enabled()` but cannot run the invoker, so an action that
|
|
1419
|
+
* matches yet bails at `start()` (empty handle) may still show its cursor —
|
|
1420
|
+
* keep `enabled` accurate for actions that declare one.
|
|
1113
1421
|
*/
|
|
1114
|
-
cursor?: CursorSpec;
|
|
1422
|
+
cursor?: CursorSpec | ((params: Record<string, unknown> | undefined) => CursorSpec | undefined);
|
|
1115
1423
|
/**
|
|
1116
1424
|
* CSS cursor shown while THIS action's ongoing handle is in flight —
|
|
1117
1425
|
* grabbing while panning, `move` while dragging a selection, `crosshair`
|
|
@@ -1124,15 +1432,22 @@ interface ActionDispatch extends BindingSource {
|
|
|
1124
1432
|
* gesture.
|
|
1125
1433
|
*/
|
|
1126
1434
|
activeCursor?: CursorSpec;
|
|
1435
|
+
/**
|
|
1436
|
+
* The modifiers an ongoing action reads while it runs, by role: Shift
|
|
1437
|
+
* holding a handle's angle, Alt breaking its mirror. The dispatcher resolves
|
|
1438
|
+
* each into `InvocationCtx.roles` on the press, every move, and the release,
|
|
1439
|
+
* so a binding's `opts.roles` or a tuning's `roles` can move one to another
|
|
1440
|
+
* key. Read them with {@link roleReader}. A binding's own `mods` are matched
|
|
1441
|
+
* once, at the press; these are read throughout.
|
|
1442
|
+
*/
|
|
1443
|
+
modifierRoles?: ModifierRoles;
|
|
1127
1444
|
}
|
|
1128
1445
|
/**
|
|
1129
|
-
* @experimental
|
|
1130
1446
|
* Single registered action.
|
|
1131
1447
|
*/
|
|
1132
1448
|
interface Action extends ActionDispatch, ActionPresentation {
|
|
1133
1449
|
}
|
|
1134
1450
|
/**
|
|
1135
|
-
* @experimental
|
|
1136
1451
|
* The whole of an actions registry the dispatcher reads. It walks the list
|
|
1137
1452
|
* once per event to build its actionId lookup and never mutates it, so the
|
|
1138
1453
|
* routing side depends on this rather than on `ActionsRegistry`, whose
|
|
@@ -1141,450 +1456,500 @@ interface Action extends ActionDispatch, ActionPresentation {
|
|
|
1141
1456
|
interface ActionSource {
|
|
1142
1457
|
list(): readonly Action[];
|
|
1143
1458
|
}
|
|
1459
|
+
/** `action`'s hover cursor for an invocation given `params`: its `cursor`,
|
|
1460
|
+
* asked when it is a function. */
|
|
1461
|
+
declare function actionCursor(action: Pick<Action, 'cursor'> | undefined, params?: Record<string, unknown>): CursorSpec | undefined;
|
|
1144
1462
|
|
|
1145
|
-
/**
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
type TunedBinding<P = Record<string, unknown>> = GestureSpec | {
|
|
1157
|
-
spec: GestureSpec;
|
|
1158
|
-
opts: Omit<BindingOpts, 'params'> & {
|
|
1159
|
-
params?: ActionParams<P>;
|
|
1160
|
-
};
|
|
1161
|
-
};
|
|
1162
|
-
/** What a host changes about one action. */
|
|
1163
|
-
interface ActionTuning<P = Record<string, unknown>> {
|
|
1164
|
-
/** Params every invocation starts from. What a binding or a `trigger` call
|
|
1165
|
-
* passes is laid over them, so these fill in what it leaves out. A thunk is
|
|
1166
|
-
* read on every call, and an ongoing action resolving its `opts.params`
|
|
1167
|
-
* mid-gesture sees the value as it is then. */
|
|
1168
|
-
params?: ActionParams<P>;
|
|
1169
|
-
/** Bindings added after the action's own `defaultBinding`. */
|
|
1170
|
-
bindings?: readonly TunedBinding<P>[];
|
|
1463
|
+
/** Which canvas hit a menu answers. Every field given must match. */
|
|
1464
|
+
interface MenuWhen {
|
|
1465
|
+
bodyTarget?: BodyTarget | readonly BodyTarget[];
|
|
1466
|
+
bodyKind?: string | readonly string[];
|
|
1467
|
+
}
|
|
1468
|
+
/** A menu as data: a prefs schema drawn as a menu (`resolveSchemaMenu`).
|
|
1469
|
+
* A def without `when` is opened only by id, from chrome. */
|
|
1470
|
+
interface MenuDef {
|
|
1471
|
+
id: string;
|
|
1472
|
+
when?: MenuWhen;
|
|
1473
|
+
menu: PrefGroup;
|
|
1171
1474
|
}
|
|
1172
|
-
/**
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
/** @experimental One renderable entry: an action, optionally with params. */
|
|
1183
|
-
interface ActionItem {
|
|
1184
|
-
/** `action.id`, or `action.id:variant.key` — unique across a registry. */
|
|
1185
|
-
key: string;
|
|
1186
|
-
action: Action;
|
|
1187
|
-
label: string;
|
|
1188
|
-
icon?: ReactNode | (() => ReactNode);
|
|
1189
|
-
/** What to pass to `trigger(action.id, params)`; absent for a plain action. */
|
|
1190
|
-
params?: Readonly<Record<string, unknown>>;
|
|
1475
|
+
/** What a right-click landed on. */
|
|
1476
|
+
interface MenuHit {
|
|
1477
|
+
bodyTarget?: BodyTarget;
|
|
1478
|
+
bodyKind?: string;
|
|
1479
|
+
}
|
|
1480
|
+
/** A def with who contributed it: a tool id, or `null` for ambient. */
|
|
1481
|
+
interface OwnedMenuDef {
|
|
1482
|
+
def: MenuDef;
|
|
1483
|
+
owner: string | null;
|
|
1191
1484
|
}
|
|
1192
|
-
/** @experimental Expand `action` into the entries a surface renders. An
|
|
1193
|
-
* action without an immediate invoker has none: `trigger` cannot start a
|
|
1194
|
-
* drag, so a button for one would do nothing. */
|
|
1195
|
-
declare function actionItems(action: Action): ActionItem[];
|
|
1196
1485
|
|
|
1197
|
-
/**
|
|
1198
|
-
*
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1486
|
+
/** What resolving reads: the registered actions, each one's deps, how to run
|
|
1487
|
+
* one, and the values behind the schema's value leaves. */
|
|
1488
|
+
interface MenuSources {
|
|
1489
|
+
actions(): readonly Action[];
|
|
1490
|
+
depsFor(action: Action): ActionDeps | undefined;
|
|
1491
|
+
trigger(id: string, params?: Record<string, unknown>): boolean;
|
|
1492
|
+
/** The value at a leaf's dotted path. Booleans, enums, and numbers are left
|
|
1493
|
+
* out of a menu without both this and `onChange`, colors and strings without
|
|
1494
|
+
* this, and `showIf`/`enableIf` are checked only with it. */
|
|
1495
|
+
values?(path: string): unknown;
|
|
1496
|
+
onChange?(path: string, value: unknown): void;
|
|
1497
|
+
/** The schema an `alias` leaf's `of` is read in. Default: the menu's own. */
|
|
1498
|
+
over?: PrefGroup;
|
|
1499
|
+
}
|
|
1500
|
+
/**
|
|
1501
|
+
* Draw a prefs schema as menu entries. Action leaves become items from the
|
|
1502
|
+
* registry (one per variant for a parametric action), an `actions` leaf one
|
|
1503
|
+
* item per action in its group, a boolean a checked item, an enum radio
|
|
1504
|
+
* rows, and a number and a string a row edited in place (all through `src.values`/`src.onChange`);
|
|
1505
|
+
* a color shows as a swatch, read-only, and so does a string as a disabled row when it is a readout
|
|
1506
|
+
* or nothing writes it. A group drawn `as` a
|
|
1507
|
+
* `menu`, `page`, `tab`, or `panel` is a submenu, and any other group's rows
|
|
1508
|
+
* sit inline between separators. Paint, field, compound, and app-defined leaves are left out.
|
|
1509
|
+
* `at` is the world point the menu was opened at and reaches `enabled` and
|
|
1510
|
+
* every action's params; `params` go to every action, under its own.
|
|
1511
|
+
*/
|
|
1512
|
+
declare function resolveSchemaMenu(schema: PrefGroup, src: MenuSources, at?: Point, params?: Record<string, unknown>): MenuEntry[];
|
|
1513
|
+
/** Turn a declared menu into model entries; see {@link resolveSchemaMenu}. */
|
|
1514
|
+
declare function resolveMenu(def: MenuDef, src: MenuSources, at?: Point, params?: Record<string, unknown>): MenuEntry[];
|
|
1203
1515
|
|
|
1204
|
-
/**
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1516
|
+
/** Ask to open a menu: by id (chrome) or by what was hit (canvas). */
|
|
1517
|
+
interface MenuOpenRequest {
|
|
1518
|
+
menu?: string;
|
|
1519
|
+
hit?: MenuHit;
|
|
1520
|
+
/** Viewport point the menu is placed at, unless `under` is set. */
|
|
1521
|
+
client: Point;
|
|
1522
|
+
/** Viewport rect of the button the menu hangs from: placed below it, or above when below does not fit. */
|
|
1523
|
+
under?: Rect;
|
|
1524
|
+
/** World point, passed to `enabled` and to every item. */
|
|
1525
|
+
world?: Point;
|
|
1526
|
+
params?: Record<string, unknown>;
|
|
1527
|
+
source?: 'pointer' | 'keyboard';
|
|
1528
|
+
}
|
|
1529
|
+
/** The menu on screen. `seq` changes on every open, so a renderer remounts. */
|
|
1530
|
+
interface OpenMenu {
|
|
1531
|
+
seq: number;
|
|
1532
|
+
entries: readonly MenuEntry[];
|
|
1533
|
+
client: Point;
|
|
1534
|
+
under?: Rect;
|
|
1535
|
+
world?: Point;
|
|
1536
|
+
source: 'pointer' | 'keyboard';
|
|
1537
|
+
}
|
|
1538
|
+
/** Holds the declared menus and the one open menu. */
|
|
1539
|
+
interface MenuHost {
|
|
1540
|
+
/** Declare a menu; `owner` is a tool id, or `null` for always. Returns a release. */
|
|
1541
|
+
define(def: MenuDef, owner?: string | null): () => void;
|
|
1542
|
+
/** Pick, resolve and show. False when nothing matched or nothing resolved. */
|
|
1543
|
+
open(req: MenuOpenRequest): boolean;
|
|
1544
|
+
/** Whether `open(req)` would show anything. Changes no state and notifies no one. */
|
|
1545
|
+
has(req: MenuOpenRequest): boolean;
|
|
1546
|
+
close(): void;
|
|
1547
|
+
current(): OpenMenu | null;
|
|
1548
|
+
subscribe(fn: () => void): () => void;
|
|
1211
1549
|
}
|
|
1212
|
-
/**
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
* with reason `'(predicate threw)'`. In dev mode, warns once per action id
|
|
1217
|
-
* when a single call exceeds 4ms (single frame at 240fps — generous; real
|
|
1218
|
-
* predicates should be sub-millisecond).
|
|
1219
|
-
*/
|
|
1220
|
-
declare function evaluateEnabled(action: Action, deps?: ActionDeps): ActionEnabledResult;
|
|
1221
|
-
/**
|
|
1222
|
-
* @experimental
|
|
1223
|
-
* Safely evaluate an action's `active` predicate. `undefined` for an action
|
|
1224
|
-
* with no pressed state; a predicate that throws reads as not active.
|
|
1225
|
-
*/
|
|
1226
|
-
declare function evaluateActive(action: Action, deps?: ActionDeps): boolean | undefined;
|
|
1550
|
+
/** Creates the host that decides which context menu is open, from the menus registered to it. */
|
|
1551
|
+
declare function createMenuHost(src: MenuSources & {
|
|
1552
|
+
activeTool(): string | null;
|
|
1553
|
+
}): MenuHost;
|
|
1227
1554
|
|
|
1228
1555
|
/**
|
|
1229
|
-
* The
|
|
1230
|
-
*
|
|
1231
|
-
*
|
|
1556
|
+
* The tree both input registries hang on. A provider is a root; every canvas
|
|
1557
|
+
* mounts a scope under the registry in scope; a yoke sits between the two. A
|
|
1558
|
+
* registration lives in the node it was made in, and a lookup starts at a leaf
|
|
1559
|
+
* and walks up, so one canvas never answers for another.
|
|
1232
1560
|
*/
|
|
1233
1561
|
|
|
1234
|
-
/**
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
register<K extends DepName>(name: K, source: () => DepSchema[K]): () => void;
|
|
1239
|
-
get<K extends DepName>(name: K): DepSchema[K] | undefined;
|
|
1240
|
-
/** Hear that what the registry answers may have changed: a source was
|
|
1241
|
-
* registered or released anywhere in its tree, or an owner called
|
|
1242
|
-
* {@link DepRegistry.changed}. Returns the unsubscribe. A hand-built
|
|
1243
|
-
* registry whose answers never change leaves this and `changed` out. */
|
|
1244
|
-
subscribe?(listener: () => void): () => void;
|
|
1245
|
-
/** Say that a source's value changed. A source is a thunk read on demand,
|
|
1246
|
-
* so its owner is the only one who knows; chrome showing an action's
|
|
1247
|
-
* `enabled` or `active` state re-reads on this. */
|
|
1248
|
-
changed?(): void;
|
|
1562
|
+
/** What every node of one tree shares: a version and the listeners to it. */
|
|
1563
|
+
interface ScopeTree {
|
|
1564
|
+
version: number;
|
|
1565
|
+
readonly listeners: Emitter;
|
|
1249
1566
|
}
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
private readonly
|
|
1255
|
-
|
|
1256
|
-
|
|
1567
|
+
declare class ScopeNode<N extends ScopeNode<N>> {
|
|
1568
|
+
readonly parent: N | null;
|
|
1569
|
+
readonly descends: boolean;
|
|
1570
|
+
readonly tree: ScopeTree;
|
|
1571
|
+
private readonly children;
|
|
1572
|
+
private activeChild;
|
|
1573
|
+
/** `descends`: whether reads made here answer from the active child down —
|
|
1574
|
+
* true for a root or a yoke, which chrome reads through; false for a scope,
|
|
1575
|
+
* which answers for itself even with a scope nested inside it. */
|
|
1576
|
+
constructor(parent: N | null, descends: boolean);
|
|
1577
|
+
/** Bump the tree's version and tell every listener in it. */
|
|
1578
|
+
changed(): void;
|
|
1579
|
+
/** Join the parent's children, newest last; returns the leave. */
|
|
1580
|
+
mount(): () => void;
|
|
1581
|
+
/** Make this node the one its ancestors resolve through. */
|
|
1582
|
+
activate(): void;
|
|
1583
|
+
/** The node a lookup made here starts from: down the active children, or the
|
|
1584
|
+
* newest where nothing has been activated yet. A yoke with no members yet
|
|
1585
|
+
* is passed over — there is no canvas behind it to answer. */
|
|
1586
|
+
leaf(): N;
|
|
1587
|
+
private newestMember;
|
|
1588
|
+
/** Where a read made through this node starts. */
|
|
1589
|
+
start(): N;
|
|
1590
|
+
/** Whether this node is on the path a read from the root takes. */
|
|
1591
|
+
isActive(): boolean;
|
|
1592
|
+
/** This node, then each ancestor up to the root. */
|
|
1593
|
+
chain(): Generator<N>;
|
|
1257
1594
|
}
|
|
1258
|
-
/**
|
|
1259
|
-
* A stock dep registry with no parent: what `<DepRegistryProvider>` mounts,
|
|
1260
|
-
* for a dispatcher driven without React. Sources registered under one name
|
|
1261
|
-
* stack, newest live, and releasing one restores the one beneath it.
|
|
1262
|
-
*/
|
|
1263
|
-
declare function createDepRegistry(): DepRegistry;
|
|
1264
|
-
|
|
1265
|
-
/**
|
|
1266
|
-
* Shared dep-bag builder — the single implementation behind both the gesture
|
|
1267
|
-
* dispatcher's per-invocation `buildDeps` and `ActionsRegistry.trigger`'s
|
|
1268
|
-
* `requires` branch. Reads the action's declared `requires`, resolves each
|
|
1269
|
-
* name against the `DepRegistry`, and (dev builds only) wraps the bag in a
|
|
1270
|
-
* Proxy that warns on undeclared reads.
|
|
1271
|
-
*/
|
|
1272
|
-
|
|
1273
|
-
/** Build the deps bag for an action from its declared `requires`. */
|
|
1274
|
-
declare function buildDepsFromRequires(action: Action, depRegistry: DepRegistry): ActionDeps;
|
|
1275
1595
|
|
|
1276
|
-
/**
|
|
1277
|
-
*
|
|
1278
|
-
*
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
/** Inline-SVG icon component output: a static `ReactNode`, or a function
|
|
1294
|
-
* returning one. */
|
|
1295
|
-
icon?: react.ReactNode | (() => react.ReactNode);
|
|
1296
|
-
/** Palette grouping key. Tools sharing a group render contiguously
|
|
1297
|
-
* with separators between groups. Free-form string; the kit
|
|
1298
|
-
* recommends 'select' | 'shape' | 'draw' | 'type' | 'view'. */
|
|
1299
|
-
group?: string;
|
|
1300
|
-
/** Display override for the keyboard shortcut. When omitted the palette
|
|
1301
|
-
* derives one from `Tool.keybinding` via its own formatter. */
|
|
1302
|
-
shortcut?: string;
|
|
1303
|
-
/** Keep the tool off palettes — for a tool that is only ever held, like a
|
|
1304
|
-
* scrub engaged by its `hotkey`. It stays registered and selectable by
|
|
1305
|
-
* id; only the palette leaves it out. */
|
|
1306
|
-
hide?: boolean;
|
|
1596
|
+
/** Which tool is active, plus the stack of tools temporarily held active by a
|
|
1597
|
+
* hotkey (space-for-hand and the like). The dispatcher reads this to decide
|
|
1598
|
+
* whose bindings are in scope. */
|
|
1599
|
+
interface ActiveToolContextValue {
|
|
1600
|
+
/** The focused tool's id, or `null` when no tool holds the active slot —
|
|
1601
|
+
* a canvas can run on ambient bindings alone. */
|
|
1602
|
+
active: string | null;
|
|
1603
|
+
hotkeyStack: string[];
|
|
1604
|
+
setActive(id: string | null): void;
|
|
1605
|
+
pushHotkey(id: string): void;
|
|
1606
|
+
/**
|
|
1607
|
+
* Disengage a hotkey-held tool. With `id`, removes that tool's own entry
|
|
1608
|
+
* wherever it now sits — holds do not have to come up in the order they went
|
|
1609
|
+
* down, and popping the top would disengage a tool whose key is still held.
|
|
1610
|
+
* With no id, pops the top.
|
|
1611
|
+
*/
|
|
1612
|
+
popHotkey(id?: string): void;
|
|
1307
1613
|
}
|
|
1308
1614
|
/**
|
|
1309
|
-
*
|
|
1310
|
-
*
|
|
1615
|
+
* One node of the tool tree. A node that `owns` a tool holds its state; one
|
|
1616
|
+
* that does not — a scope inside a yoke — reads and writes its yoke's.
|
|
1311
1617
|
*/
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
/**
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1618
|
+
declare class ToolNode extends ScopeNode<ToolNode> {
|
|
1619
|
+
private state;
|
|
1620
|
+
private value;
|
|
1621
|
+
constructor(parent: ToolNode | null, descends: boolean, owns: boolean, initialActive: string | null);
|
|
1622
|
+
/** The node whose tool a read here resolves to. */
|
|
1623
|
+
owner(): ToolNode;
|
|
1624
|
+
private update;
|
|
1625
|
+
/** A stable value per owner and state, for `useSyncExternalStore`. */
|
|
1626
|
+
snapshot(): ActiveToolContextValue;
|
|
1627
|
+
subscribe: (listener: () => void) => (() => void);
|
|
1628
|
+
}
|
|
1629
|
+
/** Props for `<ActiveToolContextProvider>`. */
|
|
1630
|
+
interface ActiveToolContextProviderProps {
|
|
1631
|
+
children: ReactNode;
|
|
1632
|
+
/** Seeds the active slot, and wins over the first `useTools` call's
|
|
1633
|
+
* `active`. Omitted, the slot starts empty and that call seeds it. */
|
|
1634
|
+
initialActive?: string | null;
|
|
1323
1635
|
}
|
|
1636
|
+
/** Provides root active-tool state. Every canvas below has a tool of its own;
|
|
1637
|
+
* reads and writes here reach the one last used. */
|
|
1638
|
+
declare function ActiveToolContextProvider({ children, initialActive, }: ActiveToolContextProviderProps): react.JSX.Element;
|
|
1639
|
+
/** The active-tool state in scope. Throws outside a provider; use
|
|
1640
|
+
* `useOptionalActiveToolContext` where one is not guaranteed. */
|
|
1641
|
+
declare function useActiveToolContext(): ActiveToolContextValue;
|
|
1324
1642
|
/**
|
|
1325
|
-
*
|
|
1326
|
-
*
|
|
1327
|
-
* the other two exist for chrome that belongs under the selection handles
|
|
1328
|
-
* (a snap-target highlight, say). With no selection overlay in the stack,
|
|
1329
|
-
* all three collapse to `'top'`.
|
|
1643
|
+
* Like `useActiveToolContext`, but returns `null` when no
|
|
1644
|
+
* `<ActiveToolContextProvider>` is in scope instead of throwing.
|
|
1330
1645
|
*/
|
|
1331
|
-
|
|
1332
|
-
/** Dep sources an entry provides while it is installed, by dep name. */
|
|
1333
|
-
type ContributionDeps = {
|
|
1334
|
-
[K in DepName]?: () => DepSchema[K];
|
|
1335
|
-
};
|
|
1646
|
+
declare function useOptionalActiveToolContext(): ActiveToolContextValue | null;
|
|
1336
1647
|
/**
|
|
1337
|
-
*
|
|
1338
|
-
*
|
|
1648
|
+
* Mounts an `<ActiveToolContextProvider>` only when no parent provider is in
|
|
1649
|
+
* scope; otherwise renders children unwrapped.
|
|
1339
1650
|
*/
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
actions?: Action[];
|
|
1345
|
-
/** Dep sources this entry provides while it is installed. A host installs
|
|
1346
|
-
* them for as long as the entry is registered, whatever its eligibility. */
|
|
1347
|
-
deps?: ContributionDeps;
|
|
1348
|
-
}
|
|
1651
|
+
declare function ActiveToolContextProviderIfRoot({ children, }: {
|
|
1652
|
+
children: ReactNode;
|
|
1653
|
+
}): react.JSX.Element;
|
|
1654
|
+
|
|
1349
1655
|
/**
|
|
1350
|
-
*
|
|
1351
|
-
*
|
|
1656
|
+
* Tuning: what a host changes about an action it did not write — the params
|
|
1657
|
+
* every invocation starts from, and bindings added beside the action's own —
|
|
1658
|
+
* without restating the descriptor. `ActionsRegistry.tune` holds tunings and
|
|
1659
|
+
* applies them to whatever descriptor is live for the id.
|
|
1352
1660
|
*/
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1661
|
+
|
|
1662
|
+
/** An action's params, or a thunk read each time the action asks for them. */
|
|
1663
|
+
type ActionParams<P = Record<string, unknown>> = Partial<P> | (() => Partial<P>);
|
|
1664
|
+
/** A binding added for an action: a gesture alone, or a gesture with the
|
|
1665
|
+
* options it passes — the {@link BoundGesture} shape, its params typed. */
|
|
1666
|
+
type TunedBinding<P = Record<string, unknown>> = GestureSpec | {
|
|
1667
|
+
spec: GestureSpec;
|
|
1668
|
+
opts: Omit<BindingOpts, 'params'> & {
|
|
1669
|
+
params?: ActionParams<P>;
|
|
1670
|
+
};
|
|
1671
|
+
};
|
|
1672
|
+
/** What a host changes about one action. */
|
|
1673
|
+
interface ActionTuning<P = Record<string, unknown>> {
|
|
1674
|
+
/** Params every invocation starts from. What a binding or a `trigger` call
|
|
1675
|
+
* passes is laid over them, so these fill in what it leaves out. A thunk is
|
|
1676
|
+
* read on every call, and an ongoing action resolving its `opts.params`
|
|
1677
|
+
* mid-gesture sees the value as it is then. */
|
|
1678
|
+
params?: ActionParams<P>;
|
|
1679
|
+
/** Bindings added after the action's own `defaultBinding`. */
|
|
1680
|
+
bindings?: readonly TunedBinding<P>[];
|
|
1681
|
+
/** Keys for some of the action's `modifierRoles`, by role id. A binding's
|
|
1682
|
+
* own `opts.roles` is laid over these. */
|
|
1683
|
+
roles?: RoleRebinds;
|
|
1363
1684
|
}
|
|
1685
|
+
/** `action` with each tuning applied in turn, a later one's params over an
|
|
1686
|
+
* earlier one's. Applying a tuning to a copy it already tuned changes nothing. */
|
|
1687
|
+
declare function tuneAction(action: Action, tunings: readonly ActionTuning[]): Action;
|
|
1688
|
+
|
|
1364
1689
|
/**
|
|
1365
|
-
*
|
|
1366
|
-
*
|
|
1367
|
-
* `bindings` and `actions`.
|
|
1690
|
+
* The entries an action contributes to a bar, menu or palette — one per
|
|
1691
|
+
* variant, or the action itself when it declares none.
|
|
1368
1692
|
*/
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1693
|
+
|
|
1694
|
+
/** One renderable entry: an action, optionally with params. */
|
|
1695
|
+
interface ActionItem {
|
|
1696
|
+
/** `action.id`, or `action.id:variant.key` — unique across a registry. */
|
|
1697
|
+
key: string;
|
|
1698
|
+
action: Action;
|
|
1699
|
+
label: string;
|
|
1700
|
+
icon?: IconRef;
|
|
1701
|
+
/** The action's tags, then the variant's own. */
|
|
1702
|
+
tags: readonly string[];
|
|
1703
|
+
/** What to pass to `trigger(action.id, params)`; absent for a plain action. */
|
|
1704
|
+
params?: Readonly<Record<string, unknown>>;
|
|
1372
1705
|
}
|
|
1706
|
+
/** Expand `action` into the entries a surface renders. An
|
|
1707
|
+
* action without an immediate invoker has none: `trigger` cannot start a
|
|
1708
|
+
* drag, so a button for one would do nothing. */
|
|
1709
|
+
declare function actionItems(action: Action): ActionItem[];
|
|
1710
|
+
/** How a palette titles `item`: a variant reads as its action's
|
|
1711
|
+
* label and its own ("New: A4"), unless its label already holds the action's
|
|
1712
|
+
* as whole words ("Zoom In", "Reset Zoom"). */
|
|
1713
|
+
declare function actionItemTitle(item: ActionItem): string;
|
|
1373
1714
|
|
|
1374
1715
|
/**
|
|
1375
|
-
*
|
|
1376
|
-
*
|
|
1716
|
+
* Project an action's keyboard bindings into the shape a shortcut chip
|
|
1717
|
+
* renders from (`formatShortcut` / `formatShortcutParts` in `@weasel-js/ui`).
|
|
1377
1718
|
*/
|
|
1378
1719
|
|
|
1379
|
-
/**
|
|
1380
|
-
*
|
|
1381
|
-
type
|
|
1382
|
-
/** A binding paired with where it came from, ready to be matched against an
|
|
1383
|
-
* event. */
|
|
1384
|
-
interface ScopedBinding {
|
|
1385
|
-
binding: GestureBinding;
|
|
1386
|
-
scope: BindingScope;
|
|
1387
|
-
/** Id of the tool or contribution that declared this binding —
|
|
1388
|
-
* `'&'`-channel phase atoms resolve to it. `null` for a registered
|
|
1389
|
-
* action's own binding. */
|
|
1390
|
-
ownerId: string | null;
|
|
1391
|
-
}
|
|
1392
|
-
/** The binding that won a match. */
|
|
1393
|
-
interface MatchResult {
|
|
1394
|
-
binding: GestureBinding;
|
|
1395
|
-
scope: BindingScope;
|
|
1396
|
-
/** Id of the entry that declared the binding — propagated from
|
|
1397
|
-
* `ScopedBinding` so the dispatcher can record it as the handle owner. */
|
|
1398
|
-
ownerId: string | null;
|
|
1399
|
-
}
|
|
1720
|
+
/** One keyboard shortcut, flattened for display: the chip a menu row draws
|
|
1721
|
+
* (`MenuShortcut` in `@weasel-js/menu`, `ShortcutInput` in `@weasel-js/gestures`). */
|
|
1722
|
+
type ActionShortcut = MenuShortcut;
|
|
1400
1723
|
/**
|
|
1401
|
-
*
|
|
1402
|
-
*
|
|
1403
|
-
* `affordance:<k>` matches on its `kind`. The body-class strings (`'empty'`,
|
|
1404
|
-
* `'selected-body'`, `'unselected-body'`) and the `kind:` forms resolve from
|
|
1405
|
-
* `bodyTarget` / `bodyKind` and never see it.
|
|
1724
|
+
* The chip form of a single gesture spec, or `undefined` when the spec has
|
|
1725
|
+
* none.
|
|
1406
1726
|
*
|
|
1407
|
-
* A
|
|
1408
|
-
*
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
/** The first of {@link matchSorted}'s matches, or null when nothing matches. */
|
|
1412
|
-
declare function matchBest(e: InputEvent, bindings: readonly ScopedBinding[], isMac: boolean, engagedChannels?: ReadonlySet<string>): MatchResult | null;
|
|
1413
|
-
/**
|
|
1414
|
-
* Find every binding that matches the event, sorted by precedence
|
|
1415
|
-
* (best → worst). Ordering rules:
|
|
1727
|
+
* - A spec's `key` may list spellings of one keycap (`['[', '{']` — the
|
|
1728
|
+
* shifted bracket reports as `'{'`). The first is the shortcut's name.
|
|
1729
|
+
* - A modifier declared `'optional'` matches held or unheld, so it isn't
|
|
1730
|
+
* part of what the user has to press.
|
|
1416
1731
|
*
|
|
1417
|
-
*
|
|
1418
|
-
*
|
|
1419
|
-
* `specificity()`).
|
|
1420
|
-
* - Among same-specificity bindings, first-declared wins.
|
|
1421
|
-
*/
|
|
1422
|
-
declare function matchSorted(e: InputEvent, bindings: readonly ScopedBinding[], isMac: boolean, engagedChannels?: ReadonlySet<string>, warn?: (message: string) => void): MatchResult[];
|
|
1423
|
-
/** {@link matchSorted}, plus what an exclusive claim on the event barred. */
|
|
1424
|
-
interface MatchOutcome {
|
|
1425
|
-
matches: MatchResult[];
|
|
1426
|
-
/** Bindings whose spec matched the event but which the claim barred, in
|
|
1427
|
-
* the same order `matches` uses. Filled only when `collectBarred` is set. */
|
|
1428
|
-
barred: MatchResult[];
|
|
1429
|
-
/** Owner of the exclusive claim, when one applied. */
|
|
1430
|
-
claimOwner?: string;
|
|
1431
|
-
}
|
|
1432
|
-
/**
|
|
1433
|
-
* {@link matchSorted} that can also report the bindings an exclusive claim
|
|
1434
|
-
* kept out. Collecting them costs a spec match per barred binding, so it is
|
|
1435
|
-
* opt-in — the dispatcher asks only when it is recording.
|
|
1732
|
+
* Non-keyboard specs (drag, wheel, click) have no chip form. Neither does a
|
|
1733
|
+
* *required* `ctrl` or `meta`: the display shape carries only `mod`.
|
|
1436
1734
|
*/
|
|
1437
|
-
declare function
|
|
1438
|
-
collectBarred?: boolean;
|
|
1439
|
-
warn?: (message: string) => void;
|
|
1440
|
-
}): MatchOutcome;
|
|
1441
|
-
|
|
1735
|
+
declare function keySpecShortcut(spec: GestureSpec): ActionShortcut | undefined;
|
|
1442
1736
|
/**
|
|
1443
|
-
*
|
|
1444
|
-
*
|
|
1445
|
-
*
|
|
1446
|
-
* `Dispatcher.explain` fills one on request without invoking anything.
|
|
1737
|
+
* Every keyboard shortcut an action answers to, in declaration order.
|
|
1738
|
+
* Specs `keySpecShortcut` has no chip for are skipped, and bindings it leaves
|
|
1739
|
+
* identical are emitted once; an action bound only to those returns empty.
|
|
1447
1740
|
*
|
|
1448
|
-
*
|
|
1449
|
-
*
|
|
1741
|
+
* With `params` — an `ActionItem`'s — only the bindings that pass those same
|
|
1742
|
+
* values count, so each variant of `flip` gets its own key.
|
|
1450
1743
|
*/
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
viewId: string | null;
|
|
1481
|
-
/** World point, for events that carry one. */
|
|
1482
|
-
world?: {
|
|
1483
|
-
x: number;
|
|
1484
|
-
y: number;
|
|
1485
|
-
};
|
|
1486
|
-
/** Active mode id, when the host supplies a rule context. */
|
|
1487
|
-
mode?: string;
|
|
1488
|
-
}
|
|
1489
|
-
/** One matched binding. */
|
|
1490
|
-
interface RecordCandidate {
|
|
1491
|
-
actionId: string;
|
|
1492
|
-
/** The binding's spec in route grammar — one per arg alternative. */
|
|
1493
|
-
routes: readonly string[];
|
|
1494
|
-
scope: BindingScope;
|
|
1495
|
-
/** The tool or contribution that declared the binding; `null` for an action's own binding. */
|
|
1496
|
-
ownerId: string | null;
|
|
1497
|
-
/** Whether the binding's `views` names the routed view. */
|
|
1498
|
-
namesView: boolean;
|
|
1499
|
-
/** `specificity(spec)`: target, modifiers, phase, exact. */
|
|
1500
|
-
specificity: readonly [number, number, number, number];
|
|
1501
|
-
/** The action's `eligible` rule as text, when it declares one. */
|
|
1502
|
-
eligible?: string;
|
|
1503
|
-
}
|
|
1504
|
-
/** A matched candidate a filter removed before ranking, and which filter. */
|
|
1505
|
-
type DroppedCandidate =
|
|
1506
|
-
/** An exclusive claim on the input barred bindings that do not read the
|
|
1507
|
-
* claim's affordance. */
|
|
1508
|
-
{
|
|
1509
|
-
candidate: RecordCandidate;
|
|
1510
|
-
filter: 'claim';
|
|
1511
|
-
owner?: string;
|
|
1512
|
-
}
|
|
1513
|
-
/** The action's `eligible` rule does not hold. */
|
|
1514
|
-
| {
|
|
1515
|
-
candidate: RecordCandidate;
|
|
1516
|
-
filter: 'ineligible';
|
|
1517
|
-
rule: string;
|
|
1518
|
-
};
|
|
1519
|
-
/** One field of the `specificity` tuple, in the order it is compared. */
|
|
1520
|
-
type SpecificityPart = 'target' | 'mods' | 'phase' | 'exact';
|
|
1521
|
-
/** The ranking step that put a candidate below the one before it. */
|
|
1522
|
-
type PlacedBy = {
|
|
1523
|
-
step: 'first';
|
|
1524
|
-
}
|
|
1525
|
-
/** The one above names the routed view; this one does not. */
|
|
1526
|
-
| {
|
|
1527
|
-
step: 'view';
|
|
1528
|
-
}
|
|
1529
|
-
/** The one above sits in a higher scope tier. */
|
|
1530
|
-
| {
|
|
1531
|
-
step: 'tier';
|
|
1532
|
-
}
|
|
1533
|
-
/** Same tier; the one above is more specific in `part`. */
|
|
1534
|
-
| {
|
|
1535
|
-
step: 'specificity';
|
|
1536
|
-
part: SpecificityPart;
|
|
1537
|
-
}
|
|
1538
|
-
/** Tied; the one above is gated by an `eligible` rule that holds. */
|
|
1539
|
-
| {
|
|
1540
|
-
step: 'context';
|
|
1541
|
-
}
|
|
1542
|
-
/** Tied on everything; registration order. */
|
|
1543
|
-
| {
|
|
1544
|
-
step: 'order';
|
|
1545
|
-
};
|
|
1546
|
-
/** What the walk did with one ranked candidate. */
|
|
1547
|
-
type WalkStep = {
|
|
1548
|
-
kind: 'fired';
|
|
1744
|
+
declare function actionShortcuts(action: Action, params?: Readonly<Record<string, unknown>>): readonly ActionShortcut[];
|
|
1745
|
+
|
|
1746
|
+
/**
|
|
1747
|
+
* Finding actions by tag: gathering the ones that carry it, and a person's
|
|
1748
|
+
* search matching it as it matches a label.
|
|
1749
|
+
*/
|
|
1750
|
+
|
|
1751
|
+
/** The actions carrying `tag`, themselves or on any variant, in
|
|
1752
|
+
* the order given. Drags included, which have no entries to show. */
|
|
1753
|
+
declare function actionsTagged(actions: readonly Action[], tag: string): Action[];
|
|
1754
|
+
/** The entries carrying `tag`: what a bar or a menu built from
|
|
1755
|
+
* a tag shows. A variant that does not carry it is left out. */
|
|
1756
|
+
declare function taggedActionItems(actions: readonly Action[], tag: string): ActionItem[];
|
|
1757
|
+
/** Whether `item` answers a search for `query`, by its palette title or
|
|
1758
|
+
* by a tag, and which. */
|
|
1759
|
+
declare function matchActionItem(item: ActionItem, query: string): QueryMatch | null;
|
|
1760
|
+
|
|
1761
|
+
/**
|
|
1762
|
+
* Palette-side evaluation of `Action.enabled`. The dispatcher calls the
|
|
1763
|
+
* predicate raw as a gate; this is the surface that also enforces the
|
|
1764
|
+
* contract the predicate's JSDoc states — no throwing, under 4ms in dev.
|
|
1765
|
+
*/
|
|
1766
|
+
|
|
1767
|
+
/**
|
|
1768
|
+
* Result of evaluating an Action's `enabled` predicate.
|
|
1769
|
+
*/
|
|
1770
|
+
interface ActionEnabledResult {
|
|
1771
|
+
enabled: boolean;
|
|
1772
|
+
reason?: ActionDisabledReason;
|
|
1549
1773
|
}
|
|
1550
|
-
/**
|
|
1551
|
-
*
|
|
1552
|
-
|
|
1553
|
-
|
|
1774
|
+
/**
|
|
1775
|
+
* Safely evaluate an action's `enabled` predicate. Returns `{enabled: true}`
|
|
1776
|
+
* when no predicate is supplied. Catches throws and treats them as disabled
|
|
1777
|
+
* with reason `'(predicate threw)'`. In dev mode, warns once per action id
|
|
1778
|
+
* when a single call exceeds 4ms (single frame at 240fps — generous; real
|
|
1779
|
+
* predicates should be sub-millisecond).
|
|
1780
|
+
*/
|
|
1781
|
+
declare function evaluateEnabled(action: Action, deps?: ActionDeps, at?: {
|
|
1782
|
+
x: number;
|
|
1783
|
+
y: number;
|
|
1784
|
+
}): ActionEnabledResult;
|
|
1785
|
+
/**
|
|
1786
|
+
* Safely evaluate an action's `active` predicate. `undefined` for an action
|
|
1787
|
+
* with no pressed state; a predicate that throws reads as not active.
|
|
1788
|
+
*/
|
|
1789
|
+
declare function evaluateActive(action: Action, deps?: ActionDeps): boolean | undefined;
|
|
1790
|
+
|
|
1791
|
+
/**
|
|
1792
|
+
* The dep registry: named "live source" thunks. Actions declare
|
|
1793
|
+
* `requires: ['selection']`; the dispatcher calls `registry.get('selection')`
|
|
1794
|
+
* at invocation time to build a typed Deps bag.
|
|
1795
|
+
*/
|
|
1796
|
+
|
|
1797
|
+
/** Holds the live sources an action's declared dependencies resolve to.
|
|
1798
|
+
* Sources are thunks, read at invocation time, so an action never captures
|
|
1799
|
+
* stale state. */
|
|
1800
|
+
interface DepRegistry {
|
|
1801
|
+
/** Adds a live source for `name` over any already there. `below` answers
|
|
1802
|
+
* what the registry would without this source, so one can handle part of
|
|
1803
|
+
* a dep and hand the rest to the default it displaced. */
|
|
1804
|
+
register<K extends DepName>(name: K, source: DepSource<K>): () => void;
|
|
1805
|
+
get<K extends DepName>(name: K): DepSchema[K] | undefined;
|
|
1806
|
+
/** Hear that what the registry answers may have changed: a source was
|
|
1807
|
+
* registered or released anywhere in its tree, or an owner called
|
|
1808
|
+
* {@link DepRegistry.changed}. Returns the unsubscribe. A hand-built
|
|
1809
|
+
* registry whose answers never change leaves this and `changed` out. */
|
|
1810
|
+
subscribe?(listener: () => void): () => void;
|
|
1811
|
+
/** Say that a source's value changed. A source is a thunk read on demand,
|
|
1812
|
+
* so its owner is the only one who knows; chrome showing an action's
|
|
1813
|
+
* `enabled` or `active` state re-reads on this. */
|
|
1814
|
+
changed?(): void;
|
|
1815
|
+
/** Every name a `get` here would answer, sorted, with where its live
|
|
1816
|
+
* source sits. For inspection; a hand-built registry may leave it out. */
|
|
1817
|
+
list?(): readonly DepListing[];
|
|
1818
|
+
}
|
|
1819
|
+
/** A live source for dep `K`: a thunk read at invocation time. `below` reads
|
|
1820
|
+
* the source this one displaced, under it in its scope or in a scope above. */
|
|
1821
|
+
type DepSource<K extends DepName> = (below: () => DepSchema[K] | undefined) => DepSchema[K];
|
|
1822
|
+
/** One name a dep registry answers, as {@link DepRegistry.list} reports it. */
|
|
1823
|
+
interface DepListing {
|
|
1824
|
+
name: string;
|
|
1825
|
+
/** How many scopes above the asking one the live source was registered: 0 for its own. */
|
|
1826
|
+
depth: number;
|
|
1827
|
+
/** How many sources that scope holds under the name, the live one included. */
|
|
1828
|
+
stacked: number;
|
|
1554
1829
|
}
|
|
1555
|
-
/**
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1830
|
+
/** One node of the dep tree: the sources registered at it, over its parent's.
|
|
1831
|
+
* Each name holds a stack of sources, newest live, so a displaced source is
|
|
1832
|
+
* restored when the one above it leaves. */
|
|
1833
|
+
declare class DepNode extends ScopeNode<DepNode> {
|
|
1834
|
+
private readonly sources;
|
|
1835
|
+
readonly registry: DepRegistry;
|
|
1836
|
+
constructor(parent: DepNode | null, descends: boolean);
|
|
1559
1837
|
}
|
|
1560
|
-
/**
|
|
1561
|
-
|
|
1562
|
-
|
|
1838
|
+
/**
|
|
1839
|
+
* A stock dep registry with no parent: what `<DepRegistryProvider>` mounts,
|
|
1840
|
+
* for a dispatcher driven without React. Sources registered under one name
|
|
1841
|
+
* stack, newest live, and releasing one restores the one beneath it.
|
|
1842
|
+
*/
|
|
1843
|
+
declare function createDepRegistry(): DepRegistry;
|
|
1844
|
+
|
|
1845
|
+
/**
|
|
1846
|
+
* Shared dep-bag builder — the single implementation behind both the gesture
|
|
1847
|
+
* dispatcher's per-invocation `buildDeps` and `ActionsRegistry.trigger`'s
|
|
1848
|
+
* `requires` branch. Reads the action's declared `requires`, resolves each
|
|
1849
|
+
* name against the `DepRegistry`, and (dev builds only) wraps the bag in a
|
|
1850
|
+
* Proxy that warns on undeclared reads.
|
|
1851
|
+
*/
|
|
1852
|
+
|
|
1853
|
+
/** Build the deps bag for an action from its declared `requires`. */
|
|
1854
|
+
declare function buildDepsFromRequires(action: Action, depRegistry: DepRegistry): ActionDeps;
|
|
1855
|
+
|
|
1856
|
+
/** Hotkey-slot trigger key. The slot is engaged while this key is held —
|
|
1857
|
+
* hence "hotkey": active as long as the key is hot. Omitted means the tool
|
|
1858
|
+
* is not eligible for the hotkey slot.
|
|
1859
|
+
*
|
|
1860
|
+
* The named keys map to their DOM names (`'space'` is `' '`, `'ctrl'` is
|
|
1861
|
+
* `'Control'`). Anything else is a `KeyboardEvent.key` value, matched
|
|
1862
|
+
* case-insensitively as every key spec is — so `'o'` holds on O. */
|
|
1863
|
+
type HotkeyTrigger = 'space' | 'alt' | 'ctrl' | 'meta' | 'shift' | (string & {});
|
|
1864
|
+
/** Presentation metadata for tool palettes / menus. Optional on every
|
|
1865
|
+
* tool — consumers that render a palette (`<ToolPalette>`) read these
|
|
1866
|
+
* fields to display the tool; consumers that don't can ignore them.
|
|
1867
|
+
*
|
|
1868
|
+
* Note: cursor is NOT here. `Tool.cursor` (inherited from `Contribution`)
|
|
1869
|
+
* is already plumbed through `<Canvas>` to `style.cursor` on the host. */
|
|
1870
|
+
interface ToolPresentation {
|
|
1871
|
+
/** Human-readable label, distinct from the `id`. Falls back to `id`. */
|
|
1872
|
+
label?: string;
|
|
1873
|
+
/** The tool's icon, by qualified name: `icon('rect')` in the package that draws it. */
|
|
1874
|
+
icon?: _weasel_js_icons.IconRef;
|
|
1875
|
+
/** Palette grouping key. Tools sharing a group render contiguously
|
|
1876
|
+
* with separators between groups. Free-form string; the kit
|
|
1877
|
+
* recommends 'select' | 'shape' | 'draw' | 'type' | 'view'. */
|
|
1878
|
+
group?: string;
|
|
1879
|
+
/** Display override for the keyboard shortcut. When omitted the palette
|
|
1880
|
+
* derives one from `Tool.keybinding` via its own formatter. */
|
|
1881
|
+
shortcut?: string;
|
|
1882
|
+
/** Keep the tool off palettes — for a tool that is only ever held, like a
|
|
1883
|
+
* scrub engaged by its `hotkey`. It stays registered and selectable by
|
|
1884
|
+
* id; only the palette leaves it out. */
|
|
1885
|
+
hide?: boolean;
|
|
1563
1886
|
}
|
|
1564
|
-
/**
|
|
1565
|
-
|
|
1566
|
-
|
|
1887
|
+
/**
|
|
1888
|
+
* When an entry's bindings are live. A set, not one value: the hand tool is
|
|
1889
|
+
* palette-selectable AND engaged by holding space, and both hold at once.
|
|
1890
|
+
*/
|
|
1891
|
+
interface Eligibility {
|
|
1892
|
+
/** Selectable as the focused entry — exclusive, one at a time. */
|
|
1893
|
+
focus?: boolean;
|
|
1894
|
+
/** Also live while this key is held. */
|
|
1895
|
+
offhand?: HotkeyTrigger;
|
|
1896
|
+
/** Live regardless of what is focused. */
|
|
1897
|
+
always?: boolean;
|
|
1898
|
+
/** Live only for input this entry's own affordances produced. */
|
|
1899
|
+
claimed?: boolean;
|
|
1900
|
+
/** Modality filter, applied wherever it would otherwise be live. */
|
|
1901
|
+
capabilities?: CapabilityTag[];
|
|
1567
1902
|
}
|
|
1568
|
-
/**
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1903
|
+
/**
|
|
1904
|
+
* Where an entry's overlay sits in the layer stack, relative to the
|
|
1905
|
+
* selection chrome. `'top'` is the default and renders above everything;
|
|
1906
|
+
* the other two exist for chrome that belongs under the selection handles
|
|
1907
|
+
* (a snap-target highlight, say). With no selection overlay in the stack,
|
|
1908
|
+
* all three collapse to `'top'`.
|
|
1909
|
+
*/
|
|
1910
|
+
type OverlayPosition = 'top' | 'before-selection' | 'after-selection';
|
|
1911
|
+
/** Dep sources an entry provides while it is installed, by dep name. */
|
|
1912
|
+
type ContributionDeps = {
|
|
1913
|
+
[K in DepName]?: () => DepSchema[K];
|
|
1914
|
+
};
|
|
1915
|
+
/**
|
|
1916
|
+
* What routing reads off an entry: when its bindings are live, which they
|
|
1917
|
+
* are, the actions they name, and the deps those actions read.
|
|
1918
|
+
*/
|
|
1919
|
+
interface ContributionRouting {
|
|
1920
|
+
id: string;
|
|
1921
|
+
eligibility: Eligibility;
|
|
1922
|
+
bindings?: GestureBinding[];
|
|
1923
|
+
actions?: Action[];
|
|
1924
|
+
/** Menus this entry declares: always, for an ambient entry; while active, for a tool. */
|
|
1925
|
+
menus?: MenuDef[];
|
|
1926
|
+
/** Dep sources this entry provides while it is installed. A host installs
|
|
1927
|
+
* them for as long as the entry is registered, whatever its eligibility. */
|
|
1928
|
+
deps?: ContributionDeps;
|
|
1573
1929
|
}
|
|
1574
|
-
/**
|
|
1575
|
-
|
|
1576
|
-
|
|
1930
|
+
/**
|
|
1931
|
+
* What an entry draws and how a palette shows it. None of it reaches the
|
|
1932
|
+
* dispatcher.
|
|
1933
|
+
*/
|
|
1934
|
+
interface ContributionChrome<TOverlay = KernelOverlay> {
|
|
1935
|
+
/** One layer, or several composed in the given order.
|
|
1936
|
+
*
|
|
1937
|
+
* Routing never reads a layer — it collects the live ones and hands them
|
|
1938
|
+
* back in scope order. `TOverlay` is what the consuming kernel draws;
|
|
1939
|
+
* `@weasel-js/core` binds it to `RenderLayer<unknown>`. */
|
|
1940
|
+
overlay?: TOverlay | TOverlay[];
|
|
1941
|
+
/** Defaults to `'top'`. Applies to every layer in `overlay`. */
|
|
1942
|
+
overlayPosition?: OverlayPosition;
|
|
1943
|
+
presentation?: ToolPresentation;
|
|
1577
1944
|
}
|
|
1578
|
-
/**
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
placedBy: PlacedBy;
|
|
1587
|
-
walk: WalkStep;
|
|
1945
|
+
/**
|
|
1946
|
+
* A registry entry: what it contributes, and when it is eligible. Every role
|
|
1947
|
+
* is optional and independent — an entry that only routes input declares only
|
|
1948
|
+
* `bindings` and `actions`.
|
|
1949
|
+
*/
|
|
1950
|
+
interface Contribution<TOverlay = KernelOverlay> extends ContributionRouting, ContributionChrome<TOverlay> {
|
|
1951
|
+
/** Reflection escape hatch — the authored form, when there was one. */
|
|
1952
|
+
def?: unknown;
|
|
1588
1953
|
}
|
|
1589
1954
|
|
|
1590
1955
|
/**
|
|
@@ -1625,6 +1990,19 @@ interface DispatcherContext {
|
|
|
1625
1990
|
* reads as the root.
|
|
1626
1991
|
*/
|
|
1627
1992
|
viewId?: string | null;
|
|
1993
|
+
/**
|
|
1994
|
+
* For a key from the page: whether this surface holds the keys, and whether
|
|
1995
|
+
* the pointer is over it. Bindings with `opts.over` are live only while it
|
|
1996
|
+
* is, the rest only while it holds them. Absent, every binding is live.
|
|
1997
|
+
*/
|
|
1998
|
+
keyReach?: KeyReach;
|
|
1999
|
+
/** Where the dispatcher records how it resolved each input. Absent records nothing. */
|
|
2000
|
+
trace?: DispatchTrace;
|
|
2001
|
+
}
|
|
2002
|
+
/** Which of a surface's key bindings a key from the page reaches. */
|
|
2003
|
+
interface KeyReach {
|
|
2004
|
+
held: boolean;
|
|
2005
|
+
over: boolean;
|
|
1628
2006
|
}
|
|
1629
2007
|
/**
|
|
1630
2008
|
* Handle returned by `Dispatcher.beginUiOngoing()` for driving an
|
|
@@ -1654,6 +2032,8 @@ interface ResolveOnlyResult {
|
|
|
1654
2032
|
scope: BindingScope;
|
|
1655
2033
|
/** Tool id owning the winning binding; `null` for ambient action bindings. */
|
|
1656
2034
|
ownerId: string | null;
|
|
2035
|
+
/** The params the winning binding would start the action with, resolved now. */
|
|
2036
|
+
params?: Record<string, unknown>;
|
|
1657
2037
|
}
|
|
1658
2038
|
/**
|
|
1659
2039
|
* One candidate from `Dispatcher.resolveAll` — a binding that matched the
|
|
@@ -1830,31 +2210,6 @@ interface Dispatcher {
|
|
|
1830
2210
|
beginUiOngoing(actionId: string, deps: ActionDeps, params?: Record<string, unknown>): UiOngoingControl | null;
|
|
1831
2211
|
}
|
|
1832
2212
|
|
|
1833
|
-
/**
|
|
1834
|
-
* Dev-only trace of what the dispatcher did: one record per resolved input and
|
|
1835
|
-
* one per mode switch, in a rolling buffer the dev tools read.
|
|
1836
|
-
*/
|
|
1837
|
-
|
|
1838
|
-
/** One entry per "mode change" — a kit state transition that isn't a
|
|
1839
|
-
* dispatched input event but explains why an input was (or wasn't) handled
|
|
1840
|
-
* later. Pushed via `recordModeSwitch()`. */
|
|
1841
|
-
interface ModeSwitchLogEntry {
|
|
1842
|
-
kind: 'mode';
|
|
1843
|
-
ts: number;
|
|
1844
|
-
/** Logical mode name, e.g. `'editAnchors.editingId'`. */
|
|
1845
|
-
mode: string;
|
|
1846
|
-
from: string | null;
|
|
1847
|
-
to: string | null;
|
|
1848
|
-
/** Optional free-form context — `'enterPathEdit'`, `'exitPathEdit'`,
|
|
1849
|
-
* `'selection-dropped target'`, etc. */
|
|
1850
|
-
detail?: string;
|
|
1851
|
-
}
|
|
1852
|
-
/** A trace entry: one {@link DispatchRecord} per resolved input, or a mode switch. */
|
|
1853
|
-
type TraceLogEntry = DispatchRecord | ModeSwitchLogEntry;
|
|
1854
|
-
/** Push a mode-switch record into the trace log. Safe to call from
|
|
1855
|
-
* anywhere; no-ops outside DEV. */
|
|
1856
|
-
declare function recordModeSwitch(mode: string, from: string | null, to: string | null, detail?: string): void;
|
|
1857
|
-
|
|
1858
2213
|
/**
|
|
1859
2214
|
* The slot an in-flight ongoing handle is keyed by:
|
|
1860
2215
|
* - `key-held`: `key-held-<code>` (`key-held-Space`), so two keys that share a
|
|
@@ -1864,6 +2219,7 @@ declare function recordModeSwitch(mode: string, from: string | null, to: string
|
|
|
1864
2219
|
* with no `pointerId` (a synthesized probe, a programmatic drag, most tests)
|
|
1865
2220
|
* keys to `pointer-mouse`.
|
|
1866
2221
|
* - `multitouch`: `multitouch-<fingers>`.
|
|
2222
|
+
* - `carry`: `carry-<pointerId>`, keyed as a pointer is.
|
|
1867
2223
|
* - any other kind that starts an ongoing invoker: `ongoing-<kind>`.
|
|
1868
2224
|
*/
|
|
1869
2225
|
|
|
@@ -1911,18 +2267,15 @@ declare function createDispatcher(opts?: {
|
|
|
1911
2267
|
}): Dispatcher;
|
|
1912
2268
|
|
|
1913
2269
|
/**
|
|
1914
|
-
* @experimental
|
|
1915
2270
|
* Partial override or full descriptor passed via `<SceneCanvas actions={...}>`.
|
|
1916
2271
|
* `null` disables a default at this id.
|
|
1917
2272
|
*/
|
|
1918
2273
|
type ActionEntry = null | Partial<Action> | Action;
|
|
1919
2274
|
/**
|
|
1920
|
-
* @experimental
|
|
1921
2275
|
* Shape of the `actions` prop on `<SceneCanvas>`. `null` disables all defaults.
|
|
1922
2276
|
*/
|
|
1923
2277
|
type ActionsProp = null | Record<string, ActionEntry>;
|
|
1924
2278
|
/**
|
|
1925
|
-
* @experimental
|
|
1926
2279
|
* Imperative API exposed by `useActionsRegistry()`.
|
|
1927
2280
|
*/
|
|
1928
2281
|
interface ActionsRegistry {
|
|
@@ -1943,14 +2296,25 @@ interface ActionsRegistry {
|
|
|
1943
2296
|
* apply to whichever descriptor is live for it, registered before the
|
|
1944
2297
|
* tuning or after. Returns a release. See {@link ActionTuning}. */
|
|
1945
2298
|
tune(id: string, tuning: ActionTuning): () => void;
|
|
2299
|
+
/** The params an invocation of `id` from this scope starts with when it
|
|
2300
|
+
* passes `given`: every tuning's params, `given` laid over them. What a
|
|
2301
|
+
* tool's chrome reads to show what the action it binds will do. */
|
|
2302
|
+
paramsOf(id: string, given?: Record<string, unknown>): Record<string, unknown>;
|
|
1946
2303
|
list(): readonly Action[];
|
|
1947
2304
|
/** Fire an immediate-invoker action by id. The optional `params` arg is
|
|
1948
2305
|
* forwarded to `ImmediateInvoker.run` as its second argument — use it for
|
|
1949
2306
|
* parametric actions (e.g. `trigger('tool.activate', { toolId: 'rect' })`).
|
|
1950
2307
|
* Returns whether it ran one: `false` for an unknown id, for an action
|
|
1951
2308
|
* whose `enabled` refuses, and for an ongoing-invoker action, which
|
|
1952
|
-
* `trigger` cannot reach (see `begin`). `enabled` is asked
|
|
2309
|
+
* `trigger` cannot reach (see `begin`). `enabled` is asked at
|
|
2310
|
+
* `{ x: params.worldX, y: params.worldY }` when both are numbers, and with
|
|
2311
|
+
* no point otherwise. */
|
|
1953
2312
|
trigger(id: string, params?: Record<string, unknown>): boolean;
|
|
2313
|
+
/** Show what firing `id` with `params` would do, by its `Action.preview`,
|
|
2314
|
+
* and return what takes it down; calling that twice is harmless. Shows
|
|
2315
|
+
* nothing for an unknown id, one with no preview, and one whose `enabled`
|
|
2316
|
+
* refuses, asked as `trigger` asks it. */
|
|
2317
|
+
preview(id: string, params?: Record<string, unknown>): () => void;
|
|
1954
2318
|
/**
|
|
1955
2319
|
* Subscribe to registry mutations. The callback fires after any
|
|
1956
2320
|
* `register`/`unregister` that changes the version. Returns an
|
|
@@ -2062,7 +2426,7 @@ declare const isSelectedBody: BodyPredicate;
|
|
|
2062
2426
|
declare const isUnselectedBody: BodyPredicate;
|
|
2063
2427
|
/** Empty-canvas hit — no node beneath cursor. */
|
|
2064
2428
|
declare const isEmpty: BodyPredicate;
|
|
2065
|
-
/** Any
|
|
2429
|
+
/** Any resize handle: a corner (`handle:top-left`, …) or an edge (`handle:top`, …). */
|
|
2066
2430
|
declare const isResizeHandle: TargetPredicate & {
|
|
2067
2431
|
readsAffordance: true;
|
|
2068
2432
|
};
|
|
@@ -2078,6 +2442,10 @@ declare const isAnchor: TargetPredicate & {
|
|
|
2078
2442
|
declare const isControlHandle: TargetPredicate & {
|
|
2079
2443
|
readsAffordance: true;
|
|
2080
2444
|
};
|
|
2445
|
+
/** A corner-shape widget, set inside a corner anchor of the edited path. */
|
|
2446
|
+
declare const isCornerWidget: TargetPredicate & {
|
|
2447
|
+
readsAffordance: true;
|
|
2448
|
+
};
|
|
2081
2449
|
/** Any of the three anchor-editing affordances (anchor + both controls). */
|
|
2082
2450
|
declare const isAnchorOrControl: TargetPredicate & {
|
|
2083
2451
|
readsAffordance: true;
|
|
@@ -2086,7 +2454,13 @@ declare const isAnchorOrControl: TargetPredicate & {
|
|
|
2086
2454
|
declare const isSegment: TargetPredicate & {
|
|
2087
2455
|
readsAffordance: true;
|
|
2088
2456
|
};
|
|
2089
|
-
/**
|
|
2457
|
+
/** The grip midway along a curved segment's balance line, `balance:N`, where
|
|
2458
|
+
* N is the flat index of the anchor the segment starts at. */
|
|
2459
|
+
declare const isBalanceGrip: TargetPredicate & {
|
|
2460
|
+
readsAffordance: true;
|
|
2461
|
+
};
|
|
2462
|
+
/** Anything path edit puts on the path: an anchor, a control, a segment, a
|
|
2463
|
+
* segment's balance grip, or a corner widget. */
|
|
2090
2464
|
declare const isPathEditPart: TargetPredicate & {
|
|
2091
2465
|
readsAffordance: true;
|
|
2092
2466
|
};
|
|
@@ -2164,6 +2538,29 @@ interface LongPressOptions {
|
|
|
2164
2538
|
/** A store holding the press currently being held, or none. Pass it as `LongPressOptions.state`. */
|
|
2165
2539
|
declare function createLongPressStore(): LongPressStore;
|
|
2166
2540
|
|
|
2541
|
+
/**
|
|
2542
|
+
* The `gestures` kit settings area. Declared here rather than in core because
|
|
2543
|
+
* the dispatcher reads it, and routing sits below core.
|
|
2544
|
+
*/
|
|
2545
|
+
/** Pointer gesture timings and travel; each falls back to its constant in `@weasel-js/gestures`. */
|
|
2546
|
+
interface GestureKitSettings {
|
|
2547
|
+
/** Longest gap between the clicks of a double click, in ms. */
|
|
2548
|
+
doubleClickMs: number;
|
|
2549
|
+
/** Farthest apart the clicks of a double click may land, in CSS px. */
|
|
2550
|
+
doubleClickPx: number;
|
|
2551
|
+
/** How long a touch or pen press is held to fire a long-press, in ms. */
|
|
2552
|
+
longPressMs: number;
|
|
2553
|
+
/** Farthest a multitouch tap's fingers may travel, in CSS px. */
|
|
2554
|
+
tapPx: number;
|
|
2555
|
+
/** Travel that makes a press a drag, in CSS px. */
|
|
2556
|
+
dragPx: number;
|
|
2557
|
+
}
|
|
2558
|
+
declare module '@weasel-js/react' {
|
|
2559
|
+
interface KitSettings {
|
|
2560
|
+
gestures: GestureKitSettings;
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
|
|
2167
2564
|
/**
|
|
2168
2565
|
* The DOM half of a drag: one pointer, held down, from press to release.
|
|
2169
2566
|
*
|
|
@@ -2221,6 +2618,60 @@ interface PointerSession {
|
|
|
2221
2618
|
*/
|
|
2222
2619
|
declare function openPointerSession(origin: Element, down: PointerEvent | React.PointerEvent, cb: PointerSessionCallbacks, opts?: PointerSessionOptions): PointerSession;
|
|
2223
2620
|
|
|
2621
|
+
/**
|
|
2622
|
+
* A press that begins on one element and is carried over another: the in-app
|
|
2623
|
+
* sibling of an OS drag. The source opens the carry; every dispatcher with a
|
|
2624
|
+
* `carry` channel registers its element as a surface, and the one under the
|
|
2625
|
+
* pointer dispatches the carry as `carry` events through its bindings.
|
|
2626
|
+
*/
|
|
2627
|
+
|
|
2628
|
+
/** What a carry brings. `type` is what a `carry` spec's `types` globs filter. */
|
|
2629
|
+
interface CarryPayload {
|
|
2630
|
+
type: string;
|
|
2631
|
+
value: unknown;
|
|
2632
|
+
}
|
|
2633
|
+
/** The pointer facts a surface reads off each step of a carry. */
|
|
2634
|
+
interface CarryPointer {
|
|
2635
|
+
clientX: number;
|
|
2636
|
+
clientY: number;
|
|
2637
|
+
altKey: boolean;
|
|
2638
|
+
ctrlKey: boolean;
|
|
2639
|
+
metaKey: boolean;
|
|
2640
|
+
shiftKey: boolean;
|
|
2641
|
+
}
|
|
2642
|
+
/** What a carry did on a surface: came over it, moved on it, was released on
|
|
2643
|
+
* it, or left it — off it, or canceled while over it. */
|
|
2644
|
+
type CarryStage = 'enter' | 'move' | 'drop' | 'leave';
|
|
2645
|
+
/** What {@link startCarry} reports to its source. Exactly one of `onEnd` /
|
|
2646
|
+
* `onCancel` fires. */
|
|
2647
|
+
interface CarryCallbacks {
|
|
2648
|
+
/** Every move of the carrying pointer. */
|
|
2649
|
+
onMove?: (e: PointerEvent) => void;
|
|
2650
|
+
/** The pointer was released. `taken` is whether a binding on the surface
|
|
2651
|
+
* under it took the carry. */
|
|
2652
|
+
onEnd?: (result: {
|
|
2653
|
+
taken: boolean;
|
|
2654
|
+
event: PointerEvent;
|
|
2655
|
+
}) => void;
|
|
2656
|
+
/** The carry ended without a release. Any surface it was over has had its leave. */
|
|
2657
|
+
onCancel?: (reason: PointerSessionCancelReason) => void;
|
|
2658
|
+
}
|
|
2659
|
+
/** A live carry, opened by {@link startCarry}. */
|
|
2660
|
+
interface Carry {
|
|
2661
|
+
readonly pointerId: number;
|
|
2662
|
+
readonly active: boolean;
|
|
2663
|
+
/** End the carry now, as a cancel. Idempotent. */
|
|
2664
|
+
cancel(): void;
|
|
2665
|
+
}
|
|
2666
|
+
/**
|
|
2667
|
+
* Carry `payload` from the press `down` on `origin` until the pointer is
|
|
2668
|
+
* released or the press is canceled. While it moves, the surface under it
|
|
2669
|
+
* hears the carry come over, move, and leave; a release over one is a drop
|
|
2670
|
+
* there. The press is captured to `origin` as any drag is, so nothing the
|
|
2671
|
+
* pointer passes over sees it as a press of its own.
|
|
2672
|
+
*/
|
|
2673
|
+
declare function startCarry(origin: Element, down: PointerEvent | React.PointerEvent, payload: CarryPayload, cb?: CarryCallbacks): Carry;
|
|
2674
|
+
|
|
2224
2675
|
/** What the registry knows at dispatch time. */
|
|
2225
2676
|
interface EligibilityState {
|
|
2226
2677
|
focusedId: string | null;
|
|
@@ -2401,6 +2852,8 @@ interface ToolDef<TOverlay = KernelOverlay> {
|
|
|
2401
2852
|
* binding that points at an action id nothing registered.
|
|
2402
2853
|
*/
|
|
2403
2854
|
actions?: Action[];
|
|
2855
|
+
/** Menus this tool adds while it is active. */
|
|
2856
|
+
menus?: MenuDef[];
|
|
2404
2857
|
/** Hook name as exported from the kit barrel (e.g. `'useHandTool'`).
|
|
2405
2858
|
* Set by built-in hooks for inspector / debugging. Consumer-authored
|
|
2406
2859
|
* tools may set this to surface their hook name; omitted is fine.
|
|
@@ -2457,6 +2910,29 @@ interface ToolDef<TOverlay = KernelOverlay> {
|
|
|
2457
2910
|
*/
|
|
2458
2911
|
declare function defineTool<TOverlay = KernelOverlay>(def: ToolDef<TOverlay>): Tool<TOverlay>;
|
|
2459
2912
|
|
|
2913
|
+
/**
|
|
2914
|
+
* Tool sets: a named group of tool ids, the unit a consumer opts into whole.
|
|
2915
|
+
* A set names ids, never tools, because a tool is built by a hook that holds
|
|
2916
|
+
* its state; whatever mounts the set supplies the tool for each id.
|
|
2917
|
+
*/
|
|
2918
|
+
/** A named group of tool ids. A member is an id or another set, whose ids it takes in order. */
|
|
2919
|
+
interface ToolSet<TId extends string = string> {
|
|
2920
|
+
readonly id: string;
|
|
2921
|
+
readonly members: readonly (TId | ToolSet<TId>)[];
|
|
2922
|
+
}
|
|
2923
|
+
/** One entry of a list of tools to mount: an id, or a set of them. */
|
|
2924
|
+
type ToolEntry<TId extends string = string> = TId | ToolSet<TId>;
|
|
2925
|
+
/** A set named `id` of `members`, in palette order. */
|
|
2926
|
+
declare function toolSet<TId extends string>(id: string, members: readonly ToolEntry<TId>[]): ToolSet<TId>;
|
|
2927
|
+
/** Whether `entry` is a set rather than a single id. */
|
|
2928
|
+
declare function isToolSet<TId extends string>(entry: ToolEntry<TId>): entry is ToolSet<TId>;
|
|
2929
|
+
/** Every id `entries` name, sets opened in place, each id once at its first place. */
|
|
2930
|
+
declare function toolIdsOf<TId extends string>(entries: readonly ToolEntry<TId>[]): TId[];
|
|
2931
|
+
/** `set` less every id `omit` names, sets opened. Keeps `set`'s id with a `-without` suffix. */
|
|
2932
|
+
declare function withoutTools<TId extends string>(set: ToolSet<TId>, ...omit: readonly ToolEntry<TId>[]): ToolSet<TId>;
|
|
2933
|
+
/** Whether `set` names `id`, directly or through a set inside it. */
|
|
2934
|
+
declare function toolSetHas<TId extends string>(set: ToolSet<TId>, id: string): boolean;
|
|
2935
|
+
|
|
2460
2936
|
/**
|
|
2461
2937
|
* One row in the route registry — a single `GestureBinding` on one tool,
|
|
2462
2938
|
* flattened into the route grammar's vocabulary so the inspector, the
|
|
@@ -2607,8 +3083,7 @@ interface ToolScopes {
|
|
|
2607
3083
|
/**
|
|
2608
3084
|
* Detect reachable route conflicts in an assembled tool set and report each one.
|
|
2609
3085
|
*
|
|
2610
|
-
* Call it once wherever a tool set is assembled, behind
|
|
2611
|
-
* `process.env.NODE_ENV !== 'production'` guard.
|
|
3086
|
+
* Call it once wherever a tool set is assembled, behind an `isDev()` guard.
|
|
2612
3087
|
*
|
|
2613
3088
|
* **Warn, never throw.** A conflict between a consumer's tool and a kit tool
|
|
2614
3089
|
* is sometimes deliberate, since the loser can still take the gesture when
|
|
@@ -2673,6 +3148,30 @@ declare function matchesKeyBinding(e: KeyboardEvent, b: KeyBinding): boolean;
|
|
|
2673
3148
|
* React keyboard event as readily as a DOM one. */
|
|
2674
3149
|
declare function historyKey(e: Pick<KeyboardEvent, 'key' | 'metaKey' | 'ctrlKey' | 'shiftKey' | 'altKey'>): 'undo' | 'redo' | null;
|
|
2675
3150
|
|
|
3151
|
+
/**
|
|
3152
|
+
* Key chords a major browser or the desktop under it takes before a page can:
|
|
3153
|
+
* reload, devtools, tab and window commands, menu accelerators. A binding on
|
|
3154
|
+
* one never fires for some users, so kit defaults steer clear of them.
|
|
3155
|
+
*
|
|
3156
|
+
* `mod` is Cmd on macOS and Ctrl elsewhere, as in a `KeySpec`. The list is
|
|
3157
|
+
* the union across Chrome, Safari, Firefox and Edge on macOS, Windows and
|
|
3158
|
+
* Linux, and errs toward listing a chord.
|
|
3159
|
+
*/
|
|
3160
|
+
|
|
3161
|
+
/** One reserved chord, and what takes it. */
|
|
3162
|
+
interface ReservedChord {
|
|
3163
|
+
key: string;
|
|
3164
|
+
mod?: boolean;
|
|
3165
|
+
alt?: boolean;
|
|
3166
|
+
shift?: boolean;
|
|
3167
|
+
/** Who takes it, for a message. */
|
|
3168
|
+
use: string;
|
|
3169
|
+
}
|
|
3170
|
+
/** Every chord the module's header describes, one entry per key. */
|
|
3171
|
+
declare const BROWSER_RESERVED_CHORDS: readonly ReservedChord[];
|
|
3172
|
+
/** The first reserved chord `spec` can fire on, or `null` when none. */
|
|
3173
|
+
declare function browserReservedChord(spec: KeySpec): ReservedChord | null;
|
|
3174
|
+
|
|
2676
3175
|
/**
|
|
2677
3176
|
* Typed keys for the gesture/behavior scratch store, `ctx.scratch`.
|
|
2678
3177
|
* `ScratchKey<T>` carries its payload type at the typing layer; at runtime
|
|
@@ -2728,12 +3227,21 @@ declare function deleteScratch<T>(store: ScratchStore, key: ScratchKey<T>): bool
|
|
|
2728
3227
|
*
|
|
2729
3228
|
* Output shape: **files first (source order), then strings (item order)**.
|
|
2730
3229
|
* For paste (`itemsFromClipboardData`) string flavors are emitted in
|
|
2731
|
-
* `INGEST_STRING_MIMES` order: text/plain, text/html, text/uri-list
|
|
3230
|
+
* `INGEST_STRING_MIMES` order: text/plain, text/html, text/uri-list, then
|
|
3231
|
+
* the admitted ones in the order they were first admitted.
|
|
2732
3232
|
*/
|
|
2733
3233
|
|
|
2734
|
-
/** The string flavors the kit
|
|
2735
|
-
*
|
|
3234
|
+
/** The string flavors the kit always keeps. Any other is dropped at
|
|
3235
|
+
* materialization unless {@link admitIngestMime} has admitted it. */
|
|
2736
3236
|
declare const INGEST_STRING_MIMES: ReadonlySet<string>;
|
|
3237
|
+
/**
|
|
3238
|
+
* Keep string flavors of `mime` on drops, drags and pastes as well as the
|
|
3239
|
+
* {@link INGEST_STRING_MIMES}, so content from the app's own UI can travel
|
|
3240
|
+
* under a type of its own. Returns a release; the flavor is kept while any
|
|
3241
|
+
* admission of it is unreleased. Core admits each exact MIME a registered
|
|
3242
|
+
* content handler names.
|
|
3243
|
+
*/
|
|
3244
|
+
declare function admitIngestMime(mime: string): () => void;
|
|
2737
3245
|
/** Wrap raw `File`s (file picker, `CanvasExtensionApi.ingest`) as
|
|
2738
3246
|
* `IngestItem`s, normalizing each MIME the same way the drop/paste paths
|
|
2739
3247
|
* do. Synchronous — `File` objects are already fully owned. */
|
|
@@ -2805,15 +3313,15 @@ declare function dlog(namespace: string, ...args: unknown[]): void;
|
|
|
2805
3313
|
* (e.g. no-op op batches suppressed). */
|
|
2806
3314
|
declare function dwarn(namespace: string, ...args: unknown[]): void;
|
|
2807
3315
|
|
|
2808
|
-
/**
|
|
2809
|
-
*
|
|
2810
|
-
*
|
|
2811
|
-
*
|
|
2812
|
-
*
|
|
2813
|
-
*
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
3316
|
+
/** The menus live now: ambient defs, then the active tool's. Defs that share
|
|
3317
|
+
* an id become one menu: each contribution's children in turn, with a
|
|
3318
|
+
* separator between contributions. Children stay at their own keys, so the
|
|
3319
|
+
* paths their values are read at do not move; a key a previous contribution
|
|
3320
|
+
* took gets `~n` appended. The first def of an id supplies the schema's
|
|
3321
|
+
* name and the def's `when`. */
|
|
3322
|
+
declare function mergeMenuDefs(defs: readonly OwnedMenuDef[], activeTool: string | null): MenuDef[];
|
|
3323
|
+
/** The first def whose `when` matches `hit`. */
|
|
3324
|
+
declare function pickMenu(defs: readonly MenuDef[], hit: MenuHit): MenuDef | undefined;
|
|
2817
3325
|
|
|
2818
3326
|
/**
|
|
2819
3327
|
* The dependency schema — the map of dep name → value type that
|
|
@@ -2836,6 +3344,10 @@ declare function dwarn(namespace: string, ...args: unknown[]): void;
|
|
|
2836
3344
|
interface DepSchema {
|
|
2837
3345
|
/** Currently active tool id + hotkey-hold stack. */
|
|
2838
3346
|
activeTool: ActiveToolContextValue;
|
|
3347
|
+
/** The menu host of the `ActionsProvider` in scope. */
|
|
3348
|
+
menuHost?: MenuHost;
|
|
3349
|
+
/** The canvas's dispatch trace, for a dep to record a mode switch into. */
|
|
3350
|
+
dispatchTrace?: DispatchTrace;
|
|
2839
3351
|
}
|
|
2840
3352
|
/** Every dep name declared by anything that has merged into {@link DepSchema}. */
|
|
2841
3353
|
type DepName = keyof DepSchema;
|
|
@@ -2846,7 +3358,7 @@ type DepName = keyof DepSchema;
|
|
|
2846
3358
|
*
|
|
2847
3359
|
* ```ts
|
|
2848
3360
|
* declare module '@weasel-js/core' {
|
|
2849
|
-
* interface ActionParamSchema {
|
|
3361
|
+
* interface ActionParamSchema { simplifyBrush: { radiusPx: number } }
|
|
2850
3362
|
* }
|
|
2851
3363
|
* ```
|
|
2852
3364
|
*
|
|
@@ -2883,4 +3395,4 @@ type KernelOverlay = OverlaySchema extends {
|
|
|
2883
3395
|
overlay: infer O;
|
|
2884
3396
|
} ? O : unknown;
|
|
2885
3397
|
|
|
2886
|
-
export {
|
|
3398
|
+
export { type Bounds as $, type Action as A, type BoundGesture as B, type Contribution as C, type DepRegistry as D, type ActionPresentation as E, type ActionSettings as F, type ActionSettingsState as G, type ActionShortcut as H, type ActionSource as I, type ActionTuning as J, type KernelOverlay as K, type LongPressOptions as L, type MenuHost as M, type NodeId as N, type OverlayPosition as O, type ActionTunings as P, type ActionsProp as Q, type RuleCtx as R, ScopeNode as S, ToolNode as T, type AnyTool as U, type AssemblyInputs as V, BROWSER_RESERVED_CHORDS as W, type BindingOpts as X, type BindingScope as Y, type BindingSource as Z, type BodyPredicate as _, type DepName as a, type PointerSessionCallbacks as a$, type BuildRuleCtxArgs as a0, COARSE_TARGET_SCALE as a1, type Carried as a2, type Carry as a3, type CarryCallbacks as a4, type CarryPayload as a5, type CarryPointer as a6, type CarryStage as a7, type Condition as a8, type Conflict as a9, type KeyReach as aA, type LongPressState as aB, type LongPressStore as aC, type MatchOutcome as aD, type MatchResult as aE, type MenuHit as aF, type MenuOpenRequest as aG, type MenuSources as aH, type MenuWhen as aI, type ModeShortcutHandlers as aJ, type ModeSwitchLogEntry as aK, type ModifierRole as aL, type ModifierRoles as aM, type ModifierState as aN, NEVER as aO, type OngoingHandle as aP, type OngoingInvoker as aQ, type OngoingOverlay as aR, type OpenMenu as aS, type OverlayRole as aT, type OverlaySchema as aU, type OwnedMenuDef as aV, PREDICATE_TARGET as aW, type PendingLongPress as aX, type PlacedBy as aY, type Point2 as aZ, type PointerSession as a_, type ContributionChrome as aa, type ContributionDeps as ab, type ContributionRouting as ac, DEFAULT_ALLOWED_CAPABILITIES as ad, DEFAULT_DEVICE_PROFILE as ae, type DebugSink as af, type DepListing as ag, type DetectedDeviceFacts as ah, type DeviceProfile as ai, type DispatchRecord as aj, type DispatchRecordInput as ak, type DispatcherContext as al, type DragSample as am, type DroppedCandidate as an, type Eligibility as ao, type EligibilityState as ap, type GestureBinding as aq, type GestureKitSettings as ar, type HandleKind as as, type HitShape as at, type HotkeyTrigger as au, INGEST_STRING_MIMES as av, type ImmediateInvoker as aw, type InvocationCtx as ax, type Invoker as ay, type KeyBinding as az, type DepSource as b, evaluateActive as b$, type PointerSessionCancelReason as b0, type PointerSessionOptions as b1, type RankedCandidate as b2, type RecordCandidate as b3, type RegistryEntry as b4, type ReservedChord as b5, type ResizeAnchor as b6, type ResolveAllOptions as b7, type ResolveOnlyResult as b8, type ResolvedCandidate as b9, actionCursor as bA, actionItemTitle as bB, actionItems as bC, actionSettingsParams as bD, actionShortcuts as bE, actionsTagged as bF, admitIngestMime as bG, assembleEntries as bH, browserReservedChord as bI, buildDepsFromRequires as bJ, buildRouteRegistry as bK, buildRuleCtx as bL, buildToolOffhandBindings as bM, clientToCanvas as bN, clientToCanvasRect as bO, createDepRegistry as bP, createDispatchTrace as bQ, createDispatcher as bR, createLongPressStore as bS, createMenuHost as bT, createTraceRing as bU, defineTool as bV, deleteScratch as bW, describeRule as bX, dlog as bY, dwarn as bZ, evaluate as b_, type RoleKeys as ba, type RoleRebinds as bb, type Rule as bc, type ScopedBinding as bd, type ScratchKey as be, type ScratchStore as bf, type SelectionApi as bg, type Selector as bh, type SpecificityPart as bi, TOOL_OFFHAND_ID as bj, type ToolDef as bk, type ToolEntry as bl, type ToolKeybinding as bm, type ToolModifiers as bn, type ToolOffhandBindingSpec as bo, type ToolPresentation as bp, type ToolScopes as bq, type ToolSet as br, type ToolSlot as bs, type TraceLogEntry as bt, type TraceRing as bu, type TunedBinding as bv, type UiOngoingControl as bw, type ViewportGestureKind as bx, type WalkStep as by, actionBindings as bz, type ActionDeps as c, tuneAction as c$, evaluateEnabled as c0, findConflicts as c1, getScratch as c2, heldKeySpec as c3, historyKey as c4, isAnchor as c5, isAnchorOrControl as c6, isBalanceGrip as c7, isBody as c8, isControlHandle as c9, modeShortcutSpec as cA, modeShortcuts as cB, multiTouchGestureId as cC, offhandKeyFor as cD, openPointerSession as cE, overlaysOf as cF, pickMenu as cG, pointerGestureId as cH, readActionSettings as cI, reportRouteConflicts as cJ, resolveDeviceProfile as cK, resolveMenu as cL, resolveParams as cM, resolveSchemaMenu as cN, roleReader as cO, routesForSpec as cP, ruleCanHoldIn as cQ, rulesExclusive as cR, scopeBindings as cS, scratchKey as cT, setScratch as cU, startCarry as cV, taggedActionItems as cW, targetConsultsAffordance as cX, toolIdsOf as cY, toolSet as cZ, toolSetHas as c_, isCornerWidget as ca, isDebugEnabled as cb, isEmpty as cc, isPathEditPart as cd, isResizeHandle as ce, isRotateHandle as cf, isSegment as cg, isSelectedBody as ch, isTool as ci, isToolSet as cj, isUnselectedBody as ck, itemTypesFromDataTransfer as cl, itemsFromClipboardData as cm, itemsFromDataTransfer as cn, itemsFromFiles as co, keySpecShortcut as cp, keysHeld as cq, liveScope as cr, makeToolOffhandAction as cs, matchActionItem as ct, matchBest as cu, matchSorted as cv, matchSortedWithBarred as cw, matchesKeyBinding as cx, mergeContributions as cy, mergeMenuDefs as cz, type ActionsRegistry as d, validateActionDefaultBinding as d0, validateActionId as d1, withoutTools as d2, ActionDisabledReason as e, type ActionVariant as f, type MenuDef as g, DepNode as h, type Tool as i, ActiveToolContextProvider as j, ActiveToolContextProviderIfRoot as k, type ActiveToolContextProviderProps as l, type ActiveToolContextValue as m, useOptionalActiveToolContext as n, type Dispatcher as o, type AffordanceHit as p, type DispatchTrace as q, type DepSchema as r, ALWAYS as s, type ActionDispatch as t, useActiveToolContext as u, type ActionEnabledResult as v, type ActionEntry as w, type ActionItem as x, type ActionParamSchema as y, type ActionParams as z };
|