@djangocfg/ui-tools 2.1.416 → 2.1.418

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.
Files changed (66) hide show
  1. package/dist/audio-player/index.cjs +2098 -0
  2. package/dist/audio-player/index.cjs.map +1 -0
  3. package/dist/audio-player/index.css +65 -0
  4. package/dist/audio-player/index.css.map +1 -0
  5. package/dist/audio-player/index.d.cts +166 -0
  6. package/dist/audio-player/index.d.ts +166 -0
  7. package/dist/audio-player/index.mjs +2075 -0
  8. package/dist/audio-player/index.mjs.map +1 -0
  9. package/dist/composer-registry/index.cjs +45 -0
  10. package/dist/composer-registry/index.cjs.map +1 -0
  11. package/dist/composer-registry/index.d.cts +73 -0
  12. package/dist/composer-registry/index.d.ts +73 -0
  13. package/dist/composer-registry/index.mjs +39 -0
  14. package/dist/composer-registry/index.mjs.map +1 -0
  15. package/dist/tree/index.cjs +82 -63
  16. package/dist/tree/index.cjs.map +1 -1
  17. package/dist/tree/index.d.cts +15 -1
  18. package/dist/tree/index.d.ts +15 -1
  19. package/dist/tree/index.mjs +83 -64
  20. package/dist/tree/index.mjs.map +1 -1
  21. package/package.json +38 -17
  22. package/src/tools/chat/composer/Composer.tsx +8 -8
  23. package/src/tools/chat/context/ChatProvider.tsx +13 -78
  24. package/src/tools/chat/hooks/useAutoFocusOnStreamEnd.ts +12 -15
  25. package/src/tools/chat/hooks/useFocusOnEmptyClick.ts +4 -5
  26. package/src/tools/chat/launcher/header/ChatHeader.tsx +14 -19
  27. package/src/tools/chat/launcher/header/ChatHeaderActionButton.tsx +8 -12
  28. package/src/tools/data/Tree/TreeRoot.tsx +33 -109
  29. package/src/tools/data/Tree/context/TreeContext.tsx +22 -3
  30. package/src/tools/data/Tree/context/menu/index.ts +1 -0
  31. package/src/tools/data/Tree/context/menu/render.tsx +75 -0
  32. package/src/tools/data/Tree/context/menu/use-resolved-menu.ts +16 -2
  33. package/src/tools/data/Tree/index.tsx +1 -0
  34. package/src/tools/data/Tree/types/index.ts +1 -1
  35. package/src/tools/data/Tree/types/root-props.ts +16 -0
  36. package/src/tools/dev/OpenapiViewer/components/DocsLayout/EndpointDoc/Header/MetaActions.tsx +6 -9
  37. package/src/tools/dev/OpenapiViewer/components/DocsLayout/index.tsx +2 -4
  38. package/src/tools/forms/CodeEditor/components/Editor.tsx +19 -0
  39. package/src/tools/forms/CodeEditor/types/index.ts +7 -0
  40. package/src/tools/forms/MarkdownEditor/MarkdownEditor.tsx +40 -0
  41. package/src/tools/forms/MarkdownEditor/styles.css +174 -21
  42. package/src/tools/forms/NotionEditor/CustomKeymap.ts +48 -0
  43. package/src/tools/forms/NotionEditor/LinkDialog.tsx +133 -0
  44. package/src/tools/forms/NotionEditor/NotionEditor.tsx +304 -0
  45. package/src/tools/forms/NotionEditor/SlashExtension.ts +32 -0
  46. package/src/tools/forms/NotionEditor/SlashList.tsx +136 -0
  47. package/src/tools/forms/NotionEditor/TaskItemView.tsx +41 -0
  48. package/src/tools/forms/NotionEditor/createSlashSuggestion.ts +121 -0
  49. package/src/tools/forms/NotionEditor/extensions.ts +105 -0
  50. package/src/tools/forms/NotionEditor/index.ts +1 -0
  51. package/src/tools/forms/NotionEditor/lazy.tsx +44 -0
  52. package/src/tools/forms/NotionEditor/slashItems.ts +159 -0
  53. package/src/tools/forms/NotionEditor/styles.css +478 -0
  54. package/src/tools/forms/NotionEditor/types.ts +28 -0
  55. package/src/tools/input/SpeechRecognition/widgets/VoiceComposerSlot.tsx +11 -12
  56. package/src/tools/integration/ComposerRegistry/index.ts +105 -0
  57. package/src/tools/media/AudioPlayer/Player.tsx +2 -0
  58. package/src/tools/media/AudioPlayer/PlayerShell.tsx +29 -22
  59. package/src/tools/media/AudioPlayer/lazy.tsx +30 -42
  60. package/src/tools/media/AudioPlayer/parts/Controls/IconButton.tsx +10 -11
  61. package/src/tools/media/AudioPlayer/parts/Controls/VolumeControl.tsx +52 -115
  62. package/src/tools/media/AudioPlayer/types.ts +8 -0
  63. package/src/tools/media/ImageViewer/components/ImageViewer.tsx +8 -0
  64. package/src/tools/media/ImageViewer/types.ts +4 -0
  65. package/src/tools/media/VideoPlayer/VideoPlayer.tsx +20 -1
  66. package/src/tools/media/VideoPlayer/types.ts +4 -0
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Minimal imperative handle every text-editor surface implements so
3
+ * an external tool (voice dictation, command palette, AI suggestion)
4
+ * can read/write its text content without traversing React.
5
+ *
6
+ * Methods are optional so a host can register a partial handle
7
+ * (e.g. only `getValue` + `setValue`), and the caller checks before use.
8
+ */
9
+ interface ComposerHandle {
10
+ /** Move keyboard focus into the composer's editable surface. */
11
+ focus: () => void;
12
+ /** Move the caret to the very end of the input. */
13
+ moveCursorToEnd?: () => void;
14
+ /** Read the current draft text. Voice dictation anchors partial
15
+ * transcripts onto the user's already-typed prefix via this. */
16
+ getValue?: () => string;
17
+ /** Replace the current draft text. Voice dictation pushes interim
18
+ * and final transcripts through this without owning a controlled
19
+ * binding. */
20
+ setValue?: (value: string) => void;
21
+ }
22
+ /**
23
+ * `@djangocfg/ui-tools/composer-registry`
24
+ *
25
+ * Cross-tool bridge: the currently-active text composer's handle.
26
+ *
27
+ * Producer side (`@djangocfg/ui-tools/chat` and TipTap hosts):
28
+ * register their composer's imperative handle via `attachComposer`.
29
+ *
30
+ * Consumer side (`@djangocfg/ui-tools/speech-recognition`):
31
+ * reads the active handle via `useActiveComposer`/`getActiveComposer`
32
+ * and pipes voice transcripts into it.
33
+ *
34
+ * Why this lives in its own subpath (not inside `chat`)
35
+ * ----------------------------------------------------
36
+ * `chat` and `speech-recognition` are sibling subpath exports. If the
37
+ * registry lived inside `chat`, then `speech-recognition` would have
38
+ * to reach into it via a cross-tool relative import — and under Vite
39
+ * dev's dependency optimizer that file ends up loaded TWICE (once via
40
+ * the `./chat` URL, once via the `./speech-recognition` relative-up
41
+ * URL), giving the producer and the consumer two separate `let active`
42
+ * slots. The active handle registered by chat would be invisible to
43
+ * speech-recognition (and vice versa).
44
+ *
45
+ * Putting the registry in its own dedicated subpath (a single tool
46
+ * that NEITHER chat nor speech-recognition cross-import — they both
47
+ * import this one as their dependency) means Vite resolves it from a
48
+ * single URL across the whole graph. One module instance, one shared
49
+ * `active` slot.
50
+ *
51
+ * Semantics: one active composer per realm. The most recent
52
+ * `registerComposer(handle)` wins; `registerComposer(null)` clears it.
53
+ */
54
+ type Listener = (handle: ComposerHandle | null) => void;
55
+ /** Set or replace the active composer handle. Pass `null` to clear. */
56
+ declare function registerComposer(handle: ComposerHandle | null): void;
57
+ /**
58
+ * Convenience for components: register on mount, unregister on
59
+ * unmount. Returns a cleanup function suitable for `useEffect`.
60
+ */
61
+ declare function attachComposer(handle: ComposerHandle): () => void;
62
+ /** Read the current active handle (no subscription). */
63
+ declare function getActiveComposer(): ComposerHandle | null;
64
+ /** Subscribe to handle changes; returns an unsubscribe fn. */
65
+ declare function subscribeComposer(listener: Listener): () => void;
66
+ /**
67
+ * React hook: re-renders the caller whenever the active composer
68
+ * changes. Built on `useSyncExternalStore` so concurrent rendering,
69
+ * SSR, and dev-mode strict-effects all behave correctly.
70
+ */
71
+ declare function useActiveComposer(): ComposerHandle | null;
72
+
73
+ export { type ComposerHandle, attachComposer, getActiveComposer, registerComposer, subscribeComposer, useActiveComposer };
@@ -0,0 +1,39 @@
1
+ import { __name } from '../chunk-PAWJFY3S.mjs';
2
+ import { useCallback, useSyncExternalStore } from 'react';
3
+
4
+ var active = null;
5
+ var listeners = /* @__PURE__ */ new Set();
6
+ function registerComposer(handle) {
7
+ active = handle;
8
+ for (const fn of listeners) fn(active);
9
+ }
10
+ __name(registerComposer, "registerComposer");
11
+ function attachComposer(handle) {
12
+ registerComposer(handle);
13
+ return () => {
14
+ if (active === handle) registerComposer(null);
15
+ };
16
+ }
17
+ __name(attachComposer, "attachComposer");
18
+ function getActiveComposer() {
19
+ return active;
20
+ }
21
+ __name(getActiveComposer, "getActiveComposer");
22
+ function subscribeComposer(listener) {
23
+ listeners.add(listener);
24
+ return () => {
25
+ listeners.delete(listener);
26
+ };
27
+ }
28
+ __name(subscribeComposer, "subscribeComposer");
29
+ function useActiveComposer() {
30
+ const subscribe = useCallback((onChange) => {
31
+ return subscribeComposer(onChange);
32
+ }, []);
33
+ return useSyncExternalStore(subscribe, getActiveComposer, () => null);
34
+ }
35
+ __name(useActiveComposer, "useActiveComposer");
36
+
37
+ export { attachComposer, getActiveComposer, registerComposer, subscribeComposer, useActiveComposer };
38
+ //# sourceMappingURL=index.mjs.map
39
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/tools/integration/ComposerRegistry/index.ts"],"names":[],"mappings":";;;AA6DA,IAAI,MAAA,GAAgC,IAAA;AACpC,IAAM,SAAA,uBAAgB,GAAA,EAAc;AAG7B,SAAS,iBAAiB,MAAA,EAAqC;AACpE,EAAA,MAAA,GAAS,MAAA;AACT,EAAA,KAAA,MAAW,EAAA,IAAM,SAAA,EAAW,EAAA,CAAG,MAAM,CAAA;AACvC;AAHgB,MAAA,CAAA,gBAAA,EAAA,kBAAA,CAAA;AAST,SAAS,eAAe,MAAA,EAAoC;AACjE,EAAA,gBAAA,CAAiB,MAAM,CAAA;AACvB,EAAA,OAAO,MAAM;AACX,IAAA,IAAI,MAAA,KAAW,MAAA,EAAQ,gBAAA,CAAiB,IAAI,CAAA;AAAA,EAC9C,CAAA;AACF;AALgB,MAAA,CAAA,cAAA,EAAA,gBAAA,CAAA;AAQT,SAAS,iBAAA,GAA2C;AACzD,EAAA,OAAO,MAAA;AACT;AAFgB,MAAA,CAAA,iBAAA,EAAA,mBAAA,CAAA;AAKT,SAAS,kBAAkB,QAAA,EAAgC;AAChE,EAAA,SAAA,CAAU,IAAI,QAAQ,CAAA;AACtB,EAAA,OAAO,MAAM;AACX,IAAA,SAAA,CAAU,OAAO,QAAQ,CAAA;AAAA,EAC3B,CAAA;AACF;AALgB,MAAA,CAAA,iBAAA,EAAA,mBAAA,CAAA;AAYT,SAAS,iBAAA,GAA2C;AACzD,EAAA,MAAM,SAAA,GAAY,WAAA,CAAY,CAAC,QAAA,KAAyB;AACtD,IAAA,OAAO,kBAAkB,QAAQ,CAAA;AAAA,EACnC,CAAA,EAAG,EAAE,CAAA;AACL,EAAA,OAAO,oBAAA,CAAqB,SAAA,EAAW,iBAAA,EAAmB,MAAM,IAAI,CAAA;AACtE;AALgB,MAAA,CAAA,iBAAA,EAAA,mBAAA,CAAA","file":"index.mjs","sourcesContent":["'use client';\n\nimport { useCallback, useSyncExternalStore } from 'react';\n\n/**\n * Minimal imperative handle every text-editor surface implements so\n * an external tool (voice dictation, command palette, AI suggestion)\n * can read/write its text content without traversing React.\n *\n * Methods are optional so a host can register a partial handle\n * (e.g. only `getValue` + `setValue`), and the caller checks before use.\n */\nexport interface ComposerHandle {\n /** Move keyboard focus into the composer's editable surface. */\n focus: () => void;\n /** Move the caret to the very end of the input. */\n moveCursorToEnd?: () => void;\n /** Read the current draft text. Voice dictation anchors partial\n * transcripts onto the user's already-typed prefix via this. */\n getValue?: () => string;\n /** Replace the current draft text. Voice dictation pushes interim\n * and final transcripts through this without owning a controlled\n * binding. */\n setValue?: (value: string) => void;\n}\n\n/**\n * `@djangocfg/ui-tools/composer-registry`\n *\n * Cross-tool bridge: the currently-active text composer's handle.\n *\n * Producer side (`@djangocfg/ui-tools/chat` and TipTap hosts):\n * register their composer's imperative handle via `attachComposer`.\n *\n * Consumer side (`@djangocfg/ui-tools/speech-recognition`):\n * reads the active handle via `useActiveComposer`/`getActiveComposer`\n * and pipes voice transcripts into it.\n *\n * Why this lives in its own subpath (not inside `chat`)\n * ----------------------------------------------------\n * `chat` and `speech-recognition` are sibling subpath exports. If the\n * registry lived inside `chat`, then `speech-recognition` would have\n * to reach into it via a cross-tool relative import — and under Vite\n * dev's dependency optimizer that file ends up loaded TWICE (once via\n * the `./chat` URL, once via the `./speech-recognition` relative-up\n * URL), giving the producer and the consumer two separate `let active`\n * slots. The active handle registered by chat would be invisible to\n * speech-recognition (and vice versa).\n *\n * Putting the registry in its own dedicated subpath (a single tool\n * that NEITHER chat nor speech-recognition cross-import — they both\n * import this one as their dependency) means Vite resolves it from a\n * single URL across the whole graph. One module instance, one shared\n * `active` slot.\n *\n * Semantics: one active composer per realm. The most recent\n * `registerComposer(handle)` wins; `registerComposer(null)` clears it.\n */\n\ntype Listener = (handle: ComposerHandle | null) => void;\n\nlet active: ComposerHandle | null = null;\nconst listeners = new Set<Listener>();\n\n/** Set or replace the active composer handle. Pass `null` to clear. */\nexport function registerComposer(handle: ComposerHandle | null): void {\n active = handle;\n for (const fn of listeners) fn(active);\n}\n\n/**\n * Convenience for components: register on mount, unregister on\n * unmount. Returns a cleanup function suitable for `useEffect`.\n */\nexport function attachComposer(handle: ComposerHandle): () => void {\n registerComposer(handle);\n return () => {\n if (active === handle) registerComposer(null);\n };\n}\n\n/** Read the current active handle (no subscription). */\nexport function getActiveComposer(): ComposerHandle | null {\n return active;\n}\n\n/** Subscribe to handle changes; returns an unsubscribe fn. */\nexport function subscribeComposer(listener: Listener): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\n/**\n * React hook: re-renders the caller whenever the active composer\n * changes. Built on `useSyncExternalStore` so concurrent rendering,\n * SSR, and dev-mode strict-effects all behave correctly.\n */\nexport function useActiveComposer(): ComposerHandle | null {\n const subscribe = useCallback((onChange: () => void) => {\n return subscribeComposer(onChange);\n }, []);\n return useSyncExternalStore(subscribe, getActiveComposer, () => null);\n}\n"]}
@@ -5,9 +5,9 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  var chunkPK6SKIKE_cjs = require('../chunk-PK6SKIKE.cjs');
6
6
  var React = require('react');
7
7
  var lib = require('@djangocfg/ui-core/lib');
8
- var components = require('@djangocfg/ui-core/components');
9
8
  var dialogService = require('@djangocfg/ui-core/lib/dialog-service');
10
9
  var lucideReact = require('lucide-react');
10
+ var components = require('@djangocfg/ui-core/components');
11
11
  var jsxRuntime = require('react/jsx-runtime');
12
12
  var core = require('@dnd-kit/core');
13
13
  var hooks = require('@djangocfg/ui-core/hooks');
@@ -1071,6 +1071,47 @@ function useResolvedMenu(opts) {
1071
1071
  ]);
1072
1072
  }
1073
1073
  chunkPK6SKIKE_cjs.__name(useResolvedMenu, "useResolvedMenu");
1074
+ function renderItemsAsContextMenu(rowProps, items, trigger) {
1075
+ return /* @__PURE__ */ jsxRuntime.jsxs(components.ContextMenu, { children: [
1076
+ /* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuTrigger, { asChild: true, children: trigger }),
1077
+ /* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuContent, { children: items.map((item, idx) => {
1078
+ if (item === "separator") {
1079
+ return /* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuSeparator, {}, `sep-${idx}`);
1080
+ }
1081
+ const Icon = item.icon;
1082
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1083
+ components.ContextMenuItem,
1084
+ {
1085
+ disabled: item.disabled,
1086
+ variant: item.destructive ? "destructive" : void 0,
1087
+ onSelect: () => item.onSelect(rowProps),
1088
+ children: [
1089
+ Icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, {}) : null,
1090
+ item.label,
1091
+ item.shortcut ? /* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuShortcut, { children: item.shortcut }) : null
1092
+ ]
1093
+ },
1094
+ item.id
1095
+ );
1096
+ }) })
1097
+ ] });
1098
+ }
1099
+ chunkPK6SKIKE_cjs.__name(renderItemsAsContextMenu, "renderItemsAsContextMenu");
1100
+ function tidyMenuItems(items) {
1101
+ const out = [];
1102
+ for (const it of items) {
1103
+ if (it === "separator") {
1104
+ if (out.length === 0) continue;
1105
+ if (out[out.length - 1] === "separator") continue;
1106
+ out.push(it);
1107
+ } else {
1108
+ out.push(it);
1109
+ }
1110
+ }
1111
+ while (out.length > 0 && out[out.length - 1] === "separator") out.pop();
1112
+ return out;
1113
+ }
1114
+ chunkPK6SKIKE_cjs.__name(tidyMenuItems, "tidyMenuItems");
1074
1115
 
1075
1116
  // src/tools/data/Tree/data/dnd.ts
1076
1117
  function resolveDropZone(input) {
@@ -1435,6 +1476,20 @@ function TreeProvider(props) {
1435
1476
  copyToClipboard: clipboard.copyToClipboard,
1436
1477
  pasteFromClipboard: clipboard.pasteFromClipboard
1437
1478
  });
1479
+ const finalRenderContextMenu = React.useMemo(
1480
+ () => {
1481
+ if (renderContextMenu) return renderContextMenu;
1482
+ const resolve = resolvedContextMenuActions;
1483
+ if (!resolve) return void 0;
1484
+ return (rowProps, trigger) => {
1485
+ const items = resolve(rowProps);
1486
+ const cleaned = items ? tidyMenuItems(items) : null;
1487
+ if (!cleaned || cleaned.length === 0) return trigger;
1488
+ return renderItemsAsContextMenu(rowProps, cleaned, trigger);
1489
+ };
1490
+ },
1491
+ [renderContextMenu, resolvedContextMenuActions]
1492
+ );
1438
1493
  const value = React.useMemo(
1439
1494
  () => ({
1440
1495
  // state
@@ -1476,7 +1531,7 @@ function TreeProvider(props) {
1476
1531
  renderIcon,
1477
1532
  renderLabel,
1478
1533
  renderActions,
1479
- renderContextMenu,
1534
+ renderContextMenu: finalRenderContextMenu,
1480
1535
  adapter,
1481
1536
  resolvedContextMenuActions,
1482
1537
  getNodeById: /* @__PURE__ */ chunkPK6SKIKE_cjs.__name((id) => nodeById.get(id), "getNodeById"),
@@ -1513,7 +1568,7 @@ function TreeProvider(props) {
1513
1568
  renderIcon,
1514
1569
  renderLabel,
1515
1570
  renderActions,
1516
- renderContextMenu,
1571
+ finalRenderContextMenu,
1517
1572
  adapter,
1518
1573
  resolvedContextMenuActions,
1519
1574
  nodeById,
@@ -2789,47 +2844,6 @@ function useTreeFinderHotkeys(opts) {
2789
2844
  return { ref };
2790
2845
  }
2791
2846
  chunkPK6SKIKE_cjs.__name(useTreeFinderHotkeys, "useTreeFinderHotkeys");
2792
- function renderItemsAsContextMenu(rowProps, items, trigger) {
2793
- return /* @__PURE__ */ jsxRuntime.jsxs(components.ContextMenu, { children: [
2794
- /* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuTrigger, { asChild: true, children: trigger }),
2795
- /* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuContent, { children: items.map((item, idx) => {
2796
- if (item === "separator") {
2797
- return /* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuSeparator, {}, `sep-${idx}`);
2798
- }
2799
- const Icon = item.icon;
2800
- return /* @__PURE__ */ jsxRuntime.jsxs(
2801
- components.ContextMenuItem,
2802
- {
2803
- disabled: item.disabled,
2804
- variant: item.destructive ? "destructive" : void 0,
2805
- onSelect: () => item.onSelect(rowProps),
2806
- children: [
2807
- Icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, {}) : null,
2808
- item.label,
2809
- item.shortcut ? /* @__PURE__ */ jsxRuntime.jsx(components.ContextMenuShortcut, { children: item.shortcut }) : null
2810
- ]
2811
- },
2812
- item.id
2813
- );
2814
- }) })
2815
- ] });
2816
- }
2817
- chunkPK6SKIKE_cjs.__name(renderItemsAsContextMenu, "renderItemsAsContextMenu");
2818
- function tidyMenuItems(items) {
2819
- const out = [];
2820
- for (const it of items) {
2821
- if (it === "separator") {
2822
- if (out.length === 0) continue;
2823
- if (out[out.length - 1] === "separator") continue;
2824
- out.push(it);
2825
- } else {
2826
- out.push(it);
2827
- }
2828
- }
2829
- while (out.length > 0 && out[out.length - 1] === "separator") out.pop();
2830
- return out;
2831
- }
2832
- chunkPK6SKIKE_cjs.__name(tidyMenuItems, "tidyMenuItems");
2833
2847
  function TreeRoot(props) {
2834
2848
  const {
2835
2849
  data,
@@ -2863,6 +2877,7 @@ function TreeRoot(props) {
2863
2877
  persistSelection = false,
2864
2878
  adapter,
2865
2879
  defaultMenuItems,
2880
+ actionsRef,
2866
2881
  className,
2867
2882
  style
2868
2883
  } = props;
@@ -2905,7 +2920,8 @@ function TreeRoot(props) {
2905
2920
  enableSearch,
2906
2921
  enableTypeAhead,
2907
2922
  enableFinderHotkeys,
2908
- renderRow
2923
+ renderRow,
2924
+ actionsRef
2909
2925
  }
2910
2926
  )
2911
2927
  }
@@ -2918,10 +2934,29 @@ function TreeRootShell({
2918
2934
  enableSearch,
2919
2935
  enableTypeAhead,
2920
2936
  enableFinderHotkeys,
2921
- renderRow
2937
+ renderRow,
2938
+ actionsRef
2922
2939
  }) {
2923
2940
  const containerRef = React.useRef(null);
2924
2941
  const ctx = useTreeContext();
2942
+ React.useEffect(() => {
2943
+ if (!actionsRef) return;
2944
+ actionsRef.current = {
2945
+ refresh: ctx.refresh,
2946
+ refreshAll: ctx.refreshAll,
2947
+ expandAll: ctx.expandAll,
2948
+ collapseAll: ctx.collapseAll
2949
+ };
2950
+ return () => {
2951
+ if (actionsRef.current) actionsRef.current = null;
2952
+ };
2953
+ }, [
2954
+ actionsRef,
2955
+ ctx.refresh,
2956
+ ctx.refreshAll,
2957
+ ctx.expandAll,
2958
+ ctx.collapseAll
2959
+ ]);
2925
2960
  const isMulti = ctx.selectionMode === "multiple";
2926
2961
  const { ref: keyboardRef } = useTreeKeyboard({
2927
2962
  rows: ctx.flatRows,
@@ -2998,21 +3033,6 @@ function TreeRootShell({
2998
3033
  onMatch: onTypeAheadMatch,
2999
3034
  enabled: enableTypeAhead
3000
3035
  });
3001
- const finalRenderContextMenu = React.useMemo(() => {
3002
- if (ctx.renderContextMenu) return ctx.renderContextMenu;
3003
- const resolve = ctx.resolvedContextMenuActions;
3004
- if (!resolve) return void 0;
3005
- return (rowProps, trigger) => {
3006
- const items = resolve(rowProps);
3007
- const cleaned = items ? tidyMenuItems(items) : null;
3008
- if (!cleaned || cleaned.length === 0) return trigger;
3009
- return renderItemsAsContextMenu(rowProps, cleaned, trigger);
3010
- };
3011
- }, [ctx.renderContextMenu, ctx.resolvedContextMenuActions]);
3012
- const childCtx = React.useMemo(
3013
- () => ({ ...ctx, renderContextMenu: finalRenderContextMenu }),
3014
- [ctx, finalRenderContextMenu]
3015
- );
3016
3036
  const treeBody = /* @__PURE__ */ jsxRuntime.jsxs(
3017
3037
  "div",
3018
3038
  {
@@ -3038,8 +3058,7 @@ function TreeRootShell({
3038
3058
  ]
3039
3059
  }
3040
3060
  );
3041
- const body = finalRenderContextMenu === ctx.renderContextMenu ? treeBody : /* @__PURE__ */ jsxRuntime.jsx(TreeContext.Provider, { value: childCtx, children: treeBody });
3042
- return /* @__PURE__ */ jsxRuntime.jsx(TreeDndProvider, { children: body });
3061
+ return /* @__PURE__ */ jsxRuntime.jsx(TreeDndProvider, { children: treeBody });
3043
3062
  }
3044
3063
  chunkPK6SKIKE_cjs.__name(TreeRootShell, "TreeRootShell");
3045
3064
  var TreeRoot_default = TreeRoot;