@weasel-js/routing 1.7.0 → 1.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/{chunk-Q3Y3DPKH.js → chunk-ZGKJWZ4S.js} +31 -10
- package/dist/chunk-ZGKJWZ4S.js.map +1 -0
- package/dist/{index-Bmq4lSHY.d.ts → index-BV53gfyB.d.ts} +115 -42
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/internal--mYnGecm.d.ts +275 -0
- package/dist/internal.d.ts +7 -0
- package/dist/internal.js +3 -0
- package/dist/internal.js.map +1 -0
- package/dist/react.d.ts +8 -272
- package/dist/react.js +121 -130
- package/dist/react.js.map +1 -1
- package/package.json +10 -5
- package/dist/chunk-Q3Y3DPKH.js.map +0 -1
|
@@ -126,10 +126,21 @@ type HitShape = {
|
|
|
126
126
|
cx: number;
|
|
127
127
|
cy: number;
|
|
128
128
|
r: number;
|
|
129
|
+
}
|
|
130
|
+
/** A world-space polygon — for a region that is a turned square on screen,
|
|
131
|
+
* which under non-uniform zoom is no rotated rect in world. */
|
|
132
|
+
| {
|
|
133
|
+
kind: 'polygon';
|
|
134
|
+
points: readonly {
|
|
135
|
+
x: number;
|
|
136
|
+
y: number;
|
|
137
|
+
}[];
|
|
129
138
|
} | {
|
|
130
139
|
kind: 'path';
|
|
131
140
|
d: Path2D;
|
|
132
141
|
};
|
|
142
|
+
/** What a recorded viewport change was: a translation or a scale change. */
|
|
143
|
+
type ViewportGestureKind = 'pan' | 'zoom';
|
|
133
144
|
/**
|
|
134
145
|
* Where a tool reports its internal hit math so the debug overlay can draw it.
|
|
135
146
|
* Routing threads a sink through `ToolCtx` and never calls one.
|
|
@@ -155,6 +166,16 @@ interface DebugSink {
|
|
|
155
166
|
y: number;
|
|
156
167
|
}, accepted: boolean): void;
|
|
157
168
|
recordLayer(id: string, label: string, space: 'world' | 'screen', index: number): void;
|
|
169
|
+
/**
|
|
170
|
+
* A viewport change a gesture made: the view it started from, the view it
|
|
171
|
+
* produced, and the world point it held fixed — a pan's grab point, a zoom's
|
|
172
|
+
* focal point — when it had one. Survives `beginFrame`; the next record
|
|
173
|
+
* replaces it.
|
|
174
|
+
*/
|
|
175
|
+
recordViewport(kind: ViewportGestureKind, from: View, to: View, anchor?: {
|
|
176
|
+
x: number;
|
|
177
|
+
y: number;
|
|
178
|
+
}): void;
|
|
158
179
|
/** Clears every non-snap array. Called at the start of each Canvas render. */
|
|
159
180
|
beginFrame(): void;
|
|
160
181
|
/** Clears the snap array. Called at gesture end. */
|
|
@@ -1470,6 +1491,7 @@ interface DispatchRecord {
|
|
|
1470
1491
|
fired: string | null;
|
|
1471
1492
|
outcome: 'handled' | 'unhandled';
|
|
1472
1493
|
}
|
|
1494
|
+
/** The input a {@link DispatchRecord} describes, reduced to plain data. */
|
|
1473
1495
|
interface DispatchRecordInput {
|
|
1474
1496
|
/** `InputEvent.kind`: `'key'`, `'pointerdown'`, `'wheel'`, … */
|
|
1475
1497
|
eventKind: string;
|
|
@@ -1506,6 +1528,7 @@ interface RecordCandidate {
|
|
|
1506
1528
|
/** The action's `eligible` rule as text, when it declares one. */
|
|
1507
1529
|
eligible?: string;
|
|
1508
1530
|
}
|
|
1531
|
+
/** A matched candidate a filter removed before ranking, and which filter. */
|
|
1509
1532
|
type DroppedCandidate =
|
|
1510
1533
|
/** An exclusive claim on the input barred bindings that do not read the
|
|
1511
1534
|
* claim's affordance. */
|
|
@@ -1520,6 +1543,7 @@ type DroppedCandidate =
|
|
|
1520
1543
|
filter: 'ineligible';
|
|
1521
1544
|
rule: string;
|
|
1522
1545
|
};
|
|
1546
|
+
/** One field of the `specificity` tuple, in the order it is compared. */
|
|
1523
1547
|
type SpecificityPart = 'target' | 'mods' | 'phase' | 'exact';
|
|
1524
1548
|
/** The ranking step that put a candidate below the one before it. */
|
|
1525
1549
|
type PlacedBy = {
|
|
@@ -1582,6 +1606,8 @@ type WalkStep = {
|
|
|
1582
1606
|
| {
|
|
1583
1607
|
kind: 'outranked';
|
|
1584
1608
|
};
|
|
1609
|
+
/** A candidate that survived the filters: why it sits where it does, and
|
|
1610
|
+
* what the walk did with it. */
|
|
1585
1611
|
interface RankedCandidate {
|
|
1586
1612
|
candidate: RecordCandidate;
|
|
1587
1613
|
placedBy: PlacedBy;
|
|
@@ -1666,15 +1692,6 @@ interface ModeSwitchLogEntry {
|
|
|
1666
1692
|
}
|
|
1667
1693
|
/** A trace entry: one {@link DispatchRecord} per resolved input, or a mode switch. */
|
|
1668
1694
|
type TraceLogEntry = DispatchRecord | ModeSwitchLogEntry;
|
|
1669
|
-
/**
|
|
1670
|
-
* Dev only: offer the record for what a press at the pointer would do, as a
|
|
1671
|
-
* thunk on `window.__weaselDispatchLive__`. The hover pump offers one per move
|
|
1672
|
-
* and a reader builds the record only when it looks, so no record is built
|
|
1673
|
-
* while nothing is watching. `null` withdraws it when the pointer leaves.
|
|
1674
|
-
*
|
|
1675
|
-
* @internal
|
|
1676
|
-
*/
|
|
1677
|
-
declare function publishLiveDispatch(explain: (() => DispatchRecord) | null): void;
|
|
1678
1695
|
/** Push a mode-switch record into the trace log. Safe to call from
|
|
1679
1696
|
* anywhere; no-ops outside DEV. */
|
|
1680
1697
|
declare function recordModeSwitch(mode: string, from: string | null, to: string | null, detail?: string): void;
|
|
@@ -1713,35 +1730,6 @@ interface DispatcherContext {
|
|
|
1713
1730
|
*/
|
|
1714
1731
|
viewId?: string | null;
|
|
1715
1732
|
}
|
|
1716
|
-
/**
|
|
1717
|
-
* Filter `matches` to only those whose backing action has no `eligible`
|
|
1718
|
-
* rule, or whose rule evaluates true against `ruleCtx`. Exported for
|
|
1719
|
-
* unit testing.
|
|
1720
|
-
*
|
|
1721
|
-
* @internal
|
|
1722
|
-
*/
|
|
1723
|
-
declare function filterEligible<M extends {
|
|
1724
|
-
binding: {
|
|
1725
|
-
actionId: string;
|
|
1726
|
-
};
|
|
1727
|
-
}>(matches: readonly M[], actionLookup: (id: string) => Action | undefined, ruleCtx: RuleCtx): M[];
|
|
1728
|
-
/**
|
|
1729
|
-
* Among matches tied on scope and specificity, put those whose action is
|
|
1730
|
-
* gated by an `eligible` rule that holds now ahead of those with no rule: a
|
|
1731
|
-
* binding that applies only in the current context — Escape while editing a
|
|
1732
|
-
* path — outranks one that applies everywhere. Otherwise stable.
|
|
1733
|
-
*
|
|
1734
|
-
* @internal
|
|
1735
|
-
*/
|
|
1736
|
-
declare function preferContextual<M extends {
|
|
1737
|
-
binding: GestureBinding;
|
|
1738
|
-
scope: string;
|
|
1739
|
-
}>(matches: readonly M[], actionLookup: (id: string) => Action | undefined,
|
|
1740
|
-
/** Whether a gated action's rule holds; skip re-asking where the matches
|
|
1741
|
-
* were already filtered by it. */
|
|
1742
|
-
holds: (action: Action) => boolean,
|
|
1743
|
-
/** The routed view: a binding naming it is never tied with one that does not. */
|
|
1744
|
-
view: string | null): M[];
|
|
1745
1733
|
/**
|
|
1746
1734
|
* Handle returned by `Dispatcher.beginUiOngoing()` for driving an
|
|
1747
1735
|
* ongoing invoker from a UI control (color picker, slider).
|
|
@@ -2122,6 +2110,85 @@ declare const isControlHandle: (target: unknown) => boolean;
|
|
|
2122
2110
|
/** Any of the three anchor-editing affordances (anchor + both controls). */
|
|
2123
2111
|
declare const isAnchorOrControl: (target: unknown) => boolean;
|
|
2124
2112
|
|
|
2113
|
+
/**
|
|
2114
|
+
* The long-press the gesture dispatcher is timing, as observable state: where
|
|
2115
|
+
* the press landed, when the hold started, how far through it is, and whether
|
|
2116
|
+
* any binding would fire on it. Feedback — a filling ring, a haptic — reads
|
|
2117
|
+
* this; nothing here decides what the feedback looks like.
|
|
2118
|
+
*/
|
|
2119
|
+
/** A press being held toward a long-press. */
|
|
2120
|
+
interface PendingLongPress {
|
|
2121
|
+
pointerId: number;
|
|
2122
|
+
/** Long-press is synthesized for these two only; a mouse never arms one. */
|
|
2123
|
+
pointerType: 'touch' | 'pen';
|
|
2124
|
+
/** Where the press landed, in client coordinates. */
|
|
2125
|
+
client: {
|
|
2126
|
+
x: number;
|
|
2127
|
+
y: number;
|
|
2128
|
+
};
|
|
2129
|
+
/** The same point in CSS pixels from the canvas element's top-left — what a
|
|
2130
|
+
* `space: 'screen'` layer draws in. */
|
|
2131
|
+
local: {
|
|
2132
|
+
x: number;
|
|
2133
|
+
y: number;
|
|
2134
|
+
};
|
|
2135
|
+
/** The same point in the routed view's world coordinates. */
|
|
2136
|
+
world: {
|
|
2137
|
+
x: number;
|
|
2138
|
+
y: number;
|
|
2139
|
+
};
|
|
2140
|
+
/** The view the press routed to; `null` is the root view. */
|
|
2141
|
+
viewId: string | null;
|
|
2142
|
+
/** `performance.now()` when the hold started. */
|
|
2143
|
+
startedAt: number;
|
|
2144
|
+
/** How long the hold must last before the long-press fires, in ms. */
|
|
2145
|
+
duration: number;
|
|
2146
|
+
/**
|
|
2147
|
+
* Whether holding to the end would fire anything — a `longPress` binding,
|
|
2148
|
+
* or a `contextMenu` binding through the fallback. Resolved at press time.
|
|
2149
|
+
* Feedback shows only when this is true: a press that will do nothing must
|
|
2150
|
+
* not promise that it will.
|
|
2151
|
+
*/
|
|
2152
|
+
armed: boolean;
|
|
2153
|
+
}
|
|
2154
|
+
/** Read side of the pending long-press. Published as the `longPress` dep. */
|
|
2155
|
+
interface LongPressState {
|
|
2156
|
+
/** The press being held, or `null` when none is. Cleared when the
|
|
2157
|
+
* long-press fires, and when movement, release, cancel or a second
|
|
2158
|
+
* pointer abandons it. */
|
|
2159
|
+
get(): PendingLongPress | null;
|
|
2160
|
+
/** How far through the hold the pending press is, 0→1, at `now`
|
|
2161
|
+
* (`performance.now()` by default). 0 when nothing is pending. */
|
|
2162
|
+
progress(now?: number): number;
|
|
2163
|
+
/** Called whenever {@link get} changes. Progress is derived from the
|
|
2164
|
+
* clock, so it does not notify — a loop drawing it asks for frames. */
|
|
2165
|
+
subscribe(fn: () => void): () => void;
|
|
2166
|
+
}
|
|
2167
|
+
/** The dispatcher's writable half of {@link LongPressState}. */
|
|
2168
|
+
interface LongPressStore extends LongPressState {
|
|
2169
|
+
set(next: PendingLongPress | null): void;
|
|
2170
|
+
}
|
|
2171
|
+
/** How long a touch or pen press is held before it fires `longPress`, in ms. */
|
|
2172
|
+
declare const LONG_PRESS_MS = 500;
|
|
2173
|
+
/** How `useGestureDispatcher` times, publishes and signals a long-press. */
|
|
2174
|
+
interface LongPressOptions {
|
|
2175
|
+
/** Hold time in ms. Default {@link LONG_PRESS_MS}. */
|
|
2176
|
+
duration?: number;
|
|
2177
|
+
/**
|
|
2178
|
+
* Where the press being held is published: position, start time, progress,
|
|
2179
|
+
* and whether any binding would fire on it. `<SceneCanvas>` passes one and
|
|
2180
|
+
* republishes it as the `longPress` dep, which its default press ring
|
|
2181
|
+
* reads. Omit it and nothing is published.
|
|
2182
|
+
*/
|
|
2183
|
+
state?: LongPressStore;
|
|
2184
|
+
/**
|
|
2185
|
+
* Default true. A long-press some binding handled pulses `navigator.vibrate`
|
|
2186
|
+
* briefly, for touch and pen pointers, where the API exists.
|
|
2187
|
+
*/
|
|
2188
|
+
haptics?: boolean;
|
|
2189
|
+
}
|
|
2190
|
+
declare function createLongPressStore(): LongPressStore;
|
|
2191
|
+
|
|
2125
2192
|
/**
|
|
2126
2193
|
* The DOM half of a drag: one pointer, held down, from press to release.
|
|
2127
2194
|
*
|
|
@@ -2141,6 +2208,7 @@ type PointerSessionCancelReason =
|
|
|
2141
2208
|
| 'aborted'
|
|
2142
2209
|
/** A new press arrived on this pointer, so the tracked one had ended. */
|
|
2143
2210
|
| 'superseded';
|
|
2211
|
+
/** What {@link openPointerSession} reports. Exactly one of `onEnd` / `onCancel` fires when the session ends. */
|
|
2144
2212
|
interface PointerSessionCallbacks {
|
|
2145
2213
|
/** Every move belonging to this pointer, while it is still held. */
|
|
2146
2214
|
onMove?: (e: PointerEvent) => void;
|
|
@@ -2149,13 +2217,16 @@ interface PointerSessionCallbacks {
|
|
|
2149
2217
|
/** The session ended without a release. Fires once. */
|
|
2150
2218
|
onCancel?: (reason: PointerSessionCancelReason) => void;
|
|
2151
2219
|
}
|
|
2220
|
+
/** Options for {@link openPointerSession}. */
|
|
2152
2221
|
interface PointerSessionOptions {
|
|
2153
2222
|
/** Ask the origin element to capture the pointer. Default `true`. Capture
|
|
2154
2223
|
* keeps hover and click off everything the drag passes over; the session
|
|
2155
2224
|
* does not depend on it to keep receiving events. */
|
|
2156
2225
|
capture?: boolean;
|
|
2157
2226
|
}
|
|
2227
|
+
/** A live drag opened by {@link openPointerSession}. */
|
|
2158
2228
|
interface PointerSession {
|
|
2229
|
+
/** The pointer this session follows, taken from the press. */
|
|
2159
2230
|
readonly pointerId: number;
|
|
2160
2231
|
/** False once the session has ended, by any route. */
|
|
2161
2232
|
readonly active: boolean;
|
|
@@ -2648,9 +2719,11 @@ declare function reportRouteConflicts(scopes: ToolScopes, warn?: (message: strin
|
|
|
2648
2719
|
* consumers don't depend on its disappearance.
|
|
2649
2720
|
*/
|
|
2650
2721
|
/**
|
|
2651
|
-
*
|
|
2652
|
-
*
|
|
2653
|
-
*
|
|
2722
|
+
* A key plus a modifier policy, as {@link matchesKeyBinding} tests it against a
|
|
2723
|
+
* `KeyboardEvent`. Every modifier left unset is forbidden, so `{ key: 'a' }`
|
|
2724
|
+
* does not fire on Cmd+A. The matcher ignores `skipInEditable`, `enabled` and
|
|
2725
|
+
* `preventDefault`; those are for whatever attaches the listener. Tools
|
|
2726
|
+
* declaring an activation key use `ToolKeybinding` instead.
|
|
2654
2727
|
*/
|
|
2655
2728
|
interface KeyBinding {
|
|
2656
2729
|
/**
|
|
@@ -2856,4 +2929,4 @@ type KernelOverlay = OverlaySchema extends {
|
|
|
2856
2929
|
overlay: infer O;
|
|
2857
2930
|
} ? O : unknown;
|
|
2858
2931
|
|
|
2859
|
-
export { type
|
|
2932
|
+
export { type GestureBinding as $, type ActionsRegistry as A, type BindingOpts as B, type Contribution as C, type DepRegistry as D, COARSE_TARGET_SCALE as E, type Condition as F, type Conflict as G, type ContributionChrome as H, type ContributionDeps as I, type ContributionRouting as J, type KernelOverlay as K, type LongPressOptions as L, DEFAULT_ALLOWED_CAPABILITIES as M, DEFAULT_DEVICE_PROFILE as N, type OverlayPosition as O, type DebugSink as P, type DetectedDeviceFacts as Q, type RuleCtx as R, ScopeNode as S, type DeviceProfile as T, type DispatchRecord as U, type DispatchRecordInput as V, type DispatcherContext as W, type DragSample as X, type DroppedCandidate as Y, type Eligibility as Z, type EligibilityState as _, type DepName as a, actionItems as a$, type HandleKind as a0, type HitShape as a1, type HotkeyTrigger as a2, INGEST_STRING_MIMES as a3, type ImmediateInvoker as a4, type InvocationCtx as a5, type Invoker as a6, type KeyBinding as a7, LONG_PRESS_MS as a8, type LongPressState as a9, type ResolveOnlyResult as aA, type ResolvedCandidate as aB, type Rule as aC, type ScratchKey as aD, type ScratchStore as aE, type SelectionApi as aF, type SelectionExtendKey as aG, type SelectionMode as aH, type Selector as aI, type SpecificityPart as aJ, TOOL_OFFHAND_ID as aK, type Tool as aL, type ToolBounds as aM, type ToolCtx as aN, type ToolDef as aO, type ToolKeybinding as aP, type ToolLifecycleCtx as aQ, type ToolModifiers as aR, type ToolOffhandBindingSpec as aS, type ToolPresentation as aT, type ToolSlot as aU, type TraceLogEntry as aV, type UiOngoingControl as aW, type View as aX, type ViewportGestureKind as aY, type WalkStep as aZ, actionBindings as a_, type LongPressStore as aa, type MatchOutcome as ab, type MatchResult as ac, type ModeShortcutHandlers as ad, type ModeSwitchLogEntry as ae, type ModifierState as af, NEVER as ag, type NodeId as ah, type OngoingHandle as ai, type OngoingInvoker as aj, type OngoingOverlay as ak, type OverlayRole as al, type OverlaySchema as am, PREDICATE_TARGET as an, type PendingLongPress as ao, type PlacedBy as ap, type Point2 as aq, type PointerSession as ar, type PointerSessionCallbacks as as, type PointerSessionCancelReason as at, type PointerSessionOptions as au, type RankedCandidate as av, type RecordCandidate as aw, type RegistryEntry as ax, type ResizeAnchor as ay, type ResolveAllOptions as az, type DepSchema as b, buildDepsFromRequires as b0, buildRouteRegistry as b1, buildRuleCtx as b2, buildToolOffhandBindings as b3, clientToCanvas as b4, clientToCanvasRect as b5, createDepRegistry as b6, createDispatcher as b7, createLongPressStore as b8, defineTool as b9, matchesKeyBinding as bA, mergeContributions as bB, modeShortcutSpec as bC, modeShortcuts as bD, offhandKeyFor as bE, openPointerSession as bF, pointerGestureId as bG, recordModeSwitch as bH, reportRouteConflicts as bI, resolveDeviceProfile as bJ, resolveParams as bK, routesForSpec as bL, ruleCanHoldIn as bM, rulesExclusive as bN, scopeBindings as bO, scratchKey as bP, setScratch as bQ, targetConsultsAffordance as bR, validateActionDefaultBinding as bS, validateActionId as bT, deleteScratch as ba, describeRule as bb, dlog as bc, evaluate as bd, evaluateEnabled as be, findConflicts as bf, getScratch as bg, isAnchor as bh, isAnchorOrControl as bi, isBody as bj, isControlHandle as bk, isEditableTarget as bl, isEmpty as bm, isResizeHandle as bn, isRotateHandle as bo, isSelectedBody as bp, isTool as bq, isUnselectedBody as br, itemsFromClipboardData as bs, itemsFromDataTransfer as bt, itemsFromFiles as bu, liveScope as bv, makeToolOffhandAction as bw, matchBest as bx, matchSorted as by, matchSortedWithBarred as bz, type Action as c, DepNode as d, type AnyToolOf as e, type ScopedBinding as f, type Dispatcher as g, type AffordanceHit as h, ALWAYS as i, type ActionDeps as j, ActionDisabledReason as k, type ActionDispatch as l, type ActionEnabledResult as m, type ActionEntry as n, type ActionItem as o, type ActionPresentation as p, type ActionSource as q, type ActionVariant as r, type ActionsProp as s, type AnyTool as t, type BindingScope as u, type BindingSource as v, type BodyPredicate as w, type BoundGesture as x, type Bounds as y, type BuildRuleCtxArgs as z };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { i as ALWAYS,
|
|
1
|
+
export { i as ALWAYS, c as Action, j as ActionDeps, k as ActionDisabledReason, k as ActionDisabledReasonValue, l as ActionDispatch, m as ActionEnabledResult, n as ActionEntry, o as ActionItem, p as ActionPresentation, q as ActionSource, r as ActionVariant, s as ActionsProp, A as ActionsRegistry, h as AffordanceHit, t as AnyTool, e as AnyToolOf, B as BindingOpts, u as BindingScope, v as BindingSource, w as BodyPredicate, x as BoundGesture, y as Bounds, z as BuildRuleCtxArgs, E as COARSE_TARGET_SCALE, F as Condition, G as Conflict, C as Contribution, H as ContributionChrome, I as ContributionDeps, J as ContributionRouting, M as DEFAULT_ALLOWED_CAPABILITIES, N as DEFAULT_DEVICE_PROFILE, P as DebugSink, a as DepName, D as DepRegistry, b as DepSchema, Q as DetectedDeviceFacts, T as DeviceProfile, U as DispatchRecord, V as DispatchRecordInput, g as Dispatcher, W as DispatcherContext, X as DragSample, Y as DroppedCandidate, Z as Eligibility, _ as EligibilityState, $ as GestureBinding, a0 as HandleKind, a1 as HitShape, a2 as HotkeyTrigger, a3 as INGEST_STRING_MIMES, a4 as ImmediateInvoker, a5 as InvocationCtx, a6 as Invoker, K as KernelOverlay, a7 as KeyBinding, a8 as LONG_PRESS_MS, L as LongPressOptions, a9 as LongPressState, aa as LongPressStore, ab as MatchOutcome, ac as MatchResult, ad as ModeShortcutHandlers, ae as ModeSwitchLogEntry, af as ModifierState, ag as NEVER, ah as NodeId, ai as OngoingHandle, aj as OngoingInvoker, ak as OngoingOverlay, O as OverlayPosition, al as OverlayRole, am as OverlaySchema, an as PREDICATE_TARGET, ao as PendingLongPress, ap as PlacedBy, aq as Point2, ar as PointerSession, as as PointerSessionCallbacks, at as PointerSessionCancelReason, au as PointerSessionOptions, av as RankedCandidate, aw as RecordCandidate, ax as RegistryEntry, ay as ResizeAnchor, az as ResolveAllOptions, aA as ResolveOnlyResult, aB as ResolvedCandidate, aC as Rule, R as RuleCtx, f as ScopedBinding, aD as ScratchKey, aE as ScratchStore, aF as SelectionApi, aG as SelectionExtendKey, aH as SelectionMode, aI as Selector, aJ as SpecificityPart, aK as TOOL_OFFHAND_ID, aL as Tool, aM as ToolBounds, aN as ToolCtx, aO as ToolDef, aP as ToolKeybinding, aQ as ToolLifecycleCtx, aR as ToolModifiers, aS as ToolOffhandBindingSpec, aT as ToolPresentation, aU as ToolSlot, aV as TraceLogEntry, aW as UiOngoingControl, aX as View, aY as ViewportGestureKind, aZ as WalkStep, a_ as actionBindings, a$ as actionItems, b0 as buildDepsFromRequires, b1 as buildRouteRegistry, b2 as buildRuleCtx, b3 as buildToolOffhandBindings, b4 as clientToCanvas, b5 as clientToCanvasRect, b6 as createDepRegistry, b7 as createDispatcher, b8 as createLongPressStore, b9 as defineTool, ba as deleteScratch, bb as describeRule, bc as dlog, bd as evaluate, be as evaluateEnabled, bf as findConflicts, bg as getScratch, bh as isAnchor, bi as isAnchorOrControl, bj as isBody, bk as isControlHandle, bl as isEditableTarget, bm as isEmpty, bn as isResizeHandle, bo as isRotateHandle, bp as isSelectedBody, bq as isTool, br as isUnselectedBody, bs as itemsFromClipboardData, bt as itemsFromDataTransfer, bu as itemsFromFiles, bv as liveScope, bw as makeToolOffhandAction, bx as matchBest, by as matchSorted, bz as matchSortedWithBarred, bA as matchesKeyBinding, bB as mergeContributions, bC as modeShortcutSpec, bD as modeShortcuts, bE as offhandKeyFor, bF as openPointerSession, bG as pointerGestureId, bH as recordModeSwitch, bI as reportRouteConflicts, bJ as resolveDeviceProfile, bK as resolveParams, bL as routesForSpec, bM as ruleCanHoldIn, bN as rulesExclusive, bO as scopeBindings, bP as scratchKey, bQ as setScratch, bR as targetConsultsAffordance, bS as validateActionDefaultBinding, bT as validateActionId } from './index-BV53gfyB.js';
|
|
2
2
|
export { ChannelRef, DRAG_THRESHOLD_PX, DescribeRouteOptions, GESTURE_DESCRIPTORS, GestureArgSpec, GestureDescriptor, GestureName, IngestItem, InputEvent, ModRequirement, ModifierKey, ParsedModifiers, ParsedRoute, PhaseAtom, PhaseContext, RESERVED_ID_NAMES, RESERVED_ID_PREFIXES, ROUTE_FIELD_DEFINITIONS, ROUTE_TERMS, RouteDescriptionPart, RouteFieldName, RouteTermLabel, canonicalModifiers, collapseShiftPairs, describeRoute, describeRouteParts, formatPhaseAtom, formatRoute, getGestureDescriptor, isKnownGestureName, matchKey, matchModifiers, matchPhase, matchSpec, matchTarget, parseRoute, pastDragThreshold, routeGestureForSpecKind, specKindForRouteGesture, specificity } from '@weasel-js/gestures';
|
|
3
3
|
import '@weasel-js/modes';
|
|
4
4
|
import 'react';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { isDev } from './chunk-
|
|
2
|
-
export { ALWAYS, COARSE_TARGET_SCALE, DEFAULT_ALLOWED_CAPABILITIES, DEFAULT_DEVICE_PROFILE, GESTURE_DESCRIPTORS, INGEST_STRING_MIMES, NEVER, PREDICATE_TARGET, RESERVED_ID_NAMES, RESERVED_ID_PREFIXES, ROUTE_FIELD_DEFINITIONS, ROUTE_TERMS, TOOL_OFFHAND_ID, actionBindings, buildDepsFromRequires, buildRouteRegistry, buildRuleCtx, buildToolOffhandBindings, canonicalModifiers, clientToCanvas, clientToCanvasRect, collapseShiftPairs, createDepRegistry, createDispatcher, describeRoute, describeRouteParts, describeRule, dlog, evaluate,
|
|
1
|
+
import { isDev } from './chunk-ZGKJWZ4S.js';
|
|
2
|
+
export { ALWAYS, COARSE_TARGET_SCALE, DEFAULT_ALLOWED_CAPABILITIES, DEFAULT_DEVICE_PROFILE, GESTURE_DESCRIPTORS, INGEST_STRING_MIMES, LONG_PRESS_MS, NEVER, PREDICATE_TARGET, RESERVED_ID_NAMES, RESERVED_ID_PREFIXES, ROUTE_FIELD_DEFINITIONS, ROUTE_TERMS, TOOL_OFFHAND_ID, actionBindings, buildDepsFromRequires, buildRouteRegistry, buildRuleCtx, buildToolOffhandBindings, canonicalModifiers, clientToCanvas, clientToCanvasRect, collapseShiftPairs, createDepRegistry, createDispatcher, createLongPressStore, describeRoute, describeRouteParts, describeRule, dlog, evaluate, findConflicts, formatPhaseAtom, formatRoute, getGestureDescriptor, isEditableTarget, isKnownGestureName, isTool, itemsFromClipboardData, itemsFromDataTransfer, itemsFromFiles, liveScope, makeToolOffhandAction, matchBest, matchKey, matchModifiers, matchPhase, matchSorted, matchSortedWithBarred, matchSpec, matchTarget, matchesKeyBinding, offhandKeyFor, openPointerSession, parseRoute, pointerGestureId, recordModeSwitch, reportRouteConflicts, resolveDeviceProfile, resolveParams, routeGestureForSpecKind, routesForSpec, ruleCanHoldIn, rulesExclusive, scopeBindings, specKindForRouteGesture, specificity, targetConsultsAffordance, validateActionDefaultBinding, validateActionId } from './chunk-ZGKJWZ4S.js';
|
|
3
3
|
import { RESERVED_ID_PREFIXES, RESERVED_ID_NAMES } from '@weasel-js/gestures';
|
|
4
4
|
export { DRAG_THRESHOLD_PX, pastDragThreshold } from '@weasel-js/gestures';
|
|
5
5
|
import { modeLabel } from '@weasel-js/modes';
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { g as Dispatcher, A as ActionsRegistry, C as Contribution, h as AffordanceHit, L as LongPressOptions, R as RuleCtx, b as DepSchema } from './index-BV53gfyB.js';
|
|
2
|
+
import { RefObject } from 'react';
|
|
3
|
+
import { BodyClassification } from '@weasel-js/gestures';
|
|
4
|
+
import { PaintedCursorState } from '@weasel-js/cursor';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Everything about handling an input event that depends on *which view* it
|
|
8
|
+
* landed in: the dispatcher that runs it, and the three lookups that resolve
|
|
9
|
+
* a client point against that view's camera.
|
|
10
|
+
*
|
|
11
|
+
* @internal One per view. The hook builds the `id: null` one from its flat
|
|
12
|
+
* options; `views` supplies the rest, and the hook chooses between them per
|
|
13
|
+
* event, so that it — and its listener set — still mounts exactly once.
|
|
14
|
+
*/
|
|
15
|
+
interface DispatcherViewTarget {
|
|
16
|
+
/** `null` is the root view — the whole canvas. Matches `ViewTarget.id`. */
|
|
17
|
+
id: string | null;
|
|
18
|
+
dispatcher: Dispatcher;
|
|
19
|
+
affordanceAt: UseGestureDispatcherOptions['affordanceAt'];
|
|
20
|
+
classifyTarget: UseGestureDispatcherOptions['classifyTarget'];
|
|
21
|
+
clientToWorld: UseGestureDispatcherOptions['clientToWorld'];
|
|
22
|
+
/**
|
|
23
|
+
* The deps this view answers for itself — its camera, its selection, and
|
|
24
|
+
* whatever else is per-view rather than per-canvas. Read per event; every
|
|
25
|
+
* name it does not carry still resolves through the canvas registry.
|
|
26
|
+
*
|
|
27
|
+
* This is why a view does not get a `DepRegistryProvider` of its own. The
|
|
28
|
+
* registry is where a consumer registers *sources*, and one per view would
|
|
29
|
+
* fragment that: a consumer overriding `insert` would have to know how many
|
|
30
|
+
* views exist and override each. An overlay keeps one place to register and
|
|
31
|
+
* one authority per dep, with the view claiming only what is genuinely its.
|
|
32
|
+
*
|
|
33
|
+
* Without this a gesture inside a panel pans the whole canvas and selects
|
|
34
|
+
* into the canvas's selection — correct coordinates alone do not make
|
|
35
|
+
* routing correct.
|
|
36
|
+
*/
|
|
37
|
+
deps?: () => Partial<DepSchema>;
|
|
38
|
+
/**
|
|
39
|
+
* This view's chrome-caps rule context, for the eligibility filter. The rule
|
|
40
|
+
* table is the surface's, but a rule keyed on selection or the in-flight
|
|
41
|
+
* action answers per view — so an action hidden in one panel must not
|
|
42
|
+
* decline a gesture in another. `undefined` means "nothing gates here", the
|
|
43
|
+
* same answer a surface with no mode registry gives.
|
|
44
|
+
*/
|
|
45
|
+
getRuleCtx?: () => RuleCtx | undefined;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The part of `features/viewports`' `ViewResolver` the dispatcher needs: which
|
|
49
|
+
* view a client point belongs to, pinned to the view a gesture began in.
|
|
50
|
+
*
|
|
51
|
+
* @internal Structural on purpose — a `ViewResolver` satisfies it, and the
|
|
52
|
+
* dispatcher stays free of the viewport module.
|
|
53
|
+
*/
|
|
54
|
+
interface ViewIdResolver {
|
|
55
|
+
begin(pointerId: number, clientX: number, clientY: number): {
|
|
56
|
+
id: string | null;
|
|
57
|
+
};
|
|
58
|
+
at(pointerId: number | null, clientX: number, clientY: number): {
|
|
59
|
+
id: string | null;
|
|
60
|
+
};
|
|
61
|
+
end(pointerId: number): void;
|
|
62
|
+
}
|
|
63
|
+
/** Per-channel switches for the listeners `useGestureDispatcher` attaches to
|
|
64
|
+
* the canvas element. Omitted or `true` attaches the channel. */
|
|
65
|
+
interface DispatcherChannels {
|
|
66
|
+
/** `pointerdown`, hover `pointermove`, `pointerleave` — press, drag, click,
|
|
67
|
+
* long-press, multi-touch and the hover-cursor pump. */
|
|
68
|
+
pointer?: boolean;
|
|
69
|
+
/** `wheel`. */
|
|
70
|
+
wheel?: boolean;
|
|
71
|
+
/** WebKit's `gesturestart` / `gesturechange` / `gestureend` — Safari's
|
|
72
|
+
* trackpad pinch, dispatched as `pinch` events. */
|
|
73
|
+
pinch?: boolean;
|
|
74
|
+
/** `contextmenu`. Off leaves the browser's native menu in place. */
|
|
75
|
+
contextMenu?: boolean;
|
|
76
|
+
/** OS drop on the element plus `paste` on the window. Off leaves the
|
|
77
|
+
* element a non-drop-target and stops `weasel-dropover` being applied. */
|
|
78
|
+
ingest?: boolean;
|
|
79
|
+
}
|
|
80
|
+
/** Options for `useGestureDispatcher`: the element to listen on, the actions
|
|
81
|
+
* and tools in play, and the hooks that turn raw DOM events into the world
|
|
82
|
+
* coordinates and hit targets bindings match against. */
|
|
83
|
+
interface UseGestureDispatcherOptions {
|
|
84
|
+
/** Ref to the canvas element. Pointer/wheel/multitouch listeners attach here. */
|
|
85
|
+
canvasRef: RefObject<HTMLElement | null>;
|
|
86
|
+
/** Action registry (ActionsRegistry from registry.tsx). */
|
|
87
|
+
actions: ActionsRegistry;
|
|
88
|
+
/** Tool definitions keyed by id. Typically passes an empty Map. */
|
|
89
|
+
entriesById: ReadonlyMap<string, Contribution<unknown>>;
|
|
90
|
+
/** Default true. Set false to opt out of dispatcher wiring (e.g. demos that disable it). */
|
|
91
|
+
enabled?: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Observer fired whenever the dispatcher synthesizes a double click, in
|
|
94
|
+
* world coordinates. Runs BEFORE the event is dispatched and independently
|
|
95
|
+
* of which binding (if any) handles it.
|
|
96
|
+
*
|
|
97
|
+
* This is deliberately not an Action. `<SceneCanvas onDoubleClick>` is a
|
|
98
|
+
* notification — "the user double-clicked, here's what they hit" — and a
|
|
99
|
+
* notification must not compete with behavior for the gesture. As a binding
|
|
100
|
+
* it would lose to `enterPathEdit` on any body hit and silently never fire.
|
|
101
|
+
* Routing it here keeps a single definition of "double click" (the point of
|
|
102
|
+
* consolidating the kit's three detectors) without giving it
|
|
103
|
+
* first-match-wins semantics it shouldn't have.
|
|
104
|
+
*/
|
|
105
|
+
onDoubleClick?: (world: {
|
|
106
|
+
x: number;
|
|
107
|
+
y: number;
|
|
108
|
+
}) => void;
|
|
109
|
+
/**
|
|
110
|
+
* Default true. Set false to leave the window `keydown`/`keyup` listeners
|
|
111
|
+
* unattached so keyboard-bound actions never dispatch — pointer, wheel, and
|
|
112
|
+
* contextmenu channels stay live. `<SceneCanvas>` wires this to
|
|
113
|
+
* `enableKeybindings`, so opting out of keybindings disables the modern
|
|
114
|
+
* dispatcher key path as well as the legacy `useKeybindings` hook.
|
|
115
|
+
*
|
|
116
|
+
* Every dispatcher hears keys on `window`, and a keystroke one of them
|
|
117
|
+
* claims — or anything else calls `preventDefault` on — is not dispatched by
|
|
118
|
+
* the rest. `'first'` listens in the capture phase, so this dispatcher gets
|
|
119
|
+
* the keystroke ahead of every dispatcher listening normally, whatever the
|
|
120
|
+
* mount order: the shape for chrome that owns a key only when it can act.
|
|
121
|
+
*/
|
|
122
|
+
keyboard?: boolean | 'first';
|
|
123
|
+
/**
|
|
124
|
+
* Optional affordance classifier. Called with the **world** coordinates of
|
|
125
|
+
* the pointer — this hook applies the routed view's `clientToWorld` before
|
|
126
|
+
* calling it, the same conversion that produces `InputEvent.x`/`y`, so a
|
|
127
|
+
* consumer hit-tests in one space. Returns an `AffordanceHit` when the
|
|
128
|
+
* pointer lands on a known affordance (resize handle, rotate handle, etc.)
|
|
129
|
+
* or `null` when the pointer hit open canvas.
|
|
130
|
+
*
|
|
131
|
+
* The hit is packed into `InputEvent.pointerdown.affordance` and flows
|
|
132
|
+
* through into `InvocationCtx.drag.affordance`. Action invokers that require
|
|
133
|
+
* a specific affordance (e.g. `resizeAction` requires `handle:*`) use this
|
|
134
|
+
* field as a guard in their `start` body.
|
|
135
|
+
*
|
|
136
|
+
* When omitted, `affordance` is always `undefined` — meaning only consumers
|
|
137
|
+
* that explicitly wire a classifier get affordance-gated behavior.
|
|
138
|
+
* `<SceneCanvas>` wires the full chrome→dispatcher bridge.
|
|
139
|
+
*/
|
|
140
|
+
affordanceAt?: (worldPoint: {
|
|
141
|
+
x: number;
|
|
142
|
+
y: number;
|
|
143
|
+
}) => AffordanceHit | null;
|
|
144
|
+
/**
|
|
145
|
+
* Optional body classifier. Called on every pointerdown with the world-space
|
|
146
|
+
* coordinates of the pointer. Its result is packed onto the event as
|
|
147
|
+
* `bodyTarget` + `bodyKind`, which `matchTarget` reads to resolve the
|
|
148
|
+
* string-form `TargetSpec` values in `Tool.bindings`.
|
|
149
|
+
*
|
|
150
|
+
* `body` is `'empty'` when nothing is under the pointer, `'selected-body'`
|
|
151
|
+
* when the topmost hit belongs to the current selection, or
|
|
152
|
+
* `'unselected-body'` when it belongs to a node that isn't selected. `kind`
|
|
153
|
+
* is the hit node's semantic kind, when the scene can name it.
|
|
154
|
+
*
|
|
155
|
+
* When omitted, every body-derived target form (`'empty'`,
|
|
156
|
+
* `'selected-body'`, `'unselected-body'`, `kind:<k>`, `kind:<k>:selected`)
|
|
157
|
+
* never matches — bindings using those specs are silently skipped.
|
|
158
|
+
* `<SceneCanvas>` wires this.
|
|
159
|
+
*
|
|
160
|
+
* World coordinates, on every path, for the same reason as `affordanceAt`.
|
|
161
|
+
*/
|
|
162
|
+
classifyTarget?: (worldPoint: {
|
|
163
|
+
x: number;
|
|
164
|
+
y: number;
|
|
165
|
+
}) => BodyClassification;
|
|
166
|
+
/**
|
|
167
|
+
* Optional pre-created `Dispatcher`. When provided, this hook pumps events
|
|
168
|
+
* into the supplied instance instead of creating its own. Lets a parent
|
|
169
|
+
* scope (e.g. `<SceneCanvas>`) share one dispatcher between the gesture
|
|
170
|
+
* mounter and other consumers (the preview-ghost layer).
|
|
171
|
+
*/
|
|
172
|
+
dispatcher?: Dispatcher;
|
|
173
|
+
/**
|
|
174
|
+
* Converts a client-space pointer position (e.g. `e.clientX`, `e.clientY`)
|
|
175
|
+
* to world-space coordinates. When supplied, every pointer/wheel event's
|
|
176
|
+
* `x`/`y` is converted before the dispatcher builds `InvocationCtx.world`.
|
|
177
|
+
*
|
|
178
|
+
* Without this, `ctx.world` is populated with the raw client coords, which
|
|
179
|
+
* silently breaks any action whose overlay/output uses absolute world
|
|
180
|
+
* positions (marquee, lasso polyline, world-space affordances). Actions
|
|
181
|
+
* that only read `drag.delta` are unaffected because client→world deltas
|
|
182
|
+
* are equal at scale 1, but as soon as a consumer pans/zooms the view the
|
|
183
|
+
* deltas diverge too.
|
|
184
|
+
*
|
|
185
|
+
* `<SceneCanvas>` always wires this via its canvas rect + current view.
|
|
186
|
+
* Tests / harnesses without a view can omit it and continue passing raw
|
|
187
|
+
* coordinates as before.
|
|
188
|
+
*/
|
|
189
|
+
clientToWorld?: (clientX: number, clientY: number) => {
|
|
190
|
+
x: number;
|
|
191
|
+
y: number;
|
|
192
|
+
};
|
|
193
|
+
/**
|
|
194
|
+
* Invoked once per pump event (`pointermove`, `pointerup`, `pointercancel`,
|
|
195
|
+
* `key-held` up-phase, `multitouch` move) when a dispatcher-side handle is
|
|
196
|
+
* in flight. Lets the host canvas schedule a redraw so dispatcher-only
|
|
197
|
+
* overlays (marquee, lasso, preview-ghost) repaint on each frame.
|
|
198
|
+
*
|
|
199
|
+
* Required because dispatcher actions don't go through the legacy
|
|
200
|
+
* `tools.dispatcher.onGestureChange` redraw bump that ambient tool drags
|
|
201
|
+
* relied on. Without it, the overlay layer's `draw` never runs between
|
|
202
|
+
* pointerdown and pointerup, and the chrome flashes once at gesture start
|
|
203
|
+
* and then sits frozen until the gesture ends.
|
|
204
|
+
*
|
|
205
|
+
* `<SceneCanvas>` wires this to the canvas's `requestRedraw`. Test
|
|
206
|
+
* harnesses can omit it; their dispatchers won't paint between events but
|
|
207
|
+
* that's already the test contract.
|
|
208
|
+
*/
|
|
209
|
+
requestRedraw?: () => void;
|
|
210
|
+
/**
|
|
211
|
+
* The surface's painted-cursor channel, read live because the canvas handle
|
|
212
|
+
* it lives on arrives a render after this hook runs.
|
|
213
|
+
*
|
|
214
|
+
* Given one, a cursor the CSS tier cannot express — sized in world units, or
|
|
215
|
+
* past the size the browser drops the image at — is published here and drawn
|
|
216
|
+
* by the painted-cursor layer instead. Without one, such a cursor resolves to
|
|
217
|
+
* `none` and the pointer shows nothing.
|
|
218
|
+
*/
|
|
219
|
+
paintedCursor?: () => PaintedCursorState | undefined;
|
|
220
|
+
/** How touch and pen long-presses are timed, published and felt. */
|
|
221
|
+
longPress?: LongPressOptions;
|
|
222
|
+
/**
|
|
223
|
+
* Thunk returning the live `RuleCtx` for the current frame. When supplied,
|
|
224
|
+
* the dispatcher filters matched candidates by their declared
|
|
225
|
+
* `Action.eligible` rule (omitted => always eligible). `<SceneCanvas>`
|
|
226
|
+
* wires this; tests / harnesses without chrome-caps state can omit it.
|
|
227
|
+
*/
|
|
228
|
+
getRuleCtx?: () => RuleCtx | undefined;
|
|
229
|
+
/**
|
|
230
|
+
* Which of the canvas element's input channels to attach. Every one
|
|
231
|
+
* defaults to `true`; naming a channel `false` leaves its listeners
|
|
232
|
+
* unattached, so the element behaves as if the dispatcher were not there.
|
|
233
|
+
*
|
|
234
|
+
* A mount that binds two gestures should not also have to take the rest of
|
|
235
|
+
* the pipeline's side effects: `contextMenu` suppresses the native menu
|
|
236
|
+
* unconditionally, and `ingest` makes the element a valid drop target and
|
|
237
|
+
* styles it while an OS drag hovers. `@weasel-js/labkit`'s loupe mounts on
|
|
238
|
+
* a lab's own canvas host for a wheel and a held key, and turning the other
|
|
239
|
+
* three off is what keeps right-click and file drops working there.
|
|
240
|
+
*
|
|
241
|
+
* The window keyboard channel is `keyboard` instead of an entry here,
|
|
242
|
+
* because `<SceneCanvas enableKeybindings>` drives it.
|
|
243
|
+
*/
|
|
244
|
+
channels?: DispatcherChannels;
|
|
245
|
+
/**
|
|
246
|
+
* Routing for a canvas hosting more than one view: the non-root dispatch
|
|
247
|
+
* records to choose between, read fresh per event, and the resolver that
|
|
248
|
+
* chooses. Omit for a single view — then every event runs on the record the
|
|
249
|
+
* flat options above describe, which is what they are: view zero.
|
|
250
|
+
*
|
|
251
|
+
* A resolved id with no live record falls back to the root, so a view that
|
|
252
|
+
* unmounts mid-gesture degrades instead of dropping the event.
|
|
253
|
+
*
|
|
254
|
+
* Keyboard and paste have no coordinates to resolve; they run on the view
|
|
255
|
+
* the last coordinate-bearing event resolved to.
|
|
256
|
+
*
|
|
257
|
+
* @internal
|
|
258
|
+
*/
|
|
259
|
+
views?: {
|
|
260
|
+
targets: () => readonly DispatcherViewTarget[];
|
|
261
|
+
resolver: ViewIdResolver;
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Attach the input pipeline to a canvas: DOM pointer, wheel, keyboard and
|
|
266
|
+
* multitouch listeners in, normalized input events out, routed through the
|
|
267
|
+
* dispatcher to whichever action a binding names.
|
|
268
|
+
*
|
|
269
|
+
* Everything it needs beyond its options — the active tool, the dep registry —
|
|
270
|
+
* comes from context, so it must be mounted inside `<SceneCanvas>`'s
|
|
271
|
+
* providers.
|
|
272
|
+
*/
|
|
273
|
+
declare function useGestureDispatcher(opts: UseGestureDispatcherOptions): void;
|
|
274
|
+
|
|
275
|
+
export { type DispatcherChannels as D, type UseGestureDispatcherOptions as U, type ViewIdResolver as V, type DispatcherViewTarget as a, useGestureDispatcher as u };
|
package/dist/internal.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"internal.js"}
|