@weasel-js/routing 1.14.2 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.d.ts CHANGED
@@ -1,16 +1,17 @@
1
- import { U as UseGestureDispatcherOptions } from './internal-CaJ4viHc.js';
2
- export { D as DispatcherChannels } from './internal-CaJ4viHc.js';
1
+ import { U as UseGestureDispatcherOptions } from './internal-C8-r3Zmo.js';
2
+ export { D as DispatcherChannels } from './internal-C8-r3Zmo.js';
3
3
  import * as react from 'react';
4
4
  import { ReactNode, ReactElement } from 'react';
5
- import { D as DepRegistry, a as DepName, b as DepSource, S as ScopeNode, A as Action, c as ActionDeps, M as MenuHost, d as ActionsRegistry, e as ActionDisabledReason, B as BoundGesture, f as ActionVariant, g as MenuDef, h as DepNode, T as ToolNode, K as KeymapDescription, W as WorldPoint, N as NodeId, i as KernelOverlay, C as Contribution, j as Tool, O as OverlayPosition } from './index-DJ7SxEKS.js';
6
- export { k as ActiveToolContextProvider, l as ActiveToolContextProviderIfRoot, m as ActiveToolContextProviderProps, n as ActiveToolContextValue, u as useActiveToolContext, o as useOptionalActiveToolContext } from './index-DJ7SxEKS.js';
5
+ import { D as DepRegistry, a as DepName, b as DepSource, S as ScopeNode, L as LastOperationKeeper, A as Action, c as ActionDeps, M as MenuHost, d as ActionsRegistry, e as ActionDisabledReason, B as BoundGesture, f as ActionVariant, g as LastOperationStore, h as LastOperation, i as MenuDef, j as DepNode, T as ToolNode, K as KeymapDescription, W as WorldPoint, N as NodeId, k as KernelOverlay, C as Contribution, l as Tool, O as OverlayPosition } from './index-C2EcHkCp.js';
6
+ export { m as ActiveToolContextProvider, n as ActiveToolContextProviderIfRoot, o as ActiveToolContextProviderProps, p as ActiveToolContextValue, u as useActiveToolContext, q as useOptionalActiveToolContext } from './index-C2EcHkCp.js';
7
7
  import { IconRef } from '@weasel-js/icons';
8
8
  import { ModeRegistry } from '@weasel-js/modes';
9
9
  import '@weasel-js/gestures';
10
10
  import '@weasel-js/cursor';
11
+ import '@weasel-js/history';
12
+ import '@weasel-js/prefs';
11
13
  import '@weasel-js/geom';
12
14
  import '@weasel-js/menu';
13
- import '@weasel-js/prefs';
14
15
  import '@weasel-js/registry';
15
16
  import '@weasel-js/select';
16
17
 
@@ -60,12 +61,17 @@ declare class ActionsNode extends ScopeNode<ActionsNode> {
60
61
  private host;
61
62
  /** Called on `activate`, so a scope's dep and tool nodes move with it. */
62
63
  readonly onActivate: (() => void)[];
64
+ /** The tree's last operation, kept at its root. */
65
+ readonly lastOperation: LastOperationKeeper;
66
+ private opHost;
63
67
  constructor(parent: ActionsNode | null, descends: boolean, contextDeps: () => DepRegistry | null);
64
68
  private resolve;
65
69
  /** Every tuning for `id` from the root down to here, root first. */
66
70
  private tuningsOf;
67
71
  /** `a` under every tuning for its id from the root down to here. */
68
72
  private tuned;
73
+ /** How the last operation reads and reruns what ran in this scope. */
74
+ private lastOperationHost;
69
75
  /** Every binding override from the root down to here, read into specs. */
70
76
  private overridesHere;
71
77
  private snapshot;
@@ -127,6 +133,14 @@ interface CommandOptions {
127
133
  */
128
134
  declare function useCommand(id: string, label: string, run: (params?: Readonly<Record<string, unknown>>) => void, options?: CommandOptions): void;
129
135
 
136
+ /** The last operation store of the actions tree `registry` belongs to, or `null` for a registry that is not one
137
+ * of the kit's. */
138
+ declare function lastOperationOf(registry: ActionsRegistry | null): LastOperationStore | null;
139
+ /** The last operation store of the actions tree in scope, or `null` outside an `<ActionsProvider>`. */
140
+ declare function useLastOperationStore(): LastOperationStore | null;
141
+ /** What `store` holds now, rerendering as it changes; `store` defaults to the one in scope. */
142
+ declare function useLastOperation(store?: LastOperationStore | null): LastOperation | null;
143
+
130
144
  /** The menu host in scope: the nearest `<ActionsProvider>`'s, else the one a
131
145
  * canvas with no provider above it makes. Null outside both. */
132
146
  declare function useMenuHost(): MenuHost | null;
@@ -329,8 +343,10 @@ interface UseToolsOptions<TOverlay = KernelOverlay> {
329
343
  /** Always-on entries — tools or other contributions, live regardless of
330
344
  * the active slot. */
331
345
  ambient?: readonly Contribution<TOverlay>[];
332
- /** The modes the canvas can be in, read by the dev-time route-conflict
333
- * check; without a registry it checks against the kit's `DEFAULT_MODES`. */
346
+ /** The modes the canvas can be in. A mode's `defaultTool` becomes active
347
+ * as the mode is entered, and the tool it replaced comes back as the mode
348
+ * is left. The dev-time route-conflict check reads them too; without a
349
+ * registry it checks against the kit's `DEFAULT_MODES`. */
334
350
  modes?: ModeRegistry;
335
351
  }
336
352
  /** The tool registry's runtime surface: which tool is active, which is
@@ -419,4 +435,4 @@ declare function useContributions<TOverlay = KernelOverlay>(opts: UseContributio
419
435
  */
420
436
  declare function useOffhandAction(entries: readonly Contribution<unknown>[]): void;
421
437
 
422
- export { ActionsProvider, type CommandOptions, type ContributionsApi, DepRegistry, DepRegistryProvider, InputScope, type InputScopeProps, type OngoingAction, PointerContextProvider, type PointerContextValue, type PointerWorldPos, SelectionContextProvider, SelectionContextProviderIfRoot, type SelectionContextValue, type ToolsApi, type UseContributionsOptions, UseGestureDispatcherOptions, type UseToolsOptions, Yoke, type YokeProps, createPointerStore, useAction, useActionsRegistry, useCommand, useContributions, useDepRegistry, useDepSource, useGestureDispatcher, useMenuHost, useMenus, useOffhandAction, useOngoingAction, useOpenKeymap, useOptionalDepRegistry, usePointerContext, usePointerPosition, usePublishSelection, useSelectionContext, useTools, useYoke };
438
+ export { ActionsProvider, type CommandOptions, type ContributionsApi, DepRegistry, DepRegistryProvider, InputScope, type InputScopeProps, type OngoingAction, PointerContextProvider, type PointerContextValue, type PointerWorldPos, SelectionContextProvider, SelectionContextProviderIfRoot, type SelectionContextValue, type ToolsApi, type UseContributionsOptions, UseGestureDispatcherOptions, type UseToolsOptions, Yoke, type YokeProps, createPointerStore, lastOperationOf, useAction, useActionsRegistry, useCommand, useContributions, useDepRegistry, useDepSource, useGestureDispatcher, useLastOperation, useLastOperationStore, useMenuHost, useMenus, useOffhandAction, useOngoingAction, useOpenKeymap, useOptionalDepRegistry, usePointerContext, usePointerPosition, usePublishSelection, useSelectionContext, useTools, useYoke };
package/dist/react.js CHANGED
@@ -1,14 +1,14 @@
1
- import { createKeymapStack, createDispatchTrace, createDispatcher, describeKeymap, assembleEntries, liveScope, offhandKeyFor, makeToolOffhandAction, buildToolOffhandBindings, dlog, reportRouteConflicts, isTool, clientToCanvas, LONG_PRESS_HAPTIC_MS, multiTouchGestureId, openPointerSession, addCarrySurface, carryGestureId, anyBinding, isEditableTarget, focusedControlOwnsKey, itemsFromClipboardData, itemsFromDataTransfer, itemTypesFromDataTransfer, immediateParams } from './chunk-HR5F4LBX.js';
2
- import { useDepRegistry, actionsNodeOf, useAction, useActionsRegistry, useOptionalDepRegistry, ActionsNode, MenuHostContext, DepRegistryContext, ActionsContext } from './chunk-INZ63QGX.js';
3
- export { ActionsProvider, DepRegistryProvider, useAction, useActionsRegistry, useDepRegistry, useDepSource, useMenuHost, useMenus, useOptionalDepRegistry } from './chunk-INZ63QGX.js';
4
- import { NO_OVERRIDES, ScopeNode, depNodeOf, DepNode, actionCursor, buildDepsFromRequires } from './chunk-EUCNVGJV.js';
1
+ import { createKeymapStack, createDispatchTrace, createDispatcher, describeKeymap, assembleEntries, liveScope, offhandKeyFor, makeToolOffhandAction, buildToolOffhandBindings, dlog, reportRouteConflicts, isTool, clientToCanvas, LONG_PRESS_HAPTIC_MS, multiTouchGestureId, openPointerSession, addCarrySurface, carryGestureId, anyBinding, isEditableTarget, focusedControlOwnsKey, itemsFromClipboardData, itemsFromDataTransfer, itemTypesFromDataTransfer, immediateParams } from './chunk-ZK72IBMI.js';
2
+ import { useDepRegistry, actionsNodeOf, useAction, useActionsRegistry, useOptionalDepRegistry, ActionsNode, MenuHostContext, DepRegistryContext, ActionsContext } from './chunk-3SPPC6UP.js';
3
+ export { ActionsProvider, DepRegistryProvider, useAction, useActionsRegistry, useDepRegistry, useDepSource, useMenuHost, useMenus, useOptionalDepRegistry } from './chunk-3SPPC6UP.js';
4
+ import { NO_OVERRIDES, ScopeNode, depNodeOf, DepNode, actionCursor, buildDepsFromRequires } from './chunk-A6MN3I6R.js';
5
5
  import { createContext, useState, useContext, useEffect, useRef, useMemo, useCallback, useSyncExternalStore } from 'react';
6
6
  import { isApplePlatform, heldPointers, createPointerRecognizer, isInControlWithin, longPressEvents, stylusOf } from '@weasel-js/gestures';
7
7
  import { useLatest, useKitSettings, useStableByContent, sameList, isDev } from '@weasel-js/react';
8
8
  import { jsx, Fragment } from 'react/jsx-runtime';
9
9
  import { createCursorSink, resolveCursorTier } from '@weasel-js/cursor';
10
10
  import { deepEqual, createEmitter } from '@weasel-js/registry';
11
- import { activeModeOf } from '@weasel-js/modes';
11
+ import { activeModeOf, defaultToolOf } from '@weasel-js/modes';
12
12
 
13
13
  var ToolNode = class extends ScopeNode {
14
14
  state;
@@ -596,6 +596,46 @@ function keymapKeys(stack) {
596
596
  };
597
597
  }
598
598
 
599
+ // src/interactions/dispatcher/channels/overKeys.ts
600
+ var OVER_ONLY = { held: false, over: true };
601
+ function followPointer(core, down, held) {
602
+ const arrive = (e) => {
603
+ for (const [physical, { key, code }] of down) {
604
+ if (held.has(physical)) continue;
605
+ core.routeAt(null, e.clientX, e.clientY);
606
+ const on = core.dispatcherNow();
607
+ const codeField = code !== void 0 ? { code } : {};
608
+ const ev = {
609
+ kind: "key-held",
610
+ key,
611
+ ...codeField,
612
+ phase: "down",
613
+ altKey: e.altKey,
614
+ ctrlKey: e.ctrlKey,
615
+ metaKey: e.metaKey,
616
+ shiftKey: e.shiftKey
617
+ };
618
+ if (core.dispatch(ev, on, OVER_ONLY) === "handled") held.set(physical, { key, ...codeField, on });
619
+ }
620
+ };
621
+ const leave = () => {
622
+ for (const [physical, { key, code, on }] of [...held]) {
623
+ const ev = {
624
+ kind: "key-held",
625
+ key,
626
+ ...code !== void 0 ? { code } : {},
627
+ phase: "up",
628
+ altKey: false,
629
+ ctrlKey: false,
630
+ metaKey: false,
631
+ shiftKey: false
632
+ };
633
+ if (core.dispatch(ev, on, OVER_ONLY) === "handled") held.delete(physical);
634
+ }
635
+ };
636
+ return { arrive, leave };
637
+ }
638
+
599
639
  // src/interactions/dispatcher/channels/keyboard.ts
600
640
  function typedWithAltGraph(e) {
601
641
  if (!e.ctrlKey || !e.altKey || e.key.length !== 1 || !e.getModifierState?.("AltGraph")) return false;
@@ -613,10 +653,12 @@ var inMenu = (t) => t instanceof Element && t.closest('[role="menu"]') !== null;
613
653
  function keyboardInput(core) {
614
654
  const { dispatch, dispatcherNow } = core;
615
655
  const heldKeys = /* @__PURE__ */ new Map();
656
+ const downKeys = /* @__PURE__ */ new Map();
616
657
  const keymaps = keymapKeys(core.keymaps);
617
658
  const codeOf = (e) => e.code ? { code: e.code } : {};
618
659
  const physicalKey = (e) => e.code || e.key.toLowerCase();
619
660
  const onKeyDown = (e) => {
661
+ downKeys.set(physicalKey(e), { key: e.key, ...codeOf(e) });
620
662
  if (e.defaultPrevented || isEditableTarget(e.target) || inMenu(e.target) || focusedControlOwnsKey(e)) return;
621
663
  const reach = { held: core.holdsKeys(), over: core.pointerOver() };
622
664
  if (!reach.held && !reach.over) return;
@@ -642,6 +684,7 @@ function keyboardInput(core) {
642
684
  };
643
685
  const onKeyUp = (e) => {
644
686
  const physical = physicalKey(e);
687
+ downKeys.delete(physical);
645
688
  keymaps.up(e, physical);
646
689
  const down = heldKeys.get(physical);
647
690
  if (down === void 0) return;
@@ -659,6 +702,7 @@ function keyboardInput(core) {
659
702
  dispatch(ev, down.on);
660
703
  };
661
704
  const releaseAll = () => {
705
+ downKeys.clear();
662
706
  keymaps.clear();
663
707
  const releasing = [...heldKeys.values()];
664
708
  heldKeys.clear();
@@ -676,7 +720,7 @@ function keyboardInput(core) {
676
720
  dispatch(ev, on);
677
721
  }
678
722
  };
679
- return { onKeyDown, onKeyUp, releaseAll };
723
+ return { onKeyDown, onKeyUp, releaseAll, ...followPointer(core, downKeys, heldKeys) };
680
724
  }
681
725
 
682
726
  // src/interactions/dispatcher/channels/listeners.ts
@@ -1001,12 +1045,7 @@ function attachInput(canvas, src, channels) {
1001
1045
  listeners.on(canvas, "pointerdown", () => {
1002
1046
  src.actions().activate();
1003
1047
  });
1004
- const setOver = (on) => () => {
1005
- core.setPointerOver(on);
1006
- };
1007
- listeners.on(canvas, "pointerenter", setOver(true));
1008
- listeners.on(canvas, "pointermove", setOver(true));
1009
- listeners.on(canvas, "pointerleave", setOver(false));
1048
+ trackPointerOver(listeners, canvas, core, keys);
1010
1049
  if (channels.pointer) {
1011
1050
  listeners.on(window, "keydown", hover.onKey);
1012
1051
  listeners.on(window, "keyup", hover.onKey);
@@ -1057,6 +1096,19 @@ function attachInput(canvas, src, channels) {
1057
1096
  src.context().keymaps?.closeAll();
1058
1097
  };
1059
1098
  }
1099
+ function trackPointerOver(listeners, canvas, core, keys) {
1100
+ const onOver = (e) => {
1101
+ if (core.pointerOver()) return;
1102
+ core.setPointerOver(true);
1103
+ keys.arrive(e);
1104
+ };
1105
+ listeners.on(canvas, "pointerenter", onOver);
1106
+ listeners.on(canvas, "pointermove", onOver);
1107
+ listeners.on(canvas, "pointerleave", () => {
1108
+ core.setPointerOver(false);
1109
+ keys.leave();
1110
+ });
1111
+ }
1060
1112
 
1061
1113
  // src/interactions/dispatcher/useGestureDispatcher.tsx
1062
1114
  function useGestureDispatcher(opts) {
@@ -1215,6 +1267,20 @@ function bindingOf(keys) {
1215
1267
  return "spec" in keys ? [keys] : keys;
1216
1268
  }
1217
1269
  var isList = (keys) => Array.isArray(keys);
1270
+ var NO_STORE = () => null;
1271
+ var NO_SUBSCRIBE2 = () => () => {
1272
+ };
1273
+ function lastOperationOf(registry) {
1274
+ return actionsNodeOf(registry)?.lastOperation ?? null;
1275
+ }
1276
+ function useLastOperationStore() {
1277
+ return lastOperationOf(useActionsRegistry());
1278
+ }
1279
+ function useLastOperation(store) {
1280
+ const inScope = useLastOperationStore();
1281
+ const s = store === void 0 ? inScope : store;
1282
+ return useSyncExternalStore(s?.subscribe ?? NO_SUBSCRIBE2, s?.get ?? NO_STORE, s?.get ?? NO_STORE);
1283
+ }
1218
1284
  function useNodes(descends, ownsTool, parent) {
1219
1285
  const parentActions = useActionsRegistry();
1220
1286
  const parentDeps = useOptionalDepRegistry();
@@ -1411,7 +1477,7 @@ function usePublishSelection(ids, kinds) {
1411
1477
  }
1412
1478
  }, [ctxRef, serializedRef]);
1413
1479
  }
1414
- var NO_SUBSCRIBE2 = () => () => {
1480
+ var NO_SUBSCRIBE3 = () => () => {
1415
1481
  };
1416
1482
  function useContributions(opts) {
1417
1483
  const ctx = useActiveToolContext();
@@ -1433,7 +1499,7 @@ function useContributions(opts) {
1433
1499
  }, [ctx]);
1434
1500
  const modes = opts.modes;
1435
1501
  const modeVersion = useSyncExternalStore(
1436
- modes ? modes.subscribe : NO_SUBSCRIBE2,
1502
+ modes ? modes.subscribe : NO_SUBSCRIBE3,
1437
1503
  () => modes?.getVersion() ?? 0
1438
1504
  );
1439
1505
  const openKeymap = useInnermostKeymap()?.id ?? null;
@@ -1505,6 +1571,32 @@ function useOffhandAction(entries) {
1505
1571
  return actionsRegistry.register(makeToolOffhandAction(buildToolOffhandBindings(specs)));
1506
1572
  }, [actionsRegistry, offhandSig]);
1507
1573
  }
1574
+ function useModeDefaultTool(modes, tools) {
1575
+ const toolsRef = useLatest(tools);
1576
+ const entered = useRef(null);
1577
+ useEffect(() => {
1578
+ if (!modes) return;
1579
+ const enter = (next, from) => {
1580
+ const target = defaultToolOf(next, toolsRef.current.has);
1581
+ entered.current = { mode: next, restore: target === null ? void 0 : from };
1582
+ const want = target ?? from;
1583
+ if (want !== toolsRef.current.active) toolsRef.current.setActive(want);
1584
+ };
1585
+ const follow = () => {
1586
+ const next = modes.current();
1587
+ const prior = entered.current;
1588
+ if (prior?.mode === next) return;
1589
+ const restore = prior?.restore;
1590
+ const back = restore !== void 0 && (restore === null || toolsRef.current.has(restore));
1591
+ enter(next, back ? restore : toolsRef.current.active);
1592
+ };
1593
+ if (entered.current === null) enter(modes.current(), toolsRef.current.active ?? toolsRef.current.initial);
1594
+ follow();
1595
+ return modes.subscribe(follow);
1596
+ }, [modes, toolsRef]);
1597
+ }
1598
+
1599
+ // src/tools/useTools.ts
1508
1600
  function declareSlot(tool, slot) {
1509
1601
  const hotkey = tool.def?.hotkey;
1510
1602
  const eligibility = {
@@ -1591,6 +1683,7 @@ function useTools(opts) {
1591
1683
  (id) => id in slotted.registry || slotted.ambient.some((t) => t.id === id),
1592
1684
  [slotted]
1593
1685
  );
1686
+ useModeDefaultTool(opts.modes, { active: contributions.focused, initial: initialActive, setActive, has });
1594
1687
  const active = contributions.focused;
1595
1688
  const getActiveOverlays = contributions.overlays;
1596
1689
  return useMemo(
@@ -1609,6 +1702,6 @@ function useTools(opts) {
1609
1702
  );
1610
1703
  }
1611
1704
 
1612
- export { ActiveToolContextProvider, ActiveToolContextProviderIfRoot, InputScope, PointerContextProvider, SelectionContextProvider, SelectionContextProviderIfRoot, Yoke, createPointerStore, useActiveToolContext, useCommand, useContributions, useGestureDispatcher, useOffhandAction, useOngoingAction, useOpenKeymap, useOptionalActiveToolContext, usePointerContext, usePointerPosition, usePublishSelection, useSelectionContext, useTools, useYoke };
1705
+ export { ActiveToolContextProvider, ActiveToolContextProviderIfRoot, InputScope, PointerContextProvider, SelectionContextProvider, SelectionContextProviderIfRoot, Yoke, createPointerStore, lastOperationOf, useActiveToolContext, useCommand, useContributions, useGestureDispatcher, useLastOperation, useLastOperationStore, useOffhandAction, useOngoingAction, useOpenKeymap, useOptionalActiveToolContext, usePointerContext, usePointerPosition, usePublishSelection, useSelectionContext, useTools, useYoke };
1613
1706
  //# sourceMappingURL=react.js.map
1614
1707
  //# sourceMappingURL=react.js.map