@weasel-js/routing 1.9.2 → 1.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -5
- package/dist/{chunk-RTCD26NN.js → chunk-Y5RURZBH.js} +264 -183
- package/dist/chunk-Y5RURZBH.js.map +1 -0
- package/dist/{index-X6vX2tYX.d.ts → index-gArM2tbt.d.ts} +373 -187
- package/dist/index.d.ts +3 -2
- package/dist/index.js +23 -20
- package/dist/index.js.map +1 -1
- package/dist/{internal-DTWYD6-s.d.ts → internal-B0SsznMG.d.ts} +9 -12
- package/dist/internal.d.ts +4 -3
- package/dist/react.d.ts +30 -73
- package/dist/react.js +410 -299
- package/dist/react.js.map +1 -1
- package/package.json +8 -7
- package/dist/chunk-RTCD26NN.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export {
|
|
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, e as AnyToolOf, 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, N as Condition, P as Conflict, C as Contribution, Q as ContributionChrome, U as ContributionDeps, V as ContributionRouting, W as DEFAULT_ALLOWED_CAPABILITIES, X as DEFAULT_DEVICE_PROFILE, Y as DebugSink, a as DepName, D as DepRegistry, b as DepSchema, Z as DetectedDeviceFacts, _ as DeviceProfile, $ as DispatchRecord, a0 as DispatchRecordInput, k as Dispatcher, a1 as DispatcherContext, a2 as DragSample, a3 as DroppedCandidate, a4 as Eligibility, a5 as EligibilityState, a6 as GestureBinding, a7 as HandleKind, a8 as HitShape, a9 as HotkeyTrigger, aa as INGEST_STRING_MIMES, ab as ImmediateInvoker, ac as InvocationCtx, ad as Invoker, K as KernelOverlay, ae as KeyBinding, af as LONG_PRESS_MS, 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, ao as NodeId, ap as OngoingHandle, aq as OngoingInvoker, ar as OngoingOverlay, O as OverlayPosition, as as OverlayRole, at as OverlaySchema, au as PREDICATE_TARGET, av as PendingLongPress, aw as PlacedBy, ax as Point2, ay as PointerSession, az as PointerSessionCallbacks, aA as PointerSessionCancelReason, aB as PointerSessionOptions, aC as RankedCandidate, aD as RecordCandidate, aE as RegistryEntry, aF as ResizeAnchor, aG as ResolveAllOptions, aH as ResolveOnlyResult, aI as ResolvedCandidate, aJ as Rule, R as RuleCtx, aK as ScopedBinding, aL as ScratchKey, aM as ScratchStore, aN as SelectionApi, aO as Selector, aP as SpecificityPart, aQ as TOOL_OFFHAND_ID, aR as Tool, aS as ToolBounds, aT as ToolCtx, aU as ToolDef, aV as ToolKeybinding, aW as ToolLifecycleCtx, aX as ToolModifiers, aY as ToolOffhandBindingSpec, aZ as ToolPresentation, a_ as ToolSlot, a$ as TraceLogEntry, b0 as UiOngoingControl, b1 as ViewportGestureKind, b2 as WalkStep, b3 as actionBindings, b4 as actionItems, b5 as activatesFocusedControl, 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 isEditableTarget, by as isEmpty, bz as isResizeHandle, bA as isRotateHandle, bB as isSelectedBody, bC as isTool, bD as isUnselectedBody, bE as itemsFromClipboardData, bF as itemsFromDataTransfer, bG as itemsFromFiles, bH as liveScope, bI as makeToolOffhandAction, bJ as matchBest, bK as matchSorted, bL as matchSortedWithBarred, bM as matchesKeyBinding, bN as mergeContributions, bO as modeShortcutSpec, bP as modeShortcuts, bQ as offhandKeyFor, bR as openPointerSession, bS as pointerGestureId, bT as recordModeSwitch, bU as reportRouteConflicts, bV as resolveDeviceProfile, bW as resolveParams, bX as routesForSpec, bY as ruleCanHoldIn, bZ as rulesExclusive, b_ as scopeBindings, b$ as scratchKey, c0 as setScratch, c1 as targetConsultsAffordance, c2 as validateActionDefaultBinding, c3 as validateActionId } from './index-gArM2tbt.js';
|
|
2
2
|
export { CONTROL_SELECTOR, ChannelRef, DRAG_THRESHOLD_PX, DescribeRouteOptions, GESTURE_DESCRIPTORS, GestureArgSpec, GestureDescriptor, GestureName, IngestItem, InputEvent, ModRequirement, ModifierKey, ParsedModifiers, ParsedRoute, PhaseAtom, PhaseContext, RESERVED_ID_NAMES, RESERVED_ID_PREFIXES, ROUTE_FIELD_DEFINITIONS, ROUTE_TERMS, RouteDescriptionPart, RouteFieldName, RouteTermLabel, canonicalModifiers, collapseShiftPairs, describeRoute, describeRouteParts, formatPhaseAtom, formatRoute, getGestureDescriptor, 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';
|
|
5
5
|
import 'react';
|
|
6
|
+
import '@weasel-js/modes';
|
|
6
7
|
import '@weasel-js/cursor';
|
|
7
8
|
import '@weasel-js/history';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { isDev } from './chunk-
|
|
2
|
-
export { ALWAYS, COARSE_TARGET_SCALE, DEFAULT_ALLOWED_CAPABILITIES, DEFAULT_DEVICE_PROFILE, GESTURE_DESCRIPTORS, INGEST_STRING_MIMES, LONG_PRESS_MS, NEVER, PREDICATE_TARGET, RESERVED_ID_NAMES, RESERVED_ID_PREFIXES, ROUTE_FIELD_DEFINITIONS, ROUTE_TERMS, TOOL_OFFHAND_ID, actionBindings, buildDepsFromRequires, buildRouteRegistry, buildRuleCtx, buildToolOffhandBindings, canonicalModifiers, clientToCanvas, clientToCanvasRect, collapseShiftPairs, createDepRegistry, createDispatcher, createLongPressStore, describeRoute, describeRouteParts, describeRule, dlog, evaluate, findConflicts, formatPhaseAtom, formatRoute, getGestureDescriptor, isEditableTarget, isKnownGestureName, isTool, itemsFromClipboardData, itemsFromDataTransfer, itemsFromFiles, liveScope, makeToolOffhandAction, matchBest, matchKey, matchModifiers, matchPhase, matchSorted, matchSortedWithBarred, matchSpec, matchTarget, matchesKeyBinding, offhandKeyFor, openPointerSession, parseRoute, pointerGestureId, recordModeSwitch, reportRouteConflicts, resolveDeviceProfile, resolveParams, routeGestureForSpecKind, routesForSpec, ruleCanHoldIn, rulesExclusive, scopeBindings, specKindForRouteGesture, specificity, targetConsultsAffordance, validateActionDefaultBinding, validateActionId } from './chunk-
|
|
3
|
-
import { RESERVED_ID_PREFIXES, RESERVED_ID_NAMES } from '@weasel-js/gestures';
|
|
1
|
+
import { isDev } from './chunk-Y5RURZBH.js';
|
|
2
|
+
export { ALWAYS, COARSE_TARGET_SCALE, DEFAULT_ALLOWED_CAPABILITIES, DEFAULT_DEVICE_PROFILE, GESTURE_DESCRIPTORS, INGEST_STRING_MIMES, LONG_PRESS_MS, NEVER, PREDICATE_TARGET, RESERVED_ID_NAMES, RESERVED_ID_PREFIXES, ROUTE_FIELD_DEFINITIONS, ROUTE_TERMS, TOOL_OFFHAND_ID, actionBindings, activatesFocusedControl, assembleEntries, buildDepsFromRequires, buildRouteRegistry, buildRuleCtx, buildToolOffhandBindings, canonicalModifiers, clientToCanvas, clientToCanvasRect, collapseShiftPairs, createDepRegistry, createDispatcher, createLongPressStore, describeRoute, describeRouteParts, describeRule, dlog, dwarn, evaluate, findConflicts, formatPhaseAtom, formatRoute, getGestureDescriptor, heldKeySpec, historyKey, isDebugEnabled, isEditableTarget, isKnownGestureName, isTool, itemsFromClipboardData, itemsFromDataTransfer, itemsFromFiles, liveScope, makeToolOffhandAction, matchBest, matchKey, matchModifiers, matchPhase, matchSorted, matchSortedWithBarred, matchSpec, matchTarget, matchesKeyBinding, offhandKeyFor, openPointerSession, parseRoute, pointerGestureId, recordModeSwitch, reportRouteConflicts, resolveDeviceProfile, resolveParams, routeGestureForSpecKind, routesForSpec, ruleCanHoldIn, rulesExclusive, scopeBindings, specKindForRouteGesture, specificity, targetConsultsAffordance, validateActionDefaultBinding, validateActionId } from './chunk-Y5RURZBH.js';
|
|
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';
|
|
6
6
|
|
|
@@ -61,34 +61,37 @@ function evaluateEnabled(action, deps) {
|
|
|
61
61
|
return { enabled: false, reason: ActionDisabledReason.PredicateThrew };
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
function evaluateActive(action, deps) {
|
|
65
|
+
if (!action.active) return void 0;
|
|
66
|
+
try {
|
|
67
|
+
return action.active(deps) === true;
|
|
68
|
+
} catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
68
71
|
}
|
|
69
|
-
var isBody = bodyPredicate((
|
|
70
|
-
var isSelectedBody = bodyPredicate((
|
|
71
|
-
var isUnselectedBody = bodyPredicate((
|
|
72
|
-
var isEmpty = bodyPredicate((
|
|
72
|
+
var isBody = bodyPredicate((bodyTarget) => bodyTarget === "selected-body" || bodyTarget === "unselected-body");
|
|
73
|
+
var isSelectedBody = bodyPredicate((bodyTarget) => bodyTarget === "selected-body");
|
|
74
|
+
var isUnselectedBody = bodyPredicate((bodyTarget) => bodyTarget === "unselected-body");
|
|
75
|
+
var isEmpty = bodyPredicate((bodyTarget) => bodyTarget === "empty");
|
|
73
76
|
function kindOf(target) {
|
|
74
77
|
if (typeof target !== "object" || target === null) return void 0;
|
|
75
78
|
const k = target.kind;
|
|
76
79
|
return typeof k === "string" ? k : void 0;
|
|
77
80
|
}
|
|
78
|
-
var isResizeHandle = (target) => {
|
|
81
|
+
var isResizeHandle = hitPredicate((target) => {
|
|
79
82
|
const k = kindOf(target);
|
|
80
83
|
return k !== void 0 && k.startsWith("handle:");
|
|
81
|
-
};
|
|
82
|
-
var isRotateHandle = (target) => kindOf(target) === "rotate-handle";
|
|
83
|
-
var isAnchor = (target) => {
|
|
84
|
+
});
|
|
85
|
+
var isRotateHandle = hitPredicate((target) => kindOf(target) === "rotate-handle");
|
|
86
|
+
var isAnchor = hitPredicate((target) => {
|
|
84
87
|
const k = kindOf(target);
|
|
85
88
|
return k !== void 0 && /^anchor:\d+$/.test(k);
|
|
86
|
-
};
|
|
87
|
-
var isControlHandle = (target) => {
|
|
89
|
+
});
|
|
90
|
+
var isControlHandle = hitPredicate((target) => {
|
|
88
91
|
const k = kindOf(target);
|
|
89
92
|
return k !== void 0 && /^(controlIn|controlOut):\d+$/.test(k);
|
|
90
|
-
};
|
|
91
|
-
var isAnchorOrControl = (target) => isAnchor(target) || isControlHandle(target);
|
|
93
|
+
});
|
|
94
|
+
var isAnchorOrControl = hitPredicate((target) => isAnchor(target) || isControlHandle(target));
|
|
92
95
|
|
|
93
96
|
// src/contributions/merge.ts
|
|
94
97
|
function mergeContributions(...bundles) {
|
|
@@ -256,6 +259,6 @@ function deleteScratch(store, key) {
|
|
|
256
259
|
return false;
|
|
257
260
|
}
|
|
258
261
|
|
|
259
|
-
export { ActionDisabledReason, actionItems, defineTool, deleteScratch, evaluateEnabled, getScratch, isAnchor, isAnchorOrControl, isBody, isControlHandle, isEmpty, isResizeHandle, isRotateHandle, isSelectedBody, isUnselectedBody, mergeContributions, modeShortcutSpec, modeShortcuts, scratchKey, setScratch };
|
|
262
|
+
export { ActionDisabledReason, actionItems, defineTool, deleteScratch, evaluateActive, evaluateEnabled, getScratch, isAnchor, isAnchorOrControl, isBody, isControlHandle, isEmpty, isResizeHandle, isRotateHandle, isSelectedBody, isUnselectedBody, mergeContributions, modeShortcutSpec, modeShortcuts, scratchKey, setScratch };
|
|
260
263
|
//# sourceMappingURL=index.js.map
|
|
261
264
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/interactions/actions/actionItems.ts","../src/interactions/actions/action.ts","../src/interactions/actions/actionEnabled.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 * @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 /** The distinct things a parametric action does, one per set of params —\n * `flip` flips horizontally or vertically, `reorder.forward` moves one step\n * or all the way. A bar, menu or palette shows one entry per variant and\n * triggers the action with that variant's `params`. Omitted = the action\n * is one entry. See `actionItems`. */\n variants?: readonly ActionVariant[];\n}\n\n/**\n * @experimental\n * One entry a parametric action contributes to a bar, menu or palette.\n */\nexport interface ActionVariant {\n /** Unique within the action; joined to its id to make the entry's key. */\n key: string;\n label: string;\n /** Falls back to the action's own `icon`. */\n icon?: ReactNode | (() => ReactNode);\n /** Passed to `trigger`, and matched against each binding's\n * `opts.params` to find the variant's shortcuts. */\n params: Readonly<Record<string, unknown>>;\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 * `at` is the world point of the event being routed, when it has one — a\n * click, a press, a long-press. An action that only applies somewhere\n * (on a path segment, say) refuses other points here, so the dispatcher\n * falls through to the next binding and hover prediction shows the\n * action's `cursor` only where it would act. Palettes and keystrokes\n * pass no point; treat absent as \"anywhere\".\n */\n enabled?: (deps?: ActionDeps, at?: { x: number; y: number }) => 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 — or, when the drag's winner declares no cursor,\n * the click. The hover-cursor pump (in `useGestureDispatcher`) runs\n * `Dispatcher.resolveOnly` on each idle pointermove — the same match\n * walk a real press takes — and applies the winning action's `cursor`,\n * so the hint and the actual target stay in 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 — an `engaged` phase entry's cursor,\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 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 * 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 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)));\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));\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): Action {\n return {\n id: `mode.${mode.id}.${role}`,\n label: `${VERB[role]} ${modeLabel(mode)}`,\n scope: 'hotkey',\n defaultBinding: {\n ...modeShortcutSpec(shortcut),\n phase: [{ channel: '*', phase: 'initial' }],\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, 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';\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<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 = KernelOverlay>(\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","/**\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"]}
|
|
1
|
+
{"version":3,"sources":["../src/interactions/actions/actionItems.ts","../src/interactions/actions/action.ts","../src/interactions/actions/actionEnabled.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 * @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 /** The distinct things a parametric action does, one per set of params —\n * `flip` flips horizontally or vertically, `reorder.forward` moves one step\n * or all the way. A bar, menu or palette shows one entry per variant and\n * triggers the action with that variant's `params`. Omitted = the action\n * is one entry. See `actionItems`. */\n variants?: readonly ActionVariant[];\n}\n\n/**\n * @experimental\n * One entry a parametric action contributes to a bar, menu or palette.\n */\nexport interface ActionVariant {\n /** Unique within the action; joined to its id to make the entry's key. */\n key: string;\n label: string;\n /** Falls back to the action's own `icon`. */\n icon?: ReactNode | (() => ReactNode);\n /** Passed to `trigger`, and matched against each binding's\n * `opts.params` to find the variant's shortcuts. */\n params: Readonly<Record<string, unknown>>;\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 saying whether the action can run right now. Return\n * `true` when it can, or a reason string (e.g. `'Selection required'`)\n * when it cannot. The command palette greys out a disabled row, skips it\n * in keyboard nav, ignores clicks, and shows the reason next to the label.\n * The dispatcher skips a disabled action too, on a key as on a pointer, and\n * falls through to the next binding that matches. `ActionsRegistry.trigger`\n * does not ask, so the action's own `run` should still 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 * `at` is the world point of the event being routed, when it has one — a\n * click, a press, a long-press. An action that only applies somewhere\n * (on a path segment, say) refuses other points here, so the dispatcher\n * falls through to the next binding and hover prediction shows the\n * action's `cursor` only where it would act. Palettes and keystrokes\n * pass no point; treat absent as \"anywhere\".\n */\n enabled?: (deps?: ActionDeps, at?: { x: number; y: number }) => true | ActionDisabledReason;\n /**\n * @experimental\n * Whether the action reads as held down right now: bold on the selected\n * text, snapping switched on. A toolbar button draws it as pressed.\n * Omitted = the action has no pressed state, which is not the same as\n * one that is off. Never read during dispatch.\n *\n * Under the same contract as `enabled`: pure, fast, and it must not\n * throw. `deps` is the bag `enabled` is given.\n */\n active?: (deps?: ActionDeps) => boolean;\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 — or, when the drag's winner declares no cursor,\n * the click. The hover-cursor pump (in `useGestureDispatcher`) runs\n * `Dispatcher.resolveOnly` on each idle pointermove — the same match\n * walk a real press takes — and applies the winning action's `cursor`,\n * so the hint and the actual target stay in 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 — an `engaged` phase entry's cursor,\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/**\n * @experimental\n * Safely evaluate an action's `active` predicate. `undefined` for an action\n * with no pressed state; a predicate that throws reads as not active.\n */\nexport function evaluateActive(action: Action, deps?: ActionDeps): boolean | undefined {\n if (!action.active) return undefined;\n try {\n return action.active(deps) === true;\n } catch {\n return false;\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 * 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 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)));\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));\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): Action {\n return {\n id: `mode.${mode.id}.${role}`,\n label: `${VERB[role]} ${modeLabel(mode)}`,\n scope: 'hotkey',\n defaultBinding: {\n ...modeShortcutSpec(shortcut),\n phase: [{ channel: '*', phase: 'initial' }],\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, 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';\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<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 = KernelOverlay>(\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","/**\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"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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-gArM2tbt.js';
|
|
2
2
|
import { RefObject } from 'react';
|
|
3
3
|
import { BodyClassification } from '@weasel-js/gestures';
|
|
4
4
|
import { PaintedCursorState } from '@weasel-js/cursor';
|
|
@@ -81,6 +81,13 @@ interface DispatcherChannels {
|
|
|
81
81
|
* and tools in play, and the hooks that turn raw DOM events into the world
|
|
82
82
|
* coordinates and hit targets bindings match against. */
|
|
83
83
|
interface UseGestureDispatcherOptions {
|
|
84
|
+
/**
|
|
85
|
+
* Take the keyboard on mount, as a press in the canvas would. Keys that
|
|
86
|
+
* reach the page go to the canvas last pressed or scrolled in and to none
|
|
87
|
+
* before that; an app whose canvas is the page sets this so its shortcuts
|
|
88
|
+
* work before the first click. Default `false`.
|
|
89
|
+
*/
|
|
90
|
+
claimKeys?: boolean;
|
|
84
91
|
/** Ref to the canvas element. Pointer/wheel/multitouch listeners attach here. */
|
|
85
92
|
canvasRef: RefObject<HTMLElement | null>;
|
|
86
93
|
/** Action registry (ActionsRegistry from registry.tsx). */
|
|
@@ -271,15 +278,5 @@ interface UseGestureDispatcherOptions {
|
|
|
271
278
|
resolver: ViewIdResolver;
|
|
272
279
|
};
|
|
273
280
|
}
|
|
274
|
-
/**
|
|
275
|
-
* Attach the input pipeline to a canvas: DOM pointer, wheel, keyboard and
|
|
276
|
-
* multitouch listeners in, normalized input events out, routed through the
|
|
277
|
-
* dispatcher to whichever action a binding names.
|
|
278
|
-
*
|
|
279
|
-
* Everything it needs beyond its options — the active tool, the dep registry —
|
|
280
|
-
* comes from context, so it must be mounted inside `<SceneCanvas>`'s
|
|
281
|
-
* providers.
|
|
282
|
-
*/
|
|
283
|
-
declare function useGestureDispatcher(opts: UseGestureDispatcherOptions): void;
|
|
284
281
|
|
|
285
|
-
export {
|
|
282
|
+
export type { DispatcherChannels as D, UseGestureDispatcherOptions as U, ViewIdResolver as V, DispatcherViewTarget as a };
|
package/dist/internal.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export { a as DispatcherViewTarget, V as ViewIdResolver } from './internal-
|
|
2
|
-
import './index-
|
|
3
|
-
import '@weasel-js/
|
|
1
|
+
export { a as DispatcherViewTarget, V as ViewIdResolver } from './internal-B0SsznMG.js';
|
|
2
|
+
import './index-gArM2tbt.js';
|
|
3
|
+
import '@weasel-js/geom';
|
|
4
4
|
import 'react';
|
|
5
|
+
import '@weasel-js/modes';
|
|
5
6
|
import '@weasel-js/cursor';
|
|
6
7
|
import '@weasel-js/gestures';
|
|
7
8
|
import '@weasel-js/history';
|
package/dist/react.d.ts
CHANGED
|
@@ -1,13 +1,27 @@
|
|
|
1
|
-
|
|
1
|
+
import { U as UseGestureDispatcherOptions } from './internal-B0SsznMG.js';
|
|
2
|
+
export { D as DispatcherChannels } from './internal-B0SsznMG.js';
|
|
2
3
|
import * as react from 'react';
|
|
3
4
|
import { ReactNode, ReactElement } from 'react';
|
|
4
|
-
import { D as DepRegistry, a as DepName, b as DepSchema, S as ScopeNode, A as ActionsRegistry, c as Action, d as DepNode, K as KernelOverlay, C as Contribution, e as AnyToolOf, O as OverlayPosition
|
|
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, K as KernelOverlay, C as Contribution, e as AnyToolOf, O as OverlayPosition } from './index-gArM2tbt.js';
|
|
6
|
+
export { f as ActiveToolContextProvider, g as ActiveToolContextProviderIfRoot, h as ActiveToolContextProviderProps, i as ActiveToolContextValue, u as useActiveToolContext, j as useOptionalActiveToolContext } from './index-gArM2tbt.js';
|
|
5
7
|
import { ModeRegistry } from '@weasel-js/modes';
|
|
6
8
|
import '@weasel-js/gestures';
|
|
7
9
|
import '@weasel-js/cursor';
|
|
10
|
+
import '@weasel-js/geom';
|
|
8
11
|
import '@weasel-js/history';
|
|
9
12
|
import '@weasel-js/select';
|
|
10
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Attach the input pipeline to a canvas: DOM pointer, wheel, keyboard and
|
|
16
|
+
* multitouch listeners in, normalized input events out, routed through the
|
|
17
|
+
* dispatcher to whichever action a binding names.
|
|
18
|
+
*
|
|
19
|
+
* Everything it needs beyond its options — the active tool, the dep registry —
|
|
20
|
+
* comes from context, so it must be mounted inside `<SceneCanvas>`'s
|
|
21
|
+
* providers.
|
|
22
|
+
*/
|
|
23
|
+
declare function useGestureDispatcher(opts: UseGestureDispatcherOptions): void;
|
|
24
|
+
|
|
11
25
|
/** Provides the dep registry for a canvas. `<SceneCanvas>` mounts one; a
|
|
12
26
|
* consumer registering its own dep sources must be inside it. */
|
|
13
27
|
declare function DepRegistryProvider({ children }: {
|
|
@@ -71,72 +85,9 @@ declare function useActionsRegistry(): ActionsRegistry | null;
|
|
|
71
85
|
*/
|
|
72
86
|
declare function useAction(action: Action): void;
|
|
73
87
|
|
|
74
|
-
/** Which tool is active, plus the stack of tools temporarily held active by a
|
|
75
|
-
* hotkey (space-for-hand and the like). The dispatcher reads this to decide
|
|
76
|
-
* whose bindings are in scope. */
|
|
77
|
-
interface ActiveToolContextValue {
|
|
78
|
-
/** The focused tool's id, or `null` when no tool holds the active slot —
|
|
79
|
-
* a canvas can run on ambient bindings alone. */
|
|
80
|
-
active: string | null;
|
|
81
|
-
hotkeyStack: string[];
|
|
82
|
-
setActive(id: string | null): void;
|
|
83
|
-
pushHotkey(id: string): void;
|
|
84
|
-
/**
|
|
85
|
-
* Disengage a hotkey-held tool. With `id`, removes that tool's own entry
|
|
86
|
-
* wherever it now sits — holds do not have to come up in the order they went
|
|
87
|
-
* down, and popping the top would disengage a tool whose key is still held.
|
|
88
|
-
* With no id, pops the top.
|
|
89
|
-
*/
|
|
90
|
-
popHotkey(id?: string): void;
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* One node of the tool tree. A node that `owns` a tool holds its state; one
|
|
94
|
-
* that does not — a scope inside a yoke — reads and writes its yoke's.
|
|
95
|
-
*/
|
|
96
|
-
declare class ToolNode extends ScopeNode<ToolNode> {
|
|
97
|
-
private state;
|
|
98
|
-
private value;
|
|
99
|
-
constructor(parent: ToolNode | null, descends: boolean, owns: boolean, initialActive: string | null);
|
|
100
|
-
/** The node whose tool a read here resolves to. */
|
|
101
|
-
owner(): ToolNode;
|
|
102
|
-
private update;
|
|
103
|
-
/** A stable value per owner and state, for `useSyncExternalStore`. */
|
|
104
|
-
snapshot(): ActiveToolContextValue;
|
|
105
|
-
subscribe: (listener: () => void) => (() => void);
|
|
106
|
-
}
|
|
107
|
-
/** Props for `<ActiveToolContextProvider>`. */
|
|
108
|
-
interface ActiveToolContextProviderProps {
|
|
109
|
-
children: ReactNode;
|
|
110
|
-
/** Seeds the active slot, and wins over the first `useTools` call's
|
|
111
|
-
* `active`. Omitted, the slot starts empty and that call seeds it. */
|
|
112
|
-
initialActive?: string | null;
|
|
113
|
-
}
|
|
114
|
-
/** Provides root active-tool state. Every canvas below has a tool of its own;
|
|
115
|
-
* reads and writes here reach the one last used. */
|
|
116
|
-
declare function ActiveToolContextProvider({ children, initialActive, }: ActiveToolContextProviderProps): react.JSX.Element;
|
|
117
|
-
/** The active-tool state in scope. Throws outside a provider; use
|
|
118
|
-
* `useOptionalActiveToolContext` where one is not guaranteed. */
|
|
119
|
-
declare function useActiveToolContext(): ActiveToolContextValue;
|
|
120
|
-
/**
|
|
121
|
-
* Like `useActiveToolContext`, but returns `null` when no
|
|
122
|
-
* `<ActiveToolContextProvider>` is in scope instead of throwing. Used by
|
|
123
|
-
* `useStandardActions` to preserve its silent-no-op contract when no provider
|
|
124
|
-
* is present.
|
|
125
|
-
*/
|
|
126
|
-
declare function useOptionalActiveToolContext(): ActiveToolContextValue | null;
|
|
127
|
-
/**
|
|
128
|
-
* Conditional `<ActiveToolContextProvider>` wrapper. Mounts a provider only
|
|
129
|
-
* when no parent provider is in scope — otherwise renders children unwrapped
|
|
130
|
-
* so `<WeaselProvider>` defers to the host's existing scope.
|
|
131
|
-
*/
|
|
132
|
-
declare function ActiveToolContextProviderIfRoot({ children, }: {
|
|
133
|
-
children: ReactNode;
|
|
134
|
-
}): react.JSX.Element;
|
|
135
|
-
|
|
136
88
|
/**
|
|
137
89
|
* @experimental
|
|
138
90
|
* Input scopes and yokes: the tiers between a root registry and a canvas.
|
|
139
|
-
* Design: docs/proposals/2026-09-27-input-scopes.md.
|
|
140
91
|
*/
|
|
141
92
|
|
|
142
93
|
/** Props for `<InputScope>`. */
|
|
@@ -155,7 +106,9 @@ interface InputScopeProps {
|
|
|
155
106
|
*
|
|
156
107
|
* Chrome above calls `trigger` / `begin` / `useActiveToolContext` on its own
|
|
157
108
|
* registries, and those reach the active scope: the one last activated — by a
|
|
158
|
-
* pointerdown or wheel — or the newest where none has been.
|
|
109
|
+
* pointerdown or wheel — or the newest where none has been. Keys are stricter:
|
|
110
|
+
* one that reaches the page goes to the canvas last pressed or scrolled in,
|
|
111
|
+
* across every root on the page, and to none before the first.
|
|
159
112
|
*/
|
|
160
113
|
declare function InputScope({ yoke, children }: InputScopeProps): ReactElement;
|
|
161
114
|
/**
|
|
@@ -206,6 +159,10 @@ declare function Yoke({ value, children }: YokeProps): ReactElement;
|
|
|
206
159
|
* on its own — a click on a swatch, a switch of paint kind.
|
|
207
160
|
* - `cancel()` ends an open edit without keeping it.
|
|
208
161
|
*
|
|
162
|
+
* An edit the dispatcher ended first — a tool switch, Escape, another control
|
|
163
|
+
* committing the same action — counts as not open: the next `input` opens a
|
|
164
|
+
* new one.
|
|
165
|
+
*
|
|
209
166
|
* All three are no-ops when no action is reachable, so a control renders the
|
|
210
167
|
* same with or without a canvas in scope.
|
|
211
168
|
*/
|
|
@@ -294,9 +251,10 @@ interface UseContributionsOptions<TOverlay = KernelOverlay> {
|
|
|
294
251
|
/** Desired focused entry id, or `null` for none. First-mount-wins against
|
|
295
252
|
* the shared `ActiveToolContext` — see `useTools` for the full semantics. */
|
|
296
253
|
focused: string | null;
|
|
297
|
-
/** The modes the canvas can be in. The
|
|
298
|
-
*
|
|
299
|
-
*
|
|
254
|
+
/** The modes the canvas can be in. The active one decides which tagged
|
|
255
|
+
* entries draw their overlays, and the dev-time route-conflict check reads
|
|
256
|
+
* them all to decide whether two gated actions can ever compete; without a
|
|
257
|
+
* registry nothing is barred and the check uses the kit's `DEFAULT_MODES`. */
|
|
300
258
|
modes?: ModeRegistry;
|
|
301
259
|
}
|
|
302
260
|
/** The assembled registry {@link useContributions} returns. */
|
|
@@ -308,10 +266,9 @@ interface ContributionsApi<TOverlay = KernelOverlay> {
|
|
|
308
266
|
/** Focus an entry, or pass `null` to focus none. Writes through to the
|
|
309
267
|
* shared `ActiveToolContext`. */
|
|
310
268
|
setFocused: (id: string | null) => void;
|
|
311
|
-
/** Every live binding, tiered by the declaring entry's eligibility. */
|
|
312
|
-
scopedBindings(): ScopedBinding[];
|
|
313
269
|
/** Overlays of every live entry declaring `position`, ordered active, then
|
|
314
|
-
* hotkey, then ambient.
|
|
270
|
+
* hotkey, then ambient. Live is what the dispatcher routes to: an entry the
|
|
271
|
+
* active mode bars draws nothing. */
|
|
315
272
|
overlays(position?: OverlayPosition): TOverlay[];
|
|
316
273
|
}
|
|
317
274
|
/**
|
|
@@ -334,4 +291,4 @@ declare function useContributions<TOverlay = KernelOverlay>(opts: UseContributio
|
|
|
334
291
|
*/
|
|
335
292
|
declare function useOffhandAction(entries: readonly Contribution<unknown>[]): void;
|
|
336
293
|
|
|
337
|
-
export { ActionsProvider,
|
|
294
|
+
export { ActionsProvider, type ContributionsApi, DepRegistry, DepRegistryProvider, InputScope, type InputScopeProps, type OngoingAction, type ToolsApi, type UseContributionsOptions, UseGestureDispatcherOptions, type UseToolsOptions, Yoke, type YokeProps, useAction, useActionsRegistry, useContributions, useDepRegistry, useDepSource, useGestureDispatcher, useOffhandAction, useOngoingAction, useOptionalDepRegistry, useTools, useYoke };
|