@weasel-js/routing 1.10.0 → 1.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,9 +2,9 @@ import { PoseLinear, View } from '@weasel-js/geom';
2
2
  import * as react from 'react';
3
3
  import { ReactNode } from 'react';
4
4
  import { Emitter } from '@weasel-js/registry';
5
+ import { ClaimableGesture, GestureSpec, IngestItem, InputEvent, TargetSpec, KeyHeldSpec, TargetPredicate, KeySpec, ParsedModifiers, GestureName } from '@weasel-js/gestures';
5
6
  import { CapabilityTag, ActiveMode, ModeRegistry } from '@weasel-js/modes';
6
7
  import { CursorSpec } from '@weasel-js/cursor';
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
  /**
@@ -825,6 +825,9 @@ type OngoingOverlay = {
825
825
  x: number;
826
826
  y: number;
827
827
  };
828
+ /** World affines the shape will also be drawn under once it lands, by
829
+ * the container it is drawn into: the preview is painted under each. */
830
+ copies?: readonly (readonly [number, number, number, number, number, number])[];
828
831
  };
829
832
  /** Handle returned from an `OngoingInvoker.start`. The dispatcher pumps
830
833
  * `onMove` on subsequent input events of the same gesture and calls
@@ -840,6 +843,12 @@ interface OngoingHandle {
840
843
  * handle is in flight.
841
844
  */
842
845
  kind?: string;
846
+ /**
847
+ * The cursor this gesture shows now, read after every move. Wins over the
848
+ * action's `activeCursor`, for a cursor that follows the gesture — the
849
+ * rotate glyph turning about its pivot. `undefined` defers to the action.
850
+ */
851
+ cursor?(): CursorSpec | undefined;
843
852
  onMove?(ctx: InvocationCtx): void;
844
853
  onEnd?(ctx: InvocationCtx, reason: 'commit' | 'cancel'): void;
845
854
  /**
@@ -903,8 +912,6 @@ type Invoker = ImmediateInvoker | OngoingInvoker;
903
912
  * GestureBinding — connects a GestureSpec to an Action id (with per-binding
904
913
  * options). Tools own arrays of these on their `bindings` field; ambient
905
914
  * gesture-bindings are registered globally.
906
- *
907
- * See `docs/superpowers/specs/2026-05-16-registry-unification-design.md`.
908
915
  */
909
916
 
910
917
  /** An interaction: a gesture spec composed with the id of the action it
@@ -916,7 +923,6 @@ interface GestureBinding {
916
923
  opts?: BindingOpts;
917
924
  }
918
925
  /**
919
- * @experimental
920
926
  * A single entry in `Action.defaultBinding[]`. Either a bare `GestureSpec`
921
927
  * (no per-binding opts) or an object form that pairs a spec with
922
928
  * `BindingOpts` for parametric actions (e.g. `{ params: { axis: 'x' } }`).
@@ -1136,6 +1142,37 @@ interface ActionSource {
1136
1142
  list(): readonly Action[];
1137
1143
  }
1138
1144
 
1145
+ /**
1146
+ * Tuning: what a host changes about an action it did not write — the params
1147
+ * every invocation starts from, and bindings added beside the action's own —
1148
+ * without restating the descriptor. `ActionsRegistry.tune` holds tunings and
1149
+ * applies them to whatever descriptor is live for the id.
1150
+ */
1151
+
1152
+ /** An action's params, or a thunk read each time the action asks for them. */
1153
+ type ActionParams<P = Record<string, unknown>> = Partial<P> | (() => Partial<P>);
1154
+ /** A binding added for an action: a gesture alone, or a gesture with the
1155
+ * options it passes — the {@link BoundGesture} shape, its params typed. */
1156
+ type TunedBinding<P = Record<string, unknown>> = GestureSpec | {
1157
+ spec: GestureSpec;
1158
+ opts: Omit<BindingOpts, 'params'> & {
1159
+ params?: ActionParams<P>;
1160
+ };
1161
+ };
1162
+ /** What a host changes about one action. */
1163
+ interface ActionTuning<P = Record<string, unknown>> {
1164
+ /** Params every invocation starts from. What a binding or a `trigger` call
1165
+ * passes is laid over them, so these fill in what it leaves out. A thunk is
1166
+ * read on every call, and an ongoing action resolving its `opts.params`
1167
+ * mid-gesture sees the value as it is then. */
1168
+ params?: ActionParams<P>;
1169
+ /** Bindings added after the action's own `defaultBinding`. */
1170
+ bindings?: readonly TunedBinding<P>[];
1171
+ }
1172
+ /** `action` with each tuning applied in turn, a later one's params over an
1173
+ * earlier one's. Applying a tuning to a copy it already tuned changes nothing. */
1174
+ declare function tuneAction(action: Action, tunings: readonly ActionTuning[]): Action;
1175
+
1139
1176
  /**
1140
1177
  * @experimental
1141
1178
  * The entries an action contributes to a bar, menu or palette — one per
@@ -1902,6 +1939,10 @@ interface ActionsRegistry {
1902
1939
  * `trigger` nor `begin` will fire it. Returns a release. This is the
1903
1940
  * "not for me" door — `unregister` is the "should not exist" one. */
1904
1941
  mute(id: string): () => void;
1942
+ /** Tune `id` here and in scopes below: its params and added bindings
1943
+ * apply to whichever descriptor is live for it, registered before the
1944
+ * tuning or after. Returns a release. See {@link ActionTuning}. */
1945
+ tune(id: string, tuning: ActionTuning): () => void;
1905
1946
  list(): readonly Action[];
1906
1947
  /** Fire an immediate-invoker action by id. The optional `params` arg is
1907
1948
  * forwarded to `ImmediateInvoker.run` as its second argument — use it for
@@ -1977,7 +2018,11 @@ declare function offhandKeyFor(trigger: HotkeyTrigger): string;
1977
2018
  /** Build the per-tool `BoundGesture` list for the consolidated action's
1978
2019
  * `defaultBinding`. Each entry pairs a `key-held` spec with
1979
2020
  * `opts.params.toolId` so the invoker's `start` can pick out which tool
1980
- * to engage. */
2021
+ * to engage.
2022
+ *
2023
+ * An offhand tool engages only between gestures. Swapping the tool out
2024
+ * mid-drag would cancel the drag, and the key is free for the gesture's own
2025
+ * tool to use — Space repositions a shape being drawn. */
1981
2026
  declare function buildToolOffhandBindings(specs: readonly ToolOffhandBindingSpec[]): BoundGesture[];
1982
2027
  /** The `key-held` spec for holding `key` (a `KeyboardEvent.key`, or several).
1983
2028
  * A modifier key gets every modifier optional, since its own keydown reports
@@ -2037,6 +2082,14 @@ declare const isControlHandle: TargetPredicate & {
2037
2082
  declare const isAnchorOrControl: TargetPredicate & {
2038
2083
  readsAffordance: true;
2039
2084
  };
2085
+ /** The outline of the path being edited, between its anchors. */
2086
+ declare const isSegment: TargetPredicate & {
2087
+ readsAffordance: true;
2088
+ };
2089
+ /** Anything path edit puts on the path: an anchor, a control, or a segment. */
2090
+ declare const isPathEditPart: TargetPredicate & {
2091
+ readsAffordance: true;
2092
+ };
2040
2093
 
2041
2094
  /** A press being held toward a long-press. */
2042
2095
  interface PendingLongPress {
@@ -2573,6 +2626,8 @@ declare function reportRouteConflicts(scopes: ToolScopes, warn?: (message: strin
2573
2626
  *
2574
2627
  * Extracted from the deleted singular `useKeybinding` hook so the remaining
2575
2628
  * consumers don't depend on its disappearance.
2629
+ *
2630
+ * @packageDocumentation
2576
2631
  */
2577
2632
 
2578
2633
  /**
@@ -2756,6 +2811,8 @@ declare function dwarn(namespace: string, ...args: unknown[]): void;
2756
2811
  * The pure half: no React, no DOM. The React seam that pumps browser events
2757
2812
  * into the dispatcher, and the providers that feed it, live behind
2758
2813
  * `@weasel-js/routing/react`.
2814
+ *
2815
+ * @packageDocumentation
2759
2816
  */
2760
2817
 
2761
2818
  /**
@@ -2782,6 +2839,29 @@ interface DepSchema {
2782
2839
  }
2783
2840
  /** Every dep name declared by anything that has merged into {@link DepSchema}. */
2784
2841
  type DepName = keyof DepSchema;
2842
+ /**
2843
+ * The params each action reads, by action id — what types a tuning's
2844
+ * `params` and the params on the bindings it adds. Whoever ships an action
2845
+ * merges its entry in, as with {@link DepSchema}:
2846
+ *
2847
+ * ```ts
2848
+ * declare module '@weasel-js/core' {
2849
+ * interface ActionParamSchema { brushDeleteAnchors: { radiusPx: number } }
2850
+ * }
2851
+ * ```
2852
+ *
2853
+ * Declare an entry as a type literal, not an interface: an interface has no
2854
+ * index signature, so it is not a `Record<string, unknown>`.
2855
+ */
2856
+ interface ActionParamSchema {
2857
+ }
2858
+ /** Tunings keyed by action id: typed by {@link ActionParamSchema} for an id it
2859
+ * declares, `Record<string, unknown>` params for any other. */
2860
+ type ActionTunings = {
2861
+ readonly [K in keyof ActionParamSchema]?: ActionTuning<ActionParamSchema[K]>;
2862
+ } & {
2863
+ readonly [id: string]: ActionTuning | undefined;
2864
+ };
2785
2865
  /**
2786
2866
  * What the kernel drawing these tools takes as an overlay. Routing never reads
2787
2867
  * an overlay, so it has no type of its own for one; the kernel merges its type
@@ -2803,4 +2883,4 @@ type KernelOverlay = OverlaySchema extends {
2803
2883
  overlay: infer O;
2804
2884
  } ? O : unknown;
2805
2885
 
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 };
2886
+ export { DEFAULT_ALLOWED_CAPABILITIES as $, type ActionsRegistry as A, type ActionVariant as B, type Contribution as C, type DepRegistry as D, type ActionsProp as E, type AnyTool as F, type AssemblyInputs as G, type BindingOpts as H, type BindingScope as I, type BindingSource as J, type KernelOverlay as K, type LongPressOptions as L, type BodyPredicate as M, type NodeId as N, type OverlayPosition as O, type BoundGesture as P, type Bounds as Q, type RuleCtx as R, ScopeNode as S, ToolNode as T, type BuildRuleCtxArgs as U, COARSE_TARGET_SCALE as V, type Condition as W, type Conflict as X, type ContributionChrome as Y, type ContributionDeps as Z, type ContributionRouting as _, type DepName as a, type TraceLogEntry as a$, DEFAULT_DEVICE_PROFILE as a0, type DebugSink as a1, type DetectedDeviceFacts as a2, type DeviceProfile as a3, type DispatchRecord as a4, type DispatchRecordInput as a5, type DispatcherContext as a6, type DragSample as a7, type DroppedCandidate as a8, type Eligibility as a9, type Point2 as aA, type PointerSession as aB, type PointerSessionCallbacks as aC, type PointerSessionCancelReason as aD, type PointerSessionOptions as aE, type RankedCandidate as aF, type RecordCandidate as aG, type RegistryEntry as aH, type ResizeAnchor as aI, type ResolveAllOptions as aJ, type ResolveOnlyResult as aK, type ResolvedCandidate as aL, type Rule as aM, type ScopedBinding as aN, type ScratchKey as aO, type ScratchStore as aP, type SelectionApi as aQ, type Selector as aR, type SpecificityPart as aS, TOOL_OFFHAND_ID as aT, type ToolDef as aU, type ToolKeybinding as aV, type ToolModifiers as aW, type ToolOffhandBindingSpec as aX, type ToolPresentation as aY, type ToolScopes as aZ, type ToolSlot as a_, type EligibilityState as aa, type GestureBinding as ab, type HandleKind as ac, type HitShape as ad, type HotkeyTrigger as ae, INGEST_STRING_MIMES as af, type ImmediateInvoker as ag, type InvocationCtx as ah, type Invoker as ai, type KeyBinding as aj, type LongPressState as ak, type LongPressStore as al, type MatchOutcome as am, type MatchResult as an, type ModeShortcutHandlers as ao, type ModeSwitchLogEntry as ap, type ModifierState as aq, NEVER as ar, type OngoingHandle as as, type OngoingInvoker as at, type OngoingOverlay as au, type OverlayRole as av, type OverlaySchema as aw, PREDICATE_TARGET as ax, type PendingLongPress as ay, type PlacedBy as az, type DepSchema as b, ruleCanHoldIn as b$, type TunedBinding as b0, type UiOngoingControl as b1, type ViewportGestureKind as b2, type WalkStep as b3, actionBindings as b4, actionItems as b5, assembleEntries as b6, buildDepsFromRequires as b7, buildRouteRegistry as b8, buildRuleCtx as b9, isRotateHandle as bA, isSegment as bB, isSelectedBody as bC, isTool as bD, isUnselectedBody as bE, itemTypesFromDataTransfer as bF, itemsFromClipboardData as bG, itemsFromDataTransfer as bH, itemsFromFiles as bI, liveScope as bJ, makeToolOffhandAction as bK, matchBest as bL, matchSorted as bM, matchSortedWithBarred as bN, matchesKeyBinding as bO, mergeContributions as bP, modeShortcutSpec as bQ, modeShortcuts as bR, multiTouchGestureId as bS, offhandKeyFor as bT, openPointerSession as bU, pointerGestureId as bV, recordModeSwitch as bW, reportRouteConflicts as bX, resolveDeviceProfile as bY, resolveParams as bZ, routesForSpec as b_, buildToolOffhandBindings as ba, clientToCanvas as bb, clientToCanvasRect as bc, createDepRegistry as bd, createDispatcher as be, createLongPressStore as bf, defineTool as bg, deleteScratch as bh, describeRule as bi, dlog as bj, dwarn as bk, evaluate as bl, evaluateActive as bm, evaluateEnabled as bn, findConflicts as bo, getScratch as bp, heldKeySpec as bq, historyKey as br, isAnchor as bs, isAnchorOrControl as bt, isBody as bu, isControlHandle as bv, isDebugEnabled as bw, isEmpty as bx, isPathEditPart as by, isResizeHandle as bz, type Action as c, rulesExclusive as c0, scopeBindings as c1, scratchKey as c2, setScratch as c3, targetConsultsAffordance as c4, tuneAction as c5, validateActionDefaultBinding as c6, validateActionId as c7, 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 ActionParamSchema as t, useActiveToolContext as u, type ActionParams as v, type ActionPresentation as w, type ActionSource as x, type ActionTuning as y, type ActionTunings as z };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { m as ALWAYS, c as Action, n as ActionDeps, o as ActionDisabledReason, o as ActionDisabledReasonValue, p as ActionDispatch, q as ActionEnabledResult, r as ActionEntry, s as ActionItem, t as ActionPresentation, v as ActionSource, w as ActionVariant, x as ActionsProp, A as ActionsRegistry, l as AffordanceHit, y as AnyTool, z as AssemblyInputs, B as BindingOpts, E as BindingScope, F as BindingSource, G as BodyPredicate, H as BoundGesture, I as Bounds, J as BuildRuleCtxArgs, M as COARSE_TARGET_SCALE, P as Condition, Q as Conflict, C as Contribution, U as ContributionChrome, V as ContributionDeps, W as ContributionRouting, X as DEFAULT_ALLOWED_CAPABILITIES, Y as DEFAULT_DEVICE_PROFILE, Z as DebugSink, a as DepName, D as DepRegistry, b as DepSchema, _ as DetectedDeviceFacts, $ as DeviceProfile, a0 as DispatchRecord, a1 as DispatchRecordInput, k as Dispatcher, a2 as DispatcherContext, a3 as DragSample, a4 as DroppedCandidate, a5 as Eligibility, a6 as EligibilityState, a7 as GestureBinding, a8 as HandleKind, a9 as HitShape, aa as HotkeyTrigger, ab as INGEST_STRING_MIMES, ac as ImmediateInvoker, ad as InvocationCtx, ae as Invoker, K as KernelOverlay, af as KeyBinding, L as LongPressOptions, ag as LongPressState, ah as LongPressStore, ai as MatchOutcome, aj as MatchResult, ak as ModeShortcutHandlers, al as ModeSwitchLogEntry, am as ModifierState, an as NEVER, N as NodeId, ao as OngoingHandle, ap as OngoingInvoker, aq as OngoingOverlay, O as OverlayPosition, ar as OverlayRole, as as OverlaySchema, at as PREDICATE_TARGET, au as PendingLongPress, av as PlacedBy, aw as Point2, ax as PointerSession, ay as PointerSessionCallbacks, az as PointerSessionCancelReason, aA as PointerSessionOptions, aB as RankedCandidate, aC as RecordCandidate, aD as RegistryEntry, aE as ResizeAnchor, aF as ResolveAllOptions, aG as ResolveOnlyResult, aH as ResolvedCandidate, aI as Rule, R as RuleCtx, aJ as ScopedBinding, aK as ScratchKey, aL as ScratchStore, aM as SelectionApi, aN as Selector, aO as SpecificityPart, aP as TOOL_OFFHAND_ID, e as Tool, aQ as ToolDef, aR as ToolKeybinding, aS as ToolModifiers, aT as ToolOffhandBindingSpec, aU as ToolPresentation, aV as ToolScopes, aW as ToolSlot, aX as TraceLogEntry, aY as UiOngoingControl, aZ as ViewportGestureKind, a_ as WalkStep, a$ as actionBindings, b0 as actionItems, b1 as assembleEntries, b2 as buildDepsFromRequires, b3 as buildRouteRegistry, b4 as buildRuleCtx, b5 as buildToolOffhandBindings, b6 as clientToCanvas, b7 as clientToCanvasRect, b8 as createDepRegistry, b9 as createDispatcher, ba as createLongPressStore, bb as defineTool, bc as deleteScratch, bd as describeRule, be as dlog, bf as dwarn, bg as evaluate, bh as evaluateActive, bi as evaluateEnabled, bj as findConflicts, bk as getScratch, bl as heldKeySpec, bm as historyKey, bn as isAnchor, bo as isAnchorOrControl, bp as isBody, bq as isControlHandle, br as isDebugEnabled, bs as isEmpty, bt as isResizeHandle, bu as isRotateHandle, bv as isSelectedBody, bw as isTool, bx as isUnselectedBody, by as itemTypesFromDataTransfer, bz as itemsFromClipboardData, bA as itemsFromDataTransfer, bB as itemsFromFiles, bC as liveScope, bD as makeToolOffhandAction, bE as matchBest, bF as matchSorted, bG as matchSortedWithBarred, bH as matchesKeyBinding, bI as mergeContributions, bJ as modeShortcutSpec, bK as modeShortcuts, bL as multiTouchGestureId, bM as offhandKeyFor, bN as openPointerSession, bO as pointerGestureId, bP as recordModeSwitch, bQ as reportRouteConflicts, bR as resolveDeviceProfile, bS as resolveParams, bT as routesForSpec, bU as ruleCanHoldIn, bV as rulesExclusive, bW as scopeBindings, bX as scratchKey, bY as setScratch, bZ as targetConsultsAffordance, b_ as validateActionDefaultBinding, b$ as validateActionId } from './index-Bm2QEV09.js';
1
+ export { m as ALWAYS, c as Action, n as ActionDeps, o as ActionDisabledReason, o as ActionDisabledReasonValue, p as ActionDispatch, q as ActionEnabledResult, r as ActionEntry, s as ActionItem, t as ActionParamSchema, v as ActionParams, w as ActionPresentation, x as ActionSource, y as ActionTuning, z as ActionTunings, B as ActionVariant, E as ActionsProp, A as ActionsRegistry, l as AffordanceHit, F as AnyTool, G as AssemblyInputs, H as BindingOpts, I as BindingScope, J as BindingSource, M as BodyPredicate, P as BoundGesture, Q as Bounds, U as BuildRuleCtxArgs, V as COARSE_TARGET_SCALE, W as Condition, X as Conflict, C as Contribution, Y as ContributionChrome, Z as ContributionDeps, _ as ContributionRouting, $ as DEFAULT_ALLOWED_CAPABILITIES, a0 as DEFAULT_DEVICE_PROFILE, a1 as DebugSink, a as DepName, D as DepRegistry, b as DepSchema, a2 as DetectedDeviceFacts, a3 as DeviceProfile, a4 as DispatchRecord, a5 as DispatchRecordInput, k as Dispatcher, a6 as DispatcherContext, a7 as DragSample, a8 as DroppedCandidate, a9 as Eligibility, aa as EligibilityState, ab as GestureBinding, ac as HandleKind, ad as HitShape, ae as HotkeyTrigger, af as INGEST_STRING_MIMES, ag as ImmediateInvoker, ah as InvocationCtx, ai as Invoker, K as KernelOverlay, aj as KeyBinding, L as LongPressOptions, ak as LongPressState, al as LongPressStore, am as MatchOutcome, an as MatchResult, ao as ModeShortcutHandlers, ap as ModeSwitchLogEntry, aq as ModifierState, ar as NEVER, N as NodeId, as as OngoingHandle, at as OngoingInvoker, au as OngoingOverlay, O as OverlayPosition, av as OverlayRole, aw as OverlaySchema, ax as PREDICATE_TARGET, ay as PendingLongPress, az as PlacedBy, aA as Point2, aB as PointerSession, aC as PointerSessionCallbacks, aD as PointerSessionCancelReason, aE as PointerSessionOptions, aF as RankedCandidate, aG as RecordCandidate, aH as RegistryEntry, aI as ResizeAnchor, aJ as ResolveAllOptions, aK as ResolveOnlyResult, aL as ResolvedCandidate, aM as Rule, R as RuleCtx, aN as ScopedBinding, aO as ScratchKey, aP as ScratchStore, aQ as SelectionApi, aR as Selector, aS as SpecificityPart, aT as TOOL_OFFHAND_ID, e as Tool, aU as ToolDef, aV as ToolKeybinding, aW as ToolModifiers, aX as ToolOffhandBindingSpec, aY as ToolPresentation, aZ as ToolScopes, a_ as ToolSlot, a$ as TraceLogEntry, b0 as TunedBinding, b1 as UiOngoingControl, b2 as ViewportGestureKind, b3 as WalkStep, b4 as actionBindings, b5 as actionItems, b6 as assembleEntries, b7 as buildDepsFromRequires, b8 as buildRouteRegistry, b9 as buildRuleCtx, ba as buildToolOffhandBindings, bb as clientToCanvas, bc as clientToCanvasRect, bd as createDepRegistry, be as createDispatcher, bf as createLongPressStore, bg as defineTool, bh as deleteScratch, bi as describeRule, bj as dlog, bk as dwarn, bl as evaluate, bm as evaluateActive, bn as evaluateEnabled, bo as findConflicts, bp as getScratch, bq as heldKeySpec, br as historyKey, bs as isAnchor, bt as isAnchorOrControl, bu as isBody, bv as isControlHandle, bw as isDebugEnabled, bx as isEmpty, by as isPathEditPart, bz as isResizeHandle, bA as isRotateHandle, bB as isSegment, bC as isSelectedBody, bD as isTool, bE as isUnselectedBody, bF as itemTypesFromDataTransfer, bG as itemsFromClipboardData, bH as itemsFromDataTransfer, bI as itemsFromFiles, bJ as liveScope, bK as makeToolOffhandAction, bL as matchBest, bM as matchSorted, bN as matchSortedWithBarred, bO as matchesKeyBinding, bP as mergeContributions, bQ as modeShortcutSpec, bR as modeShortcuts, bS as multiTouchGestureId, bT as offhandKeyFor, bU as openPointerSession, bV as pointerGestureId, bW as recordModeSwitch, bX as reportRouteConflicts, bY as resolveDeviceProfile, bZ as resolveParams, b_ as routesForSpec, b$ as ruleCanHoldIn, c0 as rulesExclusive, c1 as scopeBindings, c2 as scratchKey, c3 as setScratch, c4 as targetConsultsAffordance, c5 as tuneAction, c6 as validateActionDefaultBinding, c7 as validateActionId } from './index-DkOqUxow.js';
2
2
  export { CONTROL_SELECTOR, ChannelRef, DRAG_THRESHOLD_PX, DescribeRouteOptions, GESTURE_DESCRIPTORS, GestureArgSpec, GestureDescriptor, GestureName, IngestItem, InputEvent, LONG_PRESS_MS, ModRequirement, ModifierKey, ParsedModifiers, ParsedRoute, PhaseAtom, PhaseContext, RESERVED_ID_NAMES, RESERVED_ID_PREFIXES, ROUTE_FIELD_DEFINITIONS, ROUTE_TERMS, RouteDescriptionPart, RouteFieldName, RouteTermLabel, activatesFocusedControl, canonicalModifiers, collapseShiftPairs, describeRoute, describeRouteParts, focusedControlOwnsKey, formatPhaseAtom, formatRoute, getGestureDescriptor, isEditableTarget, isInControlWithin, isKnownGestureName, matchKey, matchModifiers, matchPhase, matchSpec, matchTarget, parseRoute, pastDragThreshold, routeGestureForSpecKind, specKindForRouteGesture, specificity } from '@weasel-js/gestures';
3
3
  export { SelectionExtendKey, SelectionMode } from '@weasel-js/select';
4
4
  export { View } from '@weasel-js/geom';
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { ActionDisabledReason } from './chunk-4DR6AJMN.js';
2
- export { ALWAYS, ActionDisabledReason, 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, activatesFocusedControl, assembleEntries, buildDepsFromRequires, buildRouteRegistry, buildRuleCtx, buildToolOffhandBindings, canonicalModifiers, clientToCanvas, clientToCanvasRect, collapseShiftPairs, createDepRegistry, createDispatcher, createLongPressStore, describeRoute, describeRouteParts, describeRule, dlog, dwarn, evaluate, evaluateActive, evaluateEnabled, findConflicts, focusedControlOwnsKey, formatPhaseAtom, formatRoute, getGestureDescriptor, heldKeySpec, historyKey, isDebugEnabled, isEditableTarget, isKnownGestureName, isTool, itemTypesFromDataTransfer, itemsFromClipboardData, itemsFromDataTransfer, itemsFromFiles, liveScope, makeToolOffhandAction, matchBest, matchKey, matchModifiers, matchPhase, matchSorted, matchSortedWithBarred, matchSpec, matchTarget, matchesKeyBinding, multiTouchGestureId, offhandKeyFor, openPointerSession, parseRoute, pointerGestureId, recordModeSwitch, reportRouteConflicts, resolveDeviceProfile, resolveParams, routeGestureForSpecKind, routesForSpec, ruleCanHoldIn, rulesExclusive, scopeBindings, specKindForRouteGesture, specificity, targetConsultsAffordance, validateActionDefaultBinding, validateActionId } from './chunk-4DR6AJMN.js';
1
+ import { ActionDisabledReason } from './chunk-VCEIQ64H.js';
2
+ export { ALWAYS, ActionDisabledReason, 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, activatesFocusedControl, assembleEntries, buildDepsFromRequires, buildRouteRegistry, buildRuleCtx, buildToolOffhandBindings, canonicalModifiers, clientToCanvas, clientToCanvasRect, collapseShiftPairs, createDepRegistry, createDispatcher, createLongPressStore, describeRoute, describeRouteParts, describeRule, dlog, dwarn, evaluate, evaluateActive, evaluateEnabled, findConflicts, focusedControlOwnsKey, formatPhaseAtom, formatRoute, getGestureDescriptor, heldKeySpec, historyKey, isDebugEnabled, isEditableTarget, isKnownGestureName, isTool, itemTypesFromDataTransfer, itemsFromClipboardData, itemsFromDataTransfer, itemsFromFiles, liveScope, makeToolOffhandAction, matchBest, matchKey, matchModifiers, matchPhase, matchSorted, matchSortedWithBarred, matchSpec, matchTarget, matchesKeyBinding, multiTouchGestureId, offhandKeyFor, openPointerSession, parseRoute, pointerGestureId, recordModeSwitch, reportRouteConflicts, resolveDeviceProfile, resolveParams, routeGestureForSpecKind, routesForSpec, ruleCanHoldIn, rulesExclusive, scopeBindings, specKindForRouteGesture, specificity, targetConsultsAffordance, tuneAction, validateActionDefaultBinding, validateActionId } from './chunk-VCEIQ64H.js';
3
3
  import { bodyPredicate, hitPredicate, RESERVED_ID_PREFIXES, RESERVED_ID_NAMES } from '@weasel-js/gestures';
4
4
  export { CONTROL_SELECTOR, DRAG_THRESHOLD_PX, isInControlWithin, pastDragThreshold } from '@weasel-js/gestures';
5
5
  import { modeLabel } from '@weasel-js/modes';
@@ -41,6 +41,8 @@ var isControlHandle = hitPredicate((target) => {
41
41
  return k !== void 0 && /^(controlIn|controlOut):\d+$/.test(k);
42
42
  });
43
43
  var isAnchorOrControl = hitPredicate((target) => isAnchor(target) || isControlHandle(target));
44
+ var isSegment = hitPredicate((target) => kindOf(target) === "segment");
45
+ var isPathEditPart = hitPredicate((target) => isAnchorOrControl(target) || isSegment(target));
44
46
 
45
47
  // src/contributions/merge.ts
46
48
  function mergeContributions(...bundles) {
@@ -209,6 +211,6 @@ function deleteScratch(store, key) {
209
211
  return false;
210
212
  }
211
213
 
212
- export { actionItems, defineTool, deleteScratch, getScratch, isAnchor, isAnchorOrControl, isBody, isControlHandle, isEmpty, isResizeHandle, isRotateHandle, isSelectedBody, isUnselectedBody, mergeContributions, modeShortcutSpec, modeShortcuts, scratchKey, setScratch };
214
+ export { actionItems, defineTool, deleteScratch, getScratch, isAnchor, isAnchorOrControl, isBody, isControlHandle, isEmpty, isPathEditPart, isResizeHandle, isRotateHandle, isSegment, isSelectedBody, isUnselectedBody, mergeContributions, modeShortcutSpec, modeShortcuts, scratchKey, setScratch };
213
215
  //# sourceMappingURL=index.js.map
214
216
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/interactions/actions/actionItems.ts","../src/interactions/dispatcher/predicates.ts","../src/contributions/merge.ts","../src/contributions/modeShortcuts.ts","../src/tools/defineTool.ts","../src/interactions/scratchKey.ts"],"names":["RESERVED_ID_PREFIXES","RESERVED_ID_NAMES"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @experimental\n * The entries an action contributes to a bar, menu or palette — one per\n * variant, or the action itself when it declares none.\n */\nimport type { ReactNode } from 'react';\nimport type { Action } from './action';\n\n/** @experimental One renderable entry: an action, optionally with params. */\nexport interface ActionItem {\n /** `action.id`, or `action.id:variant.key` — unique across a registry. */\n key: string;\n action: Action;\n label: string;\n icon?: ReactNode | (() => ReactNode);\n /** What to pass to `trigger(action.id, params)`; absent for a plain action. */\n params?: Readonly<Record<string, unknown>>;\n}\n\n/** @experimental Expand `action` into the entries a surface renders. An\n * action without an immediate invoker has none: `trigger` cannot start a\n * drag, so a button for one would do nothing. */\nexport function actionItems(action: Action): ActionItem[] {\n if (action.invoker?.timing !== 'immediate') return [];\n if (!action.variants?.length) {\n return [{ key: action.id, action, label: action.label, icon: action.icon }];\n }\n return action.variants.map((v) => ({\n key: `${action.id}:${v.key}`,\n action,\n label: v.label,\n icon: v.icon ?? action.icon,\n params: v.params,\n }));\n}\n","/**\n * Named predicates for `defaultBinding.target.kindOf`. Each matches the\n * `TargetPredicate` shape, declaring whether it reads the hit,\n * so they drop into action descriptors without a wrapper:\n *\n * target: { kindOf: isAnchor }\n *\n * Single source of truth for \"what does this affordance kind mean\" — adding\n * a new predicate here is what enables every action that needs the same\n * shape to read it. Drift prevention.\n */\nimport { bodyPredicate, hitPredicate, type TargetPredicate } from '@weasel-js/gestures';\n\n// ─── Body-class predicates (read the second arg) ───────────────────────\n\n/** A predicate that resolves from `bodyTarget` alone. */\nexport type BodyPredicate = TargetPredicate & { readsAffordance: false };\n\n/** Matches any body hit — selected or unselected node body. */\nexport const isBody: BodyPredicate = bodyPredicate((bodyTarget) =>\n bodyTarget === 'selected-body' || bodyTarget === 'unselected-body');\n\n/** Body hit specifically already in the selection. */\nexport const isSelectedBody: BodyPredicate = bodyPredicate((bodyTarget) =>\n bodyTarget === 'selected-body');\n\n/** Body hit specifically NOT in the selection. */\nexport const isUnselectedBody: BodyPredicate = bodyPredicate((bodyTarget) =>\n bodyTarget === 'unselected-body');\n\n/** Empty-canvas hit — no node beneath cursor. */\nexport const isEmpty: BodyPredicate = bodyPredicate((bodyTarget) =>\n bodyTarget === 'empty');\n\n// ─── Affordance-kind predicates (read the first arg) ───────────────────\n\nfunction kindOf(target: unknown): string | undefined {\n if (typeof target !== 'object' || target === null) return undefined;\n const k = (target as { kind?: unknown }).kind;\n return typeof k === 'string' ? k : undefined;\n}\n\n/** Any corner resize handle — top-left/top-right/bottom-left/bottom-right. */\nexport const isResizeHandle = hitPredicate((target) => {\n const k = kindOf(target);\n return k !== undefined && k.startsWith('handle:');\n});\n\n/** The rotate-around-center handle above the bounding box. */\nexport const isRotateHandle = hitPredicate((target) =>\n kindOf(target) === 'rotate-handle');\n\n/** Any path anchor (the on-curve dot). */\nexport const isAnchor = hitPredicate((target) => {\n const k = kindOf(target);\n return k !== undefined && /^anchor:\\d+$/.test(k);\n});\n\n/** Either control handle (controlIn or controlOut). */\nexport const isControlHandle = hitPredicate((target) => {\n const k = kindOf(target);\n return k !== undefined && /^(controlIn|controlOut):\\d+$/.test(k);\n});\n\n/** Any of the three anchor-editing affordances (anchor + both controls). */\nexport const isAnchorOrControl = hitPredicate((target) =>\n isAnchor(target) || isControlHandle(target));\n","import type { Contribution } from './types';\n\n/**\n * Concatenate contribution bundles into one registry, preserving order —\n * entry order decides which of two same-specificity bindings in one scope\n * tier wins, so it is part of the result, not an accident of it.\n *\n * Throws on a duplicate id or dep name rather than dropping one: a feature\n * silently losing its bindings, or reading another feature's dep, is the\n * failure this registry exists to make loud.\n */\nexport function mergeContributions<T extends Contribution<unknown>>(\n ...bundles: readonly (readonly T[])[]\n): T[] {\n const out: T[] = [];\n const seen = new Set<string>();\n const depOwner = new Map<string, string>();\n for (const bundle of bundles) {\n for (const entry of bundle) {\n if (seen.has(entry.id)) {\n throw new Error(\n `mergeContributions: duplicate contribution id \"${entry.id}\". `\n + `Two bundles registered the same id; rename one or merge them by hand.`,\n );\n }\n seen.add(entry.id);\n for (const dep of Object.keys(entry.deps ?? {})) {\n const owner = depOwner.get(dep);\n if (owner !== undefined) {\n throw new Error(\n `mergeContributions: dep \"${dep}\" is provided by both \"${owner}\" and \"${entry.id}\". `\n + `One registry answers each dep name; drop one source.`,\n );\n }\n depOwner.set(dep, entry.id);\n }\n out.push(entry);\n }\n }\n return out;\n}\n","import { modeLabel, type ModeDefinition, type ModeRegistry } from '@weasel-js/modes';\nimport type { KeySpec, ModSpec } from '@weasel-js/gestures';\nimport { ActionDisabledReason, type Action } from '../interactions/actions/action';\nimport type { Rule } from '../eligibility';\nimport type { Contribution } from './types';\n\n/**\n * What each mode shortcut does. A role left out binds nothing, so its key\n * falls through to whatever else the dispatcher has on it.\n */\nexport interface ModeShortcutHandlers {\n /** Enter mode `id` — the `entry` shortcut. */\n enter?: (id: string) => void;\n /** Whether mode `id` can be entered now. A mode that needs a target it\n * doesn't have answers no, and its entry key falls through. Default yes. */\n canEnter?: (id: string) => boolean;\n /** Leave the active soft mode, keeping its session to resume. */\n exit?: () => void;\n /** Leave the active soft mode and throw its session away. */\n discard?: () => void;\n /** Commit the active strict mode's transaction. */\n commit?: () => void;\n /** Abandon the active strict mode's transaction. */\n cancel?: () => void;\n}\n\ntype Role = 'exit' | 'discard' | 'commit' | 'cancel';\nconst LEAVING: readonly Role[] = ['exit', 'discard', 'commit', 'cancel'];\nconst VERB: Record<Role | 'entry', string> = {\n entry: 'Enter', exit: 'Exit', discard: 'Discard', commit: 'Commit', cancel: 'Cancel',\n};\n\nconst MODIFIERS: Record<string, keyof ModSpec> = {\n meta: 'meta', cmd: 'meta', command: 'meta',\n ctrl: 'ctrl', control: 'ctrl',\n alt: 'alt', option: 'alt',\n shift: 'shift',\n mod: 'mod',\n};\n\n/**\n * A `ModeDefinition` shortcut chord — `'Escape'`, `'Meta+T'` — as a key spec.\n * Modifier names are case-insensitive; the last segment is the key.\n */\nexport function modeShortcutSpec(shortcut: string): KeySpec {\n const parts = shortcut.split('+');\n const key = parts.pop();\n if (!key) throw new Error(`mode shortcut \"${shortcut}\" names no key`);\n const mods: ModSpec = {};\n for (const part of parts) {\n const mod = MODIFIERS[part.toLowerCase()];\n if (!mod) throw new Error(`mode shortcut \"${shortcut}\": unknown modifier \"${part}\"`);\n mods[mod] = true;\n }\n return Object.keys(mods).length > 0 ? { kind: 'key', key, mods } : { kind: 'key', key };\n}\n\n/**\n * The registry's declared mode shortcuts as an always-on contribution: one\n * action per shortcut, bound to its chord and gated on the mode it acts on —\n * `exit` / `discard` / `commit` / `cancel` while that mode is active, `entry`\n * while it is not. Install it with `<SceneCanvas ambient>`.\n *\n * An entry key one mode shares with another mode's leaving key is reported as\n * a route conflict unless `canEnter` is given: that is what keeps the entry\n * key from firing inside the other mode.\n *\n * The bindings ride the hotkey tier, so leaving a mode outranks what the same\n * key does otherwise (Escape clearing the selection), and they yield while a\n * gesture is in flight, so Escape mid-drag still cancels the drag first. Keys\n * typed into an editable element never reach them.\n */\nexport function modeShortcuts(\n registry: ModeRegistry,\n handlers: ModeShortcutHandlers,\n): Contribution<never> {\n const actions: Action[] = [];\n for (const mode of registry.list()) {\n const entry = mode.entry?.shortcut;\n const enter = handlers.enter;\n if (entry && enter) {\n // Without `canEnter` an entry key is live in every other mode, so it does not yield there.\n actions.push(shortcutAction(mode, 'entry', entry, { mode: { not: mode.id } },\n () => registry.current().id !== mode.id && (handlers.canEnter?.(mode.id) ?? true),\n () => enter(mode.id), handlers.canEnter !== undefined));\n }\n for (const role of LEAVING) {\n const shortcut = mode[role]?.shortcut;\n const run = handlers[role];\n if (!shortcut || !run) continue;\n actions.push(shortcutAction(mode, role, shortcut, { mode: mode.id },\n () => registry.current().id === mode.id,\n run, true));\n }\n }\n return { id: 'modes.shortcuts', eligibility: { always: true }, actions };\n}\n\nfunction shortcutAction(\n mode: ModeDefinition,\n role: Role | 'entry',\n shortcut: string,\n eligible: Rule,\n live: () => boolean,\n run: () => void,\n yields: boolean,\n): Action {\n return {\n id: `mode.${mode.id}.${role}`,\n label: `${VERB[role]} ${modeLabel(mode)}`,\n scope: 'hotkey',\n defaultBinding: [{\n spec: { ...modeShortcutSpec(shortcut), phase: [{ channel: '*', phase: 'initial' }] },\n opts: { yields },\n }],\n eligible,\n // `eligible` is the gate the dispatcher and palettes read, but a canvas\n // with no `modes` evaluates none; without this the key is\n // swallowed in every mode.\n enabled: () => (live() ? true : ActionDisabledReason.NotApplicable),\n invoker: { timing: 'immediate', run: () => run() },\n };\n}\n","// src/tools/defineTool.ts\nimport type { Tool } from './types';\nimport type { ToolDef } from './routeTypes';\nimport { RESERVED_ID_NAMES, RESERVED_ID_PREFIXES } from '@weasel-js/gestures';\nimport type { CursorSpec } from '@weasel-js/cursor';\nimport type { KernelOverlay } from '../index';\n\n/** Validate that `id` is usable as a tool / channel id in the route\n * grammar. Rejects ids that start with a reserved sigil (would shadow\n * future grammar extensions) and ids that collide with phase keywords\n * (would parse as the bare-phase shorthand). */\nfunction validateId(id: string, kind: 'tool' | 'action'): void {\n if (id.length === 0) {\n throw new Error(`weasel: ${kind} id may not be empty`);\n }\n if (RESERVED_ID_PREFIXES.has(id[0]!)) {\n throw new Error(\n `weasel: ${kind} id \"${id}\" starts with reserved sigil \"${id[0]}\" ` +\n `(reserved set: ${[...RESERVED_ID_PREFIXES].join(' ')})`,\n );\n }\n if (RESERVED_ID_NAMES.has(id)) {\n throw new Error(\n `weasel: ${kind} id \"${id}\" collides with a reserved phase keyword ` +\n `(reserved: ${[...RESERVED_ID_NAMES].join(', ')})`,\n );\n }\n}\n\n/**\n * Build a `Tool` from a declarative `ToolDef`: validates the id, applies the\n * `cursor` default, and keeps the authored form on `Tool.def` for reflection.\n */\nexport function defineTool<TOverlay = KernelOverlay>(\n def: ToolDef<TOverlay>,\n): Tool<TOverlay> {\n validateId(def.id, 'tool');\n\n const resolveCursor = (): CursorSpec => {\n if (def.cursor == null) return '';\n return typeof def.cursor === 'function' ? def.cursor() : def.cursor;\n };\n\n return {\n id: def.id,\n eligibility: {\n focus: true,\n capabilities: def.capabilities,\n ...(def.hotkey ? { offhand: def.hotkey } : {}),\n },\n actions: def.actions,\n def,\n presentation: def.presentation,\n keybinding: def.keybinding,\n onActivate: def.onActivate,\n onDeactivate: def.onDeactivate,\n cursor: resolveCursor,\n bindings: def.bindings,\n overlay: def.overlay,\n overlayPosition: def.overlayPosition,\n };\n}\n","/**\n * Typed keys for the gesture/behavior scratch store, `ctx.scratch`.\n * `ScratchKey<T>` carries its payload type at the typing layer; at runtime\n * the key is just its underlying string and the store remains a plain `Record`.\n *\n * @example\n * ```ts\n * // Module-level constant, shared by writer + reader:\n * const LASSO_VERTICES = scratchKey<readonly { x: number; y: number }[]>('lasso.vertices');\n * setScratch(ctx.scratch, LASSO_VERTICES, vertices);\n * const vertices = getScratch(ctx.scratch, LASSO_VERTICES) ?? [];\n * ```\n */\n\n/** Phantom-typed key for a scratch slot. The `__scratchKeyPayload` phantom\n * field carries the value type without occupying any runtime memory. */\nexport interface ScratchKey<T> {\n readonly name: string;\n /** Phantom — never read at runtime. Only present so TypeScript can\n * recover `T` at use sites. */\n readonly __scratchKeyPayload?: (_: T) => T;\n}\n\n/** Compatible scratch store shape. The kit's gesture context's `scratch`\n * field already satisfies this; consumers don't need to construct one. */\nexport type ScratchStore = Record<string, unknown>;\n\n/**\n * Make a typed scratch key. Two keys with the same `name` refer to the same\n * slot at runtime — the type parameter is a compile-time contract, not a\n * uniqueness guarantee — so namespace the name (`'behavior.field'`).\n */\nexport function scratchKey<T>(name: string): ScratchKey<T> {\n return { name };\n}\n\n/** Read a typed slot from the scratch store. Returns `undefined` if\n * nothing has been written under the key. */\nexport function getScratch<T>(\n store: ScratchStore,\n key: ScratchKey<T>,\n): T | undefined {\n return store[key.name] as T | undefined;\n}\n\n/** Write a value to a typed slot. Overwrites whatever was there. */\nexport function setScratch<T>(\n store: ScratchStore,\n key: ScratchKey<T>,\n value: T,\n): void {\n store[key.name] = value;\n}\n\n/** Remove a slot from the scratch store. Returns `true` if it was set,\n * `false` if it wasn't. */\nexport function deleteScratch<T>(\n store: ScratchStore,\n key: ScratchKey<T>,\n): boolean {\n if (Object.prototype.hasOwnProperty.call(store, key.name)) {\n delete store[key.name];\n return true;\n }\n return false;\n}\n"]}
1
+ {"version":3,"sources":["../src/interactions/actions/actionItems.ts","../src/interactions/dispatcher/predicates.ts","../src/contributions/merge.ts","../src/contributions/modeShortcuts.ts","../src/tools/defineTool.ts","../src/interactions/scratchKey.ts"],"names":["RESERVED_ID_PREFIXES","RESERVED_ID_NAMES"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @experimental\n * The entries an action contributes to a bar, menu or palette — one per\n * variant, or the action itself when it declares none.\n */\nimport type { ReactNode } from 'react';\nimport type { Action } from './action';\n\n/** @experimental One renderable entry: an action, optionally with params. */\nexport interface ActionItem {\n /** `action.id`, or `action.id:variant.key` — unique across a registry. */\n key: string;\n action: Action;\n label: string;\n icon?: ReactNode | (() => ReactNode);\n /** What to pass to `trigger(action.id, params)`; absent for a plain action. */\n params?: Readonly<Record<string, unknown>>;\n}\n\n/** @experimental Expand `action` into the entries a surface renders. An\n * action without an immediate invoker has none: `trigger` cannot start a\n * drag, so a button for one would do nothing. */\nexport function actionItems(action: Action): ActionItem[] {\n if (action.invoker?.timing !== 'immediate') return [];\n if (!action.variants?.length) {\n return [{ key: action.id, action, label: action.label, icon: action.icon }];\n }\n return action.variants.map((v) => ({\n key: `${action.id}:${v.key}`,\n action,\n label: v.label,\n icon: v.icon ?? action.icon,\n params: v.params,\n }));\n}\n","/**\n * Named predicates for `defaultBinding.target.kindOf`. Each matches the\n * `TargetPredicate` shape, declaring whether it reads the hit,\n * so they drop into action descriptors without a wrapper:\n *\n * target: { kindOf: isAnchor }\n *\n * Single source of truth for \"what does this affordance kind mean\" — adding\n * a new predicate here is what enables every action that needs the same\n * shape to read it. Drift prevention.\n */\nimport { bodyPredicate, hitPredicate, type TargetPredicate } from '@weasel-js/gestures';\n\n// ─── Body-class predicates (read the second arg) ───────────────────────\n\n/** A predicate that resolves from `bodyTarget` alone. */\nexport type BodyPredicate = TargetPredicate & { readsAffordance: false };\n\n/** Matches any body hit — selected or unselected node body. */\nexport const isBody: BodyPredicate = bodyPredicate((bodyTarget) =>\n bodyTarget === 'selected-body' || bodyTarget === 'unselected-body');\n\n/** Body hit specifically already in the selection. */\nexport const isSelectedBody: BodyPredicate = bodyPredicate((bodyTarget) =>\n bodyTarget === 'selected-body');\n\n/** Body hit specifically NOT in the selection. */\nexport const isUnselectedBody: BodyPredicate = bodyPredicate((bodyTarget) =>\n bodyTarget === 'unselected-body');\n\n/** Empty-canvas hit — no node beneath cursor. */\nexport const isEmpty: BodyPredicate = bodyPredicate((bodyTarget) =>\n bodyTarget === 'empty');\n\n// ─── Affordance-kind predicates (read the first arg) ───────────────────\n\nfunction kindOf(target: unknown): string | undefined {\n if (typeof target !== 'object' || target === null) return undefined;\n const k = (target as { kind?: unknown }).kind;\n return typeof k === 'string' ? k : undefined;\n}\n\n/** Any corner resize handle — top-left/top-right/bottom-left/bottom-right. */\nexport const isResizeHandle = hitPredicate((target) => {\n const k = kindOf(target);\n return k !== undefined && k.startsWith('handle:');\n});\n\n/** The rotate-around-center handle above the bounding box. */\nexport const isRotateHandle = hitPredicate((target) =>\n kindOf(target) === 'rotate-handle');\n\n/** Any path anchor (the on-curve dot). */\nexport const isAnchor = hitPredicate((target) => {\n const k = kindOf(target);\n return k !== undefined && /^anchor:\\d+$/.test(k);\n});\n\n/** Either control handle (controlIn or controlOut). */\nexport const isControlHandle = hitPredicate((target) => {\n const k = kindOf(target);\n return k !== undefined && /^(controlIn|controlOut):\\d+$/.test(k);\n});\n\n/** Any of the three anchor-editing affordances (anchor + both controls). */\nexport const isAnchorOrControl = hitPredicate((target) =>\n isAnchor(target) || isControlHandle(target));\n\n/** The outline of the path being edited, between its anchors. */\nexport const isSegment = hitPredicate((target) => kindOf(target) === 'segment');\n\n/** Anything path edit puts on the path: an anchor, a control, or a segment. */\nexport const isPathEditPart = hitPredicate((target) =>\n isAnchorOrControl(target) || isSegment(target));\n","import type { Contribution } from './types';\n\n/**\n * Concatenate contribution bundles into one registry, preserving order —\n * entry order decides which of two same-specificity bindings in one scope\n * tier wins, so it is part of the result, not an accident of it.\n *\n * Throws on a duplicate id or dep name rather than dropping one: a feature\n * silently losing its bindings, or reading another feature's dep, is the\n * failure this registry exists to make loud.\n */\nexport function mergeContributions<T extends Contribution<unknown>>(\n ...bundles: readonly (readonly T[])[]\n): T[] {\n const out: T[] = [];\n const seen = new Set<string>();\n const depOwner = new Map<string, string>();\n for (const bundle of bundles) {\n for (const entry of bundle) {\n if (seen.has(entry.id)) {\n throw new Error(\n `mergeContributions: duplicate contribution id \"${entry.id}\". `\n + `Two bundles registered the same id; rename one or merge them by hand.`,\n );\n }\n seen.add(entry.id);\n for (const dep of Object.keys(entry.deps ?? {})) {\n const owner = depOwner.get(dep);\n if (owner !== undefined) {\n throw new Error(\n `mergeContributions: dep \"${dep}\" is provided by both \"${owner}\" and \"${entry.id}\". `\n + `One registry answers each dep name; drop one source.`,\n );\n }\n depOwner.set(dep, entry.id);\n }\n out.push(entry);\n }\n }\n return out;\n}\n","import { modeLabel, type ModeDefinition, type ModeRegistry } from '@weasel-js/modes';\nimport type { KeySpec, ModSpec } from '@weasel-js/gestures';\nimport { ActionDisabledReason, type Action } from '../interactions/actions/action';\nimport type { Rule } from '../eligibility';\nimport type { Contribution } from './types';\n\n/**\n * What each mode shortcut does. A role left out binds nothing, so its key\n * falls through to whatever else the dispatcher has on it.\n */\nexport interface ModeShortcutHandlers {\n /** Enter mode `id` — the `entry` shortcut. */\n enter?: (id: string) => void;\n /** Whether mode `id` can be entered now. A mode that needs a target it\n * doesn't have answers no, and its entry key falls through. Default yes. */\n canEnter?: (id: string) => boolean;\n /** Leave the active soft mode, keeping its session to resume. */\n exit?: () => void;\n /** Leave the active soft mode and throw its session away. */\n discard?: () => void;\n /** Commit the active strict mode's transaction. */\n commit?: () => void;\n /** Abandon the active strict mode's transaction. */\n cancel?: () => void;\n}\n\ntype Role = 'exit' | 'discard' | 'commit' | 'cancel';\nconst LEAVING: readonly Role[] = ['exit', 'discard', 'commit', 'cancel'];\nconst VERB: Record<Role | 'entry', string> = {\n entry: 'Enter', exit: 'Exit', discard: 'Discard', commit: 'Commit', cancel: 'Cancel',\n};\n\nconst MODIFIERS: Record<string, keyof ModSpec> = {\n meta: 'meta', cmd: 'meta', command: 'meta',\n ctrl: 'ctrl', control: 'ctrl',\n alt: 'alt', option: 'alt',\n shift: 'shift',\n mod: 'mod',\n};\n\n/**\n * A `ModeDefinition` shortcut chord — `'Escape'`, `'Meta+T'` — as a key spec.\n * Modifier names are case-insensitive; the last segment is the key.\n */\nexport function modeShortcutSpec(shortcut: string): KeySpec {\n const parts = shortcut.split('+');\n const key = parts.pop();\n if (!key) throw new Error(`mode shortcut \"${shortcut}\" names no key`);\n const mods: ModSpec = {};\n for (const part of parts) {\n const mod = MODIFIERS[part.toLowerCase()];\n if (!mod) throw new Error(`mode shortcut \"${shortcut}\": unknown modifier \"${part}\"`);\n mods[mod] = true;\n }\n return Object.keys(mods).length > 0 ? { kind: 'key', key, mods } : { kind: 'key', key };\n}\n\n/**\n * The registry's declared mode shortcuts as an always-on contribution: one\n * action per shortcut, bound to its chord and gated on the mode it acts on —\n * `exit` / `discard` / `commit` / `cancel` while that mode is active, `entry`\n * while it is not. Install it with `<SceneCanvas ambient>`.\n *\n * An entry key one mode shares with another mode's leaving key is reported as\n * a route conflict unless `canEnter` is given: that is what keeps the entry\n * key from firing inside the other mode.\n *\n * The bindings ride the hotkey tier, so leaving a mode outranks what the same\n * key does otherwise (Escape clearing the selection), and they yield while a\n * gesture is in flight, so Escape mid-drag still cancels the drag first. Keys\n * typed into an editable element never reach them.\n */\nexport function modeShortcuts(\n registry: ModeRegistry,\n handlers: ModeShortcutHandlers,\n): Contribution<never> {\n const actions: Action[] = [];\n for (const mode of registry.list()) {\n const entry = mode.entry?.shortcut;\n const enter = handlers.enter;\n if (entry && enter) {\n // Without `canEnter` an entry key is live in every other mode, so it does not yield there.\n actions.push(shortcutAction(mode, 'entry', entry, { mode: { not: mode.id } },\n () => registry.current().id !== mode.id && (handlers.canEnter?.(mode.id) ?? true),\n () => enter(mode.id), handlers.canEnter !== undefined));\n }\n for (const role of LEAVING) {\n const shortcut = mode[role]?.shortcut;\n const run = handlers[role];\n if (!shortcut || !run) continue;\n actions.push(shortcutAction(mode, role, shortcut, { mode: mode.id },\n () => registry.current().id === mode.id,\n run, true));\n }\n }\n return { id: 'modes.shortcuts', eligibility: { always: true }, actions };\n}\n\nfunction shortcutAction(\n mode: ModeDefinition,\n role: Role | 'entry',\n shortcut: string,\n eligible: Rule,\n live: () => boolean,\n run: () => void,\n yields: boolean,\n): Action {\n return {\n id: `mode.${mode.id}.${role}`,\n label: `${VERB[role]} ${modeLabel(mode)}`,\n scope: 'hotkey',\n defaultBinding: [{\n spec: { ...modeShortcutSpec(shortcut), phase: [{ channel: '*', phase: 'initial' }] },\n opts: { yields },\n }],\n eligible,\n // `eligible` is the gate the dispatcher and palettes read, but a canvas\n // with no `modes` evaluates none; without this the key is\n // swallowed in every mode.\n enabled: () => (live() ? true : ActionDisabledReason.NotApplicable),\n invoker: { timing: 'immediate', run: () => run() },\n };\n}\n","// src/tools/defineTool.ts\nimport type { Tool } from './types';\nimport type { ToolDef } from './routeTypes';\nimport { RESERVED_ID_NAMES, RESERVED_ID_PREFIXES } from '@weasel-js/gestures';\nimport type { CursorSpec } from '@weasel-js/cursor';\nimport type { KernelOverlay } from '../index';\n\n/** Validate that `id` is usable as a tool / channel id in the route\n * grammar. Rejects ids that start with a reserved sigil (would shadow\n * future grammar extensions) and ids that collide with phase keywords\n * (would parse as the bare-phase shorthand). */\nfunction validateId(id: string, kind: 'tool' | 'action'): void {\n if (id.length === 0) {\n throw new Error(`weasel: ${kind} id may not be empty`);\n }\n if (RESERVED_ID_PREFIXES.has(id[0]!)) {\n throw new Error(\n `weasel: ${kind} id \"${id}\" starts with reserved sigil \"${id[0]}\" ` +\n `(reserved set: ${[...RESERVED_ID_PREFIXES].join(' ')})`,\n );\n }\n if (RESERVED_ID_NAMES.has(id)) {\n throw new Error(\n `weasel: ${kind} id \"${id}\" collides with a reserved phase keyword ` +\n `(reserved: ${[...RESERVED_ID_NAMES].join(', ')})`,\n );\n }\n}\n\n/**\n * Build a `Tool` from a declarative `ToolDef`: validates the id, applies the\n * `cursor` default, and keeps the authored form on `Tool.def` for reflection.\n */\nexport function defineTool<TOverlay = KernelOverlay>(\n def: ToolDef<TOverlay>,\n): Tool<TOverlay> {\n validateId(def.id, 'tool');\n\n const resolveCursor = (): CursorSpec => {\n if (def.cursor == null) return '';\n return typeof def.cursor === 'function' ? def.cursor() : def.cursor;\n };\n\n return {\n id: def.id,\n eligibility: {\n focus: true,\n capabilities: def.capabilities,\n ...(def.hotkey ? { offhand: def.hotkey } : {}),\n },\n actions: def.actions,\n def,\n presentation: def.presentation,\n keybinding: def.keybinding,\n onActivate: def.onActivate,\n onDeactivate: def.onDeactivate,\n cursor: resolveCursor,\n bindings: def.bindings,\n overlay: def.overlay,\n overlayPosition: def.overlayPosition,\n };\n}\n","/**\n * Typed keys for the gesture/behavior scratch store, `ctx.scratch`.\n * `ScratchKey<T>` carries its payload type at the typing layer; at runtime\n * the key is just its underlying string and the store remains a plain `Record`.\n *\n * @example\n * ```ts\n * // Module-level constant, shared by writer + reader:\n * const LASSO_VERTICES = scratchKey<readonly { x: number; y: number }[]>('lasso.vertices');\n * setScratch(ctx.scratch, LASSO_VERTICES, vertices);\n * const vertices = getScratch(ctx.scratch, LASSO_VERTICES) ?? [];\n * ```\n */\n\n/** Phantom-typed key for a scratch slot. The `__scratchKeyPayload` phantom\n * field carries the value type without occupying any runtime memory. */\nexport interface ScratchKey<T> {\n readonly name: string;\n /** Phantom — never read at runtime. Only present so TypeScript can\n * recover `T` at use sites. */\n readonly __scratchKeyPayload?: (_: T) => T;\n}\n\n/** Compatible scratch store shape. The kit's gesture context's `scratch`\n * field already satisfies this; consumers don't need to construct one. */\nexport type ScratchStore = Record<string, unknown>;\n\n/**\n * Make a typed scratch key. Two keys with the same `name` refer to the same\n * slot at runtime — the type parameter is a compile-time contract, not a\n * uniqueness guarantee — so namespace the name (`'behavior.field'`).\n */\nexport function scratchKey<T>(name: string): ScratchKey<T> {\n return { name };\n}\n\n/** Read a typed slot from the scratch store. Returns `undefined` if\n * nothing has been written under the key. */\nexport function getScratch<T>(\n store: ScratchStore,\n key: ScratchKey<T>,\n): T | undefined {\n return store[key.name] as T | undefined;\n}\n\n/** Write a value to a typed slot. Overwrites whatever was there. */\nexport function setScratch<T>(\n store: ScratchStore,\n key: ScratchKey<T>,\n value: T,\n): void {\n store[key.name] = value;\n}\n\n/** Remove a slot from the scratch store. Returns `true` if it was set,\n * `false` if it wasn't. */\nexport function deleteScratch<T>(\n store: ScratchStore,\n key: ScratchKey<T>,\n): boolean {\n if (Object.prototype.hasOwnProperty.call(store, key.name)) {\n delete store[key.name];\n return true;\n }\n return false;\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { k as Dispatcher, A as ActionsRegistry, C as Contribution, l as AffordanceHit, L as LongPressOptions, R as RuleCtx, b as DepSchema } from './index-Bm2QEV09.js';
1
+ import { k as Dispatcher, A as ActionsRegistry, C as Contribution, l as AffordanceHit, L as LongPressOptions, R as RuleCtx, b as DepSchema } from './index-DkOqUxow.js';
2
2
  import { RefObject } from 'react';
3
3
  import { BodyClassification } from '@weasel-js/gestures';
4
4
  import { PaintedCursorState, CursorSink } from '@weasel-js/cursor';
@@ -1,9 +1,9 @@
1
- export { a as DispatcherViewTarget, V as ViewIdResolver } from './internal-D0rDmi7t.js';
2
- import './index-Bm2QEV09.js';
1
+ export { a as DispatcherViewTarget, V as ViewIdResolver } from './internal-Boc2I3zn.js';
2
+ import './index-DkOqUxow.js';
3
3
  import '@weasel-js/geom';
4
4
  import 'react';
5
5
  import '@weasel-js/registry';
6
+ import '@weasel-js/gestures';
6
7
  import '@weasel-js/modes';
7
8
  import '@weasel-js/cursor';
8
- import '@weasel-js/gestures';
9
9
  import '@weasel-js/select';
package/dist/react.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { U as UseGestureDispatcherOptions } from './internal-D0rDmi7t.js';
2
- export { D as DispatcherChannels } from './internal-D0rDmi7t.js';
1
+ import { U as UseGestureDispatcherOptions } from './internal-Boc2I3zn.js';
2
+ export { D as DispatcherChannels } from './internal-Boc2I3zn.js';
3
3
  import * as react from 'react';
4
4
  import { ReactNode, ReactElement } from 'react';
5
- import { D as DepRegistry, a as DepName, b as DepSchema, S as ScopeNode, A as ActionsRegistry, c as Action, d as DepNode, T as ToolNode, N as NodeId, K as KernelOverlay, C as Contribution, e as Tool, O as OverlayPosition } from './index-Bm2QEV09.js';
6
- export { f as ActiveToolContextProvider, g as ActiveToolContextProviderIfRoot, h as ActiveToolContextProviderProps, i as ActiveToolContextValue, u as useActiveToolContext, j as useOptionalActiveToolContext } from './index-Bm2QEV09.js';
5
+ import { D as DepRegistry, a as DepName, b as DepSchema, S as ScopeNode, A as ActionsRegistry, c as Action, d as DepNode, T as ToolNode, N as NodeId, K as KernelOverlay, C as Contribution, e as Tool, O as OverlayPosition } from './index-DkOqUxow.js';
6
+ export { f as ActiveToolContextProvider, g as ActiveToolContextProviderIfRoot, h as ActiveToolContextProviderProps, i as ActiveToolContextValue, u as useActiveToolContext, j as useOptionalActiveToolContext } from './index-DkOqUxow.js';
7
7
  import { ModeRegistry } from '@weasel-js/modes';
8
8
  import '@weasel-js/gestures';
9
9
  import '@weasel-js/cursor';
@@ -49,12 +49,15 @@ declare class ActionsNode extends ScopeNode<ActionsNode> {
49
49
  private readonly contextDeps;
50
50
  private readonly actions;
51
51
  private readonly muted;
52
+ private readonly tunings;
52
53
  private readonly dispatchers;
53
54
  private cache;
54
55
  /** Called on `activate`, so a scope's dep and tool nodes move with it. */
55
56
  readonly onActivate: (() => void)[];
56
57
  constructor(parent: ActionsNode | null, descends: boolean, contextDeps: () => DepRegistry | null);
57
58
  private resolve;
59
+ /** `a` under every tuning for its id from the root down to here. */
60
+ private tuned;
58
61
  private snapshot;
59
62
  private deps;
60
63
  private dispatcher;
@@ -174,6 +177,7 @@ interface OngoingAction {
174
177
  *
175
178
  * An edit left open when the control unmounts, or when `actionId` changes, is
176
179
  * committed — it is what the user was looking at when the control went away.
180
+ * On unmount that happens a microtask later.
177
181
  * The returned object is stable for the life of the component.
178
182
  */
179
183
  declare function useOngoingAction(actionId: string): OngoingAction;