@weasel-js/routing 1.11.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-QBQQIFPI.js → chunk-ARDHI47M.js} +526 -57
- package/dist/chunk-ARDHI47M.js.map +1 -0
- package/dist/{index-CmfVvwWD.d.ts → index-CyBCsdie.d.ts} +1620 -1224
- package/dist/index.d.ts +2 -2
- package/dist/index.js +231 -34
- package/dist/index.js.map +1 -1
- package/dist/{internal-21M1rTVo.d.ts → internal-DejHXi0a.d.ts} +9 -1
- package/dist/internal.d.ts +5 -5
- package/dist/react.d.ts +16 -8
- package/dist/react.js +224 -83
- package/dist/react.js.map +1 -1
- package/package.json +13 -13
- package/dist/chunk-QBQQIFPI.js.map +0 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import { ModifierKey, ClaimableGesture, GestureSpec, InputEvent, TargetSpec, IngestItem, BodyTarget, BindingOverrideInput, KeyHeldSpec, TargetPredicate, KeySpec, ClickSpec, DoubleClickSpec, DragSpec, ContextMenuSpec, ParsedModifiers, GestureName } from '@weasel-js/gestures';
|
|
2
|
+
import { FramedBox, View, Point } from '@weasel-js/geom';
|
|
3
|
+
import { CursorSpec } from '@weasel-js/cursor';
|
|
1
4
|
import { MenuEntry, Rect, MenuShortcut } from '@weasel-js/menu';
|
|
2
5
|
import { PrefGroup } from '@weasel-js/prefs';
|
|
3
|
-
import { FramedBox, View, Point } from '@weasel-js/geom';
|
|
4
6
|
import { CapabilityTag, ActiveMode, ModeRegistry } from '@weasel-js/modes';
|
|
5
|
-
import { ModifierKey, ClaimableGesture, GestureSpec, IngestItem, BodyTarget, InputEvent, TargetSpec, KeyHeldSpec, TargetPredicate, KeySpec, ParsedModifiers, GestureName } from '@weasel-js/gestures';
|
|
6
7
|
import * as _weasel_js_icons from '@weasel-js/icons';
|
|
7
8
|
import { IconRef } from '@weasel-js/icons';
|
|
8
|
-
import { CursorSpec } from '@weasel-js/cursor';
|
|
9
9
|
import * as react from 'react';
|
|
10
10
|
import { ReactNode } from 'react';
|
|
11
11
|
import { Emitter, QueryMatch } from '@weasel-js/registry';
|
|
@@ -169,423 +169,180 @@ type ResizeAnchor = {
|
|
|
169
169
|
y: 'min' | 'max' | 'free';
|
|
170
170
|
};
|
|
171
171
|
|
|
172
|
+
/** The chrome an in-flight gesture paints beside the scene. */
|
|
173
|
+
|
|
172
174
|
/**
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
* eligibility filter — and discarded.
|
|
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.
|
|
176
177
|
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
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.
|
|
179
181
|
*/
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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 & {});
|
|
197
|
+
/**
|
|
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.
|
|
206
|
+
*/
|
|
207
|
+
type OngoingOverlay = {
|
|
208
|
+
kind: 'marquee';
|
|
209
|
+
start: {
|
|
210
|
+
x: number;
|
|
211
|
+
y: number;
|
|
188
212
|
};
|
|
189
|
-
|
|
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
|
+
} | {
|
|
190
230
|
/**
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
* is the only selector that ever read one and a number is all it needs. A
|
|
195
|
-
* host whose viewport is a camera has no `View` to hand over and would
|
|
196
|
-
* otherwise have to invent one to use eligibility at all; it omits this
|
|
197
|
-
* instead, and `zoomAtLeast` declines rather than matching on a fiction.
|
|
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.
|
|
198
234
|
*/
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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.
|
|
223
261
|
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
*
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
multiActive: boolean;
|
|
240
|
-
modifiers: ModifierState;
|
|
241
|
-
action: {
|
|
242
|
-
kind: string | null;
|
|
243
|
-
id: string | null;
|
|
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;
|
|
244
277
|
};
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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
|
+
|
|
293
|
+
/**
|
|
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.
|
|
297
|
+
*/
|
|
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;
|
|
256
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;
|
|
257
314
|
/**
|
|
258
|
-
*
|
|
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
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Invoker — pluggable invocation strategy for an Action.
|
|
259
327
|
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
* recommended way (capability rules outlive new modes; mode rules have to
|
|
264
|
-
* be edited) would silently hide its chrome for every consumer that hasn't
|
|
265
|
-
* opted into modality. `NORMAL.allows` plus the implicit tags is what a
|
|
266
|
-
* mode-less canvas actually behaves like.
|
|
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).
|
|
267
331
|
*/
|
|
268
|
-
declare const DEFAULT_ALLOWED_CAPABILITIES: ReadonlySet<CapabilityTag>;
|
|
269
|
-
/** Gather the current canvas and mode state into the context that action
|
|
270
|
-
* eligibility and chrome-visibility rules are evaluated against. */
|
|
271
|
-
declare function buildRuleCtx(args: BuildRuleCtxArgs): RuleCtx;
|
|
272
332
|
|
|
333
|
+
/** A 2D point in either world or screen coordinates; geom's `Point`. */
|
|
334
|
+
type Point2 = Point;
|
|
273
335
|
/**
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
not: string;
|
|
285
|
-
} | {
|
|
286
|
-
in: readonly string[];
|
|
287
|
-
};
|
|
288
|
-
capability?: CapabilityTag | readonly CapabilityTag[] | {
|
|
289
|
-
in: readonly CapabilityTag[];
|
|
290
|
-
} | {
|
|
291
|
-
not: CapabilityTag;
|
|
292
|
-
};
|
|
293
|
-
gesturing?: boolean;
|
|
294
|
-
actionIs?: string;
|
|
295
|
-
modifierHeld?: keyof ModifierState;
|
|
296
|
-
focused?: boolean;
|
|
297
|
-
hovering?: boolean;
|
|
298
|
-
hoveringSelected?: boolean;
|
|
299
|
-
zoomAtLeast?: number;
|
|
300
|
-
/** Matches `ctx.editingAnchors` — true while a path is in anchor-edit
|
|
301
|
-
* mode. Absent flag is treated as `false`. */
|
|
302
|
-
editingAnchors?: boolean;
|
|
303
|
-
/** Matches `ctx.selectionResizable`. Absent flag is treated as `true`
|
|
304
|
-
* (resizable), so `{ resizable: true }` passes for legacy ctx builders
|
|
305
|
-
* that don't compute it. */
|
|
306
|
-
resizable?: boolean;
|
|
307
|
-
/** Matches `ctx.device.coarsePointer` — the primary pointer is imprecise
|
|
308
|
-
* (touch, most styluses). Absent device is treated as `false`. */
|
|
309
|
-
coarsePointer?: boolean;
|
|
310
|
-
/** Matches `ctx.device.canHover` — the primary pointer can hover. Absent
|
|
311
|
-
* device is treated as `true`. */
|
|
312
|
-
canHover?: boolean;
|
|
313
|
-
}
|
|
314
|
-
/**
|
|
315
|
-
* Composable visibility/eligibility rule. Trees of `all`/`any`/`not` nodes
|
|
316
|
-
* over `Selector` leaves. `when` is the escape hatch — its closure is
|
|
317
|
-
* opaque to introspection and should be avoided when a declarative form
|
|
318
|
-
* exists. Empty `all` is true; empty `any` is false.
|
|
319
|
-
*/
|
|
320
|
-
type Rule = Selector | {
|
|
321
|
-
all: readonly Rule[];
|
|
322
|
-
} | {
|
|
323
|
-
any: readonly Rule[];
|
|
324
|
-
} | {
|
|
325
|
-
not: Rule;
|
|
326
|
-
} | {
|
|
327
|
-
when: (ctx: RuleCtx) => boolean;
|
|
328
|
-
};
|
|
329
|
-
/** Constant rules. Kept here so they have a single source. */
|
|
330
|
-
declare const ALWAYS: Rule;
|
|
331
|
-
/** A rule that never passes. `any` of nothing is false. */
|
|
332
|
-
declare const NEVER: Rule;
|
|
333
|
-
/**
|
|
334
|
-
* Render a {@link Rule} as a short human-readable string, for inspectors and
|
|
335
|
-
* diagnostics that need to say WHICH rule decided something.
|
|
336
|
-
*
|
|
337
|
-
* ```
|
|
338
|
-
* { capability: 'edits-page' } → capability:edits-page
|
|
339
|
-
* { all: [{ mode: 'normal' }, { focused: true }] } → all(mode:normal, focused:true)
|
|
340
|
-
* { not: { selection: { empty: true } } } → not(selection:empty=true)
|
|
341
|
-
* { when: function hasPath() { … } } → when(hasPath)
|
|
342
|
-
* ```
|
|
343
|
-
*
|
|
344
|
-
* Never throws and always terminates.
|
|
345
|
-
*/
|
|
346
|
-
declare function describeRule(rule: Rule): string;
|
|
347
|
-
/** Evaluate a rule against a context. Pure, and cheap enough to run per
|
|
348
|
-
* frame for every piece of chrome. */
|
|
349
|
-
declare function evaluate(rule: Rule, ctx: RuleCtx): boolean;
|
|
350
|
-
/**
|
|
351
|
-
* Composable eligibility predicate with a fluent surface. `cond(ctx)`
|
|
352
|
-
* evaluates its `Rule` tree, carried at `.rule`; the fluent methods return new
|
|
353
|
-
* Conditions wrapping new trees.
|
|
354
|
-
*
|
|
355
|
-
* **Chain semantics: strict left-to-right, no precedence.** `a.and(b).or(c)` is
|
|
356
|
-
* `(a && b) || c`; `a.or(b).and(c)` is `(a || b) && c`. For grouped
|
|
357
|
-
* disjunction, name the subexpression or use the top-level `or(...)`.
|
|
358
|
-
*/
|
|
359
|
-
interface Condition {
|
|
360
|
-
(ctx: RuleCtx): boolean;
|
|
361
|
-
readonly rule: Rule;
|
|
362
|
-
/** `this && other` */
|
|
363
|
-
and(other: Condition | Rule): Condition;
|
|
364
|
-
/** `this || other` */
|
|
365
|
-
or(other: Condition | Rule): Condition;
|
|
366
|
-
/** `this && !other` */
|
|
367
|
-
andNot(other: Condition | Rule): Condition;
|
|
368
|
-
/** `this || !other` */
|
|
369
|
-
orNot(other: Condition | Rule): Condition;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Whether two rules can never hold at once — no `RuleCtx` passes both.
|
|
374
|
-
*
|
|
375
|
-
* Conservative: `true` is a proof, `false` means "may overlap". It reasons
|
|
376
|
-
* about the selector values themselves (two different `mode`s, an empty
|
|
377
|
-
* selection against a non-empty one, `focused: true` against `false`) and
|
|
378
|
-
* through `all` / `any` / `not`; a `when` closure is opaque, so it overlaps
|
|
379
|
-
* everything. The route-conflict check uses it to stay quiet about bindings
|
|
380
|
-
* that share a tuple but are never eligible together.
|
|
381
|
-
*/
|
|
382
|
-
declare function rulesExclusive(a: Rule, b: Rule): boolean;
|
|
383
|
-
/**
|
|
384
|
-
* Whether `rule` can hold while `mode` is active. Only its `mode` and
|
|
385
|
-
* `capability` tests are settled by the mode; every other test could go either
|
|
386
|
-
* way, so `false` is a proof and `true` means "may hold".
|
|
387
|
-
*/
|
|
388
|
-
declare function ruleCanHoldIn(rule: Rule, mode: ActiveMode): boolean;
|
|
389
|
-
|
|
390
|
-
/** The detected half of a profile — everything except the derived scale. */
|
|
391
|
-
type DetectedDeviceFacts = Omit<DeviceProfile, 'targetScale'>;
|
|
392
|
-
/**
|
|
393
|
-
* Handle/hit multiplier applied on a coarse pointer.
|
|
394
|
-
*
|
|
395
|
-
* 8px handle → 14px; 24px rotation distance → 42px. Counting the
|
|
396
|
-
* surrounding grab zone, that lands in the Apple HIG 44pt / Material 48dp
|
|
397
|
-
* minimum-touch-target band. One constant so it is tunable in one place.
|
|
398
|
-
*/
|
|
399
|
-
declare const COARSE_TARGET_SCALE = 1.75;
|
|
400
|
-
/**
|
|
401
|
-
* Fold consumer overrides over detected facts and derive `targetScale`.
|
|
402
|
-
*
|
|
403
|
-
* `targetScale` is re-derived AFTER the merge, so an override of
|
|
404
|
-
* `coarsePointer` alone scales the chrome as expected. An explicit
|
|
405
|
-
* `targetScale` override wins over the derivation.
|
|
406
|
-
*/
|
|
407
|
-
declare function resolveDeviceProfile(detected: DetectedDeviceFacts, overrides?: Partial<DeviceProfile>): DeviceProfile;
|
|
408
|
-
/**
|
|
409
|
-
* Assumed when `matchMedia` is unavailable (SSR, jsdom) and used as the
|
|
410
|
-
* absent-means value for `RuleCtx.device`.
|
|
411
|
-
*
|
|
412
|
-
* Derived from `resolveDeviceProfile` rather than hand-written, so it can
|
|
413
|
-
* never drift from what the function would compute for the same facts. A
|
|
414
|
-
* mouse-like device is the safe default: it is what the kit has always
|
|
415
|
-
* assumed, so an absent profile changes nothing for existing consumers.
|
|
416
|
-
*/
|
|
417
|
-
declare const DEFAULT_DEVICE_PROFILE: DeviceProfile;
|
|
418
|
-
|
|
419
|
-
/**
|
|
420
|
-
* Modifier roles: the modifiers an ongoing action reads while it runs, named
|
|
421
|
-
* by what they do rather than which key they are, so a binding or a tuning can
|
|
422
|
-
* move one to another key without forking the action.
|
|
423
|
-
*/
|
|
424
|
-
|
|
425
|
-
/** The keys that hold a role: any one of them held holds it. `'mod'` is Cmd
|
|
426
|
-
* on a Mac and Ctrl elsewhere. An empty list never holds it. */
|
|
427
|
-
type RoleKeys = readonly ModifierKey[];
|
|
428
|
-
/** One modifier an action reads mid-gesture. */
|
|
429
|
-
interface ModifierRole {
|
|
430
|
-
label: string;
|
|
431
|
-
/** The keys that hold it unless a tuning or a binding says otherwise. */
|
|
432
|
-
keys: RoleKeys;
|
|
433
|
-
}
|
|
434
|
-
/** An action's roles by id, e.g. `'handle.holdAngle'`. */
|
|
435
|
-
type ModifierRoles = Readonly<Record<string, ModifierRole>>;
|
|
436
|
-
/** Keys a tuning or a binding gives some of an action's roles. */
|
|
437
|
-
type RoleRebinds = Readonly<Record<string, RoleKeys>>;
|
|
438
|
-
/** Whether any of `keys` is held. */
|
|
439
|
-
declare function keysHeld(keys: RoleKeys, modifiers: ModifierState, isMac: boolean): boolean;
|
|
440
|
-
/**
|
|
441
|
-
* A reader for an action's own roles. It answers from `ctx.roles`, which the
|
|
442
|
-
* dispatcher resolves on every event, and falls back to the role's default
|
|
443
|
-
* keys where nothing resolved them: an invoker called directly, or a context
|
|
444
|
-
* a cancel-all built with no event.
|
|
445
|
-
*/
|
|
446
|
-
declare function roleReader<R extends ModifierRoles>(roles: R): (ctx: {
|
|
447
|
-
roles?: Readonly<Record<string, boolean>>;
|
|
448
|
-
modifiers: ModifierState;
|
|
449
|
-
}, id: keyof R & string) => boolean;
|
|
450
|
-
|
|
451
|
-
/** The chrome an in-flight gesture paints beside the scene. */
|
|
452
|
-
|
|
453
|
-
/**
|
|
454
|
-
* What a `polyline` overlay is, in a word — the narrowest thing an action can
|
|
455
|
-
* say about how its run should look without naming a paint.
|
|
456
|
-
*
|
|
457
|
-
* The named roles are the kit's; a painter maps each to a style and falls back
|
|
458
|
-
* to plain chrome for one it does not know, so a package outside core can coin
|
|
459
|
-
* its own and still be drawn.
|
|
460
|
-
*/
|
|
461
|
-
type OverlayRole =
|
|
462
|
-
/** Generic in-flight chrome — the marquee/lasso paint. */
|
|
463
|
-
'chrome'
|
|
464
|
-
/** A cut the gesture would make. */
|
|
465
|
-
| 'cut'
|
|
466
|
-
/** An edge the gesture would author between two points. */
|
|
467
|
-
| 'connector'
|
|
468
|
-
/** The stretch of a path a soft-selection drag reaches. */
|
|
469
|
-
| 'falloff'
|
|
470
|
-
/** What a lock holds a dragged point to — a line, a circle. */
|
|
471
|
-
| 'lock' | (string & {});
|
|
472
|
-
/**
|
|
473
|
-
* What an in-flight gesture proposes to paint *beside* the scene, returned by
|
|
474
|
-
* `OngoingHandle.overlay()` — a marquee rect, a lasso trail, the outline of a
|
|
475
|
-
* shape being dragged out, the line a slice would cut along.
|
|
476
|
-
*
|
|
477
|
-
* Every variant is world geometry and nothing else — no `DrawCommand`, no
|
|
478
|
-
* color, no coordinate space to choose — so a surface rendering the same
|
|
479
|
-
* gesture some other way can still read it. The painter owns every style
|
|
480
|
-
* decision.
|
|
481
|
-
*/
|
|
482
|
-
type OngoingOverlay = {
|
|
483
|
-
kind: 'marquee';
|
|
484
|
-
start: {
|
|
485
|
-
x: number;
|
|
486
|
-
y: number;
|
|
487
|
-
};
|
|
488
|
-
current: {
|
|
489
|
-
x: number;
|
|
490
|
-
y: number;
|
|
491
|
-
};
|
|
492
|
-
shiftHeld: boolean;
|
|
493
|
-
} | {
|
|
494
|
-
kind: 'lasso';
|
|
495
|
-
vertices: ReadonlyArray<{
|
|
496
|
-
x: number;
|
|
497
|
-
y: number;
|
|
498
|
-
}>;
|
|
499
|
-
current: {
|
|
500
|
-
x: number;
|
|
501
|
-
y: number;
|
|
502
|
-
};
|
|
503
|
-
shiftHeld: boolean;
|
|
504
|
-
} | {
|
|
505
|
-
/**
|
|
506
|
-
* An open run of world points the gesture is proposing — a cut line, a
|
|
507
|
-
* routed connector. `role` is the whole of what an action says about
|
|
508
|
-
* appearance.
|
|
509
|
-
*/
|
|
510
|
-
kind: 'polyline';
|
|
511
|
-
/** At least two points. Anything shorter is dropped in resolution. */
|
|
512
|
-
points: ReadonlyArray<{
|
|
513
|
-
x: number;
|
|
514
|
-
y: number;
|
|
515
|
-
}>;
|
|
516
|
-
/** What the run means. Omitted reads as {@link OverlayRole} `'chrome'`. */
|
|
517
|
-
role?: OverlayRole;
|
|
518
|
-
} | {
|
|
519
|
-
/**
|
|
520
|
-
* A round brush dragged through world points: the area it has swept so
|
|
521
|
-
* far, and its footprint at the last point. The radius is in screen
|
|
522
|
-
* pixels, so the footprint keeps its size at every zoom.
|
|
523
|
-
*/
|
|
524
|
-
kind: 'brush';
|
|
525
|
-
/** The brush's path so far, at least one point; the last is where it is now. */
|
|
526
|
-
points: ReadonlyArray<{
|
|
527
|
-
x: number;
|
|
528
|
-
y: number;
|
|
529
|
-
}>;
|
|
530
|
-
radiusPx: number;
|
|
531
|
-
} | {
|
|
532
|
-
/**
|
|
533
|
-
* Live insert-drag preview. Pre-commit there is no scene node to ghost
|
|
534
|
-
* via `previewIds()`/`previewPose()`, so insert paints its preview
|
|
535
|
-
* through the overlay instead.
|
|
536
|
-
*
|
|
537
|
-
* `bounds` is the AABB of the current drag (start/current normalized).
|
|
538
|
-
* `extras` is the per-kind extras the action already collected; the
|
|
539
|
-
* overlay renderer narrows on `shape` and casts the field shape it
|
|
540
|
-
* expects.
|
|
541
|
-
*/
|
|
542
|
-
kind: 'insertPreview';
|
|
543
|
-
/** Which preview the dispatcher's overlay draws. The kit's own shape
|
|
544
|
-
* kinds are the ones it knows how to render; a consumer-defined kind
|
|
545
|
-
* is legal and simply gets no live preview. */
|
|
546
|
-
shape: string;
|
|
547
|
-
bounds: {
|
|
548
|
-
x: number;
|
|
549
|
-
y: number;
|
|
550
|
-
width: number;
|
|
551
|
-
height: number;
|
|
552
|
-
};
|
|
553
|
-
extras: unknown;
|
|
554
|
-
/** World-space point to paint a small "anchor" dot at, marking the
|
|
555
|
-
* click point — for radial shapes (polygon/star), where no vertex
|
|
556
|
-
* sits on it, and for any shape in center mode. */
|
|
557
|
-
anchorPoint?: {
|
|
558
|
-
x: number;
|
|
559
|
-
y: number;
|
|
560
|
-
};
|
|
561
|
-
/** World affines the shape will also be drawn under once it lands, by
|
|
562
|
-
* the container it is drawn into: the preview is painted under each. */
|
|
563
|
-
copies?: readonly (readonly [number, number, number, number, number, number])[];
|
|
564
|
-
};
|
|
565
|
-
/** A handle's overlays as a list: empty when it has none. */
|
|
566
|
-
declare function overlaysOf(handle: OngoingHandle): readonly OngoingOverlay[];
|
|
567
|
-
|
|
568
|
-
/**
|
|
569
|
-
* Invoker — pluggable invocation strategy for an Action.
|
|
570
|
-
*
|
|
571
|
-
* An Action's `invoker` field describes how it's run. `immediate` invokers
|
|
572
|
-
* fire once (delete, align, undo). `ongoing` invokers open a phase machine
|
|
573
|
-
* driven by the dispatcher (move, resize, pinch-zoom).
|
|
574
|
-
*/
|
|
575
|
-
|
|
576
|
-
/** A 2D point in either world or screen coordinates; geom's `Point`. */
|
|
577
|
-
type Point2 = Point;
|
|
578
|
-
/**
|
|
579
|
-
* Which UI affordance was hit at pointerdown. Populated by the dispatcher when
|
|
580
|
-
* the `affordanceAt` thunk is provided to `useGestureDispatcher`.
|
|
581
|
-
*
|
|
582
|
-
* `kind` is a discriminator string:
|
|
583
|
-
* - `'handle:top-left'` / `'handle:top-right'` / `'handle:bottom-left'` /
|
|
584
|
-
* `'handle:bottom-right'` — corner resize handles.
|
|
585
|
-
* - `'handle:top'` / `'handle:right'` / `'handle:bottom'` / `'handle:left'` —
|
|
586
|
-
* edge resize handles.
|
|
587
|
-
* - `'rotate-handle'` — the rotation affordance.
|
|
588
|
-
* - `'anchor:N'` — a path anchor at index N.
|
|
336
|
+
* Which UI affordance was hit at pointerdown. Populated by the dispatcher when
|
|
337
|
+
* the `affordanceAt` thunk is provided to `useGestureDispatcher`.
|
|
338
|
+
*
|
|
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.
|
|
589
346
|
*/
|
|
590
347
|
interface AffordanceHit {
|
|
591
348
|
/** Discriminator string, e.g. `'handle:bottom-right'`. */
|
|
@@ -747,6 +504,11 @@ interface InvocationCtx {
|
|
|
747
504
|
/** Per-invocation options the dispatcher reads from a `GestureBinding`'s
|
|
748
505
|
* `opts` field and passes to `OngoingInvoker.start`. */
|
|
749
506
|
interface BindingOpts {
|
|
507
|
+
/** The id a binding override names this binding by, `<owner>.<role>`:
|
|
508
|
+
* `undo.key`, `rect.drag`, `nudge.left.big`. Without one it is its owner and
|
|
509
|
+
* its position (`<entry>#<n>`, `<action>@<n>`), which moves when the defaults
|
|
510
|
+
* are reordered, so a binding a host may persist an override for declares one. */
|
|
511
|
+
id?: string;
|
|
750
512
|
/** Gesture behaviors the action's own invoker runs. Routing carries the
|
|
751
513
|
* array to the action and never reads an element, so the element type is
|
|
752
514
|
* open here; `@weasel-js/core` narrows it. */
|
|
@@ -777,6 +539,12 @@ interface BindingOpts {
|
|
|
777
539
|
* binding; this tells the route-conflict check the order is the design.
|
|
778
540
|
*/
|
|
779
541
|
yields?: boolean;
|
|
542
|
+
/**
|
|
543
|
+
* For a key binding: live while the pointer is over the surface, whichever
|
|
544
|
+
* canvas holds the keys, and only then. For a key that means "here" — a
|
|
545
|
+
* magnifier's peek — on a page with more than one surface that hears it.
|
|
546
|
+
*/
|
|
547
|
+
over?: boolean;
|
|
780
548
|
/**
|
|
781
549
|
* Keys for some of the action's `modifierRoles` on this binding, by role
|
|
782
550
|
* id, over the action's defaults and any tuning's.
|
|
@@ -822,8 +590,11 @@ interface OngoingHandle {
|
|
|
822
590
|
*
|
|
823
591
|
* `previewIds()` enumerates the displaced node ids; `previewPose(id)`
|
|
824
592
|
* returns the interim pose for one of them (shape opaque — the canvas
|
|
825
|
-
* casts to its `TPose` parameter).
|
|
826
|
-
*
|
|
593
|
+
* casts to its `TPose` parameter). `previewIds` returning `null`, or
|
|
594
|
+
* omitted, means "no preview this gesture". An id with no `previewPose`
|
|
595
|
+
* is shown at its effective pose: an action that publishes its poses to
|
|
596
|
+
* the scene's pose overrides, as move, resize and rotate do, leaves
|
|
597
|
+
* `previewPose` out rather than answering the same pose twice.
|
|
827
598
|
*/
|
|
828
599
|
previewIds?(): Iterable<string> | null;
|
|
829
600
|
previewPose?(id: string): unknown | null;
|
|
@@ -930,902 +701,1355 @@ interface BindingSource {
|
|
|
930
701
|
*/
|
|
931
702
|
declare function actionBindings(action: BindingSource): GestureBinding[];
|
|
932
703
|
|
|
704
|
+
/** What a key no open keymap binds does: `'modal'` swallows it and leaves the
|
|
705
|
+
* keymap open; `'cascade'` closes the keymap and dispatches it as if none were. */
|
|
706
|
+
type KeymapUnbound = 'modal' | 'cascade';
|
|
707
|
+
/** What holds a level opened with `{ hold: true }` open: no key releases it, so it closes on Escape. */
|
|
708
|
+
declare const KEYMAP_HOLD = "hold";
|
|
709
|
+
/** One open level of a {@link KeymapStack}. */
|
|
710
|
+
interface OpenKeymap {
|
|
711
|
+
readonly id: string;
|
|
712
|
+
readonly label: string;
|
|
713
|
+
/** The keymap's bindings as declared, for a hint to list. */
|
|
714
|
+
readonly bindings: readonly GestureBinding[];
|
|
715
|
+
/** The gesture id of the leader holding this level open, while it is held,
|
|
716
|
+
* or {@link KEYMAP_HOLD}. */
|
|
717
|
+
readonly heldBy?: string;
|
|
718
|
+
/** Whether input arrived while the leader was held, so its release closes the level. */
|
|
719
|
+
readonly usedWhileHeld: boolean;
|
|
720
|
+
}
|
|
721
|
+
/** What the keymaps made of the raw input in progress: `'taken'` when one of
|
|
722
|
+
* them handled it, `'cascaded'` when it went on past them, `null` before either. */
|
|
723
|
+
type KeymapVerdict = 'taken' | 'cascaded' | null;
|
|
724
|
+
/** The keymaps open on one surface, innermost last; a canvas's `keymaps` dep. */
|
|
725
|
+
interface KeymapStack {
|
|
726
|
+
/** Every open level, outermost first. */
|
|
727
|
+
open(): readonly OpenKeymap[];
|
|
728
|
+
innermost(): OpenKeymap | null;
|
|
729
|
+
/** Open a level. `heldBy` names what holds it open; `bindings` are what a hint lists. */
|
|
730
|
+
push(id: string, label: string, opts?: {
|
|
731
|
+
heldBy?: string;
|
|
732
|
+
bindings?: readonly GestureBinding[];
|
|
733
|
+
}): void;
|
|
734
|
+
/** Mark the levels the last raw input opened as held by `heldBy`: the key
|
|
735
|
+
* that opened them is still down. False when it opened none. */
|
|
736
|
+
holdOpened(heldBy: string): boolean;
|
|
737
|
+
/** The leader `heldBy` came up: close its level, those above it and the
|
|
738
|
+
* tapped ones beneath it if input arrived meanwhile, else leave it open
|
|
739
|
+
* for one more input. */
|
|
740
|
+
release(heldBy: string): void;
|
|
741
|
+
/** Close the innermost level: Escape. */
|
|
742
|
+
closeInnermost(): void;
|
|
743
|
+
/** One of the keymaps took this input, or a pointer gesture went past them.
|
|
744
|
+
* Marks held levels used and closes once-levels down to the innermost held one. */
|
|
745
|
+
inputSeen(): void;
|
|
746
|
+
/** A key no keymap binds was swallowed: held levels count it as input, and nothing closes. */
|
|
747
|
+
swallowed(): void;
|
|
748
|
+
/** This input went on past the keymaps: as `inputSeen`, remembered as a cascade. */
|
|
749
|
+
cascaded(): void;
|
|
750
|
+
/** What the keymaps made of the input in progress. */
|
|
751
|
+
verdict(): KeymapVerdict;
|
|
752
|
+
closeAll(): void;
|
|
753
|
+
/** Run `fn` as one raw input: closes made inside apply when it returns. Nests. */
|
|
754
|
+
batch<T>(fn: () => T): T;
|
|
755
|
+
subscribe(listener: () => void): () => void;
|
|
756
|
+
getVersion(): number;
|
|
757
|
+
}
|
|
758
|
+
/** An empty {@link KeymapStack}. `useGestureDispatcher` makes one per surface. */
|
|
759
|
+
declare function createKeymapStack(): KeymapStack;
|
|
760
|
+
|
|
933
761
|
/**
|
|
934
|
-
*
|
|
935
|
-
*
|
|
936
|
-
* strings via its own label map.
|
|
937
|
-
*/
|
|
938
|
-
declare const ActionDisabledReason: {
|
|
939
|
-
readonly SelectionRequired: "selection-required";
|
|
940
|
-
readonly SceneEmpty: "scene-empty";
|
|
941
|
-
readonly NotApplicable: "not-applicable";
|
|
942
|
-
/** Sentinel: the predicate threw. Surfaced by `evaluateEnabled`'s catch. */
|
|
943
|
-
readonly PredicateThrew: "predicate-threw";
|
|
944
|
-
};
|
|
945
|
-
/** Why an action is unavailable right now. */
|
|
946
|
-
type ActionDisabledReason = (typeof ActionDisabledReason)[keyof typeof ActionDisabledReason];
|
|
947
|
-
/**
|
|
948
|
-
* What a palette, menu or toolbar renders for an action. No part of this is
|
|
949
|
-
* read during dispatch.
|
|
762
|
+
* Re-exports the pure matcher primitives from `@weasel-js/gestures` and layers
|
|
763
|
+
* the actions-layer binding-scope / matchBest logic on top.
|
|
950
764
|
*/
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
*
|
|
962
|
-
*
|
|
963
|
-
|
|
964
|
-
tags?: readonly string[];
|
|
965
|
-
/** Display override for the keyboard shortcut. When omitted, palette
|
|
966
|
-
* surfaces derive a label from `defaultBinding` via their own
|
|
967
|
-
* formatter. */
|
|
968
|
-
shortcut?: string;
|
|
969
|
-
/** The distinct things a parametric action does, one per set of params —
|
|
970
|
-
* `flip` flips horizontally or vertically, `reorder.forward` moves one step
|
|
971
|
-
* or all the way. A bar, menu or palette shows one entry per variant and
|
|
972
|
-
* triggers the action with that variant's `params`. Omitted = the action
|
|
973
|
-
* is one entry. See `actionItems`. */
|
|
974
|
-
variants?: readonly ActionVariant[];
|
|
765
|
+
|
|
766
|
+
/** Where a binding came from, which is also its priority: a held hotkey beats
|
|
767
|
+
* the active tool, which beats bindings that are always in scope. */
|
|
768
|
+
type BindingScope = 'ambient' | 'active' | 'hotkey';
|
|
769
|
+
/** A binding paired with where it came from, ready to be matched against an
|
|
770
|
+
* event. */
|
|
771
|
+
interface ScopedBinding {
|
|
772
|
+
binding: GestureBinding;
|
|
773
|
+
scope: BindingScope;
|
|
774
|
+
/** Id of the tool or contribution that declared this binding —
|
|
775
|
+
* `'&'`-channel phase atoms resolve to it. `null` for a registered
|
|
776
|
+
* action's own binding. */
|
|
777
|
+
ownerId: string | null;
|
|
975
778
|
}
|
|
779
|
+
/** The binding that won a match. */
|
|
780
|
+
interface MatchResult {
|
|
781
|
+
binding: GestureBinding;
|
|
782
|
+
scope: BindingScope;
|
|
783
|
+
/** Id of the entry that declared the binding — propagated from
|
|
784
|
+
* `ScopedBinding` so the dispatcher can record it as the handle owner. */
|
|
785
|
+
ownerId: string | null;
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* True when a spec's target actually consults the affordance hit rather than
|
|
789
|
+
* only the body classification. `kindOf` predicates are handed the hit;
|
|
790
|
+
* `affordance:<k>` matches on its `kind`. The body-class strings (`'empty'`,
|
|
791
|
+
* `'selected-body'`, `'unselected-body'`) and the `kind:` forms resolve from
|
|
792
|
+
* `bodyTarget` / `bodyKind` and never see it.
|
|
793
|
+
*
|
|
794
|
+
* A predicate declares which it is with `readsAffordance`; one that declares
|
|
795
|
+
* nothing, which only untyped code can write, counts as not reading the hit.
|
|
796
|
+
*/
|
|
797
|
+
declare function targetConsultsAffordance(specTarget: TargetSpec | undefined): boolean;
|
|
798
|
+
/** The first of {@link matchSorted}'s matches, or null when nothing matches. */
|
|
799
|
+
declare function matchBest(e: InputEvent, bindings: readonly ScopedBinding[], isMac: boolean, engagedChannels?: ReadonlySet<string>): MatchResult | null;
|
|
976
800
|
/**
|
|
977
|
-
*
|
|
801
|
+
* Find every binding that matches the event, sorted by precedence
|
|
802
|
+
* (best → worst). Ordering rules:
|
|
803
|
+
*
|
|
804
|
+
* - Scopes by priority: hotkey > active > ambient.
|
|
805
|
+
* - Within a scope, more-specific bindings beat less-specific (see
|
|
806
|
+
* `specificity()`).
|
|
807
|
+
* - Among same-specificity bindings, first-declared wins.
|
|
978
808
|
*/
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
/**
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
* `opts.params` to find the variant's shortcuts. */
|
|
989
|
-
params: Readonly<Record<string, unknown>>;
|
|
809
|
+
declare function matchSorted(e: InputEvent, bindings: readonly ScopedBinding[], isMac: boolean, engagedChannels?: ReadonlySet<string>, warn?: (message: string) => void): MatchResult[];
|
|
810
|
+
/** {@link matchSorted}, plus what an exclusive claim on the event barred. */
|
|
811
|
+
interface MatchOutcome {
|
|
812
|
+
matches: MatchResult[];
|
|
813
|
+
/** Bindings whose spec matched the event but which the claim barred, in
|
|
814
|
+
* the same order `matches` uses. Filled only when `collectBarred` is set. */
|
|
815
|
+
barred: MatchResult[];
|
|
816
|
+
/** Owner of the exclusive claim, when one applied. */
|
|
817
|
+
claimOwner?: string;
|
|
990
818
|
}
|
|
991
819
|
/**
|
|
992
|
-
*
|
|
993
|
-
*
|
|
820
|
+
* {@link matchSorted} that can also report the bindings an exclusive claim
|
|
821
|
+
* kept out. Collecting them costs a spec match per barred binding, so it is
|
|
822
|
+
* opt-in — the dispatcher asks only when it is recording.
|
|
994
823
|
*/
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
*
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
* a drag still in flight they hold a MIME and nothing else, since a
|
|
1042
|
-
* browser withholds contents until the drop. An action that takes only
|
|
1043
|
-
* some content refuses the rest here, so a drag it would not take is not
|
|
1044
|
-
* accepted and a paste it would not take stays the page's.
|
|
1045
|
-
*/
|
|
1046
|
-
enabled?: (deps?: ActionDeps, at?: {
|
|
824
|
+
declare function matchSortedWithBarred(e: InputEvent, bindings: readonly ScopedBinding[], isMac: boolean, engagedChannels?: ReadonlySet<string>, opts?: {
|
|
825
|
+
collectBarred?: boolean;
|
|
826
|
+
warn?: (message: string) => void;
|
|
827
|
+
}): MatchOutcome;
|
|
828
|
+
|
|
829
|
+
/**
|
|
830
|
+
* How one input was resolved: every binding that matched it, what each filter
|
|
831
|
+
* dropped, the order the rest were ranked in and why, and what the walk down
|
|
832
|
+
* that order did. The dispatcher fills one per input in dev builds, and
|
|
833
|
+
* `Dispatcher.explain` fills one on request without invoking anything.
|
|
834
|
+
*
|
|
835
|
+
* Plain data — no functions, no live objects — so it can be kept in a trace
|
|
836
|
+
* buffer, serialized, or built by hand as a fixture.
|
|
837
|
+
*/
|
|
838
|
+
interface DispatchRecord {
|
|
839
|
+
kind: 'dispatch';
|
|
840
|
+
ts: number;
|
|
841
|
+
input: DispatchRecordInput;
|
|
842
|
+
/** Every binding whose spec matched the input, in matcher order, including
|
|
843
|
+
* those a filter then dropped. */
|
|
844
|
+
matched: readonly RecordCandidate[];
|
|
845
|
+
dropped: readonly DroppedCandidate[];
|
|
846
|
+
/** The candidates that survived both filters, best first. */
|
|
847
|
+
ranked: readonly RankedCandidate[];
|
|
848
|
+
/** True when nothing was invoked: the record answers "what would happen". */
|
|
849
|
+
predicted: boolean;
|
|
850
|
+
/** Action id of the candidate that fired (or would), else `null`. */
|
|
851
|
+
fired: string | null;
|
|
852
|
+
outcome: 'handled' | 'unhandled';
|
|
853
|
+
}
|
|
854
|
+
/** The input a {@link DispatchRecord} describes, reduced to plain data. */
|
|
855
|
+
interface DispatchRecordInput {
|
|
856
|
+
/** `InputEvent.kind`: `'key'`, `'pointerdown'`, `'wheel'`, … */
|
|
857
|
+
eventKind: string;
|
|
858
|
+
/** For `key` / `key-held` events, the key id (`'Escape'`, `' '`, `'a'`). */
|
|
859
|
+
key?: string;
|
|
860
|
+
modifiers: {
|
|
861
|
+
alt: boolean;
|
|
862
|
+
ctrl: boolean;
|
|
863
|
+
meta: boolean;
|
|
864
|
+
shift: boolean;
|
|
865
|
+
};
|
|
866
|
+
/** The view the input was routed to; `null` for the surface's own camera. */
|
|
867
|
+
viewId: string | null;
|
|
868
|
+
/** World point, for events that carry one. */
|
|
869
|
+
world?: {
|
|
1047
870
|
x: number;
|
|
1048
871
|
y: number;
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
/**
|
|
1114
|
-
* The modifiers an ongoing action reads while it runs, by role: Shift
|
|
1115
|
-
* holding a handle's angle, Alt breaking its mirror. The dispatcher resolves
|
|
1116
|
-
* each into `InvocationCtx.roles` on the press, every move, and the release,
|
|
1117
|
-
* so a binding's `opts.roles` or a tuning's `roles` can move one to another
|
|
1118
|
-
* key. Read them with {@link roleReader}. A binding's own `mods` are matched
|
|
1119
|
-
* once, at the press; these are read throughout.
|
|
1120
|
-
*/
|
|
1121
|
-
modifierRoles?: ModifierRoles;
|
|
872
|
+
};
|
|
873
|
+
/** Active mode id, when the host supplies a rule context. */
|
|
874
|
+
mode?: string;
|
|
875
|
+
}
|
|
876
|
+
/** One matched binding. */
|
|
877
|
+
interface RecordCandidate {
|
|
878
|
+
actionId: string;
|
|
879
|
+
/** The binding's spec in route grammar — one per arg alternative. */
|
|
880
|
+
routes: readonly string[];
|
|
881
|
+
scope: BindingScope;
|
|
882
|
+
/** The tool or contribution that declared the binding; `null` for an action's own binding. */
|
|
883
|
+
ownerId: string | null;
|
|
884
|
+
/** Whether the binding's `views` names the routed view. */
|
|
885
|
+
namesView: boolean;
|
|
886
|
+
/** `specificity(spec)`: target, modifiers, phase, exact. */
|
|
887
|
+
specificity: readonly [number, number, number, number];
|
|
888
|
+
/** The action's `eligible` rule as text, when it declares one. */
|
|
889
|
+
eligible?: string;
|
|
890
|
+
}
|
|
891
|
+
/** A matched candidate a filter removed before ranking, and which filter. */
|
|
892
|
+
type DroppedCandidate =
|
|
893
|
+
/** An exclusive claim on the input barred bindings that do not read the
|
|
894
|
+
* claim's affordance. */
|
|
895
|
+
{
|
|
896
|
+
candidate: RecordCandidate;
|
|
897
|
+
filter: 'claim';
|
|
898
|
+
owner?: string;
|
|
899
|
+
}
|
|
900
|
+
/** The action's `eligible` rule does not hold. */
|
|
901
|
+
| {
|
|
902
|
+
candidate: RecordCandidate;
|
|
903
|
+
filter: 'ineligible';
|
|
904
|
+
rule: string;
|
|
905
|
+
};
|
|
906
|
+
/** One field of the `specificity` tuple, in the order it is compared. */
|
|
907
|
+
type SpecificityPart = 'target' | 'mods' | 'phase' | 'exact';
|
|
908
|
+
/** The ranking step that put a candidate below the one before it. */
|
|
909
|
+
type PlacedBy = {
|
|
910
|
+
step: 'first';
|
|
911
|
+
}
|
|
912
|
+
/** The one above names the routed view; this one does not. */
|
|
913
|
+
| {
|
|
914
|
+
step: 'view';
|
|
915
|
+
}
|
|
916
|
+
/** The one above sits in a higher scope tier. */
|
|
917
|
+
| {
|
|
918
|
+
step: 'tier';
|
|
919
|
+
}
|
|
920
|
+
/** Same tier; the one above is more specific in `part`. */
|
|
921
|
+
| {
|
|
922
|
+
step: 'specificity';
|
|
923
|
+
part: SpecificityPart;
|
|
924
|
+
}
|
|
925
|
+
/** Tied; the one above is gated by an `eligible` rule that holds. */
|
|
926
|
+
| {
|
|
927
|
+
step: 'context';
|
|
928
|
+
}
|
|
929
|
+
/** Tied on everything; registration order. */
|
|
930
|
+
| {
|
|
931
|
+
step: 'order';
|
|
932
|
+
};
|
|
933
|
+
/** What the walk did with one ranked candidate. */
|
|
934
|
+
type WalkStep = {
|
|
935
|
+
kind: 'fired';
|
|
1122
936
|
}
|
|
1123
|
-
/**
|
|
1124
|
-
*
|
|
1125
|
-
|
|
1126
|
-
|
|
937
|
+
/** Predicted records only: this candidate would fire. Nothing was started,
|
|
938
|
+
* so an ongoing action here may still bail at `start()` for real. */
|
|
939
|
+
| {
|
|
940
|
+
kind: 'would-fire';
|
|
1127
941
|
}
|
|
1128
|
-
/**
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
* register / mute / trigger surface is the React provider's.
|
|
1133
|
-
*/
|
|
1134
|
-
interface ActionSource {
|
|
1135
|
-
list(): readonly Action[];
|
|
942
|
+
/** `enabled()` returned this reason. */
|
|
943
|
+
| {
|
|
944
|
+
kind: 'declined';
|
|
945
|
+
reason: string;
|
|
1136
946
|
}
|
|
1137
|
-
/**
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
/** Which canvas hit a menu answers. Every field given must match. */
|
|
1142
|
-
interface MenuWhen {
|
|
1143
|
-
bodyTarget?: BodyTarget | readonly BodyTarget[];
|
|
1144
|
-
bodyKind?: string | readonly string[];
|
|
947
|
+
/** An ongoing action's `start()` returned an empty handle. */
|
|
948
|
+
| {
|
|
949
|
+
kind: 'empty-handle';
|
|
1145
950
|
}
|
|
1146
|
-
/**
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
id: string;
|
|
1150
|
-
when?: MenuWhen;
|
|
1151
|
-
menu: PrefGroup;
|
|
951
|
+
/** An ongoing action bound to a press-time `pointerDown` spec; refused. */
|
|
952
|
+
| {
|
|
953
|
+
kind: 'misbound';
|
|
1152
954
|
}
|
|
1153
|
-
/**
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
955
|
+
/** Its action was already tried higher up; each runs at most once. */
|
|
956
|
+
| {
|
|
957
|
+
kind: 'duplicate';
|
|
958
|
+
} | {
|
|
959
|
+
kind: 'no-such-action';
|
|
1157
960
|
}
|
|
1158
|
-
/**
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
owner: string | null;
|
|
961
|
+
/** Below the winner; never asked. */
|
|
962
|
+
| {
|
|
963
|
+
kind: 'not-asked';
|
|
1162
964
|
}
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
values?(path: string): unknown;
|
|
1174
|
-
onChange?(path: string, value: unknown): void;
|
|
1175
|
-
/** The schema an `alias` leaf's `of` is read in. Default: the menu's own. */
|
|
1176
|
-
over?: PrefGroup;
|
|
965
|
+
/** Below the winner, asked anyway (`evaluateShadowed`), and it passed. */
|
|
966
|
+
| {
|
|
967
|
+
kind: 'outranked';
|
|
968
|
+
};
|
|
969
|
+
/** A candidate that survived the filters: why it sits where it does, and
|
|
970
|
+
* what the walk did with it. */
|
|
971
|
+
interface RankedCandidate {
|
|
972
|
+
candidate: RecordCandidate;
|
|
973
|
+
placedBy: PlacedBy;
|
|
974
|
+
walk: WalkStep;
|
|
1177
975
|
}
|
|
1178
|
-
/**
|
|
1179
|
-
* Draw a prefs schema as menu entries. Action leaves become items from the
|
|
1180
|
-
* registry (one per variant for a parametric action), an `actions` leaf one
|
|
1181
|
-
* item per action in its group, a boolean a checked item, an enum radio
|
|
1182
|
-
* rows, and a number a row edited in place (all through `src.values`/`src.onChange`);
|
|
1183
|
-
* a color shows as a swatch and a string as a disabled row, both read-only. A group drawn `as` a
|
|
1184
|
-
* `menu`, `page`, `tab`, or `panel` is a submenu, and any other group's rows
|
|
1185
|
-
* sit inline between separators. Paint, field, compound, and app-defined leaves are left out.
|
|
1186
|
-
* `at` is the world point the menu was opened at and reaches `enabled` and
|
|
1187
|
-
* every action's params; `params` go to every action, under its own.
|
|
1188
|
-
*/
|
|
1189
|
-
declare function resolveSchemaMenu(schema: PrefGroup, src: MenuSources, at?: Point, params?: Record<string, unknown>): MenuEntry[];
|
|
1190
|
-
/** Turn a declared menu into model entries; see {@link resolveSchemaMenu}. */
|
|
1191
|
-
declare function resolveMenu(def: MenuDef, src: MenuSources, at?: Point, params?: Record<string, unknown>): MenuEntry[];
|
|
1192
976
|
|
|
1193
|
-
/**
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
/**
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
977
|
+
/** One entry per "mode change" — a kit state transition that isn't a
|
|
978
|
+
* dispatched input event but explains why an input was (or wasn't) handled
|
|
979
|
+
* later. Pushed via {@link DispatchTrace.recordModeSwitch}. */
|
|
980
|
+
interface ModeSwitchLogEntry {
|
|
981
|
+
kind: 'mode';
|
|
982
|
+
ts: number;
|
|
983
|
+
/** Logical mode name, e.g. `'editAnchors.editingId'`. */
|
|
984
|
+
mode: string;
|
|
985
|
+
from: string | null;
|
|
986
|
+
to: string | null;
|
|
987
|
+
/** Optional free-form context — `'enterPathEdit'`, `'exitPathEdit'`,
|
|
988
|
+
* `'selection-dropped target'`, etc. */
|
|
989
|
+
detail?: string;
|
|
1205
990
|
}
|
|
1206
|
-
/**
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
991
|
+
/** A trace entry: one {@link DispatchRecord} per resolved input, or a mode switch. */
|
|
992
|
+
type TraceLogEntry = DispatchRecord | ModeSwitchLogEntry;
|
|
993
|
+
/** A rolling buffer that records in a dev build, and in any build while something watches it. */
|
|
994
|
+
interface TraceRing<T> {
|
|
995
|
+
/** Whether a push now keeps its entry. A caller building an entry checks first. */
|
|
996
|
+
recording(): boolean;
|
|
997
|
+
push(entry: T): void;
|
|
998
|
+
/** The entries pushed after `cursor`, oldest first, and the cursor to pass
|
|
999
|
+
* next time. Start from 0. Entries the buffer has dropped are skipped. */
|
|
1000
|
+
since(cursor: number): {
|
|
1001
|
+
entries: T[];
|
|
1002
|
+
cursor: number;
|
|
1003
|
+
};
|
|
1004
|
+
/** Turn recording on until the returned release is called. */
|
|
1005
|
+
watch(): () => void;
|
|
1006
|
+
/** Hear each kept push. Returns the unsubscribe. */
|
|
1007
|
+
subscribe(listener: () => void): () => void;
|
|
1214
1008
|
}
|
|
1215
|
-
/**
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
/**
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
subscribe(fn: () => void): () => void;
|
|
1009
|
+
/** A {@link TraceRing} keeping the last `limit` entries. `dev` defaults to the build's. */
|
|
1010
|
+
declare function createTraceRing<T>(limit?: number, dev?: boolean): TraceRing<T>;
|
|
1011
|
+
/** One canvas's dispatch trace. `useGestureDispatcher` makes one, puts it on
|
|
1012
|
+
* every `DispatcherContext` it builds, and publishes it as the `dispatchTrace` dep. */
|
|
1013
|
+
interface DispatchTrace extends TraceRing<TraceLogEntry> {
|
|
1014
|
+
recordModeSwitch(mode: string, from: string | null, to: string | null, detail?: string): void;
|
|
1015
|
+
/** Offer the record for what a press at the pointer would do, built only
|
|
1016
|
+
* when {@link DispatchTrace.live} reads it. `null` withdraws it. */
|
|
1017
|
+
setLive(explain: (() => DispatchRecord) | null): void;
|
|
1018
|
+
live(): DispatchRecord | null;
|
|
1226
1019
|
}
|
|
1227
|
-
/**
|
|
1228
|
-
declare function
|
|
1229
|
-
activeTool(): string | null;
|
|
1230
|
-
}): MenuHost;
|
|
1020
|
+
/** A {@link DispatchTrace} keeping the last `limit` entries. `dev` defaults to the build's. */
|
|
1021
|
+
declare function createDispatchTrace(limit?: number, dev?: boolean): DispatchTrace;
|
|
1231
1022
|
|
|
1232
1023
|
/**
|
|
1233
|
-
*
|
|
1234
|
-
*
|
|
1235
|
-
*
|
|
1236
|
-
*
|
|
1024
|
+
* Live state read by rule evaluation. Built once per frame on the consuming
|
|
1025
|
+
* surface — chrome-caps, the affordance pipeline, the dispatcher's
|
|
1026
|
+
* eligibility filter — and discarded.
|
|
1027
|
+
*
|
|
1028
|
+
* Adding a new field is additive: existing rules don't change, new
|
|
1029
|
+
* selector atoms can read it.
|
|
1237
1030
|
*/
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
readonly
|
|
1031
|
+
interface RuleCtx {
|
|
1032
|
+
readonly focused: boolean;
|
|
1033
|
+
readonly selection: readonly NodeId[];
|
|
1034
|
+
readonly multiActive: boolean;
|
|
1035
|
+
readonly modifiers: ModifierState;
|
|
1036
|
+
readonly action: {
|
|
1037
|
+
readonly kind: string | null;
|
|
1038
|
+
readonly id: string | null;
|
|
1039
|
+
};
|
|
1040
|
+
readonly hover: NodeId | null;
|
|
1041
|
+
/**
|
|
1042
|
+
* How far the viewport is zoomed in, as one number — `1` at native scale.
|
|
1043
|
+
*
|
|
1044
|
+
* A scalar rather than the `View` this used to carry, because `zoomAtLeast`
|
|
1045
|
+
* is the only selector that ever read one and a number is all it needs. A
|
|
1046
|
+
* host whose viewport is a camera has no `View` to hand over and would
|
|
1047
|
+
* otherwise have to invent one to use eligibility at all; it omits this
|
|
1048
|
+
* instead, and `zoomAtLeast` declines rather than matching on a fiction.
|
|
1049
|
+
*/
|
|
1050
|
+
readonly zoom?: number;
|
|
1051
|
+
/** Active mode id. `'normal'` when no non-default mode is engaged. */
|
|
1052
|
+
readonly mode: string;
|
|
1053
|
+
/** Capability tags allowed by the active mode (the union of
|
|
1054
|
+
* `ModeDefinition.allows` plus implicit tags). The `capability:`
|
|
1055
|
+
* selector reads this to determine whether a tag is permitted. */
|
|
1056
|
+
readonly allowedCapabilities: ReadonlySet<CapabilityTag>;
|
|
1057
|
+
/** Whether the current selection may be resized. `<SceneCanvas>` folds
|
|
1058
|
+
* `selectTool.resize.resizable` over the selection (true only when every
|
|
1059
|
+
* selected node is resizable). Read by the `resizable:` selector to gate
|
|
1060
|
+
* `selection.resize-handles`. Absent (legacy ctx builders) is treated as
|
|
1061
|
+
* resizable — back-compat: handles show unless a consumer opts a node out. */
|
|
1062
|
+
readonly selectionResizable?: boolean;
|
|
1063
|
+
/** Whether a path is currently in anchor-edit mode. Read by the
|
|
1064
|
+
* `editingAnchors:` selector, which gates the path-edit chrome.
|
|
1065
|
+
*
|
|
1066
|
+
* This is deliberately a fact about state, not about permission: the
|
|
1067
|
+
* anchor overlay and the anchor hit-test must agree, and the thing they
|
|
1068
|
+
* must agree on is "is there an edited path right now", which no
|
|
1069
|
+
* capability or mode id answers. A mode that allows `edits-anchors`
|
|
1070
|
+
* with nothing being edited should draw no anchors. Absent is treated
|
|
1071
|
+
* as false. */
|
|
1072
|
+
readonly editingAnchors?: boolean;
|
|
1073
|
+
/** Device facts — pointer coarseness, hover capability, density.
|
|
1074
|
+
*
|
|
1075
|
+
* Absent (legacy ctx builders) is treated as
|
|
1076
|
+
* {@link DEFAULT_DEVICE_PROFILE}: a fine pointer that can hover, at
|
|
1077
|
+
* density 1. That is what the kit assumed before this field existed, so
|
|
1078
|
+
* an absent profile is behavior-preserving by construction. */
|
|
1079
|
+
readonly device?: DeviceProfile;
|
|
1080
|
+
/** The view the input being dispatched landed in — a view id, or `null`
|
|
1081
|
+
* for the surface's own camera. The dispatcher fills it; absent anywhere
|
|
1082
|
+
* else a rule is evaluated. */
|
|
1083
|
+
readonly viewId?: string | null;
|
|
1243
1084
|
}
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
/**
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
/**
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
start(): N;
|
|
1267
|
-
/** Whether this node is on the path a read from the root takes. */
|
|
1268
|
-
isActive(): boolean;
|
|
1269
|
-
/** This node, then each ancestor up to the root. */
|
|
1270
|
-
chain(): Generator<N>;
|
|
1085
|
+
/** The live state a `RuleCtx` is assembled from — the chrome context plus
|
|
1086
|
+
* what the active mode allows. */
|
|
1087
|
+
interface BuildRuleCtxArgs {
|
|
1088
|
+
focused: boolean;
|
|
1089
|
+
selection: readonly NodeId[];
|
|
1090
|
+
multiActive: boolean;
|
|
1091
|
+
modifiers: ModifierState;
|
|
1092
|
+
action: {
|
|
1093
|
+
kind: string | null;
|
|
1094
|
+
id: string | null;
|
|
1095
|
+
};
|
|
1096
|
+
hover: NodeId | null;
|
|
1097
|
+
/** See {@link RuleCtx.zoom}. */
|
|
1098
|
+
zoom?: number;
|
|
1099
|
+
mode: string;
|
|
1100
|
+
allowedCapabilities: ReadonlySet<CapabilityTag>;
|
|
1101
|
+
/** Optional — omitted means "resizable" (handles show). See {@link RuleCtx}. */
|
|
1102
|
+
selectionResizable?: boolean;
|
|
1103
|
+
/** Optional — omitted means "no path is being anchor-edited". */
|
|
1104
|
+
editingAnchors?: boolean;
|
|
1105
|
+
/** Optional — omitted means {@link DEFAULT_DEVICE_PROFILE}. */
|
|
1106
|
+
device?: DeviceProfile;
|
|
1271
1107
|
}
|
|
1108
|
+
/**
|
|
1109
|
+
* Capability set to assume when no mode registry is wired.
|
|
1110
|
+
*
|
|
1111
|
+
* "No modes configured" means *everything the default mode permits*, not
|
|
1112
|
+
* *nothing permitted*. The empty set is the wrong default: it makes every
|
|
1113
|
+
* `capability:` rule evaluate false, so a kit default written the
|
|
1114
|
+
* recommended way (capability rules outlive new modes; mode rules have to
|
|
1115
|
+
* be edited) would silently hide its chrome for every consumer that hasn't
|
|
1116
|
+
* opted into modality. `NORMAL.allows` plus the implicit tags is what a
|
|
1117
|
+
* mode-less canvas actually behaves like.
|
|
1118
|
+
*/
|
|
1119
|
+
declare const DEFAULT_ALLOWED_CAPABILITIES: ReadonlySet<CapabilityTag>;
|
|
1120
|
+
/** Gather the current canvas and mode state into the context that action
|
|
1121
|
+
* eligibility and chrome-visibility rules are evaluated against. */
|
|
1122
|
+
declare function buildRuleCtx(args: BuildRuleCtxArgs): RuleCtx;
|
|
1272
1123
|
|
|
1273
|
-
/** Which tool is active, plus the stack of tools temporarily held active by a
|
|
1274
|
-
* hotkey (space-for-hand and the like). The dispatcher reads this to decide
|
|
1275
|
-
* whose bindings are in scope. */
|
|
1276
|
-
interface ActiveToolContextValue {
|
|
1277
|
-
/** The focused tool's id, or `null` when no tool holds the active slot —
|
|
1278
|
-
* a canvas can run on ambient bindings alone. */
|
|
1279
|
-
active: string | null;
|
|
1280
|
-
hotkeyStack: string[];
|
|
1281
|
-
setActive(id: string | null): void;
|
|
1282
|
-
pushHotkey(id: string): void;
|
|
1283
|
-
/**
|
|
1284
|
-
* Disengage a hotkey-held tool. With `id`, removes that tool's own entry
|
|
1285
|
-
* wherever it now sits — holds do not have to come up in the order they went
|
|
1286
|
-
* down, and popping the top would disengage a tool whose key is still held.
|
|
1287
|
-
* With no id, pops the top.
|
|
1288
|
-
*/
|
|
1289
|
-
popHotkey(id?: string): void;
|
|
1290
|
-
}
|
|
1291
1124
|
/**
|
|
1292
|
-
*
|
|
1293
|
-
*
|
|
1125
|
+
* A selector is a conjunction of key/value tests. Multiple keys at the same
|
|
1126
|
+
* level AND together. Each key maps to a selector primitive in the evaluator.
|
|
1294
1127
|
*/
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
}
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1128
|
+
interface Selector {
|
|
1129
|
+
selection?: {
|
|
1130
|
+
is?: number;
|
|
1131
|
+
atLeast?: number;
|
|
1132
|
+
empty?: boolean;
|
|
1133
|
+
};
|
|
1134
|
+
mode?: string | {
|
|
1135
|
+
not: string;
|
|
1136
|
+
} | {
|
|
1137
|
+
in: readonly string[];
|
|
1138
|
+
};
|
|
1139
|
+
capability?: CapabilityTag | readonly CapabilityTag[] | {
|
|
1140
|
+
in: readonly CapabilityTag[];
|
|
1141
|
+
} | {
|
|
1142
|
+
not: CapabilityTag;
|
|
1143
|
+
};
|
|
1144
|
+
gesturing?: boolean;
|
|
1145
|
+
actionIs?: string;
|
|
1146
|
+
modifierHeld?: keyof ModifierState;
|
|
1147
|
+
focused?: boolean;
|
|
1148
|
+
hovering?: boolean;
|
|
1149
|
+
hoveringSelected?: boolean;
|
|
1150
|
+
zoomAtLeast?: number;
|
|
1151
|
+
/** Matches `ctx.editingAnchors` — true while a path is in anchor-edit
|
|
1152
|
+
* mode. Absent flag is treated as `false`. */
|
|
1153
|
+
editingAnchors?: boolean;
|
|
1154
|
+
/** Matches `ctx.selectionResizable`. Absent flag is treated as `true`
|
|
1155
|
+
* (resizable), so `{ resizable: true }` passes for legacy ctx builders
|
|
1156
|
+
* that don't compute it. */
|
|
1157
|
+
resizable?: boolean;
|
|
1158
|
+
/** Matches `ctx.device.coarsePointer` — the primary pointer is imprecise
|
|
1159
|
+
* (touch, most styluses). Absent device is treated as `false`. */
|
|
1160
|
+
coarsePointer?: boolean;
|
|
1161
|
+
/** Matches `ctx.device.canHover` — the primary pointer can hover. Absent
|
|
1162
|
+
* device is treated as `true`. */
|
|
1163
|
+
canHover?: boolean;
|
|
1312
1164
|
}
|
|
1313
|
-
/** Provides root active-tool state. Every canvas below has a tool of its own;
|
|
1314
|
-
* reads and writes here reach the one last used. */
|
|
1315
|
-
declare function ActiveToolContextProvider({ children, initialActive, }: ActiveToolContextProviderProps): react.JSX.Element;
|
|
1316
|
-
/** The active-tool state in scope. Throws outside a provider; use
|
|
1317
|
-
* `useOptionalActiveToolContext` where one is not guaranteed. */
|
|
1318
|
-
declare function useActiveToolContext(): ActiveToolContextValue;
|
|
1319
1165
|
/**
|
|
1320
|
-
*
|
|
1321
|
-
*
|
|
1166
|
+
* Composable visibility/eligibility rule. Trees of `all`/`any`/`not` nodes
|
|
1167
|
+
* over `Selector` leaves. `when` is the escape hatch — its closure is
|
|
1168
|
+
* opaque to introspection and should be avoided when a declarative form
|
|
1169
|
+
* exists. Empty `all` is true; empty `any` is false.
|
|
1322
1170
|
*/
|
|
1323
|
-
|
|
1171
|
+
type Rule = Selector | {
|
|
1172
|
+
all: readonly Rule[];
|
|
1173
|
+
} | {
|
|
1174
|
+
any: readonly Rule[];
|
|
1175
|
+
} | {
|
|
1176
|
+
not: Rule;
|
|
1177
|
+
} | {
|
|
1178
|
+
when: (ctx: RuleCtx) => boolean;
|
|
1179
|
+
};
|
|
1180
|
+
/** Constant rules. Kept here so they have a single source. */
|
|
1181
|
+
declare const ALWAYS: Rule;
|
|
1182
|
+
/** A rule that never passes. `any` of nothing is false. */
|
|
1183
|
+
declare const NEVER: Rule;
|
|
1324
1184
|
/**
|
|
1325
|
-
*
|
|
1326
|
-
*
|
|
1185
|
+
* Render a {@link Rule} as a short human-readable string, for inspectors and
|
|
1186
|
+
* diagnostics that need to say WHICH rule decided something.
|
|
1187
|
+
*
|
|
1188
|
+
* ```
|
|
1189
|
+
* { capability: 'edits-page' } → capability:edits-page
|
|
1190
|
+
* { all: [{ mode: 'normal' }, { focused: true }] } → all(mode:normal, focused:true)
|
|
1191
|
+
* { not: { selection: { empty: true } } } → not(selection:empty=true)
|
|
1192
|
+
* { when: function hasPath() { … } } → when(hasPath)
|
|
1193
|
+
* ```
|
|
1194
|
+
*
|
|
1195
|
+
* Never throws and always terminates.
|
|
1327
1196
|
*/
|
|
1328
|
-
declare function
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1197
|
+
declare function describeRule(rule: Rule): string;
|
|
1198
|
+
/** Evaluate a rule against a context. Pure, and cheap enough to run per
|
|
1199
|
+
* frame for every piece of chrome. */
|
|
1200
|
+
declare function evaluate(rule: Rule, ctx: RuleCtx): boolean;
|
|
1332
1201
|
/**
|
|
1333
|
-
*
|
|
1334
|
-
*
|
|
1335
|
-
*
|
|
1336
|
-
*
|
|
1202
|
+
* Composable eligibility predicate with a fluent surface. `cond(ctx)`
|
|
1203
|
+
* evaluates its `Rule` tree, carried at `.rule`; the fluent methods return new
|
|
1204
|
+
* Conditions wrapping new trees.
|
|
1205
|
+
*
|
|
1206
|
+
* **Chain semantics: strict left-to-right, no precedence.** `a.and(b).or(c)` is
|
|
1207
|
+
* `(a && b) || c`; `a.or(b).and(c)` is `(a || b) && c`. For grouped
|
|
1208
|
+
* disjunction, name the subexpression or use the top-level `or(...)`.
|
|
1337
1209
|
*/
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
/**
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
/** What a host changes about one action. */
|
|
1350
|
-
interface ActionTuning<P = Record<string, unknown>> {
|
|
1351
|
-
/** Params every invocation starts from. What a binding or a `trigger` call
|
|
1352
|
-
* passes is laid over them, so these fill in what it leaves out. A thunk is
|
|
1353
|
-
* read on every call, and an ongoing action resolving its `opts.params`
|
|
1354
|
-
* mid-gesture sees the value as it is then. */
|
|
1355
|
-
params?: ActionParams<P>;
|
|
1356
|
-
/** Bindings added after the action's own `defaultBinding`. */
|
|
1357
|
-
bindings?: readonly TunedBinding<P>[];
|
|
1358
|
-
/** Keys for some of the action's `modifierRoles`, by role id. A binding's
|
|
1359
|
-
* own `opts.roles` is laid over these. */
|
|
1360
|
-
roles?: RoleRebinds;
|
|
1210
|
+
interface Condition {
|
|
1211
|
+
(ctx: RuleCtx): boolean;
|
|
1212
|
+
readonly rule: Rule;
|
|
1213
|
+
/** `this && other` */
|
|
1214
|
+
and(other: Condition | Rule): Condition;
|
|
1215
|
+
/** `this || other` */
|
|
1216
|
+
or(other: Condition | Rule): Condition;
|
|
1217
|
+
/** `this && !other` */
|
|
1218
|
+
andNot(other: Condition | Rule): Condition;
|
|
1219
|
+
/** `this || !other` */
|
|
1220
|
+
orNot(other: Condition | Rule): Condition;
|
|
1361
1221
|
}
|
|
1362
|
-
/** `action` with each tuning applied in turn, a later one's params over an
|
|
1363
|
-
* earlier one's. Applying a tuning to a copy it already tuned changes nothing. */
|
|
1364
|
-
declare function tuneAction(action: Action, tunings: readonly ActionTuning[]): Action;
|
|
1365
1222
|
|
|
1366
1223
|
/**
|
|
1367
|
-
*
|
|
1368
|
-
*
|
|
1224
|
+
* Whether two rules can never hold at once — no `RuleCtx` passes both.
|
|
1225
|
+
*
|
|
1226
|
+
* Conservative: `true` is a proof, `false` means "may overlap". It reasons
|
|
1227
|
+
* about the selector values themselves (two different `mode`s, an empty
|
|
1228
|
+
* selection against a non-empty one, `focused: true` against `false`) and
|
|
1229
|
+
* through `all` / `any` / `not`; a `when` closure is opaque, so it overlaps
|
|
1230
|
+
* everything. The route-conflict check uses it to stay quiet about bindings
|
|
1231
|
+
* that share a tuple but are never eligible together.
|
|
1369
1232
|
*/
|
|
1370
|
-
|
|
1371
|
-
/** One renderable entry: an action, optionally with params. */
|
|
1372
|
-
interface ActionItem {
|
|
1373
|
-
/** `action.id`, or `action.id:variant.key` — unique across a registry. */
|
|
1374
|
-
key: string;
|
|
1375
|
-
action: Action;
|
|
1376
|
-
label: string;
|
|
1377
|
-
icon?: IconRef;
|
|
1378
|
-
/** The action's tags, then the variant's own. */
|
|
1379
|
-
tags: readonly string[];
|
|
1380
|
-
/** What to pass to `trigger(action.id, params)`; absent for a plain action. */
|
|
1381
|
-
params?: Readonly<Record<string, unknown>>;
|
|
1382
|
-
}
|
|
1383
|
-
/** Expand `action` into the entries a surface renders. An
|
|
1384
|
-
* action without an immediate invoker has none: `trigger` cannot start a
|
|
1385
|
-
* drag, so a button for one would do nothing. */
|
|
1386
|
-
declare function actionItems(action: Action): ActionItem[];
|
|
1387
|
-
/** How a palette titles `item`: a variant reads as its action's
|
|
1388
|
-
* label and its own ("New: A4"), unless its label already holds the action's
|
|
1389
|
-
* as whole words ("Zoom In", "Reset Zoom"). */
|
|
1390
|
-
declare function actionItemTitle(item: ActionItem): string;
|
|
1391
|
-
|
|
1233
|
+
declare function rulesExclusive(a: Rule, b: Rule): boolean;
|
|
1392
1234
|
/**
|
|
1393
|
-
*
|
|
1394
|
-
*
|
|
1235
|
+
* Whether `rule` can hold while `mode` is active. Only its `mode` and
|
|
1236
|
+
* `capability` tests are settled by the mode; every other test could go either
|
|
1237
|
+
* way, so `false` is a proof and `true` means "may hold".
|
|
1395
1238
|
*/
|
|
1239
|
+
declare function ruleCanHoldIn(rule: Rule, mode: ActiveMode): boolean;
|
|
1396
1240
|
|
|
1397
|
-
/**
|
|
1398
|
-
|
|
1399
|
-
type ActionShortcut = MenuShortcut;
|
|
1241
|
+
/** The detected half of a profile — everything except the derived scale. */
|
|
1242
|
+
type DetectedDeviceFacts = Omit<DeviceProfile, 'targetScale'>;
|
|
1400
1243
|
/**
|
|
1401
|
-
*
|
|
1402
|
-
* none.
|
|
1244
|
+
* Handle/hit multiplier applied on a coarse pointer.
|
|
1403
1245
|
*
|
|
1404
|
-
*
|
|
1405
|
-
*
|
|
1406
|
-
* -
|
|
1407
|
-
|
|
1246
|
+
* 8px handle → 14px; 24px rotation distance → 42px. Counting the
|
|
1247
|
+
* surrounding grab zone, that lands in the Apple HIG 44pt / Material 48dp
|
|
1248
|
+
* minimum-touch-target band. One constant so it is tunable in one place.
|
|
1249
|
+
*/
|
|
1250
|
+
declare const COARSE_TARGET_SCALE = 1.75;
|
|
1251
|
+
/**
|
|
1252
|
+
* Fold consumer overrides over detected facts and derive `targetScale`.
|
|
1408
1253
|
*
|
|
1409
|
-
*
|
|
1410
|
-
*
|
|
1254
|
+
* `targetScale` is re-derived AFTER the merge, so an override of
|
|
1255
|
+
* `coarsePointer` alone scales the chrome as expected. An explicit
|
|
1256
|
+
* `targetScale` override wins over the derivation.
|
|
1411
1257
|
*/
|
|
1412
|
-
declare function
|
|
1258
|
+
declare function resolveDeviceProfile(detected: DetectedDeviceFacts, overrides?: Partial<DeviceProfile>): DeviceProfile;
|
|
1413
1259
|
/**
|
|
1414
|
-
*
|
|
1415
|
-
*
|
|
1416
|
-
* identical are emitted once; an action bound only to those returns empty.
|
|
1260
|
+
* Assumed when `matchMedia` is unavailable (SSR, jsdom) and used as the
|
|
1261
|
+
* absent-means value for `RuleCtx.device`.
|
|
1417
1262
|
*
|
|
1418
|
-
*
|
|
1419
|
-
*
|
|
1263
|
+
* Derived from `resolveDeviceProfile` rather than hand-written, so it can
|
|
1264
|
+
* never drift from what the function would compute for the same facts. A
|
|
1265
|
+
* mouse-like device is the safe default: it is what the kit has always
|
|
1266
|
+
* assumed, so an absent profile changes nothing for existing consumers.
|
|
1420
1267
|
*/
|
|
1421
|
-
declare
|
|
1268
|
+
declare const DEFAULT_DEVICE_PROFILE: DeviceProfile;
|
|
1422
1269
|
|
|
1423
1270
|
/**
|
|
1424
|
-
*
|
|
1425
|
-
*
|
|
1271
|
+
* A settings form an action carries: what a surface draws in place of a bare
|
|
1272
|
+
* trigger for an action whose job is to change settings on the selection.
|
|
1426
1273
|
*/
|
|
1427
1274
|
|
|
1428
|
-
/**
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
* by a tag, and which. */
|
|
1436
|
-
declare function matchActionItem(item: ActionItem, query: string): QueryMatch | null;
|
|
1437
|
-
|
|
1275
|
+
/** What a settings form shows for the selection right now. */
|
|
1276
|
+
interface ActionSettingsState {
|
|
1277
|
+
/** The values, nested as the schema's groups nest. */
|
|
1278
|
+
values: Record<string, unknown>;
|
|
1279
|
+
/** The leaf paths where the selected items disagree. */
|
|
1280
|
+
mixed?: readonly string[];
|
|
1281
|
+
}
|
|
1438
1282
|
/**
|
|
1439
|
-
*
|
|
1440
|
-
*
|
|
1441
|
-
*
|
|
1283
|
+
* The settings form an action carries. A bar or a panel draws the action as a
|
|
1284
|
+
* button opening it, a menu as a submenu of its fields, and a palette as a
|
|
1285
|
+
* dialog; each change triggers the action with the params it makes.
|
|
1442
1286
|
*/
|
|
1287
|
+
interface ActionSettings {
|
|
1288
|
+
/** The form. Its `name` titles the popover, submenu or dialog. */
|
|
1289
|
+
schema: PrefGroup;
|
|
1290
|
+
/** The selection's current values, from the deps `enabled` is given. Under
|
|
1291
|
+
* `enabled`'s contract: pure, fast, and it must not throw. */
|
|
1292
|
+
read(deps: ActionDeps): ActionSettingsState;
|
|
1293
|
+
/** The params one field's change triggers the action with, or `null` for
|
|
1294
|
+
* none. Default: `{ [path]: value }`. */
|
|
1295
|
+
params?(path: string, value: unknown): Record<string, unknown> | null;
|
|
1296
|
+
}
|
|
1297
|
+
/** `action`'s settings as they stand under `deps`, or `null` for an action
|
|
1298
|
+
* with no settings. A `read` that throws shows nothing rather than breaking
|
|
1299
|
+
* the surface. */
|
|
1300
|
+
declare function readActionSettings(action: Action, deps: ActionDeps | undefined): ActionSettingsState | null;
|
|
1301
|
+
/** The params a change of the field at `path` to `value` triggers the action with. */
|
|
1302
|
+
declare function actionSettingsParams(settings: ActionSettings, path: string, value: unknown): Record<string, unknown> | null;
|
|
1443
1303
|
|
|
1304
|
+
/** What content being routed brings to `Action.enabled`: a drop's or a
|
|
1305
|
+
* paste's items, or a carry's payload with no items. */
|
|
1306
|
+
interface Carried {
|
|
1307
|
+
items: readonly IngestItem[];
|
|
1308
|
+
carry?: {
|
|
1309
|
+
type: string;
|
|
1310
|
+
value: unknown;
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1444
1313
|
/**
|
|
1445
|
-
*
|
|
1314
|
+
* The kit's symbolic reasons an action might report itself as disabled. The
|
|
1315
|
+
* consumer (palette, menu, etc.) maps these to display strings via its own
|
|
1316
|
+
* label map. An action may also answer with any other string, which is
|
|
1317
|
+
* display text and shown as is — see {@link ActionUnavailable}.
|
|
1446
1318
|
*/
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1319
|
+
declare const ActionDisabledReason: {
|
|
1320
|
+
readonly SelectionRequired: "selection-required";
|
|
1321
|
+
readonly SceneEmpty: "scene-empty";
|
|
1322
|
+
readonly NotApplicable: "not-applicable";
|
|
1323
|
+
/** Sentinel: the predicate threw. Surfaced by `evaluateEnabled`'s catch. */
|
|
1324
|
+
readonly PredicateThrew: "predicate-threw";
|
|
1325
|
+
};
|
|
1326
|
+
/** Why an action is unavailable right now. */
|
|
1327
|
+
type ActionDisabledReason = (typeof ActionDisabledReason)[keyof typeof ActionDisabledReason];
|
|
1328
|
+
/** What `enabled` answers with when the action cannot run: one of the
|
|
1329
|
+
* symbolic {@link ActionDisabledReason}s, or a sentence to show as it is. */
|
|
1330
|
+
type ActionUnavailable = ActionDisabledReason | (string & {});
|
|
1331
|
+
/**
|
|
1332
|
+
* What a palette, menu or toolbar renders for an action. No part of this is
|
|
1333
|
+
* read during dispatch.
|
|
1334
|
+
*/
|
|
1335
|
+
interface ActionPresentation {
|
|
1336
|
+
label: string;
|
|
1337
|
+
/** The action's icon, by qualified name: `icon('group')` in the package that draws it. */
|
|
1338
|
+
icon?: IconRef;
|
|
1339
|
+
/** Grouping key for palette/menu surfaces. Free-form string; the kit
|
|
1340
|
+
* ships defaults for `'align'` (six edges/centers), `'distribute'`
|
|
1341
|
+
* (two axes), and recommends `'pathfinder'` for boolean ops. */
|
|
1342
|
+
group?: string;
|
|
1343
|
+
/** Words the action is also found by and gathered under. Where `group` is
|
|
1344
|
+
* the one place an action sits, an action carries any number of tags: a
|
|
1345
|
+
* palette search matches them as it matches the label, and
|
|
1346
|
+
* `actionsTagged` / `taggedActionItems` gather every action or entry
|
|
1347
|
+
* carrying one, for a bar or a menu built from a tag. */
|
|
1348
|
+
tags?: readonly string[];
|
|
1349
|
+
/** Display override for the keyboard shortcut. When omitted, palette
|
|
1350
|
+
* surfaces derive a label from `defaultBinding` via their own
|
|
1351
|
+
* formatter. */
|
|
1352
|
+
shortcut?: string;
|
|
1353
|
+
/** The distinct things a parametric action does, one per set of params —
|
|
1354
|
+
* `flip` flips horizontally or vertically, `reorder.forward` moves one step
|
|
1355
|
+
* or all the way. A bar, menu or palette shows one entry per variant and
|
|
1356
|
+
* triggers the action with that variant's `params`. Omitted = the action
|
|
1357
|
+
* is one entry. See `actionItems`. */
|
|
1358
|
+
variants?: readonly ActionVariant[];
|
|
1359
|
+
/** A form of the settings the action changes on the selection. A surface
|
|
1360
|
+
* draws the action as that form instead of a bare trigger, and each change
|
|
1361
|
+
* triggers it with the params `settings.params` makes. Not combined with
|
|
1362
|
+
* `variants`. */
|
|
1363
|
+
settings?: ActionSettings;
|
|
1450
1364
|
}
|
|
1451
1365
|
/**
|
|
1452
|
-
*
|
|
1453
|
-
* when no predicate is supplied. Catches throws and treats them as disabled
|
|
1454
|
-
* with reason `'(predicate threw)'`. In dev mode, warns once per action id
|
|
1455
|
-
* when a single call exceeds 4ms (single frame at 240fps — generous; real
|
|
1456
|
-
* predicates should be sub-millisecond).
|
|
1366
|
+
* One entry a parametric action contributes to a bar, menu or palette.
|
|
1457
1367
|
*/
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1368
|
+
interface ActionVariant {
|
|
1369
|
+
/** Unique within the action; joined to its id to make the entry's key. */
|
|
1370
|
+
key: string;
|
|
1371
|
+
label: string;
|
|
1372
|
+
/** Falls back to the action's own `icon`. */
|
|
1373
|
+
icon?: IconRef;
|
|
1374
|
+
/** Tags this variant carries besides its action's, which it carries too. */
|
|
1375
|
+
tags?: readonly string[];
|
|
1376
|
+
/** Passed to `trigger`, and matched against each binding's
|
|
1377
|
+
* `opts.params` to find the variant's shortcuts. */
|
|
1378
|
+
params: Readonly<Record<string, unknown>>;
|
|
1379
|
+
}
|
|
1462
1380
|
/**
|
|
1463
|
-
*
|
|
1464
|
-
*
|
|
1381
|
+
* What routing consults: the bindings that reach the action, the deps its
|
|
1382
|
+
* invoker reads, the gates it passes, and the cursors it claims.
|
|
1465
1383
|
*/
|
|
1466
|
-
|
|
1384
|
+
interface ActionDispatch extends BindingSource {
|
|
1385
|
+
/** Names of the deps this action's invoker reads (keys of `DepSchema`).
|
|
1386
|
+
* The dispatcher (and `trigger`, when `requires` is present) resolves
|
|
1387
|
+
* each name against the `DepRegistry` at invocation time and passes the
|
|
1388
|
+
* resulting bag to the invoker. Dev builds warn when the invoker reads a
|
|
1389
|
+
* dep it didn't declare here — see `buildDepsFromRequires`. */
|
|
1390
|
+
requires?: readonly DepName[];
|
|
1391
|
+
/** Pluggable invocation strategy. The gesture dispatcher routes matched
|
|
1392
|
+
* bindings through `invoker.start` / `invoker.run` depending on timing.
|
|
1393
|
+
* All kit-standard descriptors ship one; consumer-supplied actions
|
|
1394
|
+
* without an invoker can still register but won't be triggered. */
|
|
1395
|
+
invoker?: Invoker;
|
|
1396
|
+
/** When set to `'hotkey'`, this action's `defaultBinding` rides the hotkey
|
|
1397
|
+
* `BindingScope` instead of the ambient scope — meaning it beats any
|
|
1398
|
+
* active-tool binding on the same input shape. Use for tool-switch
|
|
1399
|
+
* shortcuts and global held-key triggers. Default: ambient. */
|
|
1400
|
+
scope?: 'hotkey';
|
|
1401
|
+
/**
|
|
1402
|
+
* Optional predicate saying whether the action can run right now. Return
|
|
1403
|
+
* `true` when it can, or an `ActionDisabledReason` or a sentence of display
|
|
1404
|
+
* text when it cannot. The
|
|
1405
|
+
* command palette greys out a disabled row, skips it in keyboard nav,
|
|
1406
|
+
* ignores clicks, and shows the reason next to the label. The dispatcher
|
|
1407
|
+
* skips a disabled action too, on a key as on a pointer, and falls through
|
|
1408
|
+
* to the next binding that matches. `ActionsRegistry.trigger` asks too, but
|
|
1409
|
+
* with no point, so a `run` whose answer depends on `at` should still
|
|
1410
|
+
* self-guard.
|
|
1411
|
+
*
|
|
1412
|
+
* **Contract:** must be pure (no side effects), fast (< 4ms in dev), and
|
|
1413
|
+
* must not throw. A throw is caught and treated as disabled with reason
|
|
1414
|
+
* `ActionDisabledReason.PredicateThrew`.
|
|
1415
|
+
*
|
|
1416
|
+
* The palette evaluates `enabled` once when opened and does NOT re-evaluate
|
|
1417
|
+
* on selection changes while open.
|
|
1418
|
+
*
|
|
1419
|
+
* `deps` is the same bag passed to `ImmediateInvoker.run`.
|
|
1420
|
+
*
|
|
1421
|
+
* `at` is the world point of the event being routed, when it has one — a
|
|
1422
|
+
* click, a press, a long-press. An action that only applies somewhere
|
|
1423
|
+
* (on a path segment, say) refuses other points here, so the dispatcher
|
|
1424
|
+
* falls through to the next binding and hover prediction shows the
|
|
1425
|
+
* action's `cursor` only where it would act. `trigger` passes the
|
|
1426
|
+
* `worldX`/`worldY` its params carry, so a menu row is run under the
|
|
1427
|
+
* question it was shown enabled for. Palettes and keystrokes pass no
|
|
1428
|
+
* point; treat absent as "anywhere".
|
|
1429
|
+
*
|
|
1430
|
+
* `carried` is what a drop, a paste or a carry being routed brings. For
|
|
1431
|
+
* an OS drag still in flight its items hold a MIME and nothing else, since
|
|
1432
|
+
* a browser withholds contents until the drop. A carry has no items and
|
|
1433
|
+
* hands over its payload whole. An action that takes only some content
|
|
1434
|
+
* refuses the rest here, so a drag it would not take is not accepted and a
|
|
1435
|
+
* paste it would not take stays the page's.
|
|
1436
|
+
*/
|
|
1437
|
+
enabled?: (deps?: ActionDeps, at?: {
|
|
1438
|
+
x: number;
|
|
1439
|
+
y: number;
|
|
1440
|
+
}, carried?: Carried) => true | ActionUnavailable;
|
|
1441
|
+
/**
|
|
1442
|
+
* Whether the action reads as held down right now: bold on the selected
|
|
1443
|
+
* text, snapping switched on. A toolbar button draws it as pressed.
|
|
1444
|
+
* Omitted = the action has no pressed state, which is not the same as
|
|
1445
|
+
* one that is off. Never read during dispatch.
|
|
1446
|
+
*
|
|
1447
|
+
* Under the same contract as `enabled`: pure, fast, and it must not
|
|
1448
|
+
* throw. `deps` is the bag `enabled` is given.
|
|
1449
|
+
*/
|
|
1450
|
+
active?: (deps?: ActionDeps) => boolean;
|
|
1451
|
+
/**
|
|
1452
|
+
* Show what running the action now would do, and change nothing: the
|
|
1453
|
+
* result an operation would make, drawn over the canvas. A toolbar asks
|
|
1454
|
+
* while its button is hovered or focused, through
|
|
1455
|
+
* `ActionsRegistry.preview`, and calls what this returns when that ends.
|
|
1456
|
+
* Asked only while `enabled` says yes, with the deps and params `run`
|
|
1457
|
+
* would get. Omitted = the action has nothing to show.
|
|
1458
|
+
*/
|
|
1459
|
+
preview?: (deps: ActionDeps, params?: Record<string, unknown>) => (() => void) | void;
|
|
1460
|
+
/**
|
|
1461
|
+
* Declarative eligibility rule, evaluated against the current
|
|
1462
|
+
* `RuleCtx` by the dispatcher before invoking `start()`. Omitted =
|
|
1463
|
+
* always eligible.
|
|
1464
|
+
*
|
|
1465
|
+
* Accepts either a fluent `Condition` (callable with `.rule`) or a
|
|
1466
|
+
* raw `Rule` tree; the dispatcher normalizes via `.rule` unwrap.
|
|
1467
|
+
*
|
|
1468
|
+
* Prefer `capability:`-based rules (e.g. `{ capability: 'transforms-selection' }`)
|
|
1469
|
+
* over `mode:` rules — capability rules survive new modes being added
|
|
1470
|
+
* that allow the same capability.
|
|
1471
|
+
*/
|
|
1472
|
+
eligible?: Rule | Condition;
|
|
1473
|
+
/**
|
|
1474
|
+
* CSS cursor shown while the pointer hovers a spot where this action
|
|
1475
|
+
* would win the drag — or, when the drag's winner declares no cursor,
|
|
1476
|
+
* the click. The hover-cursor pump runs the same match walk a real press
|
|
1477
|
+
* takes, so the hint and the actual target stay in sync. Omitted = no
|
|
1478
|
+
* override (the active tool's `Tool.cursor` shows). Affordance hits are
|
|
1479
|
+
* resolved earlier in the pump via `AffordanceRegion.cursor` and never
|
|
1480
|
+
* reach this field.
|
|
1481
|
+
*
|
|
1482
|
+
* A function is asked each time with the params the winning binding would
|
|
1483
|
+
* start the action with, tunings included, so a cursor can follow them — a
|
|
1484
|
+
* brush ring as wide as the brush. Read it through {@link actionCursor}.
|
|
1485
|
+
* Prediction runs `enabled()` but cannot run the invoker, so an action that
|
|
1486
|
+
* matches yet bails at `start()` (empty handle) may still show its cursor —
|
|
1487
|
+
* keep `enabled` accurate for actions that declare one.
|
|
1488
|
+
*/
|
|
1489
|
+
cursor?: CursorSpec | ((params: Record<string, unknown> | undefined) => CursorSpec | undefined);
|
|
1490
|
+
/**
|
|
1491
|
+
* CSS cursor shown while THIS action's ongoing handle is in flight —
|
|
1492
|
+
* grabbing while panning, `move` while dragging a selection, `crosshair`
|
|
1493
|
+
* while pulling a marquee.
|
|
1494
|
+
*
|
|
1495
|
+
* Separate from `cursor` because the two answer different questions:
|
|
1496
|
+
* `cursor` is a prediction ("a drag from here would pan"), this is a state
|
|
1497
|
+
* ("you are panning"). An action can declare either, both, or neither;
|
|
1498
|
+
* with only `cursor` set, the hover hint holds for the duration of the
|
|
1499
|
+
* gesture.
|
|
1500
|
+
*/
|
|
1501
|
+
activeCursor?: CursorSpec;
|
|
1502
|
+
/**
|
|
1503
|
+
* The modifiers an ongoing action reads while it runs, by role: Shift
|
|
1504
|
+
* holding a handle's angle, Alt breaking its mirror. The dispatcher resolves
|
|
1505
|
+
* each into `InvocationCtx.roles` on the press, every move, and the release,
|
|
1506
|
+
* so a binding's `opts.roles` or a tuning's `roles` can move one to another
|
|
1507
|
+
* key. Read them with {@link roleReader}. A binding's own `mods` are matched
|
|
1508
|
+
* once, at the press; these are read throughout.
|
|
1509
|
+
*/
|
|
1510
|
+
modifierRoles?: ModifierRoles;
|
|
1511
|
+
}
|
|
1512
|
+
/**
|
|
1513
|
+
* Single registered action.
|
|
1514
|
+
*/
|
|
1515
|
+
interface Action extends ActionDispatch, ActionPresentation {
|
|
1516
|
+
}
|
|
1517
|
+
/**
|
|
1518
|
+
* The whole of an actions registry the dispatcher reads. It walks the list
|
|
1519
|
+
* once per event to build its actionId lookup and never mutates it, so the
|
|
1520
|
+
* routing side depends on this rather than on `ActionsRegistry`, whose
|
|
1521
|
+
* register / mute / trigger surface is the React provider's.
|
|
1522
|
+
*/
|
|
1523
|
+
interface ActionSource {
|
|
1524
|
+
list(): readonly Action[];
|
|
1525
|
+
}
|
|
1526
|
+
/** `action`'s hover cursor for an invocation given `params`: its `cursor`,
|
|
1527
|
+
* asked when it is a function. */
|
|
1528
|
+
declare function actionCursor(action: Pick<Action, 'cursor'> | undefined, params?: Record<string, unknown>): CursorSpec | undefined;
|
|
1529
|
+
|
|
1530
|
+
/** Which canvas hit a menu answers. Every field given must match. */
|
|
1531
|
+
interface MenuWhen {
|
|
1532
|
+
bodyTarget?: BodyTarget | readonly BodyTarget[];
|
|
1533
|
+
bodyKind?: string | readonly string[];
|
|
1534
|
+
}
|
|
1535
|
+
/** A menu as data: a prefs schema drawn as a menu (`resolveSchemaMenu`).
|
|
1536
|
+
* A def without `when` is opened only by id, from chrome. */
|
|
1537
|
+
interface MenuDef {
|
|
1538
|
+
id: string;
|
|
1539
|
+
when?: MenuWhen;
|
|
1540
|
+
menu: PrefGroup;
|
|
1541
|
+
}
|
|
1542
|
+
/** What a right-click landed on. */
|
|
1543
|
+
interface MenuHit {
|
|
1544
|
+
bodyTarget?: BodyTarget;
|
|
1545
|
+
bodyKind?: string;
|
|
1546
|
+
}
|
|
1547
|
+
/** A def with who contributed it: a tool id, or `null` for ambient. */
|
|
1548
|
+
interface OwnedMenuDef {
|
|
1549
|
+
def: MenuDef;
|
|
1550
|
+
owner: string | null;
|
|
1551
|
+
}
|
|
1467
1552
|
|
|
1553
|
+
/** What resolving reads: the registered actions, each one's deps, how to run
|
|
1554
|
+
* one, and the values behind the schema's value leaves. */
|
|
1555
|
+
interface MenuSources {
|
|
1556
|
+
actions(): readonly Action[];
|
|
1557
|
+
depsFor(action: Action): ActionDeps | undefined;
|
|
1558
|
+
trigger(id: string, params?: Record<string, unknown>): boolean;
|
|
1559
|
+
/** The value at a leaf's dotted path. Booleans, enums, and numbers are left
|
|
1560
|
+
* out of a menu without both this and `onChange`, colors and strings without
|
|
1561
|
+
* this, and `showIf`/`enableIf` are checked only with it. */
|
|
1562
|
+
values?(path: string): unknown;
|
|
1563
|
+
onChange?(path: string, value: unknown): void;
|
|
1564
|
+
/** The schema an `alias` leaf's `of` is read in. Default: the menu's own. */
|
|
1565
|
+
over?: PrefGroup;
|
|
1566
|
+
}
|
|
1468
1567
|
/**
|
|
1469
|
-
*
|
|
1470
|
-
*
|
|
1471
|
-
*
|
|
1568
|
+
* Draw a prefs schema as menu entries. Action leaves become items from the
|
|
1569
|
+
* registry (one per variant for a parametric action), an `actions` leaf one
|
|
1570
|
+
* item per action in its group, a boolean a checked item, an enum radio
|
|
1571
|
+
* rows, and a number and a string a row edited in place (all through `src.values`/`src.onChange`);
|
|
1572
|
+
* a color shows as a swatch, read-only, and so does a string as a disabled row when it is a readout
|
|
1573
|
+
* or nothing writes it. A group drawn `as` a
|
|
1574
|
+
* `menu`, `page`, `tab`, or `panel` is a submenu, and any other group's rows
|
|
1575
|
+
* sit inline between separators. Paint, field, compound, and app-defined leaves are left out.
|
|
1576
|
+
* `at` is the world point the menu was opened at and reaches `enabled` and
|
|
1577
|
+
* every action's params; `params` go to every action, under its own.
|
|
1472
1578
|
*/
|
|
1579
|
+
declare function resolveSchemaMenu(schema: PrefGroup, src: MenuSources, at?: Point, params?: Record<string, unknown>): MenuEntry[];
|
|
1580
|
+
/** Turn a declared menu into model entries; see {@link resolveSchemaMenu}. */
|
|
1581
|
+
declare function resolveMenu(def: MenuDef, src: MenuSources, at?: Point, params?: Record<string, unknown>): MenuEntry[];
|
|
1473
1582
|
|
|
1474
|
-
/**
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
/**
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
changed?(): void;
|
|
1583
|
+
/** Ask to open a menu: by id (chrome) or by what was hit (canvas). */
|
|
1584
|
+
interface MenuOpenRequest {
|
|
1585
|
+
menu?: string;
|
|
1586
|
+
hit?: MenuHit;
|
|
1587
|
+
/** Viewport point the menu is placed at, unless `under` is set. */
|
|
1588
|
+
client: Point;
|
|
1589
|
+
/** Viewport rect of the button the menu hangs from: placed below it, or above when below does not fit. */
|
|
1590
|
+
under?: Rect;
|
|
1591
|
+
/** With `under`: opens above the button when it fits there, below when it does not. The canvas HUD menu host places below only and ignores it. */
|
|
1592
|
+
above?: boolean;
|
|
1593
|
+
/** World point, passed to `enabled` and to every item. */
|
|
1594
|
+
world?: Point;
|
|
1595
|
+
params?: Record<string, unknown>;
|
|
1596
|
+
source?: 'pointer' | 'keyboard';
|
|
1489
1597
|
}
|
|
1490
|
-
/**
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1598
|
+
/** The menu on screen. `seq` changes on every open, so a renderer remounts. */
|
|
1599
|
+
interface OpenMenu {
|
|
1600
|
+
seq: number;
|
|
1601
|
+
entries: readonly MenuEntry[];
|
|
1602
|
+
client: Point;
|
|
1603
|
+
under?: Rect;
|
|
1604
|
+
/** `MenuOpenRequest.above`, carried to the renderer. */
|
|
1605
|
+
above?: boolean;
|
|
1606
|
+
world?: Point;
|
|
1607
|
+
source: 'pointer' | 'keyboard';
|
|
1497
1608
|
}
|
|
1498
|
-
/**
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
*/
|
|
1503
|
-
|
|
1609
|
+
/** Holds the declared menus and the one open menu. */
|
|
1610
|
+
interface MenuHost {
|
|
1611
|
+
/** Declare a menu; `owner` is a tool id, or `null` for always. Returns a release. */
|
|
1612
|
+
define(def: MenuDef, owner?: string | null): () => void;
|
|
1613
|
+
/** Pick, resolve and show. False when nothing matched or nothing resolved. */
|
|
1614
|
+
open(req: MenuOpenRequest): boolean;
|
|
1615
|
+
/** Whether `open(req)` would show anything. Changes no state and notifies no one. */
|
|
1616
|
+
has(req: MenuOpenRequest): boolean;
|
|
1617
|
+
close(): void;
|
|
1618
|
+
current(): OpenMenu | null;
|
|
1619
|
+
subscribe(fn: () => void): () => void;
|
|
1620
|
+
}
|
|
1621
|
+
/** Creates the host that decides which context menu is open, from the menus registered to it. */
|
|
1622
|
+
declare function createMenuHost(src: MenuSources & {
|
|
1623
|
+
activeTool(): string | null;
|
|
1624
|
+
}): MenuHost;
|
|
1504
1625
|
|
|
1505
1626
|
/**
|
|
1506
|
-
*
|
|
1507
|
-
*
|
|
1508
|
-
*
|
|
1509
|
-
*
|
|
1510
|
-
* Proxy that warns on undeclared reads.
|
|
1627
|
+
* The tree both input registries hang on. A provider is a root; every canvas
|
|
1628
|
+
* mounts a scope under the registry in scope; a yoke sits between the two. A
|
|
1629
|
+
* registration lives in the node it was made in, and a lookup starts at a leaf
|
|
1630
|
+
* and walks up, so one canvas never answers for another.
|
|
1511
1631
|
*/
|
|
1512
1632
|
|
|
1513
|
-
/**
|
|
1514
|
-
|
|
1633
|
+
/** What every node of one tree shares: a version and the listeners to it. */
|
|
1634
|
+
interface ScopeTree {
|
|
1635
|
+
version: number;
|
|
1636
|
+
readonly listeners: Emitter;
|
|
1637
|
+
}
|
|
1638
|
+
declare class ScopeNode<N extends ScopeNode<N>> {
|
|
1639
|
+
readonly parent: N | null;
|
|
1640
|
+
readonly descends: boolean;
|
|
1641
|
+
readonly tree: ScopeTree;
|
|
1642
|
+
private readonly children;
|
|
1643
|
+
private activeChild;
|
|
1644
|
+
/** `descends`: whether reads made here answer from the active child down —
|
|
1645
|
+
* true for a root or a yoke, which chrome reads through; false for a scope,
|
|
1646
|
+
* which answers for itself even with a scope nested inside it. */
|
|
1647
|
+
constructor(parent: N | null, descends: boolean);
|
|
1648
|
+
/** Bump the tree's version and tell every listener in it. */
|
|
1649
|
+
changed(): void;
|
|
1650
|
+
/** Join the parent's children, newest last; returns the leave. */
|
|
1651
|
+
mount(): () => void;
|
|
1652
|
+
/** Make this node the one its ancestors resolve through. */
|
|
1653
|
+
activate(): void;
|
|
1654
|
+
/** The node a lookup made here starts from: down the active children, or the
|
|
1655
|
+
* newest where nothing has been activated yet. A yoke with no members yet
|
|
1656
|
+
* is passed over — there is no canvas behind it to answer. */
|
|
1657
|
+
leaf(): N;
|
|
1658
|
+
private newestMember;
|
|
1659
|
+
/** Where a read made through this node starts. */
|
|
1660
|
+
start(): N;
|
|
1661
|
+
/** Whether this node is on the path a read from the root takes. */
|
|
1662
|
+
isActive(): boolean;
|
|
1663
|
+
/** This node, then each ancestor up to the root. */
|
|
1664
|
+
chain(): Generator<N>;
|
|
1665
|
+
}
|
|
1515
1666
|
|
|
1516
|
-
/**
|
|
1517
|
-
*
|
|
1518
|
-
*
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
/** The tool's icon, by qualified name: `icon('rect')` in the package that draws it. */
|
|
1534
|
-
icon?: _weasel_js_icons.IconRef;
|
|
1535
|
-
/** Palette grouping key. Tools sharing a group render contiguously
|
|
1536
|
-
* with separators between groups. Free-form string; the kit
|
|
1537
|
-
* recommends 'select' | 'shape' | 'draw' | 'type' | 'view'. */
|
|
1538
|
-
group?: string;
|
|
1539
|
-
/** Display override for the keyboard shortcut. When omitted the palette
|
|
1540
|
-
* derives one from `Tool.keybinding` via its own formatter. */
|
|
1541
|
-
shortcut?: string;
|
|
1542
|
-
/** Keep the tool off palettes — for a tool that is only ever held, like a
|
|
1543
|
-
* scrub engaged by its `hotkey`. It stays registered and selectable by
|
|
1544
|
-
* id; only the palette leaves it out. */
|
|
1545
|
-
hide?: boolean;
|
|
1667
|
+
/** Which tool is active, plus the stack of tools temporarily held active by a
|
|
1668
|
+
* hotkey (space-for-hand and the like). The dispatcher reads this to decide
|
|
1669
|
+
* whose bindings are in scope. */
|
|
1670
|
+
interface ActiveToolContextValue {
|
|
1671
|
+
/** The focused tool's id, or `null` when no tool holds the active slot —
|
|
1672
|
+
* a canvas can run on ambient bindings alone. */
|
|
1673
|
+
active: string | null;
|
|
1674
|
+
hotkeyStack: string[];
|
|
1675
|
+
setActive(id: string | null): void;
|
|
1676
|
+
pushHotkey(id: string): void;
|
|
1677
|
+
/**
|
|
1678
|
+
* Disengage a hotkey-held tool. With `id`, removes that tool's own entry
|
|
1679
|
+
* wherever it now sits — holds do not have to come up in the order they went
|
|
1680
|
+
* down, and popping the top would disengage a tool whose key is still held.
|
|
1681
|
+
* With no id, pops the top.
|
|
1682
|
+
*/
|
|
1683
|
+
popHotkey(id?: string): void;
|
|
1546
1684
|
}
|
|
1547
1685
|
/**
|
|
1548
|
-
*
|
|
1549
|
-
*
|
|
1686
|
+
* One node of the tool tree. A node that `owns` a tool holds its state; one
|
|
1687
|
+
* that does not — a scope inside a yoke — reads and writes its yoke's.
|
|
1550
1688
|
*/
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
/**
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
capabilities?: CapabilityTag[];
|
|
1689
|
+
declare class ToolNode extends ScopeNode<ToolNode> {
|
|
1690
|
+
private state;
|
|
1691
|
+
private value;
|
|
1692
|
+
constructor(parent: ToolNode | null, descends: boolean, owns: boolean, initialActive: string | null);
|
|
1693
|
+
/** The node whose tool a read here resolves to. */
|
|
1694
|
+
owner(): ToolNode;
|
|
1695
|
+
private update;
|
|
1696
|
+
/** A stable value per owner and state, for `useSyncExternalStore`. */
|
|
1697
|
+
snapshot(): ActiveToolContextValue;
|
|
1698
|
+
subscribe: (listener: () => void) => (() => void);
|
|
1562
1699
|
}
|
|
1700
|
+
/** Props for `<ActiveToolContextProvider>`. */
|
|
1701
|
+
interface ActiveToolContextProviderProps {
|
|
1702
|
+
children: ReactNode;
|
|
1703
|
+
/** Seeds the active slot, and wins over the first `useTools` call's
|
|
1704
|
+
* `active`. Omitted, the slot starts empty and that call seeds it. */
|
|
1705
|
+
initialActive?: string | null;
|
|
1706
|
+
}
|
|
1707
|
+
/** Provides root active-tool state. Every canvas below has a tool of its own;
|
|
1708
|
+
* reads and writes here reach the one last used. */
|
|
1709
|
+
declare function ActiveToolContextProvider({ children, initialActive, }: ActiveToolContextProviderProps): react.JSX.Element;
|
|
1710
|
+
/** The active-tool state in scope. Throws outside a provider; use
|
|
1711
|
+
* `useOptionalActiveToolContext` where one is not guaranteed. */
|
|
1712
|
+
declare function useActiveToolContext(): ActiveToolContextValue;
|
|
1563
1713
|
/**
|
|
1564
|
-
*
|
|
1565
|
-
*
|
|
1566
|
-
* the other two exist for chrome that belongs under the selection handles
|
|
1567
|
-
* (a snap-target highlight, say). With no selection overlay in the stack,
|
|
1568
|
-
* all three collapse to `'top'`.
|
|
1714
|
+
* Like `useActiveToolContext`, but returns `null` when no
|
|
1715
|
+
* `<ActiveToolContextProvider>` is in scope instead of throwing.
|
|
1569
1716
|
*/
|
|
1570
|
-
|
|
1571
|
-
/** Dep sources an entry provides while it is installed, by dep name. */
|
|
1572
|
-
type ContributionDeps = {
|
|
1573
|
-
[K in DepName]?: () => DepSchema[K];
|
|
1574
|
-
};
|
|
1717
|
+
declare function useOptionalActiveToolContext(): ActiveToolContextValue | null;
|
|
1575
1718
|
/**
|
|
1576
|
-
*
|
|
1577
|
-
*
|
|
1578
|
-
*/
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
actions?: Action[];
|
|
1584
|
-
/** Menus this entry declares: always, for an ambient entry; while active, for a tool. */
|
|
1585
|
-
menus?: MenuDef[];
|
|
1586
|
-
/** Dep sources this entry provides while it is installed. A host installs
|
|
1587
|
-
* them for as long as the entry is registered, whatever its eligibility. */
|
|
1588
|
-
deps?: ContributionDeps;
|
|
1589
|
-
}
|
|
1719
|
+
* Mounts an `<ActiveToolContextProvider>` only when no parent provider is in
|
|
1720
|
+
* scope; otherwise renders children unwrapped.
|
|
1721
|
+
*/
|
|
1722
|
+
declare function ActiveToolContextProviderIfRoot({ children, }: {
|
|
1723
|
+
children: ReactNode;
|
|
1724
|
+
}): react.JSX.Element;
|
|
1725
|
+
|
|
1590
1726
|
/**
|
|
1591
|
-
*
|
|
1592
|
-
*
|
|
1727
|
+
* Tuning: what a host changes about an action it did not write — the params
|
|
1728
|
+
* every invocation starts from, and bindings added beside the action's own —
|
|
1729
|
+
* without restating the descriptor. `ActionsRegistry.tune` holds tunings and
|
|
1730
|
+
* applies them to whatever descriptor is live for the id.
|
|
1593
1731
|
*/
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1732
|
+
|
|
1733
|
+
/** An action's params, or a thunk read each time the action asks for them. */
|
|
1734
|
+
type ActionParams<P = Record<string, unknown>> = Partial<P> | (() => Partial<P>);
|
|
1735
|
+
/** A binding added for an action: a gesture alone, or a gesture with the
|
|
1736
|
+
* options it passes — the {@link BoundGesture} shape, its params typed. */
|
|
1737
|
+
type TunedBinding<P = Record<string, unknown>> = GestureSpec | {
|
|
1738
|
+
spec: GestureSpec;
|
|
1739
|
+
opts: Omit<BindingOpts, 'params'> & {
|
|
1740
|
+
params?: ActionParams<P>;
|
|
1741
|
+
};
|
|
1742
|
+
};
|
|
1743
|
+
/** What a host changes about one action. */
|
|
1744
|
+
interface ActionTuning<P = Record<string, unknown>> {
|
|
1745
|
+
/** Params every invocation starts from. What a binding or a `trigger` call
|
|
1746
|
+
* passes is laid over them, so these fill in what it leaves out. A thunk is
|
|
1747
|
+
* read on every call, and an ongoing action resolving its `opts.params`
|
|
1748
|
+
* mid-gesture sees the value as it is then. */
|
|
1749
|
+
params?: ActionParams<P>;
|
|
1750
|
+
/** Bindings added after the action's own `defaultBinding`. */
|
|
1751
|
+
bindings?: readonly TunedBinding<P>[];
|
|
1752
|
+
/** Keys for some of the action's `modifierRoles`, by role id. A binding's
|
|
1753
|
+
* own `opts.roles` is laid over these. */
|
|
1754
|
+
roles?: RoleRebinds;
|
|
1604
1755
|
}
|
|
1756
|
+
/** `action` with each tuning applied in turn, a later one's params over an
|
|
1757
|
+
* earlier one's. Applying a tuning to a copy it already tuned changes nothing. */
|
|
1758
|
+
declare function tuneAction(action: Action, tunings: readonly ActionTuning[]): Action;
|
|
1759
|
+
|
|
1605
1760
|
/**
|
|
1606
|
-
*
|
|
1607
|
-
*
|
|
1608
|
-
* `bindings` and `actions`.
|
|
1761
|
+
* The entries an action contributes to a bar, menu or palette — one per
|
|
1762
|
+
* variant, or the action itself when it declares none.
|
|
1609
1763
|
*/
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1764
|
+
|
|
1765
|
+
/** One renderable entry: an action, optionally with params. */
|
|
1766
|
+
interface ActionItem {
|
|
1767
|
+
/** `action.id`, or `action.id:variant.key` — unique across a registry. */
|
|
1768
|
+
key: string;
|
|
1769
|
+
action: Action;
|
|
1770
|
+
label: string;
|
|
1771
|
+
icon?: IconRef;
|
|
1772
|
+
/** The action's tags, then the variant's own. */
|
|
1773
|
+
tags: readonly string[];
|
|
1774
|
+
/** What to pass to `trigger(action.id, params)`; absent for a plain action. */
|
|
1775
|
+
params?: Readonly<Record<string, unknown>>;
|
|
1613
1776
|
}
|
|
1777
|
+
/** Expand `action` into the entries a surface renders. An
|
|
1778
|
+
* action without an immediate invoker has none: `trigger` cannot start a
|
|
1779
|
+
* drag, so a button for one would do nothing. */
|
|
1780
|
+
declare function actionItems(action: Action): ActionItem[];
|
|
1781
|
+
/** How a palette titles `item`: a variant reads as its action's
|
|
1782
|
+
* label and its own ("New: A4"), unless its label already holds the action's
|
|
1783
|
+
* as whole words ("Zoom In", "Reset Zoom"). */
|
|
1784
|
+
declare function actionItemTitle(item: ActionItem): string;
|
|
1614
1785
|
|
|
1615
1786
|
/**
|
|
1616
|
-
*
|
|
1617
|
-
*
|
|
1787
|
+
* Project an action's keyboard bindings into the shape a shortcut chip
|
|
1788
|
+
* renders from (`formatShortcut` / `formatShortcutParts` in `@weasel-js/ui`).
|
|
1618
1789
|
*/
|
|
1619
1790
|
|
|
1620
|
-
/**
|
|
1621
|
-
*
|
|
1622
|
-
type
|
|
1623
|
-
/** A binding paired with where it came from, ready to be matched against an
|
|
1624
|
-
* event. */
|
|
1625
|
-
interface ScopedBinding {
|
|
1626
|
-
binding: GestureBinding;
|
|
1627
|
-
scope: BindingScope;
|
|
1628
|
-
/** Id of the tool or contribution that declared this binding —
|
|
1629
|
-
* `'&'`-channel phase atoms resolve to it. `null` for a registered
|
|
1630
|
-
* action's own binding. */
|
|
1631
|
-
ownerId: string | null;
|
|
1632
|
-
}
|
|
1633
|
-
/** The binding that won a match. */
|
|
1634
|
-
interface MatchResult {
|
|
1635
|
-
binding: GestureBinding;
|
|
1636
|
-
scope: BindingScope;
|
|
1637
|
-
/** Id of the entry that declared the binding — propagated from
|
|
1638
|
-
* `ScopedBinding` so the dispatcher can record it as the handle owner. */
|
|
1639
|
-
ownerId: string | null;
|
|
1640
|
-
}
|
|
1791
|
+
/** One keyboard shortcut, flattened for display: the chip a menu row draws
|
|
1792
|
+
* (`MenuShortcut` in `@weasel-js/menu`, `ShortcutInput` in `@weasel-js/gestures`). */
|
|
1793
|
+
type ActionShortcut = MenuShortcut;
|
|
1641
1794
|
/**
|
|
1642
|
-
*
|
|
1643
|
-
*
|
|
1644
|
-
* `affordance:<k>` matches on its `kind`. The body-class strings (`'empty'`,
|
|
1645
|
-
* `'selected-body'`, `'unselected-body'`) and the `kind:` forms resolve from
|
|
1646
|
-
* `bodyTarget` / `bodyKind` and never see it.
|
|
1795
|
+
* The chip form of a single gesture spec, or `undefined` when the spec has
|
|
1796
|
+
* none.
|
|
1647
1797
|
*
|
|
1648
|
-
* A
|
|
1649
|
-
*
|
|
1798
|
+
* - A spec's `key` may list spellings of one keycap (`['[', '{']` — the
|
|
1799
|
+
* shifted bracket reports as `'{'`). The first is the shortcut's name.
|
|
1800
|
+
* - A modifier declared `'optional'` matches held or unheld, so it isn't
|
|
1801
|
+
* part of what the user has to press.
|
|
1802
|
+
*
|
|
1803
|
+
* Non-keyboard specs (drag, wheel, click) have no chip form. Neither does a
|
|
1804
|
+
* *required* `ctrl` or `meta`: the display shape carries only `mod`.
|
|
1650
1805
|
*/
|
|
1651
|
-
declare function
|
|
1652
|
-
/** The first of {@link matchSorted}'s matches, or null when nothing matches. */
|
|
1653
|
-
declare function matchBest(e: InputEvent, bindings: readonly ScopedBinding[], isMac: boolean, engagedChannels?: ReadonlySet<string>): MatchResult | null;
|
|
1806
|
+
declare function keySpecShortcut(spec: GestureSpec): ActionShortcut | undefined;
|
|
1654
1807
|
/**
|
|
1655
|
-
*
|
|
1656
|
-
*
|
|
1808
|
+
* Every keyboard shortcut an action answers to, in declaration order.
|
|
1809
|
+
* Specs `keySpecShortcut` has no chip for are skipped, and bindings it leaves
|
|
1810
|
+
* identical are emitted once; an action bound only to those returns empty.
|
|
1657
1811
|
*
|
|
1658
|
-
*
|
|
1659
|
-
*
|
|
1660
|
-
* `specificity()`).
|
|
1661
|
-
* - Among same-specificity bindings, first-declared wins.
|
|
1812
|
+
* With `params` — an `ActionItem`'s — only the bindings that pass those same
|
|
1813
|
+
* values count, so each variant of `flip` gets its own key.
|
|
1662
1814
|
*/
|
|
1663
|
-
declare function
|
|
1664
|
-
|
|
1665
|
-
interface MatchOutcome {
|
|
1666
|
-
matches: MatchResult[];
|
|
1667
|
-
/** Bindings whose spec matched the event but which the claim barred, in
|
|
1668
|
-
* the same order `matches` uses. Filled only when `collectBarred` is set. */
|
|
1669
|
-
barred: MatchResult[];
|
|
1670
|
-
/** Owner of the exclusive claim, when one applied. */
|
|
1671
|
-
claimOwner?: string;
|
|
1672
|
-
}
|
|
1815
|
+
declare function actionShortcuts(action: Action, params?: Readonly<Record<string, unknown>>): readonly ActionShortcut[];
|
|
1816
|
+
|
|
1673
1817
|
/**
|
|
1674
|
-
*
|
|
1675
|
-
*
|
|
1676
|
-
* opt-in — the dispatcher asks only when it is recording.
|
|
1818
|
+
* Finding actions by tag: gathering the ones that carry it, and a person's
|
|
1819
|
+
* search matching it as it matches a label.
|
|
1677
1820
|
*/
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1821
|
+
|
|
1822
|
+
/** The actions carrying `tag`, themselves or on any variant, in
|
|
1823
|
+
* the order given. Drags included, which have no entries to show. */
|
|
1824
|
+
declare function actionsTagged(actions: readonly Action[], tag: string): Action[];
|
|
1825
|
+
/** The entries carrying `tag`: what a bar or a menu built from
|
|
1826
|
+
* a tag shows. A variant that does not carry it is left out. */
|
|
1827
|
+
declare function taggedActionItems(actions: readonly Action[], tag: string): ActionItem[];
|
|
1828
|
+
/** Whether `item` answers a search for `query`, by its palette title or
|
|
1829
|
+
* by a tag, and which. */
|
|
1830
|
+
declare function matchActionItem(item: ActionItem, query: string): QueryMatch | null;
|
|
1682
1831
|
|
|
1683
1832
|
/**
|
|
1684
|
-
*
|
|
1685
|
-
*
|
|
1686
|
-
*
|
|
1687
|
-
* `Dispatcher.explain` fills one on request without invoking anything.
|
|
1688
|
-
*
|
|
1689
|
-
* Plain data — no functions, no live objects — so it can be kept in a trace
|
|
1690
|
-
* buffer, serialized, or built by hand as a fixture.
|
|
1833
|
+
* Palette-side evaluation of `Action.enabled`. The dispatcher calls the
|
|
1834
|
+
* predicate raw as a gate; this is the surface that also enforces the
|
|
1835
|
+
* contract the predicate's JSDoc states — no throwing, under 4ms in dev.
|
|
1691
1836
|
*/
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
dropped: readonly DroppedCandidate[];
|
|
1700
|
-
/** The candidates that survived both filters, best first. */
|
|
1701
|
-
ranked: readonly RankedCandidate[];
|
|
1702
|
-
/** True when nothing was invoked: the record answers "what would happen". */
|
|
1703
|
-
predicted: boolean;
|
|
1704
|
-
/** Action id of the candidate that fired (or would), else `null`. */
|
|
1705
|
-
fired: string | null;
|
|
1706
|
-
outcome: 'handled' | 'unhandled';
|
|
1707
|
-
}
|
|
1708
|
-
/** The input a {@link DispatchRecord} describes, reduced to plain data. */
|
|
1709
|
-
interface DispatchRecordInput {
|
|
1710
|
-
/** `InputEvent.kind`: `'key'`, `'pointerdown'`, `'wheel'`, … */
|
|
1711
|
-
eventKind: string;
|
|
1712
|
-
/** For `key` / `key-held` events, the key id (`'Escape'`, `' '`, `'a'`). */
|
|
1713
|
-
key?: string;
|
|
1714
|
-
modifiers: {
|
|
1715
|
-
alt: boolean;
|
|
1716
|
-
ctrl: boolean;
|
|
1717
|
-
meta: boolean;
|
|
1718
|
-
shift: boolean;
|
|
1719
|
-
};
|
|
1720
|
-
/** The view the input was routed to; `null` for the surface's own camera. */
|
|
1721
|
-
viewId: string | null;
|
|
1722
|
-
/** World point, for events that carry one. */
|
|
1723
|
-
world?: {
|
|
1724
|
-
x: number;
|
|
1725
|
-
y: number;
|
|
1726
|
-
};
|
|
1727
|
-
/** Active mode id, when the host supplies a rule context. */
|
|
1728
|
-
mode?: string;
|
|
1729
|
-
}
|
|
1730
|
-
/** One matched binding. */
|
|
1731
|
-
interface RecordCandidate {
|
|
1732
|
-
actionId: string;
|
|
1733
|
-
/** The binding's spec in route grammar — one per arg alternative. */
|
|
1734
|
-
routes: readonly string[];
|
|
1735
|
-
scope: BindingScope;
|
|
1736
|
-
/** The tool or contribution that declared the binding; `null` for an action's own binding. */
|
|
1737
|
-
ownerId: string | null;
|
|
1738
|
-
/** Whether the binding's `views` names the routed view. */
|
|
1739
|
-
namesView: boolean;
|
|
1740
|
-
/** `specificity(spec)`: target, modifiers, phase, exact. */
|
|
1741
|
-
specificity: readonly [number, number, number, number];
|
|
1742
|
-
/** The action's `eligible` rule as text, when it declares one. */
|
|
1743
|
-
eligible?: string;
|
|
1744
|
-
}
|
|
1745
|
-
/** A matched candidate a filter removed before ranking, and which filter. */
|
|
1746
|
-
type DroppedCandidate =
|
|
1747
|
-
/** An exclusive claim on the input barred bindings that do not read the
|
|
1748
|
-
* claim's affordance. */
|
|
1749
|
-
{
|
|
1750
|
-
candidate: RecordCandidate;
|
|
1751
|
-
filter: 'claim';
|
|
1752
|
-
owner?: string;
|
|
1753
|
-
}
|
|
1754
|
-
/** The action's `eligible` rule does not hold. */
|
|
1755
|
-
| {
|
|
1756
|
-
candidate: RecordCandidate;
|
|
1757
|
-
filter: 'ineligible';
|
|
1758
|
-
rule: string;
|
|
1759
|
-
};
|
|
1760
|
-
/** One field of the `specificity` tuple, in the order it is compared. */
|
|
1761
|
-
type SpecificityPart = 'target' | 'mods' | 'phase' | 'exact';
|
|
1762
|
-
/** The ranking step that put a candidate below the one before it. */
|
|
1763
|
-
type PlacedBy = {
|
|
1764
|
-
step: 'first';
|
|
1837
|
+
|
|
1838
|
+
/**
|
|
1839
|
+
* Result of evaluating an Action's `enabled` predicate.
|
|
1840
|
+
*/
|
|
1841
|
+
interface ActionEnabledResult {
|
|
1842
|
+
enabled: boolean;
|
|
1843
|
+
reason?: ActionUnavailable;
|
|
1765
1844
|
}
|
|
1766
|
-
/**
|
|
1767
|
-
|
|
1768
|
-
|
|
1845
|
+
/**
|
|
1846
|
+
* Safely evaluate an action's `enabled` predicate. Returns `{enabled: true}`
|
|
1847
|
+
* when no predicate is supplied. Catches throws and treats them as disabled
|
|
1848
|
+
* with reason `'(predicate threw)'`. In dev mode, warns once per action id
|
|
1849
|
+
* when a single call exceeds 4ms (single frame at 240fps — generous; real
|
|
1850
|
+
* predicates should be sub-millisecond).
|
|
1851
|
+
*/
|
|
1852
|
+
declare function evaluateEnabled(action: Action, deps?: ActionDeps, at?: {
|
|
1853
|
+
x: number;
|
|
1854
|
+
y: number;
|
|
1855
|
+
}): ActionEnabledResult;
|
|
1856
|
+
/**
|
|
1857
|
+
* Safely evaluate an action's `active` predicate. `undefined` for an action
|
|
1858
|
+
* with no pressed state; a predicate that throws reads as not active.
|
|
1859
|
+
*/
|
|
1860
|
+
declare function evaluateActive(action: Action, deps?: ActionDeps): boolean | undefined;
|
|
1861
|
+
|
|
1862
|
+
/**
|
|
1863
|
+
* The dep registry: named "live source" thunks. Actions declare
|
|
1864
|
+
* `requires: ['selection']`; the dispatcher calls `registry.get('selection')`
|
|
1865
|
+
* at invocation time to build a typed Deps bag.
|
|
1866
|
+
*/
|
|
1867
|
+
|
|
1868
|
+
/** Holds the live sources an action's declared dependencies resolve to.
|
|
1869
|
+
* Sources are thunks, read at invocation time, so an action never captures
|
|
1870
|
+
* stale state. */
|
|
1871
|
+
interface DepRegistry {
|
|
1872
|
+
/** Adds a live source for `name` over any already there. `below` answers
|
|
1873
|
+
* what the registry would without this source, so one can handle part of
|
|
1874
|
+
* a dep and hand the rest to the default it displaced. */
|
|
1875
|
+
register<K extends DepName>(name: K, source: DepSource<K>): () => void;
|
|
1876
|
+
get<K extends DepName>(name: K): DepSchema[K] | undefined;
|
|
1877
|
+
/** Hear that what the registry answers may have changed: a source was
|
|
1878
|
+
* registered or released anywhere in its tree, or an owner called
|
|
1879
|
+
* {@link DepRegistry.changed}. Returns the unsubscribe. A hand-built
|
|
1880
|
+
* registry whose answers never change leaves this and `changed` out. */
|
|
1881
|
+
subscribe?(listener: () => void): () => void;
|
|
1882
|
+
/** Say that a source's value changed. A source is a thunk read on demand,
|
|
1883
|
+
* so its owner is the only one who knows; chrome showing an action's
|
|
1884
|
+
* `enabled` or `active` state re-reads on this. */
|
|
1885
|
+
changed?(): void;
|
|
1886
|
+
/** Every name a `get` here would answer, sorted, with where its live
|
|
1887
|
+
* source sits. For inspection; a hand-built registry may leave it out. */
|
|
1888
|
+
list?(): readonly DepListing[];
|
|
1889
|
+
}
|
|
1890
|
+
/** A live source for dep `K`: a thunk read at invocation time. `below` reads
|
|
1891
|
+
* the source this one displaced, under it in its scope or in a scope above. */
|
|
1892
|
+
type DepSource<K extends DepName> = (below: () => DepSchema[K] | undefined) => DepSchema[K];
|
|
1893
|
+
/** One name a dep registry answers, as {@link DepRegistry.list} reports it. */
|
|
1894
|
+
interface DepListing {
|
|
1895
|
+
name: string;
|
|
1896
|
+
/** How many scopes above the asking one the live source was registered: 0 for its own. */
|
|
1897
|
+
depth: number;
|
|
1898
|
+
/** How many sources that scope holds under the name, the live one included. */
|
|
1899
|
+
stacked: number;
|
|
1769
1900
|
}
|
|
1770
|
-
/**
|
|
1771
|
-
|
|
1772
|
-
|
|
1901
|
+
/** One node of the dep tree: the sources registered at it, over its parent's.
|
|
1902
|
+
* Each name holds a stack of sources, newest live, so a displaced source is
|
|
1903
|
+
* restored when the one above it leaves. */
|
|
1904
|
+
declare class DepNode extends ScopeNode<DepNode> {
|
|
1905
|
+
private readonly sources;
|
|
1906
|
+
readonly registry: DepRegistry;
|
|
1907
|
+
constructor(parent: DepNode | null, descends: boolean);
|
|
1773
1908
|
}
|
|
1774
|
-
/**
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1909
|
+
/**
|
|
1910
|
+
* A stock dep registry with no parent: what `<DepRegistryProvider>` mounts,
|
|
1911
|
+
* for a dispatcher driven without React. Sources registered under one name
|
|
1912
|
+
* stack, newest live, and releasing one restores the one beneath it.
|
|
1913
|
+
*/
|
|
1914
|
+
declare function createDepRegistry(): DepRegistry;
|
|
1915
|
+
|
|
1916
|
+
/**
|
|
1917
|
+
* Shared dep-bag builder — the single implementation behind both the gesture
|
|
1918
|
+
* dispatcher's per-invocation `buildDeps` and `ActionsRegistry.trigger`'s
|
|
1919
|
+
* `requires` branch. Reads the action's declared `requires`, resolves each
|
|
1920
|
+
* name against the `DepRegistry`, and (dev builds only) wraps the bag in a
|
|
1921
|
+
* Proxy that warns on undeclared reads.
|
|
1922
|
+
*/
|
|
1923
|
+
|
|
1924
|
+
/** Build the deps bag for an action from its declared `requires`. */
|
|
1925
|
+
declare function buildDepsFromRequires(action: Action, depRegistry: DepRegistry): ActionDeps;
|
|
1926
|
+
|
|
1927
|
+
/**
|
|
1928
|
+
* Binding overrides: a host's replacements for bindings it did not write,
|
|
1929
|
+
* keyed by binding id. An override replaces or removes one binding, on an
|
|
1930
|
+
* action or on a contribution alike; adding a key beside the defaults is
|
|
1931
|
+
* `tune`'s job.
|
|
1932
|
+
*/
|
|
1933
|
+
|
|
1934
|
+
/** Overrides by binding id: a route string or spec to bind instead, or `null` to unbind. */
|
|
1935
|
+
type BindingOverrides = Readonly<Record<string, BindingOverrideInput>>;
|
|
1936
|
+
/** Overrides read into specs, one per binding id. */
|
|
1937
|
+
type ResolvedBindingOverrides = ReadonlyMap<string, GestureSpec | null>;
|
|
1938
|
+
/** A binding's id: its declared `opts.id`, else its owner and its position
|
|
1939
|
+
* there — `<entry>#<n>` for an entry's binding (`ownerId`), `<action>@<n>` for
|
|
1940
|
+
* an action's own (`ownerId` null), so an entry and an action of one id never
|
|
1941
|
+
* share a derived id. */
|
|
1942
|
+
declare function bindingId(b: GestureBinding, ownerId: string | null, index: number): string;
|
|
1943
|
+
/** Merge override maps into one, a later map winning per id. A route string
|
|
1944
|
+
* that does not parse is skipped with a warning, leaving the binding as it was. */
|
|
1945
|
+
declare function resolveBindingOverrides(maps: readonly BindingOverrides[]): ResolvedBindingOverrides;
|
|
1946
|
+
/** The bindings `ownerId` declares, each under its override, the unbound ones
|
|
1947
|
+
* left out: what dispatch routes by. A `null` owner is an action's own. */
|
|
1948
|
+
declare function overriddenBindings(bindings: readonly GestureBinding[], ownerId: string | null, overrides?: ResolvedBindingOverrides): GestureBinding[];
|
|
1949
|
+
|
|
1950
|
+
/** Hotkey-slot trigger key. The slot is engaged while this key is held —
|
|
1951
|
+
* hence "hotkey": active as long as the key is hot. Omitted means the tool
|
|
1952
|
+
* is not eligible for the hotkey slot.
|
|
1953
|
+
*
|
|
1954
|
+
* The named keys map to their DOM names (`'space'` is `' '`, `'ctrl'` is
|
|
1955
|
+
* `'Control'`). Anything else is a `KeyboardEvent.key` value, matched
|
|
1956
|
+
* case-insensitively as every key spec is — so `'o'` holds on O. */
|
|
1957
|
+
type HotkeyTrigger = 'space' | 'alt' | 'ctrl' | 'meta' | 'shift' | (string & {});
|
|
1958
|
+
/** Presentation metadata for tool palettes / menus. Optional on every
|
|
1959
|
+
* tool — consumers that render a palette (`<ToolPalette>`) read these
|
|
1960
|
+
* fields to display the tool; consumers that don't can ignore them.
|
|
1961
|
+
*
|
|
1962
|
+
* Note: cursor is NOT here. `Tool.cursor` (inherited from `Contribution`)
|
|
1963
|
+
* is already plumbed through `<Canvas>` to `style.cursor` on the host. */
|
|
1964
|
+
interface ToolPresentation {
|
|
1965
|
+
/** Human-readable label, distinct from the `id`. Falls back to `id`. */
|
|
1966
|
+
label?: string;
|
|
1967
|
+
/** The tool's icon, by qualified name: `icon('rect')` in the package that draws it. */
|
|
1968
|
+
icon?: _weasel_js_icons.IconRef;
|
|
1969
|
+
/** Palette grouping key. Tools sharing a group render contiguously
|
|
1970
|
+
* with separators between groups. Free-form string; the kit
|
|
1971
|
+
* recommends 'select' | 'shape' | 'draw' | 'type' | 'view'. */
|
|
1972
|
+
group?: string;
|
|
1973
|
+
/** Display override for the keyboard shortcut. When omitted the palette
|
|
1974
|
+
* derives one from `Tool.keybinding` via its own formatter. */
|
|
1975
|
+
shortcut?: string;
|
|
1976
|
+
/** Keep the tool off palettes — for a tool that is only ever held, like a
|
|
1977
|
+
* scrub engaged by its `hotkey`. It stays registered and selectable by
|
|
1978
|
+
* id; only the palette leaves it out. */
|
|
1979
|
+
hide?: boolean;
|
|
1778
1980
|
}
|
|
1779
|
-
/**
|
|
1780
|
-
|
|
1781
|
-
|
|
1981
|
+
/**
|
|
1982
|
+
* When an entry's bindings are live. A set, not one value: the hand tool is
|
|
1983
|
+
* palette-selectable AND engaged by holding space, and both hold at once.
|
|
1984
|
+
*/
|
|
1985
|
+
interface Eligibility {
|
|
1986
|
+
/** Selectable as the focused entry — exclusive, one at a time. */
|
|
1987
|
+
focus?: boolean;
|
|
1988
|
+
/** Also live while this key is held. */
|
|
1989
|
+
offhand?: HotkeyTrigger;
|
|
1990
|
+
/** Live regardless of what is focused. */
|
|
1991
|
+
always?: boolean;
|
|
1992
|
+
/** Live only for input this entry's own affordances produced. */
|
|
1993
|
+
claimed?: boolean;
|
|
1994
|
+
/** Modality filter, applied wherever it would otherwise be live. */
|
|
1995
|
+
capabilities?: CapabilityTag[];
|
|
1996
|
+
/** A keymap: live only while it is the innermost one open, at the hotkey
|
|
1997
|
+
* tier. `unbound` overrides the `gestures.keymapUnbound` kit setting for
|
|
1998
|
+
* this keymap. Open one with a leader (`keymapContribution`). */
|
|
1999
|
+
keymap?: true | {
|
|
2000
|
+
unbound?: KeymapUnbound;
|
|
2001
|
+
};
|
|
1782
2002
|
}
|
|
1783
|
-
/**
|
|
1784
|
-
|
|
1785
|
-
|
|
2003
|
+
/**
|
|
2004
|
+
* Where an entry's overlay sits in the layer stack, relative to the
|
|
2005
|
+
* selection chrome. `'top'` is the default and renders above everything;
|
|
2006
|
+
* the other two exist for chrome that belongs under the selection handles
|
|
2007
|
+
* (a snap-target highlight, say). With no selection overlay in the stack,
|
|
2008
|
+
* all three collapse to `'top'`.
|
|
2009
|
+
*/
|
|
2010
|
+
type OverlayPosition = 'top' | 'before-selection' | 'after-selection';
|
|
2011
|
+
/** Dep sources an entry provides while it is installed, by dep name. */
|
|
2012
|
+
type ContributionDeps = {
|
|
2013
|
+
[K in DepName]?: () => DepSchema[K];
|
|
1786
2014
|
};
|
|
1787
|
-
/**
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
/**
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
/** An ongoing action's `start()` returned an empty handle. */
|
|
1802
|
-
| {
|
|
1803
|
-
kind: 'empty-handle';
|
|
1804
|
-
}
|
|
1805
|
-
/** An ongoing action bound to a press-time `pointerDown` spec; refused. */
|
|
1806
|
-
| {
|
|
1807
|
-
kind: 'misbound';
|
|
1808
|
-
}
|
|
1809
|
-
/** Its action was already tried higher up; each runs at most once. */
|
|
1810
|
-
| {
|
|
1811
|
-
kind: 'duplicate';
|
|
1812
|
-
} | {
|
|
1813
|
-
kind: 'no-such-action';
|
|
2015
|
+
/**
|
|
2016
|
+
* What routing reads off an entry: when its bindings are live, which they
|
|
2017
|
+
* are, the actions they name, and the deps those actions read.
|
|
2018
|
+
*/
|
|
2019
|
+
interface ContributionRouting {
|
|
2020
|
+
id: string;
|
|
2021
|
+
eligibility: Eligibility;
|
|
2022
|
+
bindings?: GestureBinding[];
|
|
2023
|
+
actions?: Action[];
|
|
2024
|
+
/** Menus this entry declares: always, for an ambient entry; while active, for a tool. */
|
|
2025
|
+
menus?: MenuDef[];
|
|
2026
|
+
/** Dep sources this entry provides while it is installed. A host installs
|
|
2027
|
+
* them for as long as the entry is registered, whatever its eligibility. */
|
|
2028
|
+
deps?: ContributionDeps;
|
|
1814
2029
|
}
|
|
1815
|
-
/**
|
|
1816
|
-
|
|
1817
|
-
|
|
2030
|
+
/**
|
|
2031
|
+
* What an entry draws and how a palette shows it. None of it reaches the
|
|
2032
|
+
* dispatcher.
|
|
2033
|
+
*/
|
|
2034
|
+
interface ContributionChrome<TOverlay = KernelOverlay> {
|
|
2035
|
+
/** One layer, or several composed in the given order.
|
|
2036
|
+
*
|
|
2037
|
+
* Routing never reads a layer — it collects the live ones and hands them
|
|
2038
|
+
* back in scope order. `TOverlay` is what the consuming kernel draws;
|
|
2039
|
+
* `@weasel-js/core` binds it to `RenderLayer<unknown>`. */
|
|
2040
|
+
overlay?: TOverlay | TOverlay[];
|
|
2041
|
+
/** Defaults to `'top'`. Applies to every layer in `overlay`. */
|
|
2042
|
+
overlayPosition?: OverlayPosition;
|
|
2043
|
+
presentation?: ToolPresentation;
|
|
1818
2044
|
}
|
|
1819
|
-
/**
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
placedBy: PlacedBy;
|
|
1828
|
-
walk: WalkStep;
|
|
2045
|
+
/**
|
|
2046
|
+
* A registry entry: what it contributes, and when it is eligible. Every role
|
|
2047
|
+
* is optional and independent — an entry that only routes input declares only
|
|
2048
|
+
* `bindings` and `actions`.
|
|
2049
|
+
*/
|
|
2050
|
+
interface Contribution<TOverlay = KernelOverlay> extends ContributionRouting, ContributionChrome<TOverlay> {
|
|
2051
|
+
/** Reflection escape hatch — the authored form, when there was one. */
|
|
2052
|
+
def?: unknown;
|
|
1829
2053
|
}
|
|
1830
2054
|
|
|
1831
2055
|
/**
|
|
@@ -1866,6 +2090,30 @@ interface DispatcherContext {
|
|
|
1866
2090
|
* reads as the root.
|
|
1867
2091
|
*/
|
|
1868
2092
|
viewId?: string | null;
|
|
2093
|
+
/**
|
|
2094
|
+
* For a key from the page: whether this surface holds the keys, and whether
|
|
2095
|
+
* the pointer is over it. Bindings with `opts.over` are live only while it
|
|
2096
|
+
* is, the rest only while it holds them. Absent, every binding is live.
|
|
2097
|
+
*/
|
|
2098
|
+
keyReach?: KeyReach;
|
|
2099
|
+
/** The keymaps open on this surface. Absent: none can open. */
|
|
2100
|
+
keymaps?: KeymapStack;
|
|
2101
|
+
/** Which bindings this pass of an input reaches while a keymap is open:
|
|
2102
|
+
* `'only'` the innermost keymap's, `'none'` everything but it. Absent: both. */
|
|
2103
|
+
keymapPass?: 'only' | 'none';
|
|
2104
|
+
/** What a key no open keymap binds does, unless the keymap says: the
|
|
2105
|
+
* `gestures.keymapUnbound` kit setting. Default `'modal'`. */
|
|
2106
|
+
leaderUnbound?: KeymapUnbound;
|
|
2107
|
+
/** The binding overrides in force, read per input, for contribution bindings;
|
|
2108
|
+
* the registry lays them over actions' own. Absent overrides nothing. */
|
|
2109
|
+
bindingOverrides?: () => ResolvedBindingOverrides;
|
|
2110
|
+
/** Where the dispatcher records how it resolved each input. Absent records nothing. */
|
|
2111
|
+
trace?: DispatchTrace;
|
|
2112
|
+
}
|
|
2113
|
+
/** Which of a surface's key bindings a key from the page reaches. */
|
|
2114
|
+
interface KeyReach {
|
|
2115
|
+
held: boolean;
|
|
2116
|
+
over: boolean;
|
|
1869
2117
|
}
|
|
1870
2118
|
/**
|
|
1871
2119
|
* Handle returned by `Dispatcher.beginUiOngoing()` for driving an
|
|
@@ -2073,31 +2321,6 @@ interface Dispatcher {
|
|
|
2073
2321
|
beginUiOngoing(actionId: string, deps: ActionDeps, params?: Record<string, unknown>): UiOngoingControl | null;
|
|
2074
2322
|
}
|
|
2075
2323
|
|
|
2076
|
-
/**
|
|
2077
|
-
* Dev-only trace of what the dispatcher did: one record per resolved input and
|
|
2078
|
-
* one per mode switch, in a rolling buffer the dev tools read.
|
|
2079
|
-
*/
|
|
2080
|
-
|
|
2081
|
-
/** One entry per "mode change" — a kit state transition that isn't a
|
|
2082
|
-
* dispatched input event but explains why an input was (or wasn't) handled
|
|
2083
|
-
* later. Pushed via `recordModeSwitch()`. */
|
|
2084
|
-
interface ModeSwitchLogEntry {
|
|
2085
|
-
kind: 'mode';
|
|
2086
|
-
ts: number;
|
|
2087
|
-
/** Logical mode name, e.g. `'editAnchors.editingId'`. */
|
|
2088
|
-
mode: string;
|
|
2089
|
-
from: string | null;
|
|
2090
|
-
to: string | null;
|
|
2091
|
-
/** Optional free-form context — `'enterPathEdit'`, `'exitPathEdit'`,
|
|
2092
|
-
* `'selection-dropped target'`, etc. */
|
|
2093
|
-
detail?: string;
|
|
2094
|
-
}
|
|
2095
|
-
/** A trace entry: one {@link DispatchRecord} per resolved input, or a mode switch. */
|
|
2096
|
-
type TraceLogEntry = DispatchRecord | ModeSwitchLogEntry;
|
|
2097
|
-
/** Push a mode-switch record into the trace log. Safe to call from
|
|
2098
|
-
* anywhere; no-ops outside DEV. */
|
|
2099
|
-
declare function recordModeSwitch(mode: string, from: string | null, to: string | null, detail?: string): void;
|
|
2100
|
-
|
|
2101
2324
|
/**
|
|
2102
2325
|
* The slot an in-flight ongoing handle is keyed by:
|
|
2103
2326
|
* - `key-held`: `key-held-<code>` (`key-held-Space`), so two keys that share a
|
|
@@ -2188,6 +2411,12 @@ interface ActionsRegistry {
|
|
|
2188
2411
|
* passes `given`: every tuning's params, `given` laid over them. What a
|
|
2189
2412
|
* tool's chrome reads to show what the action it binds will do. */
|
|
2190
2413
|
paramsOf(id: string, given?: Record<string, unknown>): Record<string, unknown>;
|
|
2414
|
+
/** Replace or remove bindings by id, here and in scopes below. Holders
|
|
2415
|
+
* merge: an inner scope's beats an outer one's, and a later one beats an
|
|
2416
|
+
* earlier one in the same scope. Returns a release. See {@link BindingOverrides}. */
|
|
2417
|
+
overrideBindings(overrides: BindingOverrides): () => void;
|
|
2418
|
+
/** Every override in force from this scope, read into specs. */
|
|
2419
|
+
bindingOverrides(): ResolvedBindingOverrides;
|
|
2191
2420
|
list(): readonly Action[];
|
|
2192
2421
|
/** Fire an immediate-invoker action by id. The optional `params` arg is
|
|
2193
2422
|
* forwarded to `ImmediateInvoker.run` as its second argument — use it for
|
|
@@ -2269,8 +2498,8 @@ interface ToolOffhandBindingSpec {
|
|
|
2269
2498
|
declare function offhandKeyFor(trigger: HotkeyTrigger): string;
|
|
2270
2499
|
/** Build the per-tool `BoundGesture` list for the consolidated action's
|
|
2271
2500
|
* `defaultBinding`. Each entry pairs a `key-held` spec with
|
|
2272
|
-
* `opts.params.toolId
|
|
2273
|
-
* to engage.
|
|
2501
|
+
* `opts.params.toolId`, so the invoker's `start` can pick out which tool
|
|
2502
|
+
* to engage, and is overridden as `tool.offhand.<toolId>`.
|
|
2274
2503
|
*
|
|
2275
2504
|
* An offhand tool engages only between gestures. Swapping the tool out
|
|
2276
2505
|
* mid-drag would cancel the drag, and the key is free for the gesture's own
|
|
@@ -2430,6 +2659,7 @@ declare function createLongPressStore(): LongPressStore;
|
|
|
2430
2659
|
* The `gestures` kit settings area. Declared here rather than in core because
|
|
2431
2660
|
* the dispatcher reads it, and routing sits below core.
|
|
2432
2661
|
*/
|
|
2662
|
+
|
|
2433
2663
|
/** Pointer gesture timings and travel; each falls back to its constant in `@weasel-js/gestures`. */
|
|
2434
2664
|
interface GestureKitSettings {
|
|
2435
2665
|
/** Longest gap between the clicks of a double click, in ms. */
|
|
@@ -2442,6 +2672,9 @@ interface GestureKitSettings {
|
|
|
2442
2672
|
tapPx: number;
|
|
2443
2673
|
/** Travel that makes a press a drag, in CSS px. */
|
|
2444
2674
|
dragPx: number;
|
|
2675
|
+
/** What a key an open keymap does not bind does: `'modal'` swallows it,
|
|
2676
|
+
* `'cascade'` closes the keymap and lets it through. */
|
|
2677
|
+
keymapUnbound: KeymapUnbound;
|
|
2445
2678
|
}
|
|
2446
2679
|
declare module '@weasel-js/react' {
|
|
2447
2680
|
interface KitSettings {
|
|
@@ -2569,6 +2802,8 @@ interface EligibilityState {
|
|
|
2569
2802
|
engagedIds?: ReadonlySet<string>;
|
|
2570
2803
|
/** Whether the active mode allows these capability tags. Omitted → allow. */
|
|
2571
2804
|
allows?: (tags: readonly CapabilityTag[]) => boolean;
|
|
2805
|
+
/** The innermost open keymap's id: the one keymap entry that is live. */
|
|
2806
|
+
openKeymap?: string | null;
|
|
2572
2807
|
}
|
|
2573
2808
|
/**
|
|
2574
2809
|
* The scope tier an entry's bindings are live at, or null when none are.
|
|
@@ -2584,7 +2819,7 @@ declare function liveScope(id: string, eligibility: Eligibility, state: Eligibil
|
|
|
2584
2819
|
* breaks ties by declaration order, so the caller's entry order decides which
|
|
2585
2820
|
* of two same-specificity bindings in one tier wins.
|
|
2586
2821
|
*/
|
|
2587
|
-
declare function scopeBindings(entries: Iterable<Contribution<unknown>>, state: EligibilityState): ScopedBinding[];
|
|
2822
|
+
declare function scopeBindings(entries: Iterable<Contribution<unknown>>, state: EligibilityState, overrides?: ResolvedBindingOverrides): ScopedBinding[];
|
|
2588
2823
|
/** What decides which entries are live, and in what order they are walked. */
|
|
2589
2824
|
interface AssemblyInputs {
|
|
2590
2825
|
focusedId: string | null;
|
|
@@ -2593,6 +2828,8 @@ interface AssemblyInputs {
|
|
|
2593
2828
|
/** The capability tags the active mode allows. Omitted: no mode system, so
|
|
2594
2829
|
* every entry is allowed. */
|
|
2595
2830
|
allowedCapabilities?: ReadonlySet<CapabilityTag>;
|
|
2831
|
+
/** The innermost open keymap's id, if one is open. */
|
|
2832
|
+
openKeymap?: string | null;
|
|
2596
2833
|
}
|
|
2597
2834
|
/**
|
|
2598
2835
|
* The entry set as routing walks it, with the state that tiers it. The
|
|
@@ -2661,6 +2898,126 @@ declare function modeShortcutSpec(shortcut: string): KeySpec;
|
|
|
2661
2898
|
*/
|
|
2662
2899
|
declare function modeShortcuts(registry: ModeRegistry, handlers: ModeShortcutHandlers): Contribution<never>;
|
|
2663
2900
|
|
|
2901
|
+
/**
|
|
2902
|
+
* A keymap and the leader that opens it, from one definition. The keymap is an
|
|
2903
|
+
* entry whose bindings are live only while it is open; the leader is an
|
|
2904
|
+
* action on the hotkey tier, so it outranks what the active tool binds to the
|
|
2905
|
+
* same key, and it opens the keymap mid-gesture as well as between them.
|
|
2906
|
+
*/
|
|
2907
|
+
|
|
2908
|
+
/** What {@link keymapContribution} builds a keymap and its leader from. */
|
|
2909
|
+
interface KeymapDef<TOverlay = never> {
|
|
2910
|
+
/** The keymap entry's id. Its leader action is `keymap.<id>`. */
|
|
2911
|
+
id: string;
|
|
2912
|
+
/** What the hint and a shortcut chip call it: `Loupe ▸ 2`. */
|
|
2913
|
+
label: string;
|
|
2914
|
+
/** The keystroke, or keystrokes, that open it. A `key-held` spec is read as
|
|
2915
|
+
* its `key` form: holding the key is what holds the keymap open. */
|
|
2916
|
+
leader: GestureSpec | readonly GestureSpec[];
|
|
2917
|
+
/** The keymap's own bindings. Escape is not one: it always closes the keymap. */
|
|
2918
|
+
bindings: readonly GestureBinding[];
|
|
2919
|
+
/** Actions the bindings name that are not registered elsewhere. */
|
|
2920
|
+
actions?: readonly Action[];
|
|
2921
|
+
/** Overrides the `gestures.keymapUnbound` kit setting for this keymap. */
|
|
2922
|
+
unbound?: KeymapUnbound;
|
|
2923
|
+
/** Drawn while the keymap is open. */
|
|
2924
|
+
overlay?: TOverlay | TOverlay[];
|
|
2925
|
+
overlayPosition?: OverlayPosition;
|
|
2926
|
+
}
|
|
2927
|
+
/** The leader action's id for keymap `id`. */
|
|
2928
|
+
declare const keymapLeaderId: (id: string) => string;
|
|
2929
|
+
/** The binding id of a keymap's leader — what a binding override rebinds it by.
|
|
2930
|
+
* A second leader key is `<id>#1`, and so on. */
|
|
2931
|
+
declare const keymapLeaderBindingId: (id: string, index?: number) => string;
|
|
2932
|
+
/** `[keymap entry, leader entry]`. Triggered without a key, the leader opens
|
|
2933
|
+
* its keymap tapped; `trigger('keymap.<id>', { hold: true })` holds it open
|
|
2934
|
+
* until Escape. Install both, with `<SceneCanvas ambient>`
|
|
2935
|
+
* or `useTools`'s `ambient`; the leader entry carries the actions. */
|
|
2936
|
+
declare function keymapContribution<TOverlay = never>(def: KeymapDef<TOverlay>): Contribution<TOverlay>[];
|
|
2937
|
+
|
|
2938
|
+
/** One row of a keymap's hint: a key chip, or a pointer gesture in words, and what it does. */
|
|
2939
|
+
interface KeymapRow {
|
|
2940
|
+
shortcut?: MenuShortcut;
|
|
2941
|
+
gesture?: string;
|
|
2942
|
+
label: string;
|
|
2943
|
+
}
|
|
2944
|
+
/** An open keymap as a hint shows it: {@link describeKeymap}'s result. */
|
|
2945
|
+
interface KeymapDescription {
|
|
2946
|
+
id: string;
|
|
2947
|
+
label: string;
|
|
2948
|
+
/** Whether a held leader is keeping it open. */
|
|
2949
|
+
held: boolean;
|
|
2950
|
+
rows: readonly KeymapRow[];
|
|
2951
|
+
}
|
|
2952
|
+
/** The rows a hint lists for an open keymap, under the overrides in force. */
|
|
2953
|
+
declare function describeKeymap(level: OpenKeymap, actions: readonly Action[], overrides?: ResolvedBindingOverrides): KeymapDescription;
|
|
2954
|
+
/**
|
|
2955
|
+
* Every key in `def` as a menu chip, by action id, each carrying the path
|
|
2956
|
+
* through its leader (`Loupe ▸ 2`), under `overrides`. A keymap with no key
|
|
2957
|
+
* leader gives its keys no path.
|
|
2958
|
+
*/
|
|
2959
|
+
declare function keymapShortcuts(def: KeymapDef, overrides?: ResolvedBindingOverrides): ReadonlyMap<string, readonly MenuShortcut[]>;
|
|
2960
|
+
|
|
2961
|
+
/** Options for {@link captureKey}. */
|
|
2962
|
+
interface CaptureKeyOptions {
|
|
2963
|
+
/** Capture the held form, `key-held`, rather than the keystroke. */
|
|
2964
|
+
held?: boolean;
|
|
2965
|
+
/** Stop listening and resolve `null`. */
|
|
2966
|
+
signal?: AbortSignal;
|
|
2967
|
+
/** Whether Cmd is Mod. Defaults to the detected platform. */
|
|
2968
|
+
apple?: boolean;
|
|
2969
|
+
}
|
|
2970
|
+
/**
|
|
2971
|
+
* The next keystroke on `target` as a key spec: {@link captureGesture} taking
|
|
2972
|
+
* a keystroke alone, and leaving the pointer to the page. A bare modifier is
|
|
2973
|
+
* waited past. Write the result with `keySpecRoute` to persist it as a binding
|
|
2974
|
+
* override.
|
|
2975
|
+
*/
|
|
2976
|
+
declare function captureKey(target: EventTarget, opts?: CaptureKeyOptions): Promise<KeySpec | KeyHeldSpec | null>;
|
|
2977
|
+
|
|
2978
|
+
/** A spec kind a binding editor can capture. */
|
|
2979
|
+
type CaptureKind = 'key' | 'key-held' | 'click' | 'doubleClick' | 'drag' | 'contextMenu';
|
|
2980
|
+
/** What `captureGesture` resolves to. */
|
|
2981
|
+
type CapturedSpec = KeySpec | KeyHeldSpec | ClickSpec | DoubleClickSpec | DragSpec | ContextMenuSpec;
|
|
2982
|
+
|
|
2983
|
+
/** The click-and-drag thresholds a capture reads, each defaulting to its constant. */
|
|
2984
|
+
interface CaptureThresholds {
|
|
2985
|
+
dragPx?: number;
|
|
2986
|
+
doubleClickMs?: number;
|
|
2987
|
+
doubleClickPx?: number;
|
|
2988
|
+
}
|
|
2989
|
+
|
|
2990
|
+
/** Options for {@link captureGesture}. */
|
|
2991
|
+
interface CaptureGestureOptions extends CaptureThresholds {
|
|
2992
|
+
/** The kinds to take; input of any other kind is waited past. A keystroke
|
|
2993
|
+
* is taken as `key` when both `key` and `key-held` are listed. Defaults to
|
|
2994
|
+
* `key`, `click`, `doubleClick`, `drag` and `contextMenu`. */
|
|
2995
|
+
kinds?: readonly CaptureKind[];
|
|
2996
|
+
/** Stop listening and resolve `null`. */
|
|
2997
|
+
signal?: AbortSignal;
|
|
2998
|
+
/** Whether Cmd is Mod. Defaults to the detected platform. */
|
|
2999
|
+
apple?: boolean;
|
|
3000
|
+
/** Travel that makes a press a drag, in CSS px: pass the `gestures.dragPx`
|
|
3001
|
+
* kit setting. Defaults to `DRAG_THRESHOLD_PX`. */
|
|
3002
|
+
dragPx?: number;
|
|
3003
|
+
/** The `gestures.doubleClickMs` kit setting. Defaults to `DOUBLE_CLICK_MAX_MS`. */
|
|
3004
|
+
doubleClickMs?: number;
|
|
3005
|
+
/** The `gestures.doubleClickPx` kit setting. Defaults to `DOUBLE_CLICK_MAX_PX`. */
|
|
3006
|
+
doubleClickPx?: number;
|
|
3007
|
+
}
|
|
3008
|
+
/**
|
|
3009
|
+
* The next input on `target` of one of `opts.kinds`, as a gesture spec, for a
|
|
3010
|
+
* binding editor: its modifiers with the platform's primary one as `mod`. A
|
|
3011
|
+
* press is told a click or a drag by the dispatcher's own recognizer, at its
|
|
3012
|
+
* drag threshold; a click is held back for a double click only when both are
|
|
3013
|
+
* asked for. A bare modifier key is waited past. Escape, or an abort, resolves
|
|
3014
|
+
* `null`. What it takes is `preventDefault`ed and stopped, the click a browser
|
|
3015
|
+
* follows a press with included, so nothing else acts on it. The pointer is
|
|
3016
|
+
* left alone when no pointer kind is asked for, and keys other than Escape
|
|
3017
|
+
* when no key kind is.
|
|
3018
|
+
*/
|
|
3019
|
+
declare function captureGesture(target: EventTarget, opts?: CaptureGestureOptions): Promise<CapturedSpec | null>;
|
|
3020
|
+
|
|
2664
3021
|
/**
|
|
2665
3022
|
* A tool's activation key, as `ToolDef.keybinding` declares it. Captures
|
|
2666
3023
|
* only the fields meaningful to a tool-select key —
|
|
@@ -2798,6 +3155,29 @@ interface ToolDef<TOverlay = KernelOverlay> {
|
|
|
2798
3155
|
*/
|
|
2799
3156
|
declare function defineTool<TOverlay = KernelOverlay>(def: ToolDef<TOverlay>): Tool<TOverlay>;
|
|
2800
3157
|
|
|
3158
|
+
/**
|
|
3159
|
+
* Tool sets: a named group of tool ids, the unit a consumer opts into whole.
|
|
3160
|
+
* A set names ids, never tools, because a tool is built by a hook that holds
|
|
3161
|
+
* its state; whatever mounts the set supplies the tool for each id.
|
|
3162
|
+
*/
|
|
3163
|
+
/** A named group of tool ids. A member is an id or another set, whose ids it takes in order. */
|
|
3164
|
+
interface ToolSet<TId extends string = string> {
|
|
3165
|
+
readonly id: string;
|
|
3166
|
+
readonly members: readonly (TId | ToolSet<TId>)[];
|
|
3167
|
+
}
|
|
3168
|
+
/** One entry of a list of tools to mount: an id, or a set of them. */
|
|
3169
|
+
type ToolEntry<TId extends string = string> = TId | ToolSet<TId>;
|
|
3170
|
+
/** A set named `id` of `members`, in palette order. */
|
|
3171
|
+
declare function toolSet<TId extends string>(id: string, members: readonly ToolEntry<TId>[]): ToolSet<TId>;
|
|
3172
|
+
/** Whether `entry` is a set rather than a single id. */
|
|
3173
|
+
declare function isToolSet<TId extends string>(entry: ToolEntry<TId>): entry is ToolSet<TId>;
|
|
3174
|
+
/** Every id `entries` name, sets opened in place, each id once at its first place. */
|
|
3175
|
+
declare function toolIdsOf<TId extends string>(entries: readonly ToolEntry<TId>[]): TId[];
|
|
3176
|
+
/** `set` less every id `omit` names, sets opened. Keeps `set`'s id with a `-without` suffix. */
|
|
3177
|
+
declare function withoutTools<TId extends string>(set: ToolSet<TId>, ...omit: readonly ToolEntry<TId>[]): ToolSet<TId>;
|
|
3178
|
+
/** Whether `set` names `id`, directly or through a set inside it. */
|
|
3179
|
+
declare function toolSetHas<TId extends string>(set: ToolSet<TId>, id: string): boolean;
|
|
3180
|
+
|
|
2801
3181
|
/**
|
|
2802
3182
|
* One row in the route registry — a single `GestureBinding` on one tool,
|
|
2803
3183
|
* flattened into the route grammar's vocabulary so the inspector, the
|
|
@@ -2944,6 +3324,9 @@ interface ToolScopes {
|
|
|
2944
3324
|
* definition). Two gated actions conflict only when some mode lets both
|
|
2945
3325
|
* rules hold. Default: the kit's `DEFAULT_MODES`. */
|
|
2946
3326
|
modes?: readonly ActiveMode[];
|
|
3327
|
+
/** Binding overrides in force, applied to the entries' bindings. Actions are
|
|
3328
|
+
* read as given: an `ActionsRegistry`'s `list()` has applied them already. */
|
|
3329
|
+
overrides?: ResolvedBindingOverrides;
|
|
2947
3330
|
}
|
|
2948
3331
|
/**
|
|
2949
3332
|
* Detect reachable route conflicts in an assembled tool set and report each one.
|
|
@@ -3092,12 +3475,21 @@ declare function deleteScratch<T>(store: ScratchStore, key: ScratchKey<T>): bool
|
|
|
3092
3475
|
*
|
|
3093
3476
|
* Output shape: **files first (source order), then strings (item order)**.
|
|
3094
3477
|
* For paste (`itemsFromClipboardData`) string flavors are emitted in
|
|
3095
|
-
* `INGEST_STRING_MIMES` order: text/plain, text/html, text/uri-list
|
|
3478
|
+
* `INGEST_STRING_MIMES` order: text/plain, text/html, text/uri-list, then
|
|
3479
|
+
* the admitted ones in the order they were first admitted.
|
|
3096
3480
|
*/
|
|
3097
3481
|
|
|
3098
|
-
/** The string flavors the kit
|
|
3099
|
-
*
|
|
3482
|
+
/** The string flavors the kit always keeps. Any other is dropped at
|
|
3483
|
+
* materialization unless {@link admitIngestMime} has admitted it. */
|
|
3100
3484
|
declare const INGEST_STRING_MIMES: ReadonlySet<string>;
|
|
3485
|
+
/**
|
|
3486
|
+
* Keep string flavors of `mime` on drops, drags and pastes as well as the
|
|
3487
|
+
* {@link INGEST_STRING_MIMES}, so content from the app's own UI can travel
|
|
3488
|
+
* under a type of its own. Returns a release; the flavor is kept while any
|
|
3489
|
+
* admission of it is unreleased. Core admits each exact MIME a registered
|
|
3490
|
+
* content handler names.
|
|
3491
|
+
*/
|
|
3492
|
+
declare function admitIngestMime(mime: string): () => void;
|
|
3101
3493
|
/** Wrap raw `File`s (file picker, `CanvasExtensionApi.ingest`) as
|
|
3102
3494
|
* `IngestItem`s, normalizing each MIME the same way the drop/paste paths
|
|
3103
3495
|
* do. Synchronous — `File` objects are already fully owned. */
|
|
@@ -3202,6 +3594,10 @@ interface DepSchema {
|
|
|
3202
3594
|
activeTool: ActiveToolContextValue;
|
|
3203
3595
|
/** The menu host of the `ActionsProvider` in scope. */
|
|
3204
3596
|
menuHost?: MenuHost;
|
|
3597
|
+
/** The canvas's dispatch trace, for a dep to record a mode switch into. */
|
|
3598
|
+
dispatchTrace?: DispatchTrace;
|
|
3599
|
+
/** The keymaps open on the canvas: what a leader opens and a hint reads. */
|
|
3600
|
+
keymaps?: KeymapStack;
|
|
3205
3601
|
}
|
|
3206
3602
|
/** Every dep name declared by anything that has merged into {@link DepSchema}. */
|
|
3207
3603
|
type DepName = keyof DepSchema;
|
|
@@ -3249,4 +3645,4 @@ type KernelOverlay = OverlaySchema extends {
|
|
|
3249
3645
|
overlay: infer O;
|
|
3250
3646
|
} ? O : unknown;
|
|
3251
3647
|
|
|
3252
|
-
export { type
|
|
3648
|
+
export { type BindingScope as $, type Action as A, type BoundGesture as B, type Contribution as C, type DepRegistry as D, type ActionParams as E, type ActionPresentation as F, type ActionSettings as G, type ActionSettingsState as H, type ActionShortcut as I, type ActionSource as J, type KeymapDescription as K, type LongPressOptions as L, type MenuHost as M, type NodeId as N, type OverlayPosition as O, type ActionTuning as P, type ActionTunings as Q, type RuleCtx as R, ScopeNode as S, ToolNode as T, type ActionUnavailable as U, type ActionsProp as V, type AnyTool as W, type AssemblyInputs as X, BROWSER_RESERVED_CHORDS as Y, type BindingOpts as Z, type BindingOverrides as _, type DepName as a, NEVER as a$, type BindingSource as a0, type BodyPredicate as a1, type Bounds as a2, type BuildRuleCtxArgs as a3, COARSE_TARGET_SCALE as a4, type CaptureGestureOptions as a5, type CaptureKeyOptions as a6, type CaptureKind as a7, type CapturedSpec as a8, type Carried as a9, type HitShape as aA, type HotkeyTrigger as aB, INGEST_STRING_MIMES as aC, type ImmediateInvoker as aD, type InvocationCtx as aE, type Invoker as aF, KEYMAP_HOLD as aG, type KeyBinding as aH, type KeyReach as aI, type KeymapDef as aJ, type KeymapRow as aK, type KeymapStack as aL, type KeymapUnbound as aM, type KeymapVerdict as aN, type LongPressState as aO, type LongPressStore as aP, type MatchOutcome as aQ, type MatchResult as aR, type MenuHit as aS, type MenuOpenRequest as aT, type MenuSources as aU, type MenuWhen as aV, type ModeShortcutHandlers as aW, type ModeSwitchLogEntry as aX, type ModifierRole as aY, type ModifierRoles as aZ, type ModifierState as a_, type Carry as aa, type CarryCallbacks as ab, type CarryPayload as ac, type CarryPointer as ad, type CarryStage as ae, type Condition as af, type Conflict as ag, type ContributionChrome as ah, type ContributionDeps as ai, type ContributionRouting as aj, DEFAULT_ALLOWED_CAPABILITIES as ak, DEFAULT_DEVICE_PROFILE as al, type DebugSink as am, type DepListing as an, type DetectedDeviceFacts as ao, type DeviceProfile as ap, type DispatchRecord as aq, type DispatchRecordInput as ar, type DispatcherContext as as, type DragSample as at, type DroppedCandidate as au, type Eligibility as av, type EligibilityState as aw, type GestureBinding as ax, type GestureKitSettings as ay, type HandleKind as az, type DepSource as b, buildRuleCtx as b$, type OngoingHandle as b0, type OngoingInvoker as b1, type OngoingOverlay as b2, type OpenKeymap as b3, type OpenMenu as b4, type OverlayRole as b5, type OverlaySchema as b6, type OwnedMenuDef as b7, PREDICATE_TARGET as b8, type PendingLongPress as b9, type ToolEntry as bA, type ToolKeybinding as bB, type ToolModifiers as bC, type ToolOffhandBindingSpec as bD, type ToolPresentation as bE, type ToolScopes as bF, type ToolSet as bG, type ToolSlot as bH, type TraceLogEntry as bI, type TraceRing as bJ, type TunedBinding as bK, type UiOngoingControl as bL, type ViewportGestureKind as bM, type WalkStep as bN, actionBindings as bO, actionCursor as bP, actionItemTitle as bQ, actionItems as bR, actionSettingsParams as bS, actionShortcuts as bT, actionsTagged as bU, admitIngestMime as bV, assembleEntries as bW, bindingId as bX, browserReservedChord as bY, buildDepsFromRequires as bZ, buildRouteRegistry as b_, type PlacedBy as ba, type Point2 as bb, type PointerSession as bc, type PointerSessionCallbacks as bd, type PointerSessionCancelReason as be, type PointerSessionOptions as bf, type RankedCandidate as bg, type RecordCandidate as bh, type RegistryEntry as bi, type ReservedChord as bj, type ResizeAnchor as bk, type ResolveAllOptions as bl, type ResolveOnlyResult as bm, type ResolvedBindingOverrides as bn, type ResolvedCandidate as bo, type RoleKeys as bp, type RoleRebinds as bq, type Rule as br, type ScopedBinding as bs, type ScratchKey as bt, type ScratchStore as bu, type SelectionApi as bv, type Selector as bw, type SpecificityPart as bx, TOOL_OFFHAND_ID as by, type ToolDef as bz, type ActionDeps as c, offhandKeyFor as c$, buildToolOffhandBindings as c0, captureGesture as c1, captureKey as c2, clientToCanvas as c3, clientToCanvasRect as c4, createDepRegistry as c5, createDispatchTrace as c6, createDispatcher as c7, createKeymapStack as c8, createLongPressStore as c9, isSegment as cA, isSelectedBody as cB, isTool as cC, isToolSet as cD, isUnselectedBody as cE, itemTypesFromDataTransfer as cF, itemsFromClipboardData as cG, itemsFromDataTransfer as cH, itemsFromFiles as cI, keySpecShortcut as cJ, keymapContribution as cK, keymapLeaderBindingId as cL, keymapLeaderId as cM, keymapShortcuts as cN, keysHeld as cO, liveScope as cP, makeToolOffhandAction as cQ, matchActionItem as cR, matchBest as cS, matchSorted as cT, matchSortedWithBarred as cU, matchesKeyBinding as cV, mergeContributions as cW, mergeMenuDefs as cX, modeShortcutSpec as cY, modeShortcuts as cZ, multiTouchGestureId as c_, createMenuHost as ca, createTraceRing as cb, defineTool as cc, deleteScratch as cd, describeKeymap as ce, describeRule as cf, dlog as cg, dwarn as ch, evaluate as ci, evaluateActive as cj, evaluateEnabled as ck, findConflicts as cl, getScratch as cm, heldKeySpec as cn, historyKey as co, isAnchor as cp, isAnchorOrControl as cq, isBalanceGrip as cr, isBody as cs, isControlHandle as ct, isCornerWidget as cu, isDebugEnabled as cv, isEmpty as cw, isPathEditPart as cx, isResizeHandle as cy, isRotateHandle as cz, type ActionsRegistry as d, openPointerSession as d0, overlaysOf as d1, overriddenBindings as d2, pickMenu as d3, pointerGestureId as d4, readActionSettings as d5, reportRouteConflicts as d6, resolveBindingOverrides as d7, resolveDeviceProfile as d8, resolveMenu as d9, resolveParams as da, resolveSchemaMenu as db, roleReader as dc, routesForSpec as dd, ruleCanHoldIn as de, rulesExclusive as df, scopeBindings as dg, scratchKey as dh, setScratch as di, startCarry as dj, taggedActionItems as dk, targetConsultsAffordance as dl, toolIdsOf as dm, toolSet as dn, toolSetHas as dp, tuneAction as dq, validateActionDefaultBinding as dr, validateActionId as ds, withoutTools as dt, ActionDisabledReason as e, type ActionVariant as f, type MenuDef as g, DepNode as h, type KernelOverlay as i, type Tool as j, ActiveToolContextProvider as k, ActiveToolContextProviderIfRoot as l, type ActiveToolContextProviderProps as m, type ActiveToolContextValue as n, useOptionalActiveToolContext as o, type Dispatcher as p, type AffordanceHit as q, type DispatchTrace as r, type DepSchema as s, ALWAYS as t, useActiveToolContext as u, type ActionDispatch as v, type ActionEnabledResult as w, type ActionEntry as x, type ActionItem as y, type ActionParamSchema as z };
|