@weasel-js/routing 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
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 ActionPresentation, p as ActionSource, q as ActionsProp, A as ActionsRegistry, a as AffordanceHit, r as AnyTool, d as AnyToolOf, B as BindingOpts, s as BindingScope, t as BindingSource, v as BodyPredicate, w as BoundGesture, x as Bounds, y as BuildRuleCtxArgs, z as COARSE_TARGET_SCALE, E as Condition, F as Conflict, C as Contribution, G as ContributionChrome, H as ContributionRouting, I as DEFAULT_ALLOWED_CAPABILITIES, J as DEFAULT_DEVICE_PROFILE, K as DebugSink, L as DepName, b as DepSchema, M as DetectedDeviceFacts, N as DeviceProfile, P as DispatchLogEntry, D as Dispatcher, Q as DispatcherContext, U as DragSample, V as Eligibility, W as EligibilityState, X as GestureBinding, Y as HandleKind, Z as HitShape, _ as HotkeyTrigger, $ as INGEST_STRING_MIMES, a0 as ImmediateInvoker, a1 as InvocationCtx, a2 as Invoker, a3 as KeyBinding, a4 as MatchResult, a5 as ModeSwitchLogEntry, a6 as ModifierState, a7 as NEVER, a8 as NodeId, a9 as OngoingHandle, aa as OngoingInvoker, ab as OngoingOverlay, O as OverlayPosition, ac as OverlayRole, ad as PREDICATE_TARGET, ae as Point2, af as PointerSession, ag as PointerSessionCallbacks, ah as PointerSessionCancelReason, ai as PointerSessionOptions, aj as RegistryEntry, ak as ResizeAnchor, al as ResolveAllOptions, am as ResolveOnlyResult, an as ResolvedCandidate, ao as Rule, R as RuleCtx, S as ScopedBinding, ap as ScratchKey, aq as ScratchStore, ar as SelectionApi, as as SelectionExtendKey, at as SelectionMode, au as Selector, av as TOOL_OFFHAND_ID, T as Tool, aw as ToolBounds, ax as ToolCtx, ay as ToolDef, az as ToolKeybinding, aA as ToolModifiers, aB as ToolOffhandBindingSpec, aC as ToolPresentation, aD as ToolSlot, aE as TraceLogEntry, aF as UiOngoingControl, aG as View, aH as ViewportToolDef, aI as actionBindings, aJ as buildDepsFromRequires, aK as buildRouteRegistry, aL as buildRuleCtx, aM as buildToolOffhandBindings, aN as clientToCanvas, aO as clientToCanvasRect, aP as createDispatcher, aQ as defineTool, aR as defineViewportTool, aS as deleteScratch, aT as describeRule, aU as dlog, aV as evaluate, aW as evaluateEnabled, aX as filterEligible, aY as findConflicts, aZ as getScratch, a_ as isAnchor, a$ as isAnchorOrControl, b0 as isBody, b1 as isControlHandle, b2 as isEditableTarget, b3 as isEmpty, b4 as isResizeHandle, b5 as isRotateHandle, b6 as isSelectedBody, b7 as isUnselectedBody, b8 as itemsFromClipboardData, b9 as itemsFromDataTransfer, ba as itemsFromFiles, bb as liveScope, bc as makeToolOffhandAction, bd as matchBest, be as matchSorted, bf as matchesKeyBinding, bg as mergeContributions, bh as offhandKeyFor, bi as openPointerSession, bj as pointerGestureId, bk as recordModeSwitch, bl as reportRouteConflicts, bm as resolveDeviceProfile, bn as resolveParams, bo as routeGestureForSpecKind, bp as routesForSpec, bq as scopeBindings, br as scratchKey, bs as setScratch, bt as specificity, bu as targetConsultsAffordance, bv as validateActionDefaultBinding, bw as validateActionId } from './index-BOlkWrA-.js';
2
+ export { ChannelRef, 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 } from '@weasel-js/gestures';
3
+ import '@weasel-js/modes';
4
+ import 'react';
5
+ import '@weasel-js/cursor';
6
+ import 'react/jsx-runtime';
7
+ import '@weasel-js/history';
package/dist/index.js ADDED
@@ -0,0 +1,160 @@
1
+ import { isDev } from './chunk-Y5FHAA4U.js';
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, createDispatcher, describeRoute, describeRouteParts, describeRule, dlog, evaluate, filterEligible, findConflicts, formatPhaseAtom, formatRoute, getGestureDescriptor, isEditableTarget, isKnownGestureName, itemsFromClipboardData, itemsFromDataTransfer, itemsFromFiles, liveScope, makeToolOffhandAction, matchBest, matchKey, matchModifiers, matchPhase, matchSorted, matchSpec, matchTarget, matchesKeyBinding, offhandKeyFor, openPointerSession, parseRoute, pointerGestureId, recordModeSwitch, reportRouteConflicts, resolveDeviceProfile, resolveParams, routeGestureForSpecKind, routesForSpec, scopeBindings, specificity, targetConsultsAffordance, validateActionDefaultBinding, validateActionId } from './chunk-Y5FHAA4U.js';
3
+ import { RESERVED_ID_PREFIXES, RESERVED_ID_NAMES } from '@weasel-js/gestures';
4
+
5
+ // src/interactions/actions/action.ts
6
+ var ActionDisabledReason = {
7
+ SelectionRequired: "selection-required",
8
+ SceneEmpty: "scene-empty",
9
+ NotApplicable: "not-applicable",
10
+ /** Sentinel: the predicate threw. Surfaced by `evaluateEnabled`'s catch. */
11
+ PredicateThrew: "predicate-threw"
12
+ };
13
+
14
+ // src/interactions/actions/actionEnabled.ts
15
+ var enabledSlowWarned = /* @__PURE__ */ new Set();
16
+ var enabledThrewWarned = /* @__PURE__ */ new Set();
17
+ var ENABLED_BUDGET_MS = 4;
18
+ function evaluateEnabled(action, deps) {
19
+ if (!action.enabled) return { enabled: true };
20
+ const dev = isDev();
21
+ const now = typeof performance !== "undefined" && typeof performance.now === "function" ? () => performance.now() : () => Date.now();
22
+ const start = dev ? now() : 0;
23
+ try {
24
+ const result = action.enabled(deps);
25
+ if (dev) {
26
+ const elapsed = now() - start;
27
+ if (elapsed > ENABLED_BUDGET_MS && !enabledSlowWarned.has(action.id)) {
28
+ enabledSlowWarned.add(action.id);
29
+ console.warn(
30
+ `weasel actions: enabled() for action "${action.id}" took ${elapsed.toFixed(2)}ms (budget ${ENABLED_BUDGET_MS}ms). The predicate must be pure and fast \u2014 see Action.enabled JSDoc.`
31
+ );
32
+ }
33
+ }
34
+ if (result === true) return { enabled: true };
35
+ return { enabled: false, reason: result };
36
+ } catch (e) {
37
+ if (dev && !enabledThrewWarned.has(action.id)) {
38
+ enabledThrewWarned.add(action.id);
39
+ console.warn(
40
+ `weasel actions: enabled() for action "${action.id}" threw; treating as disabled. The predicate must not throw \u2014 see Action.enabled JSDoc.`,
41
+ e
42
+ );
43
+ }
44
+ return { enabled: false, reason: ActionDisabledReason.PredicateThrew };
45
+ }
46
+ }
47
+
48
+ // src/interactions/dispatcher/predicates.ts
49
+ function bodyPredicate(fn) {
50
+ return Object.assign(fn, { readsAffordance: false });
51
+ }
52
+ var isBody = bodyPredicate((_target, bodyTarget) => bodyTarget === "selected-body" || bodyTarget === "unselected-body");
53
+ var isSelectedBody = bodyPredicate((_target, bodyTarget) => bodyTarget === "selected-body");
54
+ var isUnselectedBody = bodyPredicate((_target, bodyTarget) => bodyTarget === "unselected-body");
55
+ var isEmpty = bodyPredicate((_target, bodyTarget) => bodyTarget === "empty");
56
+ function kindOf(target) {
57
+ if (typeof target !== "object" || target === null) return void 0;
58
+ const k = target.kind;
59
+ return typeof k === "string" ? k : void 0;
60
+ }
61
+ var isResizeHandle = (target) => {
62
+ const k = kindOf(target);
63
+ return k !== void 0 && k.startsWith("handle:");
64
+ };
65
+ var isRotateHandle = (target) => kindOf(target) === "rotate-handle";
66
+ var isAnchor = (target) => {
67
+ const k = kindOf(target);
68
+ return k !== void 0 && /^anchor:\d+$/.test(k);
69
+ };
70
+ var isControlHandle = (target) => {
71
+ const k = kindOf(target);
72
+ return k !== void 0 && /^(controlIn|controlOut):\d+$/.test(k);
73
+ };
74
+ var isAnchorOrControl = (target) => isAnchor(target) || isControlHandle(target);
75
+
76
+ // src/contributions/merge.ts
77
+ function mergeContributions(...bundles) {
78
+ const out = [];
79
+ const seen = /* @__PURE__ */ new Set();
80
+ for (const bundle of bundles) {
81
+ for (const entry of bundle) {
82
+ if (seen.has(entry.id)) {
83
+ throw new Error(
84
+ `mergeContributions: duplicate contribution id "${entry.id}". Two bundles registered the same id; rename one or merge them by hand.`
85
+ );
86
+ }
87
+ seen.add(entry.id);
88
+ out.push(entry);
89
+ }
90
+ }
91
+ return out;
92
+ }
93
+ function validateId(id, kind) {
94
+ if (id.length === 0) {
95
+ throw new Error(`weasel: ${kind} id may not be empty`);
96
+ }
97
+ if (RESERVED_ID_PREFIXES.has(id[0])) {
98
+ throw new Error(
99
+ `weasel: ${kind} id "${id}" starts with reserved sigil "${id[0]}" (reserved set: ${[...RESERVED_ID_PREFIXES].join(" ")})`
100
+ );
101
+ }
102
+ if (RESERVED_ID_NAMES.has(id)) {
103
+ throw new Error(
104
+ `weasel: ${kind} id "${id}" collides with a reserved phase keyword (reserved: ${[...RESERVED_ID_NAMES].join(", ")})`
105
+ );
106
+ }
107
+ }
108
+ function defineTool(def) {
109
+ validateId(def.id, "tool");
110
+ const resolveCursor = (ctx) => {
111
+ if (def.cursor == null) return "";
112
+ return typeof def.cursor === "function" ? def.cursor(ctx) : def.cursor;
113
+ };
114
+ return {
115
+ id: def.id,
116
+ eligibility: {
117
+ focus: true,
118
+ capabilities: def.capabilities,
119
+ ...def.hotkey ? { offhand: def.hotkey } : {}
120
+ },
121
+ actions: def.actions,
122
+ def,
123
+ presentation: def.presentation,
124
+ keybinding: def.keybinding,
125
+ onActivate: def.onActivate,
126
+ onDeactivate: def.onDeactivate,
127
+ initScratch: def.initScratch ?? (() => null),
128
+ cursor: resolveCursor,
129
+ bindings: def.bindings,
130
+ overlay: def.overlay,
131
+ overlayPosition: def.overlayPosition
132
+ };
133
+ }
134
+
135
+ // src/tools/defineViewportTool.ts
136
+ function defineViewportTool(def) {
137
+ return defineTool(def);
138
+ }
139
+
140
+ // src/interactions/scratchKey.ts
141
+ function scratchKey(name) {
142
+ return { name };
143
+ }
144
+ function getScratch(store, key) {
145
+ return store[key.name];
146
+ }
147
+ function setScratch(store, key, value) {
148
+ store[key.name] = value;
149
+ }
150
+ function deleteScratch(store, key) {
151
+ if (Object.prototype.hasOwnProperty.call(store, key.name)) {
152
+ delete store[key.name];
153
+ return true;
154
+ }
155
+ return false;
156
+ }
157
+
158
+ export { ActionDisabledReason, defineTool, defineViewportTool, deleteScratch, evaluateEnabled, getScratch, isAnchor, isAnchorOrControl, isBody, isControlHandle, isEmpty, isResizeHandle, isRotateHandle, isSelectedBody, isUnselectedBody, mergeContributions, scratchKey, setScratch };
159
+ //# sourceMappingURL=index.js.map
160
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/interactions/actions/action.ts","../src/interactions/actions/actionEnabled.ts","../src/interactions/dispatcher/predicates.ts","../src/contributions/merge.ts","../src/tools/defineTool.ts","../src/tools/defineViewportTool.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 `Action` descriptor, split along the line the dispatcher reads.\n * `ActionDispatch` is everything routing consults to decide whether an action\n * runs and what happens when it does; `ActionPresentation` is what a palette,\n * menu or toolbar renders. `Action` is both.\n */\nimport type { ReactNode } from 'react';\nimport type { CursorSpec } from '@weasel-js/cursor';\nimport type { ActionDeps, Invoker } from './invoker';\nimport type { BindingSource } from './binding';\nimport type { DepName } from '../../index';\n\n/**\n * @experimental\n * Closed enum of reasons an action might report itself as disabled. The\n * consumer (palette, menu, etc.) maps these symbolic values to display\n * strings via its own label map.\n */\nexport const ActionDisabledReason = {\n SelectionRequired: 'selection-required',\n SceneEmpty: 'scene-empty',\n NotApplicable: 'not-applicable',\n /** Sentinel: the predicate threw. Surfaced by `evaluateEnabled`'s catch. */\n PredicateThrew: 'predicate-threw',\n} as const;\n/** Why an action is unavailable right now. */\nexport type ActionDisabledReason =\n (typeof ActionDisabledReason)[keyof typeof ActionDisabledReason];\n\n/**\n * @experimental\n * What a palette, menu or toolbar renders for an action. No part of this is\n * read during dispatch.\n */\nexport interface ActionPresentation {\n label: string;\n /** Inline-SVG icon for palette / toolbar surfaces. Mirrors\n * `ToolPresentation.icon` so a generic `<ActionBar>` can render from\n * action metadata the same way `<ToolPalette>` renders from tool\n * metadata. May be a static `ReactNode` or a function (rare; useful\n * for state-aware icons like a \"lock\" toggle). */\n icon?: ReactNode | (() => ReactNode);\n /** Grouping key for palette/menu surfaces. Free-form string; the kit\n * ships defaults for `'align'` (six edges/centers), `'distribute'`\n * (two axes), and recommends `'pathfinder'` for boolean ops. */\n group?: string;\n /** Display override for the keyboard shortcut. When omitted, palette\n * surfaces derive a label from `defaultBinding` via their own\n * formatter. */\n shortcut?: string;\n}\n\n/**\n * @experimental\n * What routing consults: the bindings that reach the action, the deps its\n * invoker reads, the gates it passes, and the cursors it claims.\n */\nexport interface ActionDispatch extends BindingSource {\n /** Names of the deps this action's invoker reads (keys of `DepSchema`).\n * The dispatcher (and `trigger`, when `requires` is present) resolves\n * each name against the `DepRegistry` at invocation time and passes the\n * resulting bag to the invoker. Dev builds warn when the invoker reads a\n * dep it didn't declare here — see `buildDepsFromRequires`. */\n requires?: readonly DepName[];\n /** Pluggable invocation strategy. The gesture dispatcher routes matched\n * bindings through `invoker.start` / `invoker.run` depending on timing.\n * All kit-standard descriptors ship one; consumer-supplied actions\n * without an invoker can still register but won't be triggered. */\n invoker?: Invoker;\n /** When set to `'hotkey'`, this action's `defaultBinding` rides the hotkey\n * `BindingScope` instead of the ambient scope — meaning it beats any\n * active-tool binding on the same input shape. Use for tool-switch\n * shortcuts and global held-key triggers. Default: ambient. */\n scope?: 'hotkey';\n /**\n * @experimental\n * Optional predicate the command palette consults when rendering. Return\n * `true` when the action is currently triggerable. Return a reason string\n * (e.g. `'Selection required'`) when disabled — the palette greys out\n * the row, skips it in keyboard nav, ignores clicks, and shows the\n * reason next to the label. Keystroke dispatch (the registered binding)\n * is unaffected; the action's own `run` should self-guard.\n *\n * **Contract:** must be pure (no side effects), fast (< 4ms in dev), and\n * must not throw. If a call throws or exceeds the budget in dev mode,\n * `evaluateEnabled` logs a one-time warning per action id; throws are\n * caught and treated as disabled with reason `'(predicate threw)'`.\n *\n * Snapshot-on-open semantics: the palette evaluates `enabled` once when\n * opened and does NOT re-evaluate on selection changes while open. Live\n * reactive updates are deferred — palette is short-lived.\n *\n * The reason set is a closed enum — to add a new reason, edit\n * `ActionDisabledReason` and the consumer's display map.\n *\n * The optional `deps` argument is the same bag passed to\n * `ImmediateInvoker.run`; callers (`evaluateEnabled` / the ActionBar) may\n * synthesize it from the surrounding `DepRegistry` so predicates can\n * inspect selection / scene / etc. Predicates that don't need deps just\n * ignore the arg.\n */\n enabled?: (deps?: ActionDeps) => true | ActionDisabledReason;\n /**\n * Declarative eligibility rule, evaluated against the current\n * `RuleCtx` by the dispatcher before invoking `start()`. Omitted =\n * always eligible.\n *\n * Accepts either a fluent `Condition` (callable with `.rule`) or a\n * raw `Rule` tree; the dispatcher normalizes via `.rule` unwrap.\n *\n * Prefer `capability:`-based rules (e.g. `{ capability: 'transforms-selection' }`)\n * over `mode:` rules — capability rules survive new modes being added\n * that allow the same capability.\n */\n eligible?:\n | import('../../eligibility').Rule\n | import('../../eligibility').Condition;\n /**\n * CSS cursor shown while the pointer hovers a spot where this action\n * would win the drag. The hover-cursor pump (in `useGestureDispatcher`)\n * runs `Dispatcher.resolveOnly` on each idle pointermove — the same\n * match walk a real pointerdown takes — and applies the winning\n * action's `cursor`, so the hint and the actual click target stay in\n * sync by construction. Omitted = no override (the active tool's\n * `Tool.cursor` shows). Affordance hits are resolved earlier in the\n * pump via `AffordanceRegion.cursor` and never reach this field.\n *\n * Static value only. Prediction runs `enabled()` but cannot run the\n * invoker, so an action that matches yet bails at `start()` (empty\n * handle) may still show its cursor — keep `enabled` accurate for\n * actions that declare one.\n */\n cursor?: CursorSpec;\n /**\n * CSS cursor shown while THIS action's ongoing handle is in flight —\n * grabbing while panning, `move` while dragging a selection, `crosshair`\n * while pulling a marquee.\n *\n * Separate from `cursor` because the two answer different questions:\n * `cursor` is a prediction (\"a drag from here would pan\"), this is a state\n * (\"you are panning\"). An action can declare either, both, or neither;\n * with only `cursor` set, the hover hint holds for the duration of the\n * gesture.\n *\n * This is where mid-gesture cursors live now. They used to come from the\n * tool side — `ViewportToolDef.engaged.cursor` for a phase-gated string,\n * or a function-form `Tool.cursor` reading the gesture scratch out of the\n * tool-routing dispatcher. Both belonged to a pipeline whose whole job was\n * being taken over by bindings, and neither could describe a cursor for an\n * action a tool doesn't own.\n */\n activeCursor?: CursorSpec;\n}\n\n/**\n * @experimental\n * Single registered action. v1: one binding per action.\n */\nexport interface Action extends ActionDispatch, ActionPresentation {}\n\n/**\n * @experimental\n * The whole of an actions registry the dispatcher reads. It walks the list\n * once per event to build its actionId lookup and never mutates it, so the\n * routing side depends on this rather than on `ActionsRegistry`, whose\n * register / mute / trigger surface is the React provider's.\n */\nexport interface ActionSource {\n list(): readonly Action[];\n}\n","/**\n * @experimental\n * Palette-side evaluation of `Action.enabled`. The dispatcher calls the\n * predicate raw as a gate; this is the surface that also enforces the\n * contract the predicate's JSDoc states — no throwing, under 4ms in dev.\n */\nimport type { ActionDeps } from './invoker';\nimport { ActionDisabledReason, type Action } from './action';\nimport { isDev } from '../../devFlag';\n\n/**\n * @experimental\n * Result of evaluating an Action's `enabled` predicate.\n */\nexport interface ActionEnabledResult {\n enabled: boolean;\n reason?: ActionDisabledReason;\n}\n\nconst enabledSlowWarned = new Set<string>();\nconst enabledThrewWarned = new Set<string>();\nconst ENABLED_BUDGET_MS = 4;\n/**\n * @experimental\n * Safely evaluate an action's `enabled` predicate. Returns `{enabled: true}`\n * when no predicate is supplied. Catches throws and treats them as disabled\n * with reason `'(predicate threw)'`. In dev mode, warns once per action id\n * when a single call exceeds 4ms (single frame at 240fps — generous; real\n * predicates should be sub-millisecond).\n */\nexport function evaluateEnabled(\n action: Action,\n deps?: ActionDeps,\n): ActionEnabledResult {\n if (!action.enabled) return { enabled: true };\n const dev = isDev();\n const now = (typeof performance !== 'undefined' && typeof performance.now === 'function')\n ? () => performance.now()\n : () => Date.now();\n const start = dev ? now() : 0;\n try {\n const result = action.enabled(deps);\n if (dev) {\n const elapsed = now() - start;\n if (elapsed > ENABLED_BUDGET_MS && !enabledSlowWarned.has(action.id)) {\n enabledSlowWarned.add(action.id);\n console.warn(\n `weasel actions: enabled() for action \"${action.id}\" took ${elapsed.toFixed(2)}ms ` +\n `(budget ${ENABLED_BUDGET_MS}ms). The predicate must be pure and fast — see Action.enabled JSDoc.`,\n );\n }\n }\n if (result === true) return { enabled: true };\n return { enabled: false, reason: result };\n } catch (e) {\n if (dev && !enabledThrewWarned.has(action.id)) {\n enabledThrewWarned.add(action.id);\n console.warn(\n `weasel actions: enabled() for action \"${action.id}\" threw; treating as disabled. ` +\n `The predicate must not throw — see Action.enabled JSDoc.`,\n e,\n );\n }\n return { enabled: false, reason: ActionDisabledReason.PredicateThrew };\n }\n}\n","/**\n * Named predicates for `defaultBinding.target.kindOf`. Each matches the\n * existing `kindOf` signature `(target: unknown, bodyTarget?: string) => boolean`\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 */\n\n// ─── Body-class predicates (read the second arg) ───────────────────────\n\n/** A predicate that resolves from `bodyTarget` alone. The exclusive-claim\n * filter reads `readsAffordance`: without it a body predicate looks like it\n * consults the hit, and survives a claim that should have barred it. */\nexport type BodyPredicate = ((target: unknown, bodyTarget?: string) => boolean)\n & { readsAffordance: false };\n\nfunction bodyPredicate(\n fn: (target: unknown, bodyTarget?: string) => boolean,\n): BodyPredicate {\n return Object.assign(fn, { readsAffordance: false as const });\n}\n\n/** Matches any body hit — selected or unselected node body. */\nexport const isBody: BodyPredicate = bodyPredicate((_target, bodyTarget) =>\n bodyTarget === 'selected-body' || bodyTarget === 'unselected-body');\n\n/** Body hit specifically already in the selection. */\nexport const isSelectedBody: BodyPredicate = bodyPredicate((_target, bodyTarget) =>\n bodyTarget === 'selected-body');\n\n/** Body hit specifically NOT in the selection. */\nexport const isUnselectedBody: BodyPredicate = bodyPredicate((_target, bodyTarget) =>\n bodyTarget === 'unselected-body');\n\n/** Empty-canvas hit — no node beneath cursor. */\nexport const isEmpty: BodyPredicate = bodyPredicate((_target, 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 = (target: unknown): boolean => {\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 = (target: unknown): boolean =>\n kindOf(target) === 'rotate-handle';\n\n/** Any path anchor (the on-curve dot). */\nexport const isAnchor = (target: unknown): boolean => {\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 = (target: unknown): boolean => {\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 = (target: unknown): boolean =>\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 rather than dropping one: a feature silently\n * losing its bindings is the failure this registry exists to make loud.\n */\nexport function mergeContributions(...bundles: readonly Contribution[][]): Contribution[] {\n const out: Contribution[] = [];\n const seen = new Set<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 out.push(entry);\n }\n }\n return out;\n}\n","// src/tools/defineTool.ts\nimport type { Tool, ToolCtx } 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';\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<TScratch>` from a declarative `ToolDef<TScratch>`.\n *\n * This used to be a translator: `ToolDef` carried `initial` / `engaged` phase\n * tables of hit-keyed route handlers, and `defineTool` compiled them into the\n * imperative `pointer` / `drag` / `keyboard` / `wheel` handlers the\n * tool-routing dispatcher called. Both ends of that are gone — tools declare\n * `bindings` and the gesture dispatcher routes them — so what remains is\n * identity plumbing plus the id check and the cursor resolver.\n *\n * It stays a function rather than becoming a spread because the id validation\n * and the `initScratch` / `cursor` defaults are worth applying uniformly, and\n * because `Tool.def` gives reflection a handle on the authored form.\n */\nexport function defineTool<TScratch = void, TOverlay = unknown>(\n def: ToolDef<TScratch, TOverlay>,\n): Tool<TScratch, TOverlay> {\n validateId(def.id, 'tool');\n\n // Normalize `CursorSpec | ((ctx) => CursorSpec)` to the function form so\n // callers have one shape to deal with. Returns '' (not undefined) so the\n // signature satisfies `Tool.cursor: (ctx) => CursorSpec`.\n const resolveCursor = (ctx: ToolCtx<TScratch>): CursorSpec => {\n if (def.cursor == null) return '';\n return typeof def.cursor === 'function' ? def.cursor(ctx) : 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 as Tool<TScratch>['presentation'],\n keybinding: def.keybinding,\n onActivate: def.onActivate,\n onDeactivate: def.onDeactivate,\n initScratch: def.initScratch ?? (() => null as unknown as TScratch),\n cursor: resolveCursor,\n bindings: def.bindings,\n overlay: def.overlay,\n overlayPosition: def.overlayPosition,\n };\n}\n","import type { Tool } from './types';\nimport type { ViewportToolDef } from './routeTypes';\nimport { defineTool } from './defineTool';\n\n/**\n * Define a tool that acts on the viewport rather than the scene.\n *\n * This used to do real work: `ViewportPhaseDef` was a narrowed `PhaseDef`\n * (no click routes, drag restricted to the function form), and the factory\n * lifted it back to the permissive shape before handing it to `defineTool`.\n * With phase tables gone there is no shape left to narrow — a viewport tool\n * declares `bindings` like any other, pointing at `viewport.*` actions.\n *\n * It survives as an authoring signal. `defineViewportTool` at the top of a\n * hook says \"this tool moves the camera, not the drawing\", which is worth\n * more than the type gymnastics it replaced.\n */\nexport function defineViewportTool<TScratch = void, TOverlay = unknown>(\n def: ViewportToolDef<TScratch, TOverlay>,\n): Tool<TScratch, TOverlay> {\n return defineTool<TScratch, TOverlay>(def);\n}\n","/**\n * Typed keys for the gesture/behavior scratch store.\n *\n * Gestures expose `ctx.scratch: Record<string, unknown>` as a per-gesture\n * mutable bag that behaviors use to stash state across `onDown` / `onMove`\n * / `onEnd` calls. The store is `unknown`-valued because behaviors are\n * tool-agnostic — a single behavior may run under different tools with\n * different scratch shapes. Without a typing convention every read site\n * has to `(ctx.scratch as <some shape>)[KEY]`, and a writer changing the\n * shape can silently break readers with no compile-time signal.\n *\n * `ScratchKey<T>` is a phantom-typed string that carries its payload type\n * via a branded interface. `getScratch` and `setScratch` use the brand to\n * narrow / constrain at the typing layer; at runtime the key is just its\n * underlying string and the store remains a plain `Record`.\n *\n * @example\n * ```ts\n * // Module-level (or behavior-level) constant, shared by writer + reader:\n * const LASSO_VERTICES = scratchKey<readonly { x: number; y: number }[]>('lasso.vertices');\n *\n * // Writer (a tool's onMove):\n * setScratch(ctx.scratch, LASSO_VERTICES, vertices);\n *\n * // Reader (a behavior's onEnd):\n * const vertices = getScratch(ctx.scratch, LASSO_VERTICES) ?? [];\n * // ^? readonly { x: number; y: number }[] | undefined\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. Use module-level constants so the same key\n * is shared by writer and reader.\n *\n * Namespace the name by behavior or feature to avoid collisions\n * (`'behavior.field'` is a good convention). Two keys with the same\n * `name` refer to the same slot at runtime — the type parameter is a\n * compile-time contract, not a uniqueness guarantee.\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"]}
@@ -0,0 +1,405 @@
1
+ import { D as Dispatcher, A as ActionsRegistry, T as Tool, a as AffordanceHit, R as RuleCtx, b as DepSchema, c as Action, d as AnyToolOf, O as OverlayPosition, C as Contribution, S as ScopedBinding } from './index-BOlkWrA-.js';
2
+ export { e as DepRegistry, f as DepRegistryProvider, u as useDepRegistry, g as useDepSource, h as useOptionalDepRegistry } from './index-BOlkWrA-.js';
3
+ import { RefObject, ReactNode, ReactElement } from 'react';
4
+ import { BodyClassification } from '@weasel-js/gestures';
5
+ import { PaintedCursorState } from '@weasel-js/cursor';
6
+ import * as react_jsx_runtime from 'react/jsx-runtime';
7
+ import '@weasel-js/modes';
8
+ import '@weasel-js/history';
9
+
10
+ /**
11
+ * Everything about handling an input event that depends on *which view* it
12
+ * landed in: the dispatcher that runs it, and the three lookups that resolve
13
+ * a client point against that view's camera.
14
+ *
15
+ * @internal One per view. The hook builds the `id: null` one from its flat
16
+ * options; `views` supplies the rest, and the hook chooses between them per
17
+ * event, so that it — and its listener set — still mounts exactly once.
18
+ */
19
+ interface DispatcherViewTarget {
20
+ /** `null` is the root view — the whole canvas. Matches `ViewTarget.id`. */
21
+ id: string | null;
22
+ dispatcher: Dispatcher;
23
+ affordanceAt: UseGestureDispatcherOptions['affordanceAt'];
24
+ classifyTarget: UseGestureDispatcherOptions['classifyTarget'];
25
+ clientToWorld: UseGestureDispatcherOptions['clientToWorld'];
26
+ /**
27
+ * The deps this view answers for itself — its camera, its selection, and
28
+ * whatever else is per-view rather than per-canvas. Read per event; every
29
+ * name it does not carry still resolves through the canvas registry.
30
+ *
31
+ * This is why a view does not get a `DepRegistryProvider` of its own. The
32
+ * registry is where a consumer registers *sources*, and one per view would
33
+ * fragment that: a consumer overriding `insert` would have to know how many
34
+ * views exist and override each. An overlay keeps one place to register and
35
+ * one authority per dep, with the view claiming only what is genuinely its.
36
+ *
37
+ * Without this a gesture inside a panel pans the whole canvas and selects
38
+ * into the canvas's selection — correct coordinates alone do not make
39
+ * routing correct.
40
+ */
41
+ deps?: () => Partial<DepSchema>;
42
+ /**
43
+ * This view's chrome-caps rule context, for the eligibility filter. The rule
44
+ * table is the surface's, but a rule keyed on selection or the in-flight
45
+ * action answers per view — so an action hidden in one panel must not
46
+ * decline a gesture in another. `undefined` means "nothing gates here", the
47
+ * same answer a surface with no mode registry gives.
48
+ */
49
+ getRuleCtx?: () => RuleCtx | undefined;
50
+ }
51
+ /**
52
+ * The part of `features/viewports`' `ViewResolver` the dispatcher needs: which
53
+ * view a client point belongs to, pinned to the view a gesture began in.
54
+ *
55
+ * @internal Structural on purpose — a `ViewResolver` satisfies it, and the
56
+ * dispatcher stays free of the viewport module.
57
+ */
58
+ interface ViewIdResolver {
59
+ begin(pointerId: number, clientX: number, clientY: number): {
60
+ id: string | null;
61
+ };
62
+ at(pointerId: number | null, clientX: number, clientY: number): {
63
+ id: string | null;
64
+ };
65
+ end(pointerId: number): void;
66
+ }
67
+ /** Options for `useGestureDispatcher`: the element to listen on, the actions
68
+ * and tools in play, and the hooks that turn raw DOM events into the world
69
+ * coordinates and hit targets bindings match against. */
70
+ interface UseGestureDispatcherOptions {
71
+ /** Ref to the canvas element. Pointer/wheel/multitouch listeners attach here. */
72
+ canvasRef: RefObject<HTMLElement | null>;
73
+ /** Action registry (ActionsRegistry from registry.tsx). */
74
+ actions: ActionsRegistry;
75
+ /** Tool definitions keyed by id. Typically passes an empty Map. */
76
+ toolsById: ReadonlyMap<string, Tool>;
77
+ /** Default true. Set false to opt out of dispatcher wiring (e.g. demos that disable it). */
78
+ enabled?: boolean;
79
+ /**
80
+ * Observer fired whenever the dispatcher synthesizes a double click, in
81
+ * world coordinates. Runs BEFORE the event is dispatched and independently
82
+ * of which binding (if any) handles it.
83
+ *
84
+ * This is deliberately not an Action. `<SceneCanvas onDoubleClick>` is a
85
+ * notification — "the user double-clicked, here's what they hit" — and a
86
+ * notification must not compete with behavior for the gesture. As a binding
87
+ * it would lose to `enterPathEdit` on any body hit and silently never fire.
88
+ * Routing it here keeps a single definition of "double click" (the point of
89
+ * consolidating the kit's three detectors) without giving it
90
+ * first-match-wins semantics it shouldn't have.
91
+ */
92
+ onDoubleClick?: (world: {
93
+ x: number;
94
+ y: number;
95
+ }) => void;
96
+ /**
97
+ * Default true. Set false to leave the window `keydown`/`keyup` listeners
98
+ * unattached so keyboard-bound actions never dispatch — pointer, wheel, and
99
+ * contextmenu channels stay live. `<SceneCanvas>` wires this to
100
+ * `enableKeybindings`, so opting out of keybindings disables the modern
101
+ * dispatcher key path as well as the legacy `useKeybindings` hook.
102
+ */
103
+ keyboard?: boolean;
104
+ /**
105
+ * Optional affordance classifier. Called with the **world** coordinates of
106
+ * the pointer — this hook applies the routed view's `clientToWorld` before
107
+ * calling it, the same conversion that produces `InputEvent.x`/`y`, so a
108
+ * consumer hit-tests in one space. Returns an `AffordanceHit` when the
109
+ * pointer lands on a known affordance (resize handle, rotate handle, etc.)
110
+ * or `null` when the pointer hit open canvas.
111
+ *
112
+ * The hit is packed into `InputEvent.pointerdown.affordance` and flows
113
+ * through into `InvocationCtx.drag.affordance`. Action invokers that require
114
+ * a specific affordance (e.g. `resizeAction` requires `handle:*`) use this
115
+ * field as a guard in their `start` body.
116
+ *
117
+ * When omitted, `affordance` is always `undefined` — meaning only consumers
118
+ * that explicitly wire a classifier get affordance-gated behavior.
119
+ * `<SceneCanvas>` wires the full chrome→dispatcher bridge.
120
+ */
121
+ affordanceAt?: (worldPoint: {
122
+ x: number;
123
+ y: number;
124
+ }) => AffordanceHit | null;
125
+ /**
126
+ * Optional body classifier. Called on every pointerdown with the world-space
127
+ * coordinates of the pointer. Its result is packed onto the event as
128
+ * `bodyTarget` + `bodyKind`, which `matchTarget` reads to resolve the
129
+ * string-form `TargetSpec` values in `Tool.bindings`.
130
+ *
131
+ * `body` is `'empty'` when nothing is under the pointer, `'selected-body'`
132
+ * when the topmost hit belongs to the current selection, or
133
+ * `'unselected-body'` when it belongs to a node that isn't selected. `kind`
134
+ * is the hit node's semantic kind, when the scene can name it.
135
+ *
136
+ * When omitted, every body-derived target form (`'empty'`,
137
+ * `'selected-body'`, `'unselected-body'`, `kind:<k>`, `kind:<k>:selected`)
138
+ * never matches — bindings using those specs are silently skipped.
139
+ * `<SceneCanvas>` wires this.
140
+ *
141
+ * World coordinates, on every path, for the same reason as `affordanceAt`.
142
+ */
143
+ classifyTarget?: (worldPoint: {
144
+ x: number;
145
+ y: number;
146
+ }) => BodyClassification;
147
+ /**
148
+ * Optional pre-created `Dispatcher`. When provided, this hook pumps events
149
+ * into the supplied instance instead of creating its own. Lets a parent
150
+ * scope (e.g. `<SceneCanvas>`) share one dispatcher between the gesture
151
+ * mounter and other consumers (the preview-ghost layer).
152
+ */
153
+ dispatcher?: Dispatcher;
154
+ /**
155
+ * Converts a client-space pointer position (e.g. `e.clientX`, `e.clientY`)
156
+ * to world-space coordinates. When supplied, every pointer/wheel event's
157
+ * `x`/`y` is converted before the dispatcher builds `InvocationCtx.world`.
158
+ *
159
+ * Without this, `ctx.world` is populated with the raw client coords, which
160
+ * silently breaks any action whose overlay/output uses absolute world
161
+ * positions (marquee, lasso polyline, world-space affordances). Actions
162
+ * that only read `drag.delta` are unaffected because client→world deltas
163
+ * are equal at scale 1, but as soon as a consumer pans/zooms the view the
164
+ * deltas diverge too.
165
+ *
166
+ * `<SceneCanvas>` always wires this via its canvas rect + current view.
167
+ * Tests / harnesses without a view can omit it and continue passing raw
168
+ * coordinates as before.
169
+ */
170
+ clientToWorld?: (clientX: number, clientY: number) => {
171
+ x: number;
172
+ y: number;
173
+ };
174
+ /**
175
+ * Invoked once per pump event (`pointermove`, `pointerup`, `pointercancel`,
176
+ * `key-held` up-phase, `multitouch` move) when a dispatcher-side handle is
177
+ * in flight. Lets the host canvas schedule a redraw so dispatcher-only
178
+ * overlays (marquee, lasso, preview-ghost) repaint on each frame.
179
+ *
180
+ * Required because dispatcher actions don't go through the legacy
181
+ * `tools.dispatcher.onGestureChange` redraw bump that ambient tool drags
182
+ * relied on. Without it, the overlay layer's `draw` never runs between
183
+ * pointerdown and pointerup, and the chrome flashes once at gesture start
184
+ * and then sits frozen until the gesture ends.
185
+ *
186
+ * `<SceneCanvas>` wires this to the canvas's `requestRedraw`. Test
187
+ * harnesses can omit it; their dispatchers won't paint between events but
188
+ * that's already the test contract.
189
+ */
190
+ requestRedraw?: () => void;
191
+ /**
192
+ * The surface's painted-cursor channel, read live because the canvas handle
193
+ * it lives on arrives a render after this hook runs.
194
+ *
195
+ * Given one, a cursor the CSS tier cannot express — sized in world units, or
196
+ * past the size the browser drops the image at — is published here and drawn
197
+ * by the painted-cursor layer instead. Without one, such a cursor resolves to
198
+ * `none` and the pointer shows nothing.
199
+ */
200
+ paintedCursor?: () => PaintedCursorState | undefined;
201
+ /**
202
+ * Thunk returning the live `RuleCtx` for the current frame. When supplied,
203
+ * the dispatcher filters matched candidates by their declared
204
+ * `Action.eligible` rule (omitted => always eligible). `<SceneCanvas>`
205
+ * wires this; tests / harnesses without chrome-caps state can omit it.
206
+ */
207
+ getRuleCtx?: () => RuleCtx | undefined;
208
+ /**
209
+ * Routing for a canvas hosting more than one view: the non-root dispatch
210
+ * records to choose between, read fresh per event, and the resolver that
211
+ * chooses. Omit for a single view — then every event runs on the record the
212
+ * flat options above describe, which is what they are: view zero.
213
+ *
214
+ * A resolved id with no live record falls back to the root, so a view that
215
+ * unmounts mid-gesture degrades instead of dropping the event.
216
+ *
217
+ * Keyboard and paste have no coordinates to resolve; they run on the view
218
+ * the last coordinate-bearing event resolved to.
219
+ *
220
+ * @internal
221
+ */
222
+ views?: {
223
+ targets: () => readonly DispatcherViewTarget[];
224
+ resolver: ViewIdResolver;
225
+ };
226
+ }
227
+ /**
228
+ * Attach the input pipeline to a canvas: DOM pointer, wheel, keyboard and
229
+ * multitouch listeners in, normalized input events out, routed through the
230
+ * dispatcher to whichever action a binding names.
231
+ *
232
+ * Everything it needs beyond its options — the active tool, the dep registry —
233
+ * comes from context, so it must be mounted inside `<SceneCanvas>`'s
234
+ * providers.
235
+ */
236
+ declare function useGestureDispatcher(opts: UseGestureDispatcherOptions): void;
237
+
238
+ /**
239
+ * @experimental
240
+ * The React half of the actions registry: the context, the provider that owns
241
+ * the id → descriptor stack, the mute scope, and the two hooks. The store
242
+ * contract it implements is in `registry.tsx`.
243
+ */
244
+
245
+ /**
246
+ * @experimental
247
+ * A view of the registry in scope that can declare ids not for itself. Wrap
248
+ * anything that should be able to opt out of an action — a second
249
+ * `<SceneCanvas>` sharing the host's `<ActionsProvider>` mounts one — and its
250
+ * `mute` calls stay inside it. Renders nothing of its own.
251
+ *
252
+ * Not a `BindingScope`: that names the tier a binding matches at (hotkey /
253
+ * active / ambient), which this has nothing to do with.
254
+ */
255
+ declare function ActionsScope({ children }: {
256
+ children: ReactNode;
257
+ }): ReactElement;
258
+ declare function ActionsProvider({ children }: {
259
+ children: ReactNode;
260
+ }): ReactElement;
261
+ declare function useActionsRegistry(): ActionsRegistry | null;
262
+ /**
263
+ * @experimental
264
+ * Register an `Action` for the lifetime of the calling component. No-op (with
265
+ * a dev-only warning) when no `ActionsProvider` is in scope. Re-registers on
266
+ * `action` reference change (consumers should memoize stable identities to
267
+ * avoid churn).
268
+ */
269
+ declare function useAction(action: Action): void;
270
+
271
+ /** Which tool is active, plus the stack of tools temporarily held active by a
272
+ * hotkey (space-for-hand and the like). The dispatcher reads this to decide
273
+ * whose bindings are in scope. */
274
+ interface ActiveToolContextValue {
275
+ active: string;
276
+ hotkeyStack: string[];
277
+ setActive(id: string): void;
278
+ pushHotkey(id: string): void;
279
+ /**
280
+ * Disengage a hotkey-held tool. With `id`, removes that tool's own entry
281
+ * wherever it now sits — holds do not have to come up in the order they went
282
+ * down, and popping the top would disengage a tool whose key is still held.
283
+ * With no id, pops the top.
284
+ */
285
+ popHotkey(id?: string): void;
286
+ }
287
+ /** Props for `<ActiveToolContextProvider>`. */
288
+ interface ActiveToolContextProviderProps {
289
+ children: ReactNode;
290
+ initialActive?: string;
291
+ }
292
+ /** Provides active-tool state for a canvas. `<SceneCanvas>` mounts one. */
293
+ declare function ActiveToolContextProvider({ children, initialActive, }: ActiveToolContextProviderProps): react_jsx_runtime.JSX.Element;
294
+ /** The active-tool state in scope. Throws outside a provider; use
295
+ * `useActiveToolContextOptional` where one is not guaranteed. */
296
+ declare function useActiveToolContext(): ActiveToolContextValue;
297
+ /**
298
+ * Like `useActiveToolContext`, but returns `null` when no
299
+ * `<ActiveToolContextProvider>` is in scope instead of throwing. Used by
300
+ * `useStandardActions` to preserve its silent-no-op contract when no provider
301
+ * is present.
302
+ */
303
+ declare function useOptionalActiveToolContext(): ActiveToolContextValue | null;
304
+ /**
305
+ * Conditional `<ActiveToolContextProvider>` wrapper. Mounts a provider only
306
+ * when no parent provider is in scope — otherwise renders children unwrapped
307
+ * so callers (e.g. `<WeaselProvider>`, `<SceneCanvas>`) defer to the host's
308
+ * existing scope. Mirrors `ActionsProviderIfRoot` / `DepRegistryProviderIfRoot`.
309
+ */
310
+ declare function ActiveToolContextProviderIfRoot({ children, }: {
311
+ children: ReactNode;
312
+ }): react_jsx_runtime.JSX.Element;
313
+
314
+ /** Options for `useTools`: which tools exist, which one starts active, and
315
+ * which run continuously regardless of the active one. */
316
+ interface UseToolsOptions<TOverlay = unknown> {
317
+ /** Initial active-slot tool id. Must exist in `registry`. */
318
+ active: string;
319
+ /** Tools eligible for the active slot or hotkey slot. The keys are the
320
+ * tool ids; the values are the tool records. A tool with `hotkey` set
321
+ * is wired into the hotkey slot whenever the engagement state matches. */
322
+ registry: Record<string, AnyToolOf<TOverlay>>;
323
+ /** Always-on tools — listen continuously regardless of active slot. */
324
+ ambient?: AnyToolOf<TOverlay>[];
325
+ }
326
+ /** The tool registry's runtime surface: which tool is active, which is
327
+ * temporarily held by a hotkey, and how to change either. */
328
+ interface ToolsApi<TOverlay = unknown> {
329
+ /** Current active-slot tool id. */
330
+ active: string;
331
+ /** Set the active-slot tool. The gesture dispatcher watches the active
332
+ * tool and cancels any in-flight handle itself. */
333
+ setActive: (id: string) => void;
334
+ /** Currently hotkey-engaged tool id (or `null`). Derived as the top of
335
+ * the hotkey stack for backwards compat with the pre-stack API. */
336
+ hotkeyEngaged: string | null;
337
+ /** Engage a hotkey-slot tool by id. */
338
+ engageHotkey: (id: string) => void;
339
+ /** Disengage the hotkey-slot tool, if any. */
340
+ disengageHotkey: () => void;
341
+ /** All always-on tools, in registration order. */
342
+ ambient: readonly AnyToolOf<TOverlay>[];
343
+ /** Full registry — for userland UI (palette buttons, etc.). */
344
+ registry: Readonly<Record<string, AnyToolOf<TOverlay>>>;
345
+ /** Returns true if a tool with the given id is in the registry or ambient list. */
346
+ has(id: string): boolean;
347
+ /** All overlay layers from currently-engaged tools (active slot, hotkey
348
+ * slot if engaged, all ambient slot tools) that declare `position`.
349
+ * Filters out tools with no `overlay` field. Order: active, then hotkey
350
+ * (if engaged), then ambient (registration order). */
351
+ getActiveOverlays(position?: OverlayPosition): TOverlay[];
352
+ }
353
+ /**
354
+ * Manages the active tool and hotkey slot.
355
+ *
356
+ * A thin shim over {@link useContributions}: the `registry` / `ambient`
357
+ * arguments are restated as declared eligibility (`focus` / `always`), and
358
+ * assembly happens in one place for tools and non-tool contributions alike.
359
+ *
360
+ * Requires `<ActiveToolContextProvider>` (or `<WeaselProvider>` /
361
+ * `<SceneCanvas>`, which mount one internally) in scope: active/hotkey state
362
+ * lives in the context so the gesture dispatcher and any sibling
363
+ * `useTools` calls all read the same source of truth.
364
+ *
365
+ * **First-mount-wins semantics**: if `opts.active` differs from the context
366
+ * default (`'select'`) on first mount, `useTools` pushes `opts.active` to
367
+ * the context. Subsequent mounts respect whatever the context currently
368
+ * holds (the first caller wins).
369
+ */
370
+ declare function useTools<TOverlay = unknown>(opts: UseToolsOptions<TOverlay>): ToolsApi<TOverlay>;
371
+
372
+ interface UseContributionsOptions<TOverlay = unknown> {
373
+ /** Every registry entry, in declaration order. Order decides which of two
374
+ * same-specificity bindings in one scope tier wins. */
375
+ entries: readonly Contribution<TOverlay>[];
376
+ /** Desired focused entry id. First-mount-wins against the shared
377
+ * `ActiveToolContext` — see `useTools` for the full semantics. */
378
+ focused: string;
379
+ }
380
+ interface ContributionsApi<TOverlay = unknown> {
381
+ /** Every entry, as passed in. */
382
+ entries: readonly Contribution<TOverlay>[];
383
+ /** Currently focused entry id. */
384
+ focused: string;
385
+ /** Focus an entry. Writes through to the shared `ActiveToolContext`. */
386
+ setFocused: (id: string) => void;
387
+ /** Every live binding, tiered by the declaring entry's eligibility. */
388
+ scopedBindings(): ScopedBinding[];
389
+ /** Overlays of every live entry declaring `position`, ordered active, then
390
+ * hotkey, then ambient. */
391
+ overlays(position?: OverlayPosition): TOverlay[];
392
+ }
393
+ /**
394
+ * Assembles one registry: every entry's bindings and overlays, each tiered by
395
+ * what the entry declares about its own eligibility rather than by which
396
+ * argument a consumer passed it in.
397
+ *
398
+ * Requires `<ActiveToolContextProvider>` (or `<WeaselProvider>` /
399
+ * `<SceneCanvas>`, which mount one internally): focus and held-key state live
400
+ * in the context so the gesture dispatcher and every sibling caller read the
401
+ * same source of truth.
402
+ */
403
+ declare function useContributions<TOverlay = unknown>(opts: UseContributionsOptions<TOverlay>): ContributionsApi<TOverlay>;
404
+
405
+ export { ActionsProvider, ActionsScope, ActiveToolContextProvider, ActiveToolContextProviderIfRoot, type ActiveToolContextProviderProps, type ActiveToolContextValue, type ContributionsApi, type DispatcherViewTarget, type ToolsApi, type UseContributionsOptions, type UseGestureDispatcherOptions, type UseToolsOptions, type ViewIdResolver, useAction, useActionsRegistry, useActiveToolContext, useContributions, useGestureDispatcher, useOptionalActiveToolContext, useTools };