@weasel-js/routing 1.9.4 → 1.10.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-Y5RURZBH.js → chunk-4DR6AJMN.js} +979 -860
- package/dist/chunk-4DR6AJMN.js.map +1 -0
- package/dist/{index-gArM2tbt.d.ts → index-Bm2QEV09.d.ts} +286 -592
- package/dist/index.d.ts +3 -3
- package/dist/index.js +14 -64
- package/dist/index.js.map +1 -1
- package/dist/{internal-B0SsznMG.d.ts → internal-D0rDmi7t.d.ts} +38 -67
- package/dist/internal.d.ts +3 -3
- package/dist/react.d.ts +119 -34
- package/dist/react.js +590 -727
- package/dist/react.js.map +1 -1
- package/package.json +9 -8
- package/dist/chunk-Y5RURZBH.js.map +0 -1
|
@@ -1,29 +1,20 @@
|
|
|
1
|
-
import { View } from '@weasel-js/geom';
|
|
1
|
+
import { PoseLinear, View } from '@weasel-js/geom';
|
|
2
2
|
import * as react from 'react';
|
|
3
3
|
import { ReactNode } from 'react';
|
|
4
|
+
import { Emitter } from '@weasel-js/registry';
|
|
4
5
|
import { CapabilityTag, ActiveMode, ModeRegistry } from '@weasel-js/modes';
|
|
5
6
|
import { CursorSpec } from '@weasel-js/cursor';
|
|
6
|
-
import { ClaimableGesture, GestureSpec, InputEvent, TargetSpec, KeyHeldSpec, TargetPredicate, KeySpec, ParsedModifiers, GestureName
|
|
7
|
-
import { Op } from '@weasel-js/history';
|
|
7
|
+
import { ClaimableGesture, GestureSpec, IngestItem, InputEvent, TargetSpec, KeyHeldSpec, TargetPredicate, KeySpec, ParsedModifiers, GestureName } from '@weasel-js/gestures';
|
|
8
8
|
import '@weasel-js/select';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* The primitives the routing surface is typed in.
|
|
12
12
|
*
|
|
13
|
-
* Routing
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* which is declared in `@weasel-js/history` and re-exported by
|
|
19
|
-
* `core/ops/types`. The selection click policy's own vocabulary comes from
|
|
20
|
-
* `@weasel-js/select`, where the rules that read it live.
|
|
21
|
-
*
|
|
22
|
-
* Authority for the *behavior* behind each of these stays in core: `View` is
|
|
23
|
-
* what `core/viewport` maintains, `SelectionApi` is what `useSelection`
|
|
24
|
-
* returns, `DeviceProfile` is what `resolveDeviceProfile` detects. Changing
|
|
25
|
-
* one of these shapes is a change to core's contract that happens to be
|
|
26
|
-
* spelled here.
|
|
13
|
+
* Routing has to name a viewport, a rect, a node id, and a selection, but it
|
|
14
|
+
* never constructs, projects, or renders any of them. So they are declared
|
|
15
|
+
* here as structural interfaces with no dependencies, and `@weasel-js/core`
|
|
16
|
+
* re-exports each. The behavior behind them stays in core: changing one of
|
|
17
|
+
* these shapes is a change to core's contract that happens to be spelled here.
|
|
27
18
|
*/
|
|
28
19
|
|
|
29
20
|
/** An opaque scene-node identifier. Branded so a bare string cannot stand in. */
|
|
@@ -33,8 +24,9 @@ type NodeId = string & {
|
|
|
33
24
|
|
|
34
25
|
/**
|
|
35
26
|
* Axis-aligned rectangle in world space. The optional `rotation` (radians,
|
|
36
|
-
* around the AABB center)
|
|
37
|
-
*
|
|
27
|
+
* around the AABB center) and `transform` (inside it, about the same center)
|
|
28
|
+
* let chrome carry the frame a box is drawn in without a parallel type;
|
|
29
|
+
* `poseFrameOf` composes the two.
|
|
38
30
|
*/
|
|
39
31
|
interface Bounds {
|
|
40
32
|
x: number;
|
|
@@ -42,6 +34,7 @@ interface Bounds {
|
|
|
42
34
|
width: number;
|
|
43
35
|
height: number;
|
|
44
36
|
rotation?: number;
|
|
37
|
+
transform?: PoseLinear;
|
|
45
38
|
}
|
|
46
39
|
/** The selection as routing reads and writes it. */
|
|
47
40
|
interface SelectionApi {
|
|
@@ -65,6 +58,7 @@ interface SelectionApi {
|
|
|
65
58
|
* Apply a click to the selection per the configured mode/extend key.
|
|
66
59
|
* - `single`: replaces selection with `[id]`, regardless of modifiers.
|
|
67
60
|
* - `multi`: with the extend key held, toggles `id` in/out of the selection;
|
|
61
|
+
* with the range key held, selects from the last pressed id through `id`;
|
|
68
62
|
* otherwise replaces with `[id]`.
|
|
69
63
|
*/
|
|
70
64
|
applyClick(id: NodeId, modifiers: {
|
|
@@ -126,8 +120,8 @@ type HitShape = {
|
|
|
126
120
|
/** What a recorded viewport change was: a translation or a scale change. */
|
|
127
121
|
type ViewportGestureKind = 'pan' | 'zoom';
|
|
128
122
|
/**
|
|
129
|
-
* Where
|
|
130
|
-
*
|
|
123
|
+
* Where an action reports its hit math so the debug overlay can draw it. An
|
|
124
|
+
* action reaches the canvas's sink as its `debug` dep; routing never calls one.
|
|
131
125
|
*/
|
|
132
126
|
interface DebugSink {
|
|
133
127
|
recordHitbox(id: string, kind: 'body' | 'handle' | 'rotation' | 'anchor', shape: HitShape): void;
|
|
@@ -188,10 +182,11 @@ type ResizeAnchor = {
|
|
|
188
182
|
* registration lives in the node it was made in, and a lookup starts at a leaf
|
|
189
183
|
* and walks up, so one canvas never answers for another.
|
|
190
184
|
*/
|
|
185
|
+
|
|
191
186
|
/** What every node of one tree shares: a version and the listeners to it. */
|
|
192
187
|
interface ScopeTree {
|
|
193
188
|
version: number;
|
|
194
|
-
readonly listeners:
|
|
189
|
+
readonly listeners: Emitter;
|
|
195
190
|
}
|
|
196
191
|
declare class ScopeNode<N extends ScopeNode<N>> {
|
|
197
192
|
readonly parent: N | null;
|
|
@@ -270,15 +265,12 @@ declare function ActiveToolContextProvider({ children, initialActive, }: ActiveT
|
|
|
270
265
|
declare function useActiveToolContext(): ActiveToolContextValue;
|
|
271
266
|
/**
|
|
272
267
|
* Like `useActiveToolContext`, but returns `null` when no
|
|
273
|
-
* `<ActiveToolContextProvider>` is in scope instead of throwing.
|
|
274
|
-
* `useStandardActions` to preserve its silent-no-op contract when no provider
|
|
275
|
-
* is present.
|
|
268
|
+
* `<ActiveToolContextProvider>` is in scope instead of throwing.
|
|
276
269
|
*/
|
|
277
270
|
declare function useOptionalActiveToolContext(): ActiveToolContextValue | null;
|
|
278
271
|
/**
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
* so `<WeaselProvider>` defers to the host's existing scope.
|
|
272
|
+
* Mounts an `<ActiveToolContextProvider>` only when no parent provider is in
|
|
273
|
+
* scope; otherwise renders children unwrapped.
|
|
282
274
|
*/
|
|
283
275
|
declare function ActiveToolContextProviderIfRoot({ children, }: {
|
|
284
276
|
children: ReactNode;
|
|
@@ -456,25 +448,20 @@ declare const NEVER: Rule;
|
|
|
456
448
|
* { when: function hasPath() { … } } → when(hasPath)
|
|
457
449
|
* ```
|
|
458
450
|
*
|
|
459
|
-
* Never throws and always terminates
|
|
460
|
-
* on a cyclic value and renders the `when` arm as a useless `{}`.
|
|
451
|
+
* Never throws and always terminates.
|
|
461
452
|
*/
|
|
462
453
|
declare function describeRule(rule: Rule): string;
|
|
463
454
|
/** Evaluate a rule against a context. Pure, and cheap enough to run per
|
|
464
455
|
* frame for every piece of chrome. */
|
|
465
456
|
declare function evaluate(rule: Rule, ctx: RuleCtx): boolean;
|
|
466
457
|
/**
|
|
467
|
-
* Composable eligibility predicate with a fluent surface.
|
|
468
|
-
*
|
|
469
|
-
*
|
|
470
|
-
*
|
|
471
|
-
* Callable form `cond(ctx)` evaluates the tree against ctx. The fluent methods
|
|
472
|
-
* return new Conditions wrapping new trees.
|
|
458
|
+
* Composable eligibility predicate with a fluent surface. `cond(ctx)`
|
|
459
|
+
* evaluates its `Rule` tree, carried at `.rule`; the fluent methods return new
|
|
460
|
+
* Conditions wrapping new trees.
|
|
473
461
|
*
|
|
474
462
|
* **Chain semantics: strict left-to-right, no precedence.** `a.and(b).or(c)` is
|
|
475
|
-
* `(a && b) || c`; `a.or(b).and(c)` is `(a || b) && c`.
|
|
476
|
-
*
|
|
477
|
-
* the subexpression or use the top-level `or(...)`.
|
|
463
|
+
* `(a && b) || c`; `a.or(b).and(c)` is `(a || b) && c`. For grouped
|
|
464
|
+
* disjunction, name the subexpression or use the top-level `or(...)`.
|
|
478
465
|
*/
|
|
479
466
|
interface Condition {
|
|
480
467
|
(ctx: RuleCtx): boolean;
|
|
@@ -542,9 +529,6 @@ declare const DEFAULT_DEVICE_PROFILE: DeviceProfile;
|
|
|
542
529
|
* An Action's `invoker` field describes how it's run. `immediate` invokers
|
|
543
530
|
* fire once (delete, align, undo). `ongoing` invokers open a phase machine
|
|
544
531
|
* driven by the dispatcher (move, resize, pinch-zoom).
|
|
545
|
-
*
|
|
546
|
-
* See `docs/superpowers/specs/2026-05-16-registry-unification-design.md`
|
|
547
|
-
* § "Types" for the full design.
|
|
548
532
|
*/
|
|
549
533
|
|
|
550
534
|
/** A 2D point in either world or screen coordinates. */
|
|
@@ -553,31 +537,20 @@ interface Point2 {
|
|
|
553
537
|
y: number;
|
|
554
538
|
}
|
|
555
539
|
/**
|
|
556
|
-
*
|
|
557
|
-
*
|
|
558
|
-
* Populated by the dispatcher when the `affordanceAt` thunk is provided to
|
|
559
|
-
* `useGestureDispatcher`. Tools / action invokers that only fire on a specific
|
|
560
|
-
* affordance (e.g. a resize handle) use this field as a guard — if the
|
|
561
|
-
* affordance is absent or is the wrong kind, they return `{}` and let other
|
|
562
|
-
* bindings handle the drag.
|
|
540
|
+
* Which UI affordance was hit at pointerdown. Populated by the dispatcher when
|
|
541
|
+
* the `affordanceAt` thunk is provided to `useGestureDispatcher`.
|
|
563
542
|
*
|
|
564
543
|
* `kind` is a discriminator string:
|
|
565
544
|
* - `'handle:top-left'` / `'handle:top-right'` / `'handle:bottom-left'` /
|
|
566
545
|
* `'handle:bottom-right'` — corner resize handles.
|
|
567
546
|
* - `'rotate-handle'` — the rotation affordance.
|
|
568
547
|
* - `'anchor:N'` — a path anchor at index N.
|
|
569
|
-
*
|
|
570
|
-
* `fixedPoint` is the world-space point that should remain stationary during
|
|
571
|
-
* the gesture. For resize handles this is the opposite (diagonally fixed)
|
|
572
|
-
* corner; for rotate it is the pivot.
|
|
573
|
-
*
|
|
574
|
-
* `targetIds` are the node ids this affordance belongs to.
|
|
575
548
|
*/
|
|
576
549
|
interface AffordanceHit {
|
|
577
550
|
/** Discriminator string, e.g. `'handle:bottom-right'`. */
|
|
578
551
|
kind: string;
|
|
579
552
|
/** Id of whatever produced this hit — a kit affordance's `id`, or the
|
|
580
|
-
* registered layer's id.
|
|
553
|
+
* registered layer's id. */
|
|
581
554
|
owner?: string;
|
|
582
555
|
/** `'exclusive'` means no binding may act on this point unless its target
|
|
583
556
|
* consults the affordance. `'shared'` (the default) competes on scope and
|
|
@@ -592,25 +565,18 @@ interface AffordanceHit {
|
|
|
592
565
|
};
|
|
593
566
|
/** Which nodes this affordance belongs to. */
|
|
594
567
|
targetIds?: string[];
|
|
595
|
-
/** Set when `kind` matches `'handle:*'
|
|
596
|
-
*
|
|
597
|
-
* Other affordance kinds (rotate-handle, anchor:N, controlIn:N, controlOut:N)
|
|
598
|
-
* leave this undefined. */
|
|
568
|
+
/** Set only when `kind` matches `'handle:*'`: which corner stays fixed
|
|
569
|
+
* during a resize. */
|
|
599
570
|
anchor?: ResizeAnchor;
|
|
600
|
-
/** Cursor to show while the pointer hovers this affordance (no
|
|
601
|
-
*
|
|
602
|
-
*
|
|
603
|
-
* action-cursor prediction, then to the active tool's cursor. */
|
|
571
|
+
/** Cursor to show while the pointer hovers this affordance (no gesture in
|
|
572
|
+
* flight). Unset falls through to action-cursor prediction, then to the
|
|
573
|
+
* active tool's cursor. */
|
|
604
574
|
cursor?: CursorSpec;
|
|
605
575
|
/**
|
|
606
576
|
* Free-form payload from whatever produced the hit, carried through to the
|
|
607
|
-
* matching action untouched.
|
|
608
|
-
*
|
|
609
|
-
* Kit affordances describe themselves fully in the fields above and leave
|
|
610
|
-
* this unset. It exists for affordances the kit doesn't know the shape of —
|
|
577
|
+
* matching action untouched. Kit affordances leave it unset; it exists for
|
|
611
578
|
* a registered layer's own chrome, where the hit-test already resolved
|
|
612
|
-
* *which* of its pieces was hit
|
|
613
|
-
* redo that work. `@weasel-js/hud` passes the hit widget here.
|
|
579
|
+
* *which* of its pieces was hit.
|
|
614
580
|
*/
|
|
615
581
|
payload?: unknown;
|
|
616
582
|
}
|
|
@@ -619,9 +585,7 @@ interface AffordanceHit {
|
|
|
619
585
|
* stylus state the originating `PointerEvent` carried.
|
|
620
586
|
*
|
|
621
587
|
* The stylus fields are absent for mouse/touch on browsers that don't report
|
|
622
|
-
* them, and for synthetic events.
|
|
623
|
-
* (e.g. `Stroke.vertexWidths` from a pencil stroke) read them off the samples
|
|
624
|
-
* their `insert` dep receives — see `apps/site/demos/StrokeAndFillDemo.tsx`.
|
|
588
|
+
* them, and for synthetic events.
|
|
625
589
|
*/
|
|
626
590
|
interface DragSample extends Point2 {
|
|
627
591
|
/** 0..1. Mouse/touch report 0.5 while a button is held, per the spec. */
|
|
@@ -659,23 +623,19 @@ interface InvocationCtx {
|
|
|
659
623
|
* mutates the viewport itself (pan, view-zoom), because world-space
|
|
660
624
|
* deltas become self-referential as the view shifts mid-drag.
|
|
661
625
|
*
|
|
662
|
-
*
|
|
663
|
-
*
|
|
664
|
-
* provide them.
|
|
626
|
+
* Absent when the underlying pointer events carried no
|
|
627
|
+
* `clientX`/`clientY`.
|
|
665
628
|
*/
|
|
666
629
|
screenDelta?: Point2;
|
|
667
630
|
affordance?: AffordanceHit;
|
|
668
631
|
/**
|
|
669
632
|
* Full pointermove history for the current drag, in world space, with
|
|
670
633
|
* per-sample stylus state when the browser reported it.
|
|
671
|
-
* Accumulated by the dispatcher on every `pointermove` pump event.
|
|
672
634
|
* Available only during `onMove` and `onEnd` calls (not on `start`).
|
|
673
635
|
*
|
|
674
636
|
* During `onMove` this is the dispatcher's live accumulator, and it grows
|
|
675
637
|
* under you — read it, don't retain it. On `onEnd` it is a snapshot, so
|
|
676
638
|
* that is where an action that keeps the trail should take it.
|
|
677
|
-
* Used by `lassoSelectAction` to build its polygon vertex list and by
|
|
678
|
-
* `insertAction`'s pencil kind to carry the freehand stroke.
|
|
679
639
|
*/
|
|
680
640
|
points?: DragSample[];
|
|
681
641
|
};
|
|
@@ -697,12 +657,13 @@ interface InvocationCtx {
|
|
|
697
657
|
centroid: Point2;
|
|
698
658
|
/** Distance between the two primary pointers, in CSS pixels. */
|
|
699
659
|
spread: number;
|
|
660
|
+
/** Degrees the line between the two primary pointers has turned since
|
|
661
|
+
* the gesture began, clockwise positive. It keeps counting past a half
|
|
662
|
+
* turn. Zero where the events carry no `MultiTouchEvent.angle`. */
|
|
700
663
|
rotation: number;
|
|
701
664
|
/**
|
|
702
|
-
* Pinch-zoom geometry
|
|
703
|
-
* handle is in flight and a pointermove-pump fires.
|
|
665
|
+
* Pinch-zoom geometry, populated while a multitouch handle is in flight.
|
|
704
666
|
* `startSpread` is the spread at the moment the gesture began.
|
|
705
|
-
* `currentSpread` is the spread at the current frame.
|
|
706
667
|
*/
|
|
707
668
|
pinch?: {
|
|
708
669
|
startSpread: number;
|
|
@@ -723,33 +684,27 @@ interface InvocationCtx {
|
|
|
723
684
|
viewId?: string | null;
|
|
724
685
|
/**
|
|
725
686
|
* Per-invocation parameters. Populated by `ActionsRegistry.begin()` for
|
|
726
|
-
* UI-driven ongoing actions (color picker, opacity slider)
|
|
727
|
-
*
|
|
728
|
-
*
|
|
729
|
-
* receive params via `BindingOpts.params` on `start` (the `opts` arg).
|
|
687
|
+
* UI-driven ongoing actions (color picker, opacity slider). The gesture
|
|
688
|
+
* dispatcher does not populate this field; gesture-driven actions receive
|
|
689
|
+
* params via `BindingOpts.params` on `start` (the `opts` arg).
|
|
730
690
|
*/
|
|
731
691
|
params?: Record<string, unknown>;
|
|
732
692
|
}
|
|
733
693
|
/** Per-invocation options the dispatcher reads from a `GestureBinding`'s
|
|
734
|
-
* `opts` field and passes to `OngoingInvoker.start`.
|
|
735
|
-
* behaviors; extensible. */
|
|
694
|
+
* `opts` field and passes to `OngoingInvoker.start`. */
|
|
736
695
|
interface BindingOpts {
|
|
737
696
|
/** Gesture behaviors the action's own invoker runs. Routing carries the
|
|
738
697
|
* array to the action and never reads an element, so the element type is
|
|
739
|
-
* open here; `@weasel-js/core` narrows it
|
|
740
|
-
* unknown, unknown>[]`, which is what a behavior is authored against. */
|
|
698
|
+
* open here; `@weasel-js/core` narrows it. */
|
|
741
699
|
behaviors?: unknown[];
|
|
742
|
-
/** Per-binding action parameters
|
|
743
|
-
*
|
|
744
|
-
* invokers, when needed). Loose typing (Record<string, unknown>) for
|
|
745
|
-
* now; consider per-action typing later via BindingOpts<A>.
|
|
700
|
+
/** Per-binding action parameters, which an immediate invoker reads via the
|
|
701
|
+
* second arg to `run`.
|
|
746
702
|
*
|
|
747
|
-
*
|
|
748
|
-
*
|
|
749
|
-
*
|
|
750
|
-
*
|
|
751
|
-
*
|
|
752
|
-
* via `resolveParams(opts?.params)`. */
|
|
703
|
+
* May also be a thunk evaluated each time the value is needed, so a tool
|
|
704
|
+
* can close over refs that mutate during a gesture (e.g. polygon `sides`
|
|
705
|
+
* adjusted mid-drag via ArrowUp). An ongoing invoker that wants the latest
|
|
706
|
+
* values at commit re-calls it inside `onEnd` via
|
|
707
|
+
* `resolveParams(opts?.params)`. */
|
|
753
708
|
params?: Record<string, unknown> | (() => Record<string, unknown>);
|
|
754
709
|
/**
|
|
755
710
|
* The views this binding is live in, by id, where `null` is the surface's
|
|
@@ -761,6 +716,13 @@ interface BindingOpts {
|
|
|
761
716
|
* drag bound inside a minimap view loses to the active tool's marquee.
|
|
762
717
|
*/
|
|
763
718
|
views?: readonly (string | null)[];
|
|
719
|
+
/**
|
|
720
|
+
* Says this binding is declared ahead of another on the same input on
|
|
721
|
+
* purpose: its action declines through `enabled()` when the input is not its
|
|
722
|
+
* to take, and the next binding fires. The dispatcher does that for any
|
|
723
|
+
* binding; this tells the route-conflict check the order is the design.
|
|
724
|
+
*/
|
|
725
|
+
yields?: boolean;
|
|
764
726
|
}
|
|
765
727
|
/** Resolve `BindingOpts.params` to a concrete record (calling the thunk if
|
|
766
728
|
* needed). Returns `undefined` for absent params. */
|
|
@@ -792,12 +754,10 @@ type OverlayRole =
|
|
|
792
754
|
* `OngoingHandle.overlay()` — a marquee rect, a lasso trail, the outline of a
|
|
793
755
|
* shape being dragged out, the line a slice would cut along.
|
|
794
756
|
*
|
|
795
|
-
* Every variant is world geometry and nothing else
|
|
796
|
-
* color, no coordinate space to choose
|
|
797
|
-
*
|
|
798
|
-
*
|
|
799
|
-
* normalizes this into `ResolvedOverlay`, which is what a painter consumes —
|
|
800
|
-
* `useDispatcherOverlayLayer` is core's, and it owns every style decision.
|
|
757
|
+
* Every variant is world geometry and nothing else — no `DrawCommand`, no
|
|
758
|
+
* color, no coordinate space to choose — so a surface rendering the same
|
|
759
|
+
* gesture some other way can still read it. The painter owns every style
|
|
760
|
+
* decision.
|
|
801
761
|
*/
|
|
802
762
|
type OngoingOverlay = {
|
|
803
763
|
kind: 'marquee';
|
|
@@ -823,13 +783,9 @@ type OngoingOverlay = {
|
|
|
823
783
|
shiftHeld: boolean;
|
|
824
784
|
} | {
|
|
825
785
|
/**
|
|
826
|
-
* An open run of world points the gesture is proposing —
|
|
827
|
-
*
|
|
828
|
-
*
|
|
829
|
-
* Geometry only, like every other variant: `role` is the whole of what
|
|
830
|
-
* an action says about appearance, and the painting surface is what
|
|
831
|
-
* turns it into a stroke. An action that picked its own color would be
|
|
832
|
-
* unpaintable by any renderer but the one it had in mind.
|
|
786
|
+
* An open run of world points the gesture is proposing — a cut line, a
|
|
787
|
+
* routed connector. `role` is the whole of what an action says about
|
|
788
|
+
* appearance.
|
|
833
789
|
*/
|
|
834
790
|
kind: 'polyline';
|
|
835
791
|
/** At least two points. Anything shorter is dropped in resolution. */
|
|
@@ -841,19 +797,14 @@ type OngoingOverlay = {
|
|
|
841
797
|
role?: OverlayRole;
|
|
842
798
|
} | {
|
|
843
799
|
/**
|
|
844
|
-
* Live insert-drag preview
|
|
845
|
-
*
|
|
846
|
-
*
|
|
847
|
-
* preview through the dispatcher overlay layer instead.
|
|
800
|
+
* Live insert-drag preview. Pre-commit there is no scene node to ghost
|
|
801
|
+
* via `previewIds()`/`previewPose()`, so insert paints its preview
|
|
802
|
+
* through the overlay instead.
|
|
848
803
|
*
|
|
849
|
-
* `
|
|
850
|
-
* the
|
|
851
|
-
*
|
|
852
|
-
*
|
|
853
|
-
* commit factory uses, so the preview matches the eventual node.
|
|
854
|
-
*
|
|
855
|
-
* `extras` is opaque (`unknown`) at the union level; the overlay
|
|
856
|
-
* renderer narrows on `shape` and casts the field shape it expects.
|
|
804
|
+
* `bounds` is the AABB of the current drag (start/current normalized).
|
|
805
|
+
* `extras` is the per-kind extras the action already collected; the
|
|
806
|
+
* overlay renderer narrows on `shape` and casts the field shape it
|
|
807
|
+
* expects.
|
|
857
808
|
*/
|
|
858
809
|
kind: 'insertPreview';
|
|
859
810
|
/** Which preview the dispatcher's overlay draws. The kit's own shape
|
|
@@ -867,11 +818,9 @@ type OngoingOverlay = {
|
|
|
867
818
|
height: number;
|
|
868
819
|
};
|
|
869
820
|
extras: unknown;
|
|
870
|
-
/** World-space point to paint a small "anchor" dot at
|
|
871
|
-
* click point
|
|
872
|
-
*
|
|
873
|
-
* click point, and for any shape in center mode where the dot
|
|
874
|
-
* marks the center the shape grows around. */
|
|
821
|
+
/** World-space point to paint a small "anchor" dot at, marking the
|
|
822
|
+
* click point — for radial shapes (polygon/star), where no vertex
|
|
823
|
+
* sits on it, and for any shape in center mode. */
|
|
875
824
|
anchorPoint?: {
|
|
876
825
|
x: number;
|
|
877
826
|
y: number;
|
|
@@ -884,42 +833,23 @@ type OngoingOverlay = {
|
|
|
884
833
|
interface OngoingHandle {
|
|
885
834
|
/**
|
|
886
835
|
* Optional logical action kind — a stable, human-readable tag the
|
|
887
|
-
* dispatcher exposes via `getActiveAction()`
|
|
888
|
-
*
|
|
889
|
-
* is currently in flight" without inspecting handles directly.
|
|
890
|
-
*
|
|
891
|
-
* Examples: `'marquee'`, `'lasso'`, `'move'`, `'resize'`, `'rotate'`,
|
|
892
|
-
* `'pan'`, `'pinch'`.
|
|
893
|
-
*
|
|
894
|
-
* Distinct from the dispatcher's internal `gestureId` (`pointer-mouse`,
|
|
895
|
-
* `key-held-Space`, etc.) which keys per-pointer state and is not
|
|
896
|
-
* meaningful to consumers.
|
|
836
|
+
* dispatcher exposes via `getActiveAction()`, e.g. `'marquee'`, `'move'`,
|
|
837
|
+
* `'resize'`, `'pan'`.
|
|
897
838
|
*
|
|
898
|
-
* When omitted,
|
|
899
|
-
*
|
|
900
|
-
* actions that don't have visible chrome of their own.
|
|
839
|
+
* When omitted, `getActiveAction().kind` reports `null` even though a
|
|
840
|
+
* handle is in flight.
|
|
901
841
|
*/
|
|
902
842
|
kind?: string;
|
|
903
843
|
onMove?(ctx: InvocationCtx): void;
|
|
904
844
|
onEnd?(ctx: InvocationCtx, reason: 'commit' | 'cancel'): void;
|
|
905
845
|
/**
|
|
906
|
-
* Optional preview surface
|
|
907
|
-
*
|
|
908
|
-
* An ongoing-action implementation may populate `previewIds()` +
|
|
909
|
-
* `previewPose(id)` to expose its in-flight preview state for the
|
|
910
|
-
* canvas's preview-ghost layer (`usePreviewGhostLayer`) to render on
|
|
911
|
-
* top of the committed scene during the gesture.
|
|
912
|
-
*
|
|
913
|
-
* Returning `null` (or omitting the method entirely) means "no preview
|
|
914
|
-
* this gesture" — the canvas will skip this handle as a source.
|
|
846
|
+
* Optional preview surface: the in-flight state the canvas's preview-ghost
|
|
847
|
+
* layer renders on top of the committed scene during the gesture.
|
|
915
848
|
*
|
|
916
|
-
*
|
|
917
|
-
*
|
|
918
|
-
*
|
|
919
|
-
*
|
|
920
|
-
* merges all sources via first-non-null semantics, with tool-side
|
|
921
|
-
* previews taking precedence over dispatcher-side (preserves
|
|
922
|
-
* backwards-compat during the registry-unification migration).
|
|
849
|
+
* `previewIds()` enumerates the displaced node ids; `previewPose(id)`
|
|
850
|
+
* returns the interim pose for one of them (shape opaque — the canvas
|
|
851
|
+
* casts to its `TPose` parameter). Returning `null`, or omitting the
|
|
852
|
+
* method, means "no preview this gesture".
|
|
923
853
|
*/
|
|
924
854
|
previewIds?(): Iterable<string> | null;
|
|
925
855
|
previewPose?(id: string): unknown | null;
|
|
@@ -932,40 +862,22 @@ interface OngoingHandle {
|
|
|
932
862
|
*/
|
|
933
863
|
previewOpaqueIds?(): Iterable<string> | null;
|
|
934
864
|
/**
|
|
935
|
-
* When `false`, the preview-ghost layer paints the ghost AND the
|
|
936
|
-
*
|
|
937
|
-
* `true
|
|
938
|
-
* during the gesture). Clone overrides to `false` so the original
|
|
939
|
-
* stays put and the ghost appears at the drag target.
|
|
865
|
+
* When `false`, the preview-ghost layer paints the ghost AND the source
|
|
866
|
+
* node stays visible at its committed pose, as a clone wants. Defaults to
|
|
867
|
+
* `true`: the ghost replaces the source during the gesture.
|
|
940
868
|
*/
|
|
941
869
|
previewHidesSource?: boolean;
|
|
942
870
|
/**
|
|
943
|
-
* Optional per-id preview *data
|
|
944
|
-
*
|
|
945
|
-
*
|
|
946
|
-
* `data
|
|
947
|
-
* ghost layer assembles a synthetic node from `{ ...node, pose:
|
|
948
|
-
* previewPose ?? node.pose, data: previewData ?? node.data }` before
|
|
949
|
-
* calling the scene slot's `drawOne`.
|
|
950
|
-
*
|
|
951
|
-
* Sources compose first-non-null per axis: an action can emit only
|
|
952
|
-
* `previewPose` (translation), only `previewData` (data-only edit),
|
|
953
|
-
* or both (pose + data both change, e.g. anchor drag on a data.path
|
|
954
|
-
* node where the bounds shift).
|
|
871
|
+
* Optional per-id preview *data*, for a gesture that mutates `node.data`
|
|
872
|
+
* (e.g. anchor-edit on nodes that store the polygon on `data.path`) rather
|
|
873
|
+
* than, or in addition to, the pose. Falls back to the committed
|
|
874
|
+
* `node.data` when null/absent.
|
|
955
875
|
*/
|
|
956
876
|
previewData?(id: string): unknown | null;
|
|
957
877
|
/**
|
|
958
|
-
* Optional chrome surface
|
|
959
|
-
*
|
|
960
|
-
*
|
|
961
|
-
* non-ghost visual (marquee rectangle, lasso polyline) for the canvas's
|
|
962
|
-
* `useDispatcherOverlayLayer` to paint while the gesture is in flight.
|
|
963
|
-
* Returning `null` (or omitting the method) means "no overlay this
|
|
964
|
-
* gesture" — the canvas will skip this handle as a chrome source.
|
|
965
|
-
*
|
|
966
|
-
* Distinct from the `previewIds()`/`previewPose(id)` ghost surface,
|
|
967
|
-
* which paints displaced scene-node silhouettes. Marquee and lasso
|
|
968
|
-
* gestures don't displace any node, but still need on-screen feedback.
|
|
878
|
+
* Optional chrome surface: a non-ghost visual (marquee rectangle, lasso
|
|
879
|
+
* polyline) painted while the gesture is in flight. Returning `null`, or
|
|
880
|
+
* omitting the method, means "no overlay this gesture".
|
|
969
881
|
*/
|
|
970
882
|
overlay?(): OngoingOverlay | null;
|
|
971
883
|
}
|
|
@@ -984,9 +896,7 @@ interface OngoingInvoker {
|
|
|
984
896
|
timing: 'ongoing';
|
|
985
897
|
start(ctx: InvocationCtx, opts?: BindingOpts): OngoingHandle;
|
|
986
898
|
}
|
|
987
|
-
/** Pluggable invocation strategy for an Action.
|
|
988
|
-
* (`longPress`, `twoStage`, `modal`) extend this union without touching
|
|
989
|
-
* the `Action` type. */
|
|
899
|
+
/** Pluggable invocation strategy for an Action. */
|
|
990
900
|
type Invoker = ImmediateInvoker | OngoingInvoker;
|
|
991
901
|
|
|
992
902
|
/**
|
|
@@ -1064,11 +974,9 @@ type ActionDisabledReason = (typeof ActionDisabledReason)[keyof typeof ActionDis
|
|
|
1064
974
|
*/
|
|
1065
975
|
interface ActionPresentation {
|
|
1066
976
|
label: string;
|
|
1067
|
-
/** Inline-SVG icon for palette / toolbar surfaces.
|
|
1068
|
-
* `
|
|
1069
|
-
*
|
|
1070
|
-
* metadata. May be a static `ReactNode` or a function (rare; useful
|
|
1071
|
-
* for state-aware icons like a "lock" toggle). */
|
|
977
|
+
/** Inline-SVG icon for palette / toolbar surfaces. May be a static
|
|
978
|
+
* `ReactNode` or a function (rare; useful for state-aware icons like a
|
|
979
|
+
* "lock" toggle). */
|
|
1072
980
|
icon?: ReactNode | (() => ReactNode);
|
|
1073
981
|
/** Grouping key for palette/menu surfaces. Free-form string; the kit
|
|
1074
982
|
* ships defaults for `'align'` (six edges/centers), `'distribute'`
|
|
@@ -1124,30 +1032,21 @@ interface ActionDispatch extends BindingSource {
|
|
|
1124
1032
|
/**
|
|
1125
1033
|
* @experimental
|
|
1126
1034
|
* Optional predicate saying whether the action can run right now. Return
|
|
1127
|
-
* `true` when it can, or
|
|
1128
|
-
*
|
|
1129
|
-
*
|
|
1130
|
-
*
|
|
1131
|
-
*
|
|
1132
|
-
*
|
|
1035
|
+
* `true` when it can, or an `ActionDisabledReason` when it cannot. The
|
|
1036
|
+
* command palette greys out a disabled row, skips it in keyboard nav,
|
|
1037
|
+
* ignores clicks, and shows the reason next to the label. The dispatcher
|
|
1038
|
+
* skips a disabled action too, on a key as on a pointer, and falls through
|
|
1039
|
+
* to the next binding that matches. `ActionsRegistry.trigger` does not ask,
|
|
1040
|
+
* so the action's own `run` should still self-guard.
|
|
1133
1041
|
*
|
|
1134
1042
|
* **Contract:** must be pure (no side effects), fast (< 4ms in dev), and
|
|
1135
|
-
* must not throw.
|
|
1136
|
-
* `
|
|
1137
|
-
* caught and treated as disabled with reason `'(predicate threw)'`.
|
|
1138
|
-
*
|
|
1139
|
-
* Snapshot-on-open semantics: the palette evaluates `enabled` once when
|
|
1140
|
-
* opened and does NOT re-evaluate on selection changes while open. Live
|
|
1141
|
-
* reactive updates are deferred — palette is short-lived.
|
|
1043
|
+
* must not throw. A throw is caught and treated as disabled with reason
|
|
1044
|
+
* `ActionDisabledReason.PredicateThrew`.
|
|
1142
1045
|
*
|
|
1143
|
-
* The
|
|
1144
|
-
*
|
|
1046
|
+
* The palette evaluates `enabled` once when opened and does NOT re-evaluate
|
|
1047
|
+
* on selection changes while open.
|
|
1145
1048
|
*
|
|
1146
|
-
*
|
|
1147
|
-
* `ImmediateInvoker.run`; callers (`evaluateEnabled` / the ActionBar) may
|
|
1148
|
-
* synthesize it from the surrounding `DepRegistry` so predicates can
|
|
1149
|
-
* inspect selection / scene / etc. Predicates that don't need deps just
|
|
1150
|
-
* ignore the arg.
|
|
1049
|
+
* `deps` is the same bag passed to `ImmediateInvoker.run`.
|
|
1151
1050
|
*
|
|
1152
1051
|
* `at` is the world point of the event being routed, when it has one — a
|
|
1153
1052
|
* click, a press, a long-press. An action that only applies somewhere
|
|
@@ -1155,10 +1054,18 @@ interface ActionDispatch extends BindingSource {
|
|
|
1155
1054
|
* falls through to the next binding and hover prediction shows the
|
|
1156
1055
|
* action's `cursor` only where it would act. Palettes and keystrokes
|
|
1157
1056
|
* pass no point; treat absent as "anywhere".
|
|
1057
|
+
*
|
|
1058
|
+
* `carried` is what a drop or a paste being routed brings: its items. For
|
|
1059
|
+
* a drag still in flight they hold a MIME and nothing else, since a
|
|
1060
|
+
* browser withholds contents until the drop. An action that takes only
|
|
1061
|
+
* some content refuses the rest here, so a drag it would not take is not
|
|
1062
|
+
* accepted and a paste it would not take stays the page's.
|
|
1158
1063
|
*/
|
|
1159
1064
|
enabled?: (deps?: ActionDeps, at?: {
|
|
1160
1065
|
x: number;
|
|
1161
1066
|
y: number;
|
|
1067
|
+
}, carried?: {
|
|
1068
|
+
items: readonly IngestItem[];
|
|
1162
1069
|
}) => true | ActionDisabledReason;
|
|
1163
1070
|
/**
|
|
1164
1071
|
* @experimental
|
|
@@ -1187,12 +1094,11 @@ interface ActionDispatch extends BindingSource {
|
|
|
1187
1094
|
/**
|
|
1188
1095
|
* CSS cursor shown while the pointer hovers a spot where this action
|
|
1189
1096
|
* would win the drag — or, when the drag's winner declares no cursor,
|
|
1190
|
-
* the click. The hover-cursor pump
|
|
1191
|
-
*
|
|
1192
|
-
*
|
|
1193
|
-
*
|
|
1194
|
-
*
|
|
1195
|
-
* pump via `AffordanceRegion.cursor` and never reach this field.
|
|
1097
|
+
* the click. The hover-cursor pump runs the same match walk a real press
|
|
1098
|
+
* takes, so the hint and the actual target stay in sync. Omitted = no
|
|
1099
|
+
* override (the active tool's `Tool.cursor` shows). Affordance hits are
|
|
1100
|
+
* resolved earlier in the pump via `AffordanceRegion.cursor` and never
|
|
1101
|
+
* reach this field.
|
|
1196
1102
|
*
|
|
1197
1103
|
* Static value only. Prediction runs `enabled()` but cannot run the
|
|
1198
1104
|
* invoker, so an action that matches yet bails at `start()` (empty
|
|
@@ -1210,19 +1116,12 @@ interface ActionDispatch extends BindingSource {
|
|
|
1210
1116
|
* ("you are panning"). An action can declare either, both, or neither;
|
|
1211
1117
|
* with only `cursor` set, the hover hint holds for the duration of the
|
|
1212
1118
|
* gesture.
|
|
1213
|
-
*
|
|
1214
|
-
* This is where mid-gesture cursors live now. They used to come from the
|
|
1215
|
-
* tool side — an `engaged` phase entry's cursor,
|
|
1216
|
-
* or a function-form `Tool.cursor` reading the gesture scratch out of the
|
|
1217
|
-
* tool-routing dispatcher. Both belonged to a pipeline whose whole job was
|
|
1218
|
-
* being taken over by bindings, and neither could describe a cursor for an
|
|
1219
|
-
* action a tool doesn't own.
|
|
1220
1119
|
*/
|
|
1221
1120
|
activeCursor?: CursorSpec;
|
|
1222
1121
|
}
|
|
1223
1122
|
/**
|
|
1224
1123
|
* @experimental
|
|
1225
|
-
* Single registered action.
|
|
1124
|
+
* Single registered action.
|
|
1226
1125
|
*/
|
|
1227
1126
|
interface Action extends ActionDispatch, ActionPresentation {
|
|
1228
1127
|
}
|
|
@@ -1290,20 +1189,9 @@ declare function evaluateEnabled(action: Action, deps?: ActionDeps): ActionEnabl
|
|
|
1290
1189
|
declare function evaluateActive(action: Action, deps?: ActionDeps): boolean | undefined;
|
|
1291
1190
|
|
|
1292
1191
|
/**
|
|
1293
|
-
*
|
|
1294
|
-
*
|
|
1295
|
-
*
|
|
1296
|
-
* the dispatcher calls `registry.get('selection')` at invocation time to
|
|
1297
|
-
* build a typed Deps bag.
|
|
1298
|
-
*
|
|
1299
|
-
* `DepSchema` — the map of dep name → value type — is the single source of
|
|
1300
|
-
* truth for what `requires`/`register`/`get` accept. Its concrete fields live
|
|
1301
|
-
* in `./depSchema` (co-located with the dep value types). It is declared there
|
|
1302
|
-
* as a plain `export interface` rather than via a cross-module
|
|
1303
|
-
* `declare module './depRegistry'` augmentation: that augmentation does NOT
|
|
1304
|
-
* merge once rollup-plugin-dts flattens both files into one `.d.ts` chunk,
|
|
1305
|
-
* which silently emptied `DepSchema` for consumers. Consumers still extend it
|
|
1306
|
-
* via `declare module '@weasel-js/core'` augmentation.
|
|
1192
|
+
* The dep registry: named "live source" thunks. Actions declare
|
|
1193
|
+
* `requires: ['selection']`; the dispatcher calls `registry.get('selection')`
|
|
1194
|
+
* at invocation time to build a typed Deps bag.
|
|
1307
1195
|
*/
|
|
1308
1196
|
|
|
1309
1197
|
/** Holds the live sources an action's declared dependencies resolve to.
|
|
@@ -1362,12 +1250,12 @@ type HotkeyTrigger = 'space' | 'alt' | 'ctrl' | 'meta' | 'shift' | (string & {})
|
|
|
1362
1250
|
*
|
|
1363
1251
|
* Note: cursor is NOT here. `Tool.cursor` (inherited from `Contribution`)
|
|
1364
1252
|
* is already plumbed through `<Canvas>` to `style.cursor` on the host. */
|
|
1365
|
-
interface ToolPresentation
|
|
1253
|
+
interface ToolPresentation {
|
|
1366
1254
|
/** Human-readable label, distinct from the `id`. Falls back to `id`. */
|
|
1367
1255
|
label?: string;
|
|
1368
|
-
/** Inline-SVG icon component output
|
|
1369
|
-
*
|
|
1370
|
-
icon?: react.ReactNode | ((
|
|
1256
|
+
/** Inline-SVG icon component output: a static `ReactNode`, or a function
|
|
1257
|
+
* returning one. */
|
|
1258
|
+
icon?: react.ReactNode | (() => react.ReactNode);
|
|
1371
1259
|
/** Palette grouping key. Tools sharing a group render contiguously
|
|
1372
1260
|
* with separators between groups. Free-form string; the kit
|
|
1373
1261
|
* recommends 'select' | 'shape' | 'draw' | 'type' | 'view'. */
|
|
@@ -1447,9 +1335,8 @@ interface Contribution<TOverlay = KernelOverlay> extends ContributionRouting, Co
|
|
|
1447
1335
|
}
|
|
1448
1336
|
|
|
1449
1337
|
/**
|
|
1450
|
-
*
|
|
1451
|
-
*
|
|
1452
|
-
* binding-scope / matchBest logic on top.
|
|
1338
|
+
* Re-exports the pure matcher primitives from `@weasel-js/gestures` and layers
|
|
1339
|
+
* the actions-layer binding-scope / matchBest logic on top.
|
|
1453
1340
|
*/
|
|
1454
1341
|
|
|
1455
1342
|
/** Where a binding came from, which is also its priority: a held hotkey beats
|
|
@@ -1478,25 +1365,13 @@ interface MatchResult {
|
|
|
1478
1365
|
* only the body classification. `kindOf` predicates are handed the hit;
|
|
1479
1366
|
* `affordance:<k>` matches on its `kind`. The body-class strings (`'empty'`,
|
|
1480
1367
|
* `'selected-body'`, `'unselected-body'`) and the `kind:` forms resolve from
|
|
1481
|
-
* `bodyTarget` / `bodyKind` and never see it
|
|
1482
|
-
* over empty canvas used to read as empty canvas.
|
|
1368
|
+
* `bodyTarget` / `bodyKind` and never see it.
|
|
1483
1369
|
*
|
|
1484
1370
|
* A predicate declares which it is with `readsAffordance`; one that declares
|
|
1485
1371
|
* nothing, which only untyped code can write, counts as not reading the hit.
|
|
1486
1372
|
*/
|
|
1487
1373
|
declare function targetConsultsAffordance(specTarget: TargetSpec | undefined): boolean;
|
|
1488
|
-
/**
|
|
1489
|
-
* Find the best-matching binding across all scopes.
|
|
1490
|
-
*
|
|
1491
|
-
* Precedence: hotkey > active > ambient.
|
|
1492
|
-
* Within a scope, more-specific bindings win; same-specificity bindings keep
|
|
1493
|
-
* registration order (see `matchSorted`).
|
|
1494
|
-
* Returns null when nothing matches.
|
|
1495
|
-
*
|
|
1496
|
-
* Note: this returns only the single best match. The dispatcher uses
|
|
1497
|
-
* `matchSorted` so it can fall through to lower-specificity matches when a
|
|
1498
|
-
* higher-specificity match's `enabled()` reports disabled.
|
|
1499
|
-
*/
|
|
1374
|
+
/** The first of {@link matchSorted}'s matches, or null when nothing matches. */
|
|
1500
1375
|
declare function matchBest(e: InputEvent, bindings: readonly ScopedBinding[], isMac: boolean, engagedChannels?: ReadonlySet<string>): MatchResult | null;
|
|
1501
1376
|
/**
|
|
1502
1377
|
* Find every binding that matches the event, sorted by precedence
|
|
@@ -1505,12 +1380,7 @@ declare function matchBest(e: InputEvent, bindings: readonly ScopedBinding[], is
|
|
|
1505
1380
|
* - Scopes by priority: hotkey > active > ambient.
|
|
1506
1381
|
* - Within a scope, more-specific bindings beat less-specific (see
|
|
1507
1382
|
* `specificity()`).
|
|
1508
|
-
* - Among same-specificity bindings, first-declared wins
|
|
1509
|
-
* preserves registration order — same tiebreaker as pre-specificity).
|
|
1510
|
-
*
|
|
1511
|
-
* Used by the dispatcher to implement specificity-ordered fall-through:
|
|
1512
|
-
* when the top match's `enabled()` returns a disabled reason (or the
|
|
1513
|
-
* action's `start()` returns an empty handle), the next match is tried.
|
|
1383
|
+
* - Among same-specificity bindings, first-declared wins.
|
|
1514
1384
|
*/
|
|
1515
1385
|
declare function matchSorted(e: InputEvent, bindings: readonly ScopedBinding[], isMac: boolean, engagedChannels?: ReadonlySet<string>, warn?: (message: string) => void): MatchResult[];
|
|
1516
1386
|
/** {@link matchSorted}, plus what an exclusive claim on the event barred. */
|
|
@@ -1686,11 +1556,9 @@ interface RankedCandidate {
|
|
|
1686
1556
|
* `createDispatcher` in `dispatcher.ts` implements it.
|
|
1687
1557
|
*/
|
|
1688
1558
|
|
|
1689
|
-
/** Everything the dispatcher must consult to route one input event
|
|
1690
|
-
*
|
|
1691
|
-
*
|
|
1692
|
-
* evaluated against. Rebuilt per event rather than held, so the dispatcher
|
|
1693
|
-
* itself stays stateless apart from in-flight gestures. */
|
|
1559
|
+
/** Everything the dispatcher must consult to route one input event. Rebuilt
|
|
1560
|
+
* per event rather than held, so the dispatcher itself stays stateless apart
|
|
1561
|
+
* from in-flight gestures. */
|
|
1694
1562
|
interface DispatcherContext {
|
|
1695
1563
|
/** All registered actions; the dispatcher walks `.defaultBinding` for ambient bindings. */
|
|
1696
1564
|
actions: ActionSource;
|
|
@@ -1741,9 +1609,7 @@ interface UiOngoingControl {
|
|
|
1741
1609
|
}
|
|
1742
1610
|
/**
|
|
1743
1611
|
* Successful `Dispatcher.resolveOnly` prediction: the binding + action that
|
|
1744
|
-
* would fire if `event` were dispatched for real.
|
|
1745
|
-
* descriptor so callers (the hover-cursor pump) can read metadata like
|
|
1746
|
-
* `Action.cursor` without a second registry lookup.
|
|
1612
|
+
* would fire if `event` were dispatched for real.
|
|
1747
1613
|
*/
|
|
1748
1614
|
interface ResolveOnlyResult {
|
|
1749
1615
|
actionId: string;
|
|
@@ -1796,14 +1662,12 @@ interface ResolveAllOptions {
|
|
|
1796
1662
|
* Evaluate eligibility and `enabled()` for candidates below the winner
|
|
1797
1663
|
* instead of short-circuiting them to `shadowed`.
|
|
1798
1664
|
*
|
|
1799
|
-
* Off by default
|
|
1800
|
-
*
|
|
1801
|
-
*
|
|
1665
|
+
* Off by default: the default walk's early exit is what keeps
|
|
1666
|
+
* `resolveOnly`'s `enabled()` call count identical to a real dispatch, and
|
|
1667
|
+
* `enabled()` predicates are only contractually pure — not free.
|
|
1802
1668
|
*
|
|
1803
|
-
*
|
|
1804
|
-
*
|
|
1805
|
-
* anyway". With it on, `shadowed` narrows to its precise meaning: this
|
|
1806
|
-
* candidate would have fired, but something above it did.
|
|
1669
|
+
* With it on, `shadowed` narrows to its precise meaning: this candidate
|
|
1670
|
+
* would have fired, but something above it did.
|
|
1807
1671
|
*/
|
|
1808
1672
|
evaluateShadowed?: boolean;
|
|
1809
1673
|
}
|
|
@@ -1825,12 +1689,10 @@ interface Dispatcher {
|
|
|
1825
1689
|
*/
|
|
1826
1690
|
handleInput(event: InputEvent, ctx: DispatcherContext): 'handled' | 'unhandled';
|
|
1827
1691
|
/**
|
|
1828
|
-
* Predict which action `event` would route to WITHOUT invoking it
|
|
1829
|
-
*
|
|
1830
|
-
*
|
|
1831
|
-
*
|
|
1832
|
-
* unhandled. Pure query: no invoker runs, no in-flight state changes, no
|
|
1833
|
-
* trace-log entry.
|
|
1692
|
+
* Predict which action `event` would route to WITHOUT invoking it: the
|
|
1693
|
+
* first candidate `handleInput`'s walk would fire, or `null` when the event
|
|
1694
|
+
* would go unhandled. Pure query: no invoker runs, no in-flight state
|
|
1695
|
+
* changes, no trace-log entry.
|
|
1834
1696
|
*
|
|
1835
1697
|
* Known divergence from a real dispatch: an ongoing invoker that matches
|
|
1836
1698
|
* but returns an empty handle at `start()` (runtime bail) makes the real
|
|
@@ -1841,25 +1703,15 @@ interface Dispatcher {
|
|
|
1841
1703
|
resolveOnly(event: InputEvent, ctx: DispatcherContext): ResolveOnlyResult | null;
|
|
1842
1704
|
/**
|
|
1843
1705
|
* Every binding that matches `event`, in dispatch precedence order, each
|
|
1844
|
-
* with a verdict explaining whether it would fire.
|
|
1845
|
-
* `resolveOnly`
|
|
1846
|
-
*
|
|
1847
|
-
*
|
|
1848
|
-
* `resolveOnly`'s walk would filter out are kept here and labelled
|
|
1849
|
-
* `ineligible` instead. Pure query: no invoker runs, no in-flight state
|
|
1850
|
-
* changes, no trace-log entry.
|
|
1706
|
+
* with a verdict explaining whether it would fire. Nothing is dropped:
|
|
1707
|
+
* candidates that `resolveOnly`'s walk would filter out are kept here and
|
|
1708
|
+
* labeled `ineligible` instead. Pure query, and it shares `resolveOnly`'s
|
|
1709
|
+
* known divergence from a real dispatch.
|
|
1851
1710
|
*
|
|
1852
1711
|
* `resolveOnly` is the first `would-fire` entry of this list.
|
|
1853
1712
|
*
|
|
1854
|
-
*
|
|
1855
|
-
*
|
|
1856
|
-
* real dispatch fall through, and this cannot see that.
|
|
1857
|
-
*
|
|
1858
|
-
* By default everything below the winner is `shadowed` without being asked,
|
|
1859
|
-
* which is what keeps this walk as cheap as the dispatch it replays. Pass
|
|
1860
|
-
* `{ evaluateShadowed: true }` to keep evaluating past the winner, so a
|
|
1861
|
-
* lower candidate that is ALSO ineligible or disabled says so — see
|
|
1862
|
-
* {@link ResolveAllOptions.evaluateShadowed}.
|
|
1713
|
+
* By default everything below the winner is `shadowed` without being asked
|
|
1714
|
+
* — see {@link ResolveAllOptions.evaluateShadowed}.
|
|
1863
1715
|
*/
|
|
1864
1716
|
resolveAll(event: InputEvent, ctx: DispatcherContext, opts?: ResolveAllOptions): ResolvedCandidate[];
|
|
1865
1717
|
/**
|
|
@@ -1872,7 +1724,6 @@ interface Dispatcher {
|
|
|
1872
1724
|
explain(event: InputEvent, ctx: DispatcherContext, opts?: ResolveAllOptions): DispatchRecord;
|
|
1873
1725
|
/**
|
|
1874
1726
|
* Synthesize an end-of-gesture for every in-flight ongoing handle.
|
|
1875
|
-
* Used by tool-switch cancellation (Q2 decision).
|
|
1876
1727
|
*/
|
|
1877
1728
|
cancelAll(reason: 'commit' | 'cancel'): void;
|
|
1878
1729
|
/**
|
|
@@ -1888,41 +1739,31 @@ interface Dispatcher {
|
|
|
1888
1739
|
*/
|
|
1889
1740
|
inFlightCursor(): CursorSpec | null;
|
|
1890
1741
|
/**
|
|
1891
|
-
* Read-only iterator over currently in-flight `OngoingHandle` instances
|
|
1892
|
-
*
|
|
1893
|
-
*
|
|
1894
|
-
* to walk each handle's `previewIds()` / `previewPose(id)` and render
|
|
1895
|
-
* dispatcher-driven gesture previews. Read-only by design:
|
|
1896
|
-
* external consumers must not mutate the in-flight map.
|
|
1742
|
+
* Read-only iterator over currently in-flight `OngoingHandle` instances,
|
|
1743
|
+
* for a preview layer to walk each handle's `previewIds()` /
|
|
1744
|
+
* `previewPose(id)`.
|
|
1897
1745
|
*/
|
|
1898
1746
|
getInFlightHandles(): Iterable<OngoingHandle>;
|
|
1899
1747
|
/**
|
|
1900
|
-
* Subscribe to in-flight state changes. The callback fires after
|
|
1901
|
-
*
|
|
1902
|
-
*
|
|
1903
|
-
*
|
|
1904
|
-
* flight. Input that arrives with nothing in flight and starts nothing
|
|
1905
|
-
* does not fire it. Consumers re-read `getInFlightHandles()` and re-render.
|
|
1748
|
+
* Subscribe to in-flight state changes. The callback fires after handle
|
|
1749
|
+
* start, every `onMove` pump, end, cancel, and cancel-all, and after any
|
|
1750
|
+
* other input handled while a gesture is in flight. Input that arrives
|
|
1751
|
+
* with nothing in flight and starts nothing does not fire it.
|
|
1906
1752
|
*
|
|
1907
1753
|
* Returns an unsubscribe function.
|
|
1908
1754
|
*/
|
|
1909
1755
|
subscribe(fn: () => void): () => void;
|
|
1910
1756
|
/**
|
|
1911
1757
|
* Monotonic counter bumped on exactly the events {@link subscribe} fires
|
|
1912
|
-
* on
|
|
1913
|
-
* with `subscribe` to drive a render off in-flight gesture state without
|
|
1914
|
-
* a `useReducer` force-rerender.
|
|
1758
|
+
* on: the snapshot half of the `useSyncExternalStore` contract.
|
|
1915
1759
|
*
|
|
1916
|
-
*
|
|
1917
|
-
*
|
|
1918
|
-
*
|
|
1919
|
-
* counts while a gesture is in flight — see `subscribe`).
|
|
1760
|
+
* A bump does **not** guarantee that anything observable changed (input
|
|
1761
|
+
* that matched no binding still counts while a gesture is in flight — see
|
|
1762
|
+
* `subscribe`).
|
|
1920
1763
|
*/
|
|
1921
1764
|
getVersion(): number;
|
|
1922
1765
|
/**
|
|
1923
|
-
* Snapshot of the currently active action
|
|
1924
|
-
* visibility rules, debug HUDs) that need to react to "what action
|
|
1925
|
-
* is in flight right now."
|
|
1766
|
+
* Snapshot of the currently active action.
|
|
1926
1767
|
*
|
|
1927
1768
|
* - `kind` — the `OngoingHandle.kind` reported by the in-flight
|
|
1928
1769
|
* handle (e.g. `'marquee'`, `'move'`). `null` when no action is
|
|
@@ -1933,26 +1774,15 @@ interface Dispatcher {
|
|
|
1933
1774
|
*
|
|
1934
1775
|
* When multiple handles are in flight simultaneously (e.g. a key-held
|
|
1935
1776
|
* action overlapping a pointer action), the most-recently-started
|
|
1936
|
-
* handle wins.
|
|
1937
|
-
* the one consumers care about.
|
|
1938
|
-
*
|
|
1939
|
-
* That rule used to be near-vacuous on the pointer side, because every
|
|
1940
|
-
* pointer shared one handle slot and two pointer drags could not coexist.
|
|
1941
|
-
* With per-pointer keying they can — but only via paths that bypass the
|
|
1942
|
-
* multi-pointer policy in `useGestureDispatcher` (which stops a second
|
|
1943
|
-
* finger from opening a drag while a pinch is live), such as a mouse and
|
|
1944
|
-
* a pen used together. Latest-start remains the right answer there.
|
|
1777
|
+
* handle wins.
|
|
1945
1778
|
*/
|
|
1946
1779
|
getActiveAction(): {
|
|
1947
1780
|
kind: string | null;
|
|
1948
1781
|
id: string | null;
|
|
1949
1782
|
};
|
|
1950
1783
|
/**
|
|
1951
|
-
* Start an ongoing action driven by UI (not a gesture)
|
|
1952
|
-
*
|
|
1953
|
-
* `action.invoker.start(ctx, { params })`, and registers the returned
|
|
1954
|
-
* handle in the in-flight map so `getInFlightHandles()` reports it —
|
|
1955
|
-
* enabling preview rendering via `SceneCanvas`.
|
|
1784
|
+
* Start an ongoing action driven by UI (not a gesture), and register its
|
|
1785
|
+
* handle in the in-flight map so `getInFlightHandles()` reports it.
|
|
1956
1786
|
*
|
|
1957
1787
|
* Returns `null` if `actionId` is unknown, the action's invoker is not
|
|
1958
1788
|
* ongoing, or `start` returned an empty handle.
|
|
@@ -1969,10 +1799,8 @@ interface Dispatcher {
|
|
|
1969
1799
|
*/
|
|
1970
1800
|
|
|
1971
1801
|
/** One entry per "mode change" — a kit state transition that isn't a
|
|
1972
|
-
* dispatched input event but
|
|
1973
|
-
*
|
|
1974
|
-
* consumers include the editAnchors dep (editingId changes), and
|
|
1975
|
-
* could expand to active-tool / hotkey-stack / focus changes. */
|
|
1802
|
+
* dispatched input event but explains why an input was (or wasn't) handled
|
|
1803
|
+
* later. Pushed via `recordModeSwitch()`. */
|
|
1976
1804
|
interface ModeSwitchLogEntry {
|
|
1977
1805
|
kind: 'mode';
|
|
1978
1806
|
ts: number;
|
|
@@ -1991,12 +1819,31 @@ type TraceLogEntry = DispatchRecord | ModeSwitchLogEntry;
|
|
|
1991
1819
|
declare function recordModeSwitch(mode: string, from: string | null, to: string | null, detail?: string): void;
|
|
1992
1820
|
|
|
1993
1821
|
/**
|
|
1994
|
-
*
|
|
1822
|
+
* The slot an in-flight ongoing handle is keyed by:
|
|
1823
|
+
* - `key-held`: `key-held-<code>` (`key-held-Space`), so two keys that share a
|
|
1824
|
+
* `key`, such as the two Shifts, hold separately. An event with no code keys
|
|
1825
|
+
* by `key`.
|
|
1826
|
+
* - `pointerdown` and the drag that follows: `pointer-<pointerId>`. An event
|
|
1827
|
+
* with no `pointerId` (a synthesized probe, a programmatic drag, most tests)
|
|
1828
|
+
* keys to `pointer-mouse`.
|
|
1829
|
+
* - `multitouch`: `multitouch-<fingers>`.
|
|
1830
|
+
* - any other kind that starts an ongoing invoker: `ongoing-<kind>`.
|
|
1831
|
+
*/
|
|
1832
|
+
|
|
1833
|
+
/**
|
|
1834
|
+
* The in-flight handle slot a pointer's gestures key into. Exported because
|
|
1835
|
+
* the React seam (`useGestureDispatcher`) has to name the same slot when it
|
|
1836
|
+
* asks `dispatcher.inFlight()` whether a given pointer is mid-drag.
|
|
1995
1837
|
*
|
|
1996
|
-
*
|
|
1997
|
-
*
|
|
1998
|
-
|
|
1999
|
-
|
|
1838
|
+
* @param pointerId - `PointerEvent.pointerId`, or `undefined` for a
|
|
1839
|
+
* synthesized event with no originating DOM pointer.
|
|
1840
|
+
*/
|
|
1841
|
+
declare function pointerGestureId(pointerId: number | undefined): string;
|
|
1842
|
+
/** The in-flight handle slot a multitouch gesture of `fingers` fingers keys into. */
|
|
1843
|
+
declare function multiTouchGestureId(fingers: number): string;
|
|
1844
|
+
|
|
1845
|
+
/**
|
|
1846
|
+
* Dispatcher orchestrator — pure module, no React, no DOM.
|
|
2000
1847
|
*
|
|
2001
1848
|
* ## Precedence and fall-through
|
|
2002
1849
|
* An exclusive affordance claim first bars every binding that does not
|
|
@@ -2012,46 +1859,14 @@ declare function recordModeSwitch(mode: string, from: string | null, to: string
|
|
|
2012
1859
|
* Candidates whose `eligible` rule is false are dropped first. The dispatcher
|
|
2013
1860
|
* walks the rest and fires the first action whose `enabled()` returns
|
|
2014
1861
|
* `true`. If every candidate's `enabled()` returns a disabled reason, the
|
|
2015
|
-
* event is unhandled.
|
|
2016
|
-
* `:not(:disabled)` filter, and lets a tool declare a high-specificity
|
|
2017
|
-
* binding (e.g. drag-on-empty → areaSelect) that gracefully falls through
|
|
2018
|
-
* to a lower-specificity ambient binding (e.g. drag → viewport.dragPan)
|
|
2019
|
-
* when its required deps aren't wired.
|
|
2020
|
-
*
|
|
2021
|
-
* ## gestureId scheme
|
|
2022
|
-
* - `key-held` ongoing actions: `key-held-<code>` (`key-held-Space`), so two
|
|
2023
|
-
* keys that share a `key`, such as the two Shifts, hold separately. An event
|
|
2024
|
-
* with no code keys by `key`.
|
|
2025
|
-
* Chosen because key-held gestures are identified by the held key alone.
|
|
2026
|
-
* - `pointerdown` / drag ongoing actions: `pointer-<pointerId>`, taken from
|
|
2027
|
-
* the originating DOM `PointerEvent`. Each physical pointer — mouse, each
|
|
2028
|
-
* touch, the stylus — gets its own handle slot. Events with no
|
|
2029
|
-
* `pointerId` (synthesized probes, programmatic drags, most tests) key to
|
|
2030
|
-
* `pointer-mouse`, so a single synthetic pointer behaves as it always has.
|
|
2031
|
-
* - `multitouch` ongoing actions: `multitouch-<fingers>`.
|
|
2032
|
-
* - Fallback for any other kind that triggers an ongoing invoker: `ongoing-<kind>`.
|
|
1862
|
+
* event is unhandled.
|
|
2033
1863
|
*
|
|
2034
1864
|
* ## Action-lookup miss behavior
|
|
2035
1865
|
* When the walk reaches a binding whose `actionId` has no entry in
|
|
2036
1866
|
* `ctx.actions.list()`, the dispatcher emits `console.warn` and returns
|
|
2037
|
-
* `'unhandled'
|
|
2038
|
-
* This preserves input flow (nothing is swallowed silently) while flagging
|
|
2039
|
-
* the misconfiguration at dev time.
|
|
1867
|
+
* `'unhandled'`, so the gesture falls through as if unmatched.
|
|
2040
1868
|
*/
|
|
2041
1869
|
|
|
2042
|
-
/**
|
|
2043
|
-
* The in-flight handle slot a pointer's gestures key into.
|
|
2044
|
-
*
|
|
2045
|
-
* Exported because the React seam (`useGestureDispatcher`) has to name the
|
|
2046
|
-
* same slot when it asks `dispatcher.inFlight()` whether a given pointer is
|
|
2047
|
-
* mid-drag. That used to be the string literal `'pointer-mouse'` on both
|
|
2048
|
-
* sides, which was correct only because `gestureIdFor` ignored the pointer id
|
|
2049
|
-
* entirely — the two lies cancelled out.
|
|
2050
|
-
*
|
|
2051
|
-
* @param pointerId - `PointerEvent.pointerId`, or `undefined` for a
|
|
2052
|
-
* synthesized event with no originating DOM pointer.
|
|
2053
|
-
*/
|
|
2054
|
-
declare function pointerGestureId(pointerId: number | undefined): string;
|
|
2055
1870
|
/** Build a dispatcher. `getAction` overrides how action ids are resolved;
|
|
2056
1871
|
* by default the `DispatcherContext`'s registry is used. */
|
|
2057
1872
|
declare function createDispatcher(opts?: {
|
|
@@ -2091,9 +1906,9 @@ interface ActionsRegistry {
|
|
|
2091
1906
|
/** Fire an immediate-invoker action by id. The optional `params` arg is
|
|
2092
1907
|
* forwarded to `ImmediateInvoker.run` as its second argument — use it for
|
|
2093
1908
|
* parametric actions (e.g. `trigger('tool.activate', { toolId: 'rect' })`).
|
|
2094
|
-
* Returns whether it ran one: `false` for an unknown id,
|
|
2095
|
-
* ongoing-invoker action, which
|
|
2096
|
-
*
|
|
1909
|
+
* Returns whether it ran one: `false` for an unknown id, for an action
|
|
1910
|
+
* whose `enabled` refuses, and for an ongoing-invoker action, which
|
|
1911
|
+
* `trigger` cannot reach (see `begin`). `enabled` is asked with no point. */
|
|
2097
1912
|
trigger(id: string, params?: Record<string, unknown>): boolean;
|
|
2098
1913
|
/**
|
|
2099
1914
|
* Subscribe to registry mutations. The callback fires after any
|
|
@@ -2223,12 +2038,6 @@ declare const isAnchorOrControl: TargetPredicate & {
|
|
|
2223
2038
|
readsAffordance: true;
|
|
2224
2039
|
};
|
|
2225
2040
|
|
|
2226
|
-
/**
|
|
2227
|
-
* The long-press the gesture dispatcher is timing, as observable state: where
|
|
2228
|
-
* the press landed, when the hold started, how far through it is, and whether
|
|
2229
|
-
* any binding would fire on it. Feedback — a filling ring, a haptic — reads
|
|
2230
|
-
* this; nothing here decides what the feedback looks like.
|
|
2231
|
-
*/
|
|
2232
2041
|
/** A press being held toward a long-press. */
|
|
2233
2042
|
interface PendingLongPress {
|
|
2234
2043
|
pointerId: number;
|
|
@@ -2281,8 +2090,7 @@ interface LongPressState {
|
|
|
2281
2090
|
interface LongPressStore extends LongPressState {
|
|
2282
2091
|
set(next: PendingLongPress | null): void;
|
|
2283
2092
|
}
|
|
2284
|
-
|
|
2285
|
-
declare const LONG_PRESS_MS = 500;
|
|
2093
|
+
|
|
2286
2094
|
/** How `useGestureDispatcher` times, publishes and signals a long-press. */
|
|
2287
2095
|
interface LongPressOptions {
|
|
2288
2096
|
/** Hold time in ms. Default {@link LONG_PRESS_MS}. */
|
|
@@ -2450,6 +2258,10 @@ declare function modeShortcutSpec(shortcut: string): KeySpec;
|
|
|
2450
2258
|
* `exit` / `discard` / `commit` / `cancel` while that mode is active, `entry`
|
|
2451
2259
|
* while it is not. Install it with `<SceneCanvas ambient>`.
|
|
2452
2260
|
*
|
|
2261
|
+
* An entry key one mode shares with another mode's leaving key is reported as
|
|
2262
|
+
* a route conflict unless `canEnter` is given: that is what keeps the entry
|
|
2263
|
+
* key from firing inside the other mode.
|
|
2264
|
+
*
|
|
2453
2265
|
* The bindings ride the hotkey tier, so leaving a mode outranks what the same
|
|
2454
2266
|
* key does otherwise (Escape clearing the selection), and they yield while a
|
|
2455
2267
|
* gesture is in flight, so Escape mid-drag still cancels the drag first. Keys
|
|
@@ -2479,101 +2291,36 @@ interface ToolKeybinding {
|
|
|
2479
2291
|
}
|
|
2480
2292
|
/** Modifier-key snapshot at event dispatch time. */
|
|
2481
2293
|
type ToolModifiers = ModifierState;
|
|
2482
|
-
/** Per-event context passed to every channel handler. `scratch` is typed
|
|
2483
|
-
* via the tool's `TScratch` parameter; it survives across a single
|
|
2484
|
-
* gesture (pointer-down through end/cancel) and is replaced on next
|
|
2485
|
-
* gesture start by `initScratch()`. */
|
|
2486
|
-
interface ToolCtx<TScratch = unknown> {
|
|
2487
|
-
worldX: number;
|
|
2488
|
-
worldY: number;
|
|
2489
|
-
modifiers: ToolModifiers;
|
|
2490
|
-
selection: SelectionApi;
|
|
2491
|
-
/** Adapter/scene access — opaque at this layer; tools that need it
|
|
2492
|
-
* cast to a known shape. This layer doesn't constrain it. */
|
|
2493
|
-
adapter: unknown;
|
|
2494
|
-
applyOps: (ops: Op[], label: string) => void;
|
|
2495
|
-
/** Current viewport. Reflects camera-position semantics — see
|
|
2496
|
-
* `View` JSDoc. */
|
|
2497
|
-
view: View;
|
|
2498
|
-
/** Mutate the viewport. In controlled mode this calls the consumer's
|
|
2499
|
-
* `onViewChange`; in uncontrolled mode it updates Canvas's internal
|
|
2500
|
-
* state. View changes are not undoable. */
|
|
2501
|
-
setView: (next: View) => void;
|
|
2502
|
-
/** Bounding rect of the canvas element in viewport coords. Used by
|
|
2503
|
-
* zoom/pan tools to convert event clientX/clientY to canvas-relative
|
|
2504
|
-
* anchors. */
|
|
2505
|
-
canvasRect: DOMRect;
|
|
2506
|
-
/** Optional debug sink. When `<Canvas debug={...}>` is enabled, Canvas
|
|
2507
|
-
* threads its sink here so tool-internal hit math (handle hitboxes,
|
|
2508
|
-
* rotation handle, etc.) lands in the same overlay as Canvas's own
|
|
2509
|
-
* bounds/origin records. Tools should call this conditionally with `?.`. */
|
|
2510
|
-
debug?: DebugSink;
|
|
2511
|
-
scratch: TScratch;
|
|
2512
|
-
}
|
|
2513
|
-
/**
|
|
2514
|
-
* What `onActivate` / `onDeactivate` receive: the tool's scratch, from
|
|
2515
|
-
* `initScratch()`. A lifecycle moment has no pointer, hit or modifier state to
|
|
2516
|
-
* report, so nothing else is populated; a callback needing the selection or
|
|
2517
|
-
* the scene closes over it, as the tool's actions do.
|
|
2518
|
-
*/
|
|
2519
|
-
interface ToolLifecycleCtx<TScratch = unknown> {
|
|
2520
|
-
scratch: TScratch;
|
|
2521
|
-
}
|
|
2522
|
-
/** World-space AABB shape used by `previewBounds`. Alias of the kit-wide
|
|
2523
|
-
* `Bounds` type — the optional `rotation` field carries through so a tool
|
|
2524
|
-
* can report an oriented preview rect (e.g. mid-rotate). */
|
|
2525
|
-
type ToolBounds = Bounds;
|
|
2526
2294
|
/**
|
|
2527
2295
|
* The focus-declaring case of a `Contribution`: a mode the user switches
|
|
2528
|
-
* into, plus the hooks that only make sense for one (
|
|
2529
|
-
*
|
|
2530
|
-
*
|
|
2296
|
+
* into, plus the hooks that only make sense for one (activate/deactivate,
|
|
2297
|
+
* `cursor`). Everything else — bindings, actions, overlay, presentation — is
|
|
2298
|
+
* inherited.
|
|
2531
2299
|
*/
|
|
2532
|
-
interface Tool<
|
|
2300
|
+
interface Tool<TOverlay = KernelOverlay> extends Contribution<TOverlay> {
|
|
2533
2301
|
/** The key that activates this tool. `useKeybindings` appends a binding
|
|
2534
2302
|
* entry for it to the consolidated `tool.activate` action (with
|
|
2535
2303
|
* `opts.params.toolId` set so the invoker knows which tool to switch to).
|
|
2536
2304
|
* A tool with none has no activation key. */
|
|
2537
2305
|
keybinding?: ToolKeybinding;
|
|
2538
|
-
|
|
2539
|
-
cursor?: CursorSpec | ((ctx: ToolCtx<TScratch>) => CursorSpec);
|
|
2306
|
+
cursor?: CursorSpec | (() => CursorSpec);
|
|
2540
2307
|
/** The tool became live: it took the active slot, or its `hotkey` went
|
|
2541
|
-
* down, while it held neither.
|
|
2542
|
-
|
|
2308
|
+
* down, while it held neither. A lifecycle moment has no pointer, hit or
|
|
2309
|
+
* modifier state to report; a callback needing the selection or the scene
|
|
2310
|
+
* closes over it, as the tool's actions do. */
|
|
2311
|
+
onActivate?: () => void;
|
|
2543
2312
|
/** The tool stopped being live in both slots, or its canvas unmounted. */
|
|
2544
|
-
onDeactivate?: (
|
|
2545
|
-
/** Returns the in-flight preview pose for `id` if this tool is mid-gesture
|
|
2546
|
-
* on it; otherwise `null`. Lets `Canvas.helpersRef.getEffectivePose`
|
|
2547
|
-
* reflect live gesture state without reaching into hook internals. The
|
|
2548
|
-
* return type is `unknown` here because the Tool interface is pose-agnostic;
|
|
2549
|
-
* callers that know the pose shape (e.g. Canvas typed by `TPose`) cast at
|
|
2550
|
-
* the use site. */
|
|
2551
|
-
previewPose?: (id: string) => unknown;
|
|
2552
|
-
/** Returns the in-flight preview bounds for `id` if this tool is mid-gesture
|
|
2553
|
-
* on it; otherwise `null`. Optional companion to `previewPose` for tools that
|
|
2554
|
-
* can compute bounds without round-tripping through a geometry adapter. */
|
|
2555
|
-
previewBounds?: (id: string) => ToolBounds | null;
|
|
2556
|
-
/** Returns ids whose committed scene-render should be suppressed while this
|
|
2557
|
-
* tool is mid-gesture (e.g. cascade move's dragged + descendant ids whose
|
|
2558
|
-
* preview ghosts replace the committed pose). The standard scene slot
|
|
2559
|
-
* consults this alongside `previewPose` to avoid double-rendering. Returns
|
|
2560
|
-
* `null` when no gesture is in flight. */
|
|
2561
|
-
previewIds?: () => Iterable<string> | null;
|
|
2313
|
+
onDeactivate?: () => void;
|
|
2562
2314
|
}
|
|
2563
2315
|
/** Whether `entry` is a tool: a contribution that can hold focus, chosen from
|
|
2564
|
-
* a palette or held on a key. Only a tool's focus hooks (`
|
|
2565
|
-
* `
|
|
2566
|
-
declare function isTool<TOverlay>(entry: Contribution<TOverlay>): entry is Tool<
|
|
2316
|
+
* a palette or held on a key. Only a tool's focus hooks (`cursor`,
|
|
2317
|
+
* `onActivate`, `onDeactivate`) ever run. */
|
|
2318
|
+
declare function isTool<TOverlay>(entry: Contribution<TOverlay>): entry is Tool<TOverlay>;
|
|
2567
2319
|
/** Internal — which slot a tool occupies in the dispatch order. */
|
|
2568
2320
|
type ToolSlot = 'hotkey' | 'active' | 'ambient';
|
|
2569
|
-
/**
|
|
2570
|
-
* dispatchers that
|
|
2571
|
-
|
|
2572
|
-
* is too strict for containers that accept any concrete `Tool<T>`. */
|
|
2573
|
-
type AnyTool = Tool<any, any>;
|
|
2574
|
-
/** "A tool of any scratch type that draws `TOverlay`" — the container form for
|
|
2575
|
-
* registries that hold heterogeneous scratch shapes but one overlay type. */
|
|
2576
|
-
type AnyToolOf<TOverlay> = Tool<any, TOverlay>;
|
|
2321
|
+
/** A tool drawing any overlay type — the element type for registries and
|
|
2322
|
+
* dispatchers that never read the overlay. */
|
|
2323
|
+
type AnyTool = Tool<any>;
|
|
2577
2324
|
|
|
2578
2325
|
/**
|
|
2579
2326
|
* What a tool declares.
|
|
@@ -2584,7 +2331,7 @@ type AnyToolOf<TOverlay> = Tool<any, TOverlay>;
|
|
|
2584
2331
|
* definition is mostly `bindings`, plus presentation and any actions the tool
|
|
2585
2332
|
* itself introduces.
|
|
2586
2333
|
*/
|
|
2587
|
-
interface ToolDef<
|
|
2334
|
+
interface ToolDef<TOverlay = KernelOverlay> {
|
|
2588
2335
|
id: string;
|
|
2589
2336
|
/** Capability tags for modality eligibility. `defineTool` forwards these
|
|
2590
2337
|
* onto `Tool.eligibility.capabilities`, which is where every gate reads
|
|
@@ -2607,7 +2354,7 @@ interface ToolDef<TScratch = void, TOverlay = KernelOverlay> {
|
|
|
2607
2354
|
* Introspection-only — do not make this load-bearing in production.
|
|
2608
2355
|
* Read off the def via `Tool.def` (the reflection escape hatch). */
|
|
2609
2356
|
hookName?: string;
|
|
2610
|
-
presentation?: ToolPresentation
|
|
2357
|
+
presentation?: ToolPresentation;
|
|
2611
2358
|
/** Optional caller-supplied activation key. Most built-in tools have their
|
|
2612
2359
|
* activation key declared in `BUILTIN_SELECT_KEYS` in `useKeybindings.ts`;
|
|
2613
2360
|
* this field is for tools that want their activation key to be
|
|
@@ -2625,17 +2372,10 @@ interface ToolDef<TScratch = void, TOverlay = KernelOverlay> {
|
|
|
2625
2372
|
hotkey?: HotkeyTrigger;
|
|
2626
2373
|
/** Fires as the tool becomes live in the active or hotkey slot. See
|
|
2627
2374
|
* `Tool.onActivate`. */
|
|
2628
|
-
onActivate?: (
|
|
2375
|
+
onActivate?: () => void;
|
|
2629
2376
|
/** Fires as the tool stops being live in either slot. */
|
|
2630
|
-
onDeactivate?: (
|
|
2631
|
-
cursor?: CursorSpec | ((
|
|
2632
|
-
/** Override the default scratch initializer. Default is `() => null`
|
|
2633
|
-
* cast to `TScratch`, which works for tools whose scratch is fresh
|
|
2634
|
-
* every gesture. Tools that need scratch identity to survive across
|
|
2635
|
-
* gesture boundaries (e.g. the pen tool's multi-click subpath state)
|
|
2636
|
-
* pass a stable-ref-returning thunk here. The factory forwards this
|
|
2637
|
-
* onto the returned `Tool.initScratch`. */
|
|
2638
|
-
initScratch?: () => TScratch;
|
|
2377
|
+
onDeactivate?: () => void;
|
|
2378
|
+
cursor?: CursorSpec | (() => CursorSpec);
|
|
2639
2379
|
/** Declarative gesture bindings, forwarded onto `Tool.bindings`. The
|
|
2640
2380
|
* gesture dispatcher consults these at active scope while this tool is the
|
|
2641
2381
|
* active one, and at hotkey scope while it is held. This is the tool's
|
|
@@ -2648,7 +2388,7 @@ interface ToolDef<TScratch = void, TOverlay = KernelOverlay> {
|
|
|
2648
2388
|
*
|
|
2649
2389
|
* The layer's `draw` closure should read dynamic state through refs or
|
|
2650
2390
|
* closures captured in the enclosing render scope, and gate on it there —
|
|
2651
|
-
* `if (!
|
|
2391
|
+
* `if (!state.something) return []` — rather than expecting the kit to
|
|
2652
2392
|
* swap layers as the gesture progresses.
|
|
2653
2393
|
*
|
|
2654
2394
|
* Pass an array to contribute several layers; they render in order. */
|
|
@@ -2659,20 +2399,10 @@ interface ToolDef<TScratch = void, TOverlay = KernelOverlay> {
|
|
|
2659
2399
|
}
|
|
2660
2400
|
|
|
2661
2401
|
/**
|
|
2662
|
-
* Build a `Tool
|
|
2663
|
-
*
|
|
2664
|
-
* This used to be a translator: `ToolDef` carried `initial` / `engaged` phase
|
|
2665
|
-
* tables of hit-keyed route handlers, and `defineTool` compiled them into the
|
|
2666
|
-
* imperative `pointer` / `drag` / `keyboard` / `wheel` handlers the
|
|
2667
|
-
* tool-routing dispatcher called. Both ends of that are gone — tools declare
|
|
2668
|
-
* `bindings` and the gesture dispatcher routes them — so what remains is
|
|
2669
|
-
* identity plumbing plus the id check and the cursor resolver.
|
|
2670
|
-
*
|
|
2671
|
-
* It stays a function rather than becoming a spread because the id validation
|
|
2672
|
-
* and the `initScratch` / `cursor` defaults are worth applying uniformly, and
|
|
2673
|
-
* because `Tool.def` gives reflection a handle on the authored form.
|
|
2402
|
+
* Build a `Tool` from a declarative `ToolDef`: validates the id, applies the
|
|
2403
|
+
* `cursor` default, and keeps the authored form on `Tool.def` for reflection.
|
|
2674
2404
|
*/
|
|
2675
|
-
declare function defineTool<
|
|
2405
|
+
declare function defineTool<TOverlay = KernelOverlay>(def: ToolDef<TOverlay>): Tool<TOverlay>;
|
|
2676
2406
|
|
|
2677
2407
|
/**
|
|
2678
2408
|
* One row in the route registry — a single `GestureBinding` on one tool,
|
|
@@ -2720,6 +2450,10 @@ interface RegistryEntry {
|
|
|
2720
2450
|
/** The binding's `opts.views`: the views it is live in. Absent when it is
|
|
2721
2451
|
* live in every view. */
|
|
2722
2452
|
views?: readonly (string | null)[];
|
|
2453
|
+
/** The binding's `opts.params`, as declared: a thunk is not called. */
|
|
2454
|
+
params?: unknown;
|
|
2455
|
+
/** The binding's `opts.yields`, when it is set. */
|
|
2456
|
+
yields?: true;
|
|
2723
2457
|
}
|
|
2724
2458
|
/**
|
|
2725
2459
|
* Stand-in target for a `{ kindOf }` predicate spec.
|
|
@@ -2756,10 +2490,8 @@ declare function buildRouteRegistry(entries: readonly Contribution<unknown>[]):
|
|
|
2756
2490
|
*/
|
|
2757
2491
|
declare function routesForSpec(spec: GestureSpec): readonly string[];
|
|
2758
2492
|
|
|
2759
|
-
/** Two or more
|
|
2760
|
-
* modifiers) tuple
|
|
2761
|
-
* arbitrarily (well, deterministically by slot order, but the author
|
|
2762
|
-
* probably didn't intend the duplication). */
|
|
2493
|
+
/** Two or more bindings declare the same exact (phase, gesture, arg, target,
|
|
2494
|
+
* modifiers) tuple, so slot order alone picks one. */
|
|
2763
2495
|
interface Conflict {
|
|
2764
2496
|
phase: 'initial' | 'engaged' | 'any';
|
|
2765
2497
|
gesture: GestureName;
|
|
@@ -2780,39 +2512,32 @@ interface Conflict {
|
|
|
2780
2512
|
* required against Alt required fire on different inputs, and Shift
|
|
2781
2513
|
* required outranks Shift optional. An optional modifier against a
|
|
2782
2514
|
* forbidden one is flagged, since both take the bare press.
|
|
2783
|
-
* -
|
|
2784
|
-
*
|
|
2785
|
-
*
|
|
2786
|
-
* their UI layer.
|
|
2787
|
-
*
|
|
2788
|
-
* - Two `{ kindOf }` predicate targets. The route grammar renders every
|
|
2789
|
-
* predicate as the single token {@link PREDICATE_TARGET}, so bucketing on
|
|
2790
|
-
* the rendered target alone reported select's `resize` / `rotate` / `move`
|
|
2791
|
-
* drags — three genuinely different predicates — as a three-way conflict.
|
|
2792
|
-
* Predicate entries bucket by function identity instead, which means two
|
|
2793
|
-
* *separately written but equivalent* predicates go unflagged. That's the
|
|
2794
|
-
* right way to be wrong here: this check has to be silent when nothing is
|
|
2795
|
-
* wrong or nobody will keep it on.
|
|
2796
|
-
*
|
|
2515
|
+
* - Two `{ kindOf }` predicate targets, unless they are the same function.
|
|
2516
|
+
* Predicates bucket by identity, so two separately written but
|
|
2517
|
+
* equivalent ones go unflagged.
|
|
2797
2518
|
* - Two actions whose `eligible` rules can never hold together — each
|
|
2798
2519
|
* mode's own Escape exit, say. See {@link rulesExclusive}.
|
|
2799
|
-
*
|
|
2800
2520
|
* - Two bindings whose `opts.views` keep them from being live and equally
|
|
2801
|
-
* ranked in any one view.
|
|
2521
|
+
* ranked in any one view.
|
|
2522
|
+
* - Two bindings of one action with equal params that tie on one platform
|
|
2523
|
+
* only: `mod` against `ctrl`, which are one key off a Mac and two on it.
|
|
2524
|
+
* The same pair tying on both platforms is a plain duplicate and is flagged.
|
|
2802
2525
|
*
|
|
2803
|
-
*
|
|
2804
|
-
*
|
|
2805
|
-
*
|
|
2526
|
+
* A binding whose action declines via `enabled()` so a lower-priority one
|
|
2527
|
+
* takes the gesture is flagged all the same, since seeing that intent would
|
|
2528
|
+
* mean evaluating the action, unless it says so with `opts.yields`. A pair
|
|
2529
|
+
* whose earlier binding yields is not a conflict; one whose later binding
|
|
2530
|
+
* yields still is, as the earlier one decides alone.
|
|
2806
2531
|
*
|
|
2807
|
-
*
|
|
2808
|
-
*
|
|
2809
|
-
*
|
|
2532
|
+
* Two bindings on the same tool can share a tuple, so a conflict may name
|
|
2533
|
+
* one tool twice.
|
|
2534
|
+
*
|
|
2535
|
+
* Feeding this a whole tool *registry* over-reports, because registry tools
|
|
2536
|
+
* take turns in the active slot and can't collide with each other — see
|
|
2537
|
+
* `findScopedConflicts`.
|
|
2810
2538
|
*/
|
|
2811
2539
|
declare function findConflicts(entries: readonly Contribution<unknown>[]): Conflict[];
|
|
2812
|
-
/**
|
|
2813
|
-
* The tool scopes as the dispatcher sees them — which is what decides whether
|
|
2814
|
-
* two same-tuple bindings can actually collide.
|
|
2815
|
-
*/
|
|
2540
|
+
/** The tool scopes as the dispatcher sees them. */
|
|
2816
2541
|
interface ToolScopes {
|
|
2817
2542
|
/** Tools eligible for the active slot, keyed by id or as a flat list. */
|
|
2818
2543
|
registry: readonly Contribution<unknown>[] | Readonly<Record<string, Contribution<unknown>>>;
|
|
@@ -2829,17 +2554,13 @@ interface ToolScopes {
|
|
|
2829
2554
|
/**
|
|
2830
2555
|
* Detect reachable route conflicts in an assembled tool set and report each one.
|
|
2831
2556
|
*
|
|
2832
|
-
*
|
|
2833
|
-
*
|
|
2834
|
-
* looked. Call it once wherever a tool set is assembled (`useTools` does),
|
|
2835
|
-
* behind a `process.env.NODE_ENV !== 'production'` guard.
|
|
2557
|
+
* Call it once wherever a tool set is assembled, behind a
|
|
2558
|
+
* `process.env.NODE_ENV !== 'production'` guard.
|
|
2836
2559
|
*
|
|
2837
2560
|
* **Warn, never throw.** A conflict between a consumer's tool and a kit tool
|
|
2838
|
-
* is
|
|
2839
|
-
* the
|
|
2840
|
-
*
|
|
2841
|
-
* always a bug, but the place to fail on that is the kit's own test suite
|
|
2842
|
-
* (`canvas/SceneCanvas.routeConflicts.test.tsx`), not a consumer's console.
|
|
2561
|
+
* is sometimes deliberate, since the loser can still take the gesture when
|
|
2562
|
+
* the winner declines through `enabled()`. A binding ordered that way on
|
|
2563
|
+
* purpose says so with `opts.yields`, and is not reported.
|
|
2843
2564
|
*
|
|
2844
2565
|
* @returns The conflicts found, so callers can dedupe repeat reports.
|
|
2845
2566
|
*/
|
|
@@ -2853,6 +2574,7 @@ declare function reportRouteConflicts(scopes: ToolScopes, warn?: (message: strin
|
|
|
2853
2574
|
* Extracted from the deleted singular `useKeybinding` hook so the remaining
|
|
2854
2575
|
* consumers don't depend on its disappearance.
|
|
2855
2576
|
*/
|
|
2577
|
+
|
|
2856
2578
|
/**
|
|
2857
2579
|
* A key plus a modifier policy, as {@link matchesKeyBinding} tests it against a
|
|
2858
2580
|
* `KeyboardEvent`. Every modifier left unset is forbidden, so `{ key: 'a' }`
|
|
@@ -2885,14 +2607,6 @@ interface KeyBinding {
|
|
|
2885
2607
|
/** Call `preventDefault` before the handler. Default `true`. */
|
|
2886
2608
|
preventDefault?: boolean;
|
|
2887
2609
|
}
|
|
2888
|
-
/** Returns true when the target is an input, textarea, or contenteditable element. */
|
|
2889
|
-
declare function isEditableTarget(target: EventTarget | null): boolean;
|
|
2890
|
-
/**
|
|
2891
|
-
* True when `e` is Space or Enter on a focused control those keys activate — a
|
|
2892
|
-
* button, a link, a checkbox. The key is that control's, so a page-level
|
|
2893
|
-
* listener that claims it cancels the control's own activation.
|
|
2894
|
-
*/
|
|
2895
|
-
declare function activatesFocusedControl(e: KeyboardEvent): boolean;
|
|
2896
2610
|
/**
|
|
2897
2611
|
* Pure matcher for `KeyBinding` against a `KeyboardEvent`. Only checks key +
|
|
2898
2612
|
* modifier policy — does NOT apply the editable-target skip or
|
|
@@ -2900,38 +2614,21 @@ declare function activatesFocusedControl(e: KeyboardEvent): boolean;
|
|
|
2900
2614
|
* gesture dispatcher so the rules can't drift.
|
|
2901
2615
|
*/
|
|
2902
2616
|
declare function matchesKeyBinding(e: KeyboardEvent, b: KeyBinding): boolean;
|
|
2903
|
-
type ModifierKeys = Pick<KeyboardEvent, 'key' | 'metaKey' | 'ctrlKey' | 'shiftKey' | 'altKey'>;
|
|
2904
2617
|
/** Which history step `e` asks for: Mod+Z undoes, Shift+Mod+Z and Mod+Y redo. `null` for anything else. Takes a
|
|
2905
2618
|
* React keyboard event as readily as a DOM one. */
|
|
2906
|
-
declare function historyKey(e:
|
|
2619
|
+
declare function historyKey(e: Pick<KeyboardEvent, 'key' | 'metaKey' | 'ctrlKey' | 'shiftKey' | 'altKey'>): 'undo' | 'redo' | null;
|
|
2907
2620
|
|
|
2908
2621
|
/**
|
|
2909
|
-
* Typed keys for the gesture/behavior scratch store.
|
|
2910
|
-
*
|
|
2911
|
-
*
|
|
2912
|
-
* mutable bag that behaviors use to stash state across `onDown` / `onMove`
|
|
2913
|
-
* / `onEnd` calls. The store is `unknown`-valued because behaviors are
|
|
2914
|
-
* tool-agnostic — a single behavior may run under different tools with
|
|
2915
|
-
* different scratch shapes. Without a typing convention every read site
|
|
2916
|
-
* has to `(ctx.scratch as <some shape>)[KEY]`, and a writer changing the
|
|
2917
|
-
* shape can silently break readers with no compile-time signal.
|
|
2918
|
-
*
|
|
2919
|
-
* `ScratchKey<T>` is a phantom-typed string that carries its payload type
|
|
2920
|
-
* via a branded interface. `getScratch` and `setScratch` use the brand to
|
|
2921
|
-
* narrow / constrain at the typing layer; at runtime the key is just its
|
|
2922
|
-
* underlying string and the store remains a plain `Record`.
|
|
2622
|
+
* Typed keys for the gesture/behavior scratch store, `ctx.scratch`.
|
|
2623
|
+
* `ScratchKey<T>` carries its payload type at the typing layer; at runtime
|
|
2624
|
+
* the key is just its underlying string and the store remains a plain `Record`.
|
|
2923
2625
|
*
|
|
2924
2626
|
* @example
|
|
2925
2627
|
* ```ts
|
|
2926
|
-
* // Module-level
|
|
2628
|
+
* // Module-level constant, shared by writer + reader:
|
|
2927
2629
|
* const LASSO_VERTICES = scratchKey<readonly { x: number; y: number }[]>('lasso.vertices');
|
|
2928
|
-
*
|
|
2929
|
-
* // Writer (a tool's onMove):
|
|
2930
2630
|
* setScratch(ctx.scratch, LASSO_VERTICES, vertices);
|
|
2931
|
-
*
|
|
2932
|
-
* // Reader (a behavior's onEnd):
|
|
2933
2631
|
* const vertices = getScratch(ctx.scratch, LASSO_VERTICES) ?? [];
|
|
2934
|
-
* // ^? readonly { x: number; y: number }[] | undefined
|
|
2935
2632
|
* ```
|
|
2936
2633
|
*/
|
|
2937
2634
|
/** Phantom-typed key for a scratch slot. The `__scratchKeyPayload` phantom
|
|
@@ -2946,13 +2643,9 @@ interface ScratchKey<T> {
|
|
|
2946
2643
|
* field already satisfies this; consumers don't need to construct one. */
|
|
2947
2644
|
type ScratchStore = Record<string, unknown>;
|
|
2948
2645
|
/**
|
|
2949
|
-
* Make a typed scratch key.
|
|
2950
|
-
* is
|
|
2951
|
-
*
|
|
2952
|
-
* Namespace the name by behavior or feature to avoid collisions
|
|
2953
|
-
* (`'behavior.field'` is a good convention). Two keys with the same
|
|
2954
|
-
* `name` refer to the same slot at runtime — the type parameter is a
|
|
2955
|
-
* compile-time contract, not a uniqueness guarantee.
|
|
2646
|
+
* Make a typed scratch key. Two keys with the same `name` refer to the same
|
|
2647
|
+
* slot at runtime — the type parameter is a compile-time contract, not a
|
|
2648
|
+
* uniqueness guarantee — so namespace the name (`'behavior.field'`).
|
|
2956
2649
|
*/
|
|
2957
2650
|
declare function scratchKey<T>(name: string): ScratchKey<T>;
|
|
2958
2651
|
/** Read a typed slot from the scratch store. Returns `undefined` if
|
|
@@ -2993,6 +2686,14 @@ declare function itemsFromFiles(files: File[]): IngestItem[];
|
|
|
2993
2686
|
/** Materialize a drop's `DataTransfer`. MUST be called during the `drop`
|
|
2994
2687
|
* event dispatch (see module doc). */
|
|
2995
2688
|
declare function itemsFromDataTransfer(dt: DataTransfer): Promise<IngestItem[]>;
|
|
2689
|
+
/**
|
|
2690
|
+
* What a drag still in flight says it carries: one item per flavor, MIME
|
|
2691
|
+
* only. A browser withholds contents until the drop, so nothing may read
|
|
2692
|
+
* `file` or `text` off these; they exist to ask which bindings would match.
|
|
2693
|
+
* A file the browser does not name yet (Firefox reports
|
|
2694
|
+
* `application/x-moz-file`, others only a `Files` type) is `UNKNOWN_MIME`.
|
|
2695
|
+
*/
|
|
2696
|
+
declare function itemTypesFromDataTransfer(dt: DataTransfer): readonly IngestItem[];
|
|
2996
2697
|
/** Materialize a paste's `clipboardData`. Fully synchronous. */
|
|
2997
2698
|
declare function itemsFromClipboardData(cd: DataTransfer): IngestItem[];
|
|
2998
2699
|
|
|
@@ -3063,10 +2764,8 @@ declare function dwarn(namespace: string, ...args: unknown[]): void;
|
|
|
3063
2764
|
* keyed on.
|
|
3064
2765
|
*
|
|
3065
2766
|
* It declares one entry, `activeTool`, because the value and the action that
|
|
3066
|
-
* requires it (`tool.offhand`) are both routing's.
|
|
3067
|
-
*
|
|
3068
|
-
* invocation time; it does not know what a selection, a scene, or a viewport
|
|
3069
|
-
* is. Whoever supplies those merges them in:
|
|
2767
|
+
* requires it (`tool.offhand`) are both routing's. Whoever supplies the rest
|
|
2768
|
+
* merges them in:
|
|
3070
2769
|
*
|
|
3071
2770
|
* ```ts
|
|
3072
2771
|
* declare module '@weasel-js/routing' {
|
|
@@ -3075,12 +2774,7 @@ declare function dwarn(namespace: string, ...args: unknown[]): void;
|
|
|
3075
2774
|
* ```
|
|
3076
2775
|
*
|
|
3077
2776
|
* `@weasel-js/core` merges the kit's own deps this way and re-exports
|
|
3078
|
-
* `DepSchema`, so a consumer adds its own against `'@weasel-js/core'
|
|
3079
|
-
* never has to know this module exists. The merge carries through the
|
|
3080
|
-
* re-export in both directions — `scripts/smoke-consumer-bundle.mjs` asserts
|
|
3081
|
-
* it against the published `.d.ts`, which is where it once broke: a
|
|
3082
|
-
* cross-module augmentation stops merging when `rollup-plugin-dts` flattens
|
|
3083
|
-
* both files into one chunk.
|
|
2777
|
+
* `DepSchema`, so a consumer adds its own against `'@weasel-js/core'`.
|
|
3084
2778
|
*/
|
|
3085
2779
|
interface DepSchema {
|
|
3086
2780
|
/** Currently active tool id + hotkey-hold stack. */
|
|
@@ -3109,4 +2803,4 @@ type KernelOverlay = OverlaySchema extends {
|
|
|
3109
2803
|
overlay: infer O;
|
|
3110
2804
|
} ? O : unknown;
|
|
3111
2805
|
|
|
3112
|
-
export { type
|
|
2806
|
+
export { type DeviceProfile as $, type ActionsRegistry as A, type BindingOpts as B, type Contribution as C, type DepRegistry as D, type BindingScope as E, type BindingSource as F, type BodyPredicate as G, type BoundGesture as H, type Bounds as I, type BuildRuleCtxArgs as J, type KernelOverlay as K, type LongPressOptions as L, COARSE_TARGET_SCALE as M, type NodeId as N, type OverlayPosition as O, type Condition as P, type Conflict as Q, type RuleCtx as R, ScopeNode as S, ToolNode as T, type ContributionChrome as U, type ContributionDeps as V, type ContributionRouting as W, DEFAULT_ALLOWED_CAPABILITIES as X, DEFAULT_DEVICE_PROFILE as Y, type DebugSink as Z, type DetectedDeviceFacts as _, type DepName as a, actionBindings as a$, type DispatchRecord as a0, type DispatchRecordInput as a1, type DispatcherContext as a2, type DragSample as a3, type DroppedCandidate as a4, type Eligibility as a5, type EligibilityState as a6, type GestureBinding as a7, type HandleKind as a8, type HitShape as a9, type PointerSessionOptions as aA, type RankedCandidate as aB, type RecordCandidate as aC, type RegistryEntry as aD, type ResizeAnchor as aE, type ResolveAllOptions as aF, type ResolveOnlyResult as aG, type ResolvedCandidate as aH, type Rule as aI, type ScopedBinding as aJ, type ScratchKey as aK, type ScratchStore as aL, type SelectionApi as aM, type Selector as aN, type SpecificityPart as aO, TOOL_OFFHAND_ID as aP, type ToolDef as aQ, type ToolKeybinding as aR, type ToolModifiers as aS, type ToolOffhandBindingSpec as aT, type ToolPresentation as aU, type ToolScopes as aV, type ToolSlot as aW, type TraceLogEntry as aX, type UiOngoingControl as aY, type ViewportGestureKind as aZ, type WalkStep as a_, type HotkeyTrigger as aa, INGEST_STRING_MIMES as ab, type ImmediateInvoker as ac, type InvocationCtx as ad, type Invoker as ae, type KeyBinding as af, type LongPressState as ag, type LongPressStore as ah, type MatchOutcome as ai, type MatchResult as aj, type ModeShortcutHandlers as ak, type ModeSwitchLogEntry as al, type ModifierState as am, NEVER as an, type OngoingHandle as ao, type OngoingInvoker as ap, type OngoingOverlay as aq, type OverlayRole as ar, type OverlaySchema as as, PREDICATE_TARGET as at, type PendingLongPress as au, type PlacedBy as av, type Point2 as aw, type PointerSession as ax, type PointerSessionCallbacks as ay, type PointerSessionCancelReason as az, type DepSchema as b, validateActionId as b$, actionItems as b0, assembleEntries as b1, buildDepsFromRequires as b2, buildRouteRegistry as b3, buildRuleCtx as b4, buildToolOffhandBindings as b5, clientToCanvas as b6, clientToCanvasRect as b7, createDepRegistry as b8, createDispatcher as b9, itemsFromDataTransfer as bA, itemsFromFiles as bB, liveScope as bC, makeToolOffhandAction as bD, matchBest as bE, matchSorted as bF, matchSortedWithBarred as bG, matchesKeyBinding as bH, mergeContributions as bI, modeShortcutSpec as bJ, modeShortcuts as bK, multiTouchGestureId as bL, offhandKeyFor as bM, openPointerSession as bN, pointerGestureId as bO, recordModeSwitch as bP, reportRouteConflicts as bQ, resolveDeviceProfile as bR, resolveParams as bS, routesForSpec as bT, ruleCanHoldIn as bU, rulesExclusive as bV, scopeBindings as bW, scratchKey as bX, setScratch as bY, targetConsultsAffordance as bZ, validateActionDefaultBinding as b_, createLongPressStore as ba, defineTool as bb, deleteScratch as bc, describeRule as bd, dlog as be, dwarn as bf, evaluate as bg, evaluateActive as bh, evaluateEnabled as bi, findConflicts as bj, getScratch as bk, heldKeySpec as bl, historyKey as bm, isAnchor as bn, isAnchorOrControl as bo, isBody as bp, isControlHandle as bq, isDebugEnabled as br, isEmpty as bs, isResizeHandle as bt, isRotateHandle as bu, isSelectedBody as bv, isTool as bw, isUnselectedBody as bx, itemTypesFromDataTransfer as by, itemsFromClipboardData as bz, type Action as c, DepNode as d, type Tool as e, ActiveToolContextProvider as f, ActiveToolContextProviderIfRoot as g, type ActiveToolContextProviderProps as h, type ActiveToolContextValue as i, useOptionalActiveToolContext as j, type Dispatcher as k, type AffordanceHit as l, ALWAYS as m, type ActionDeps as n, ActionDisabledReason as o, type ActionDispatch as p, type ActionEnabledResult as q, type ActionEntry as r, type ActionItem as s, type ActionPresentation as t, useActiveToolContext as u, type ActionSource as v, type ActionVariant as w, type ActionsProp as x, type AnyTool as y, type AssemblyInputs as z };
|