@dxos/plugin-navtree 0.8.4-main.8360d9e660 → 0.8.4-main.8baae0fced

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 (235) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +1 -1
  3. package/dist/lib/{browser/CommandsDialogContent-2DKIOLJL.mjs → neutral/CommandsDialogContent-UBGIQ5FI.mjs} +19 -20
  4. package/dist/lib/neutral/CommandsDialogContent-UBGIQ5FI.mjs.map +7 -0
  5. package/dist/lib/{browser/CommandsTrigger-ABODVVMP.mjs → neutral/CommandsTrigger-BH5WLMHK.mjs} +8 -14
  6. package/dist/lib/neutral/CommandsTrigger-BH5WLMHK.mjs.map +7 -0
  7. package/dist/lib/neutral/NavTreeContainer-6AR4BFDD.mjs +12 -0
  8. package/dist/lib/{browser/NavTreeDocumentTitle-L6NLLWLD.mjs → neutral/NavTreeDocumentTitle-H3E6ZLYE.mjs} +5 -9
  9. package/dist/lib/neutral/NavTreeDocumentTitle-H3E6ZLYE.mjs.map +7 -0
  10. package/dist/lib/neutral/NavTreePlugin.mjs +56 -0
  11. package/dist/lib/neutral/NavTreePlugin.mjs.map +7 -0
  12. package/dist/lib/neutral/NavTreePlugin.node.mjs +18 -0
  13. package/dist/lib/neutral/NavTreePlugin.node.mjs.map +7 -0
  14. package/dist/lib/neutral/NavTreePlugin.workerd.mjs +16 -0
  15. package/dist/lib/neutral/NavTreePlugin.workerd.mjs.map +7 -0
  16. package/dist/lib/{browser/app-graph-builder-Z43OTNI2.mjs → neutral/app-graph-builder-Z347LKSF.mjs} +10 -12
  17. package/dist/lib/neutral/app-graph-builder-Z347LKSF.mjs.map +7 -0
  18. package/dist/lib/neutral/capabilities/index.mjs +17 -0
  19. package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
  20. package/dist/lib/neutral/chunk-5OPDKH6B.mjs +273 -0
  21. package/dist/lib/neutral/chunk-5OPDKH6B.mjs.map +7 -0
  22. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
  23. package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +7 -0
  24. package/dist/lib/neutral/chunk-JOSNCOV4.mjs +79 -0
  25. package/dist/lib/neutral/chunk-JOSNCOV4.mjs.map +7 -0
  26. package/dist/lib/neutral/chunk-T7QS2EFI.mjs +8 -0
  27. package/dist/lib/neutral/chunk-T7QS2EFI.mjs.map +7 -0
  28. package/dist/lib/{browser/chunk-A4VDCIJC.mjs → neutral/chunk-VRTNKDBX.mjs} +6 -2
  29. package/dist/lib/neutral/chunk-VRTNKDBX.mjs.map +7 -0
  30. package/dist/lib/neutral/chunk-ZSWYB7JW.mjs +27 -0
  31. package/dist/lib/neutral/chunk-ZSWYB7JW.mjs.map +7 -0
  32. package/dist/lib/{browser/chunk-OGUYILOW.mjs → neutral/components/index.mjs} +140 -723
  33. package/dist/lib/neutral/components/index.mjs.map +7 -0
  34. package/dist/lib/neutral/containers/index.mjs +20 -0
  35. package/dist/lib/{browser/chunk-DINDC6HE.mjs.map → neutral/containers/index.mjs.map} +3 -3
  36. package/dist/lib/neutral/expose-26FV4W5D.mjs +38 -0
  37. package/dist/lib/neutral/expose-26FV4W5D.mjs.map +7 -0
  38. package/dist/lib/neutral/hooks/index.mjs +169 -0
  39. package/dist/lib/neutral/hooks/index.mjs.map +7 -0
  40. package/dist/lib/neutral/index.mjs +34 -0
  41. package/dist/lib/neutral/index.mjs.map +7 -0
  42. package/dist/lib/{browser/keyboard-HYGFQO7T.mjs → neutral/keyboard-EJ65BVTC.mjs} +4 -5
  43. package/dist/lib/neutral/keyboard-EJ65BVTC.mjs.map +7 -0
  44. package/dist/lib/neutral/meta.json +1 -0
  45. package/dist/lib/neutral/meta.mjs +12 -0
  46. package/dist/lib/neutral/meta.mjs.map +7 -0
  47. package/dist/lib/neutral/operation-handler-ORDOJUL2.mjs +13 -0
  48. package/dist/lib/neutral/operation-handler-ORDOJUL2.mjs.map +7 -0
  49. package/dist/lib/neutral/operations/index.mjs +8 -0
  50. package/dist/lib/neutral/operations/index.mjs.map +7 -0
  51. package/dist/lib/neutral/plugin.mjs +16 -0
  52. package/dist/lib/neutral/plugin.mjs.map +7 -0
  53. package/dist/lib/{browser/react-surface-LFMKIKNY.mjs → neutral/react-surface-EWCZMKVX.mjs} +11 -20
  54. package/dist/lib/neutral/react-surface-EWCZMKVX.mjs.map +7 -0
  55. package/dist/lib/{browser/state-CWMNNF7K.mjs → neutral/state-VVITVXWM.mjs} +6 -10
  56. package/dist/lib/neutral/state-VVITVXWM.mjs.map +7 -0
  57. package/dist/lib/neutral/testing/index.mjs +436 -0
  58. package/dist/lib/neutral/testing/index.mjs.map +7 -0
  59. package/dist/lib/neutral/translations.mjs +23 -0
  60. package/dist/lib/neutral/translations.mjs.map +7 -0
  61. package/dist/lib/neutral/types/index.mjs +10 -0
  62. package/dist/lib/neutral/types/index.mjs.map +7 -0
  63. package/dist/types/src/NavTreePlugin.d.ts +1 -0
  64. package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
  65. package/dist/types/src/NavTreePlugin.node.d.ts +4 -0
  66. package/dist/types/src/NavTreePlugin.node.d.ts.map +1 -0
  67. package/dist/types/src/NavTreePlugin.test.d.ts +2 -0
  68. package/dist/types/src/NavTreePlugin.test.d.ts.map +1 -0
  69. package/dist/types/src/NavTreePlugin.workerd.d.ts +4 -0
  70. package/dist/types/src/NavTreePlugin.workerd.d.ts.map +1 -0
  71. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -0
  72. package/dist/types/src/capabilities/index.d.ts +7 -5
  73. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  74. package/dist/types/src/capabilities/keyboard.d.ts.map +1 -0
  75. package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
  76. package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
  77. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
  78. package/dist/types/src/capabilities/state.d.ts +6 -0
  79. package/dist/types/src/capabilities/state.d.ts.map +1 -0
  80. package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
  81. package/dist/types/src/components/NavTree/NavTree.stories.d.ts +9 -11
  82. package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -1
  83. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts +5 -3
  84. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -1
  85. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +1 -1
  86. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts.map +1 -1
  87. package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts.map +1 -1
  88. package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
  89. package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
  90. package/dist/types/src/components/Sidebar/L1Tabs.d.ts.map +1 -1
  91. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.d.ts.map +1 -1
  92. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts.map +1 -1
  93. package/dist/types/src/components/types.d.ts +1 -2
  94. package/dist/types/src/components/types.d.ts.map +1 -1
  95. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.d.ts.map +1 -1
  96. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.stories.d.ts +28 -0
  97. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.stories.d.ts.map +1 -0
  98. package/dist/types/src/containers/CommandsDialogContent/index.d.ts +1 -2
  99. package/dist/types/src/containers/CommandsDialogContent/index.d.ts.map +1 -1
  100. package/dist/types/src/containers/CommandsTrigger/CommandsTrigger.d.ts.map +1 -1
  101. package/dist/types/src/containers/CommandsTrigger/index.d.ts +1 -2
  102. package/dist/types/src/containers/CommandsTrigger/index.d.ts.map +1 -1
  103. package/dist/types/src/containers/NavTreeContainer/NavTreeContainer.d.ts.map +1 -1
  104. package/dist/types/src/containers/NavTreeDocumentTitle/NavTreeDocumentTitle.d.ts.map +1 -1
  105. package/dist/types/src/containers/NavTreeDocumentTitle/index.d.ts +1 -2
  106. package/dist/types/src/containers/NavTreeDocumentTitle/index.d.ts.map +1 -1
  107. package/dist/types/src/experimental/Tree.d.ts +2 -2
  108. package/dist/types/src/experimental/Tree.d.ts.map +1 -1
  109. package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
  110. package/dist/types/src/hooks/useActions.d.ts +1 -1
  111. package/dist/types/src/hooks/useActions.d.ts.map +1 -1
  112. package/dist/types/src/hooks/useLoadDescendents.d.ts.map +1 -1
  113. package/dist/types/src/hooks/useNavTreeModel.d.ts +1 -1
  114. package/dist/types/src/hooks/useNavTreeModel.d.ts.map +1 -1
  115. package/dist/types/src/hooks/useNavTreeState.d.ts +2 -6
  116. package/dist/types/src/hooks/useNavTreeState.d.ts.map +1 -1
  117. package/dist/types/src/index.d.ts +1 -2
  118. package/dist/types/src/index.d.ts.map +1 -1
  119. package/dist/types/src/meta.d.ts.map +1 -1
  120. package/dist/types/src/operations/expose.d.ts +5 -0
  121. package/dist/types/src/operations/expose.d.ts.map +1 -0
  122. package/dist/types/src/operations/index.d.ts +3 -0
  123. package/dist/types/src/operations/index.d.ts.map +1 -0
  124. package/dist/types/src/plugin.d.ts +4 -0
  125. package/dist/types/src/plugin.d.ts.map +1 -0
  126. package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -1
  127. package/dist/types/src/testing/generator.d.ts.map +1 -1
  128. package/dist/types/src/testing/update-state.d.ts.map +1 -1
  129. package/dist/types/src/translations.d.ts +8 -11
  130. package/dist/types/src/translations.d.ts.map +1 -1
  131. package/dist/types/src/types/NavTreeCapabilities.d.ts +13 -0
  132. package/dist/types/src/types/NavTreeCapabilities.d.ts.map +1 -0
  133. package/dist/types/src/types/NavTreeEvents.d.ts +3 -0
  134. package/dist/types/src/types/NavTreeEvents.d.ts.map +1 -0
  135. package/dist/types/src/types/index.d.ts +2 -2
  136. package/dist/types/src/types/index.d.ts.map +1 -1
  137. package/dist/types/src/util.d.ts +2 -2
  138. package/dist/types/src/util.d.ts.map +1 -1
  139. package/dist/types/tsconfig.tsbuildinfo +1 -1
  140. package/package.json +110 -45
  141. package/src/NavTreePlugin.node.ts +17 -0
  142. package/src/NavTreePlugin.test.ts +27 -0
  143. package/src/NavTreePlugin.tsx +10 -27
  144. package/src/NavTreePlugin.workerd.ts +16 -0
  145. package/src/capabilities/{app-graph-builder/app-graph-builder.ts → app-graph-builder.ts} +7 -7
  146. package/src/capabilities/index.ts +11 -5
  147. package/src/capabilities/{keyboard/keyboard.ts → keyboard.ts} +1 -1
  148. package/src/capabilities/operation-handler.ts +16 -0
  149. package/src/capabilities/{react-surface/react-surface.tsx → react-surface.tsx} +9 -9
  150. package/src/capabilities/{state/state.ts → state.ts} +13 -9
  151. package/src/components/NavTree/NavTree.stories.tsx +17 -16
  152. package/src/components/NavTree/NavTree.tsx +5 -4
  153. package/src/components/NavTreeItem/NavTreeItemAction.stories.tsx +8 -10
  154. package/src/components/NavTreeItem/NavTreeItemAction.tsx +47 -48
  155. package/src/components/NavTreeItem/NavTreeItemColumns.tsx +5 -5
  156. package/src/components/Sidebar/L0Menu.tsx +50 -58
  157. package/src/components/Sidebar/L1Panel.tsx +97 -150
  158. package/src/components/Sidebar/L1Tabs.tsx +0 -1
  159. package/src/components/types.ts +1 -2
  160. package/src/containers/CommandsDialogContent/CommandsDialogContent.stories.tsx +46 -0
  161. package/src/containers/CommandsDialogContent/CommandsDialogContent.tsx +51 -59
  162. package/src/containers/CommandsDialogContent/index.ts +1 -3
  163. package/src/containers/CommandsTrigger/CommandsTrigger.tsx +7 -5
  164. package/src/containers/CommandsTrigger/index.ts +1 -3
  165. package/src/containers/NavTreeContainer/NavTreeContainer.tsx +24 -24
  166. package/src/containers/NavTreeDocumentTitle/NavTreeDocumentTitle.tsx +2 -2
  167. package/src/containers/NavTreeDocumentTitle/index.ts +1 -3
  168. package/src/experimental/Tree.stories.tsx +27 -27
  169. package/src/experimental/Tree.tsx +2 -2
  170. package/src/hooks/useActions.ts +1 -1
  171. package/src/hooks/useNavTreeModel.ts +3 -3
  172. package/src/hooks/useNavTreeState.ts +2 -9
  173. package/src/index.ts +1 -2
  174. package/src/meta.ts +2 -0
  175. package/src/operations/expose.ts +42 -0
  176. package/src/operations/index.ts +7 -0
  177. package/src/plugin.ts +11 -0
  178. package/src/testing/app-graph-builder.ts +35 -31
  179. package/src/testing/generator.ts +21 -21
  180. package/src/translations.ts +8 -10
  181. package/src/types/NavTreeCapabilities.ts +21 -0
  182. package/src/types/NavTreeEvents.ts +12 -0
  183. package/src/types/index.ts +3 -2
  184. package/src/util.ts +2 -2
  185. package/dist/lib/browser/CommandsDialogContent-2DKIOLJL.mjs.map +0 -7
  186. package/dist/lib/browser/CommandsTrigger-ABODVVMP.mjs.map +0 -7
  187. package/dist/lib/browser/NavTreeContainer-IQZCK4Y4.mjs +0 -12
  188. package/dist/lib/browser/NavTreeDocumentTitle-L6NLLWLD.mjs.map +0 -7
  189. package/dist/lib/browser/app-graph-builder-Z43OTNI2.mjs.map +0 -7
  190. package/dist/lib/browser/chunk-A4VDCIJC.mjs.map +0 -7
  191. package/dist/lib/browser/chunk-DINDC6HE.mjs +0 -14
  192. package/dist/lib/browser/chunk-KF26WA4O.mjs +0 -23
  193. package/dist/lib/browser/chunk-KF26WA4O.mjs.map +0 -7
  194. package/dist/lib/browser/chunk-OGUYILOW.mjs.map +0 -7
  195. package/dist/lib/browser/index.mjs +0 -141
  196. package/dist/lib/browser/index.mjs.map +0 -7
  197. package/dist/lib/browser/keyboard-HYGFQO7T.mjs.map +0 -7
  198. package/dist/lib/browser/meta.json +0 -1
  199. package/dist/lib/browser/operation-resolver-VDWVUII5.mjs +0 -49
  200. package/dist/lib/browser/operation-resolver-VDWVUII5.mjs.map +0 -7
  201. package/dist/lib/browser/react-surface-LFMKIKNY.mjs.map +0 -7
  202. package/dist/lib/browser/state-CWMNNF7K.mjs.map +0 -7
  203. package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts.map +0 -1
  204. package/dist/types/src/capabilities/app-graph-builder/index.d.ts +0 -3
  205. package/dist/types/src/capabilities/app-graph-builder/index.d.ts.map +0 -1
  206. package/dist/types/src/capabilities/keyboard/index.d.ts +0 -3
  207. package/dist/types/src/capabilities/keyboard/index.d.ts.map +0 -1
  208. package/dist/types/src/capabilities/keyboard/keyboard.d.ts.map +0 -1
  209. package/dist/types/src/capabilities/operation-resolver/index.d.ts +0 -3
  210. package/dist/types/src/capabilities/operation-resolver/index.d.ts.map +0 -1
  211. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +0 -5
  212. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +0 -1
  213. package/dist/types/src/capabilities/react-surface/index.d.ts +0 -3
  214. package/dist/types/src/capabilities/react-surface/index.d.ts.map +0 -1
  215. package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +0 -1
  216. package/dist/types/src/capabilities/state/index.d.ts +0 -3
  217. package/dist/types/src/capabilities/state/index.d.ts.map +0 -1
  218. package/dist/types/src/capabilities/state/state.d.ts +0 -6
  219. package/dist/types/src/capabilities/state/state.d.ts.map +0 -1
  220. package/dist/types/src/types/capabilities.d.ts +0 -16
  221. package/dist/types/src/types/capabilities.d.ts.map +0 -1
  222. package/dist/types/src/types/events.d.ts +0 -5
  223. package/dist/types/src/types/events.d.ts.map +0 -1
  224. package/src/capabilities/app-graph-builder/index.ts +0 -7
  225. package/src/capabilities/keyboard/index.ts +0 -7
  226. package/src/capabilities/operation-resolver/index.ts +0 -7
  227. package/src/capabilities/operation-resolver/operation-resolver.ts +0 -45
  228. package/src/capabilities/react-surface/index.ts +0 -7
  229. package/src/capabilities/state/index.ts +0 -7
  230. package/src/types/capabilities.ts +0 -21
  231. package/src/types/events.ts +0 -12
  232. /package/dist/lib/{browser/NavTreeContainer-IQZCK4Y4.mjs.map → neutral/NavTreeContainer-6AR4BFDD.mjs.map} +0 -0
  233. /package/dist/types/src/capabilities/{app-graph-builder/app-graph-builder.d.ts → app-graph-builder.d.ts} +0 -0
  234. /package/dist/types/src/capabilities/{keyboard/keyboard.d.ts → keyboard.d.ts} +0 -0
  235. /package/dist/types/src/capabilities/{react-surface/react-surface.d.ts → react-surface.d.ts} +0 -0
@@ -2,23 +2,22 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import * as Effect from 'effect/Effect';
6
5
  import * as Option from 'effect/Option';
7
6
  import React, { memo, useCallback, useMemo } from 'react';
8
7
 
9
8
  import { Node } from '@dxos/app-graph';
10
9
  import { isPinnedWorkspace } from '@dxos/app-toolkit';
11
10
  import { useAppGraph } from '@dxos/app-toolkit/ui';
12
- import { Graph, useActionRunner, useConnections, useEdges } from '@dxos/plugin-graph';
11
+ import { Graph, useActionRunner, useEdges } from '@dxos/plugin-graph';
13
12
  import { DensityProvider, IconButton, ScrollArea, toLocalizedString, useTranslation } from '@dxos/react-ui';
14
- import { useAttended } from '@dxos/react-ui-attention';
15
13
  import { Tree } from '@dxos/react-ui-list';
16
14
  import { Menu, type MenuItem } from '@dxos/react-ui-menu';
17
15
  import { Tabs } from '@dxos/react-ui-tabs';
18
16
  import { hoverableControlItem, hoverableOpenControlItem } from '@dxos/ui-theme';
19
17
 
20
- import { useActions, useIsAlternateTree, useLoadDescendents } from '../../hooks';
21
- import { meta } from '../../meta';
18
+ import { useActions, useLoadDescendents } from '#hooks';
19
+ import { meta } from '#meta';
20
+
22
21
  import { NAV_TREE_ITEM } from '../NavTree';
23
22
  import { useNavTreeContext } from '../NavTreeContext';
24
23
  import { NavTreeItemAction, NavTreeItemColumns } from '../NavTreeItem';
@@ -41,7 +40,7 @@ const L1Panel$ = ({ open, path, item, isCurrent, onBack }: L1PanelProps) => {
41
40
  const shouldRenderContent = isCurrent || isActivated;
42
41
 
43
42
  return (
44
- <Tabs.Tabpanel
43
+ <Tabs.Panel
45
44
  key={item.id}
46
45
  value={item.id}
47
46
  classNames={[
@@ -56,7 +55,7 @@ const L1Panel$ = ({ open, path, item, isCurrent, onBack }: L1PanelProps) => {
56
55
  {...(!open && { inert: true })}
57
56
  >
58
57
  {shouldRenderContent && <L1PanelContent open={open} path={path} item={item} onBack={onBack} />}
59
- </Tabs.Tabpanel>
58
+ </Tabs.Panel>
60
59
  );
61
60
  };
62
61
 
@@ -83,49 +82,28 @@ const useIsActivatedWorkspace = (item: Node.Node): boolean => {
83
82
  const L1PanelContent = ({ path, item, onBack }: Pick<L1PanelProps, 'open' | 'path' | 'item' | 'onBack'>) => {
84
83
  const navTreeContext = useNavTreeContext();
85
84
 
86
- const alternateTree = useAlternateTreeItem(item);
87
- const alternatePath = useMemo(() => [...path, item.id], [item.id, path]);
88
- const isAlternate = useIsAlternateTree(alternatePath, item);
89
-
90
85
  return (
91
86
  <DensityProvider density='fine'>
92
87
  <L1PanelHeader path={path} item={item} onBack={onBack} />
93
88
  <ScrollArea.Root thin orientation='vertical'>
94
89
  <ScrollArea.Viewport>
95
- {isAlternate && alternateTree ? (
96
- <Tree
97
- model={navTreeContext.model}
98
- id={alternateTree.id}
99
- rootId={alternateTree.id}
100
- path={alternatePath}
101
- levelOffset={5}
102
- gridTemplateColumns='[tree-row-start] 1fr min-content min-content min-content [tree-row-end]'
103
- renderColumns={NavTreeItemColumns}
104
- blockInstruction={navTreeContext.blockInstruction}
105
- canDrop={navTreeContext.canDrop}
106
- canSelect={navTreeContext.canSelect}
107
- onOpenChange={navTreeContext.onOpenChange}
108
- onSelect={navTreeContext.onSelect}
109
- onItemHover={navTreeContext.onItemHover}
110
- />
111
- ) : (
112
- <Tree
113
- model={navTreeContext.model}
114
- id={item.id}
115
- rootId={item.id}
116
- path={path}
117
- levelOffset={5}
118
- gridTemplateColumns='[tree-row-start] 1fr min-content min-content min-content [tree-row-end]'
119
- draggable
120
- renderColumns={NavTreeItemColumns}
121
- blockInstruction={navTreeContext.blockInstruction}
122
- canDrop={navTreeContext.canDrop}
123
- canSelect={navTreeContext.canSelect}
124
- onOpenChange={navTreeContext.onOpenChange}
125
- onSelect={navTreeContext.onSelect}
126
- onItemHover={navTreeContext.onItemHover}
127
- />
128
- )}
90
+ <Tree
91
+ classNames='pt-[2px]'
92
+ model={navTreeContext.model}
93
+ id={item.id}
94
+ rootId={item.id}
95
+ path={path}
96
+ levelOffset={5}
97
+ draggable
98
+ gridTemplateColumns='[tree-row-start] 1fr min-content min-content min-content [tree-row-end]'
99
+ renderColumns={NavTreeItemColumns}
100
+ blockInstruction={navTreeContext.blockInstruction}
101
+ canDrop={navTreeContext.canDrop}
102
+ canSelect={navTreeContext.canSelect}
103
+ onOpenChange={navTreeContext.onOpenChange}
104
+ onSelect={navTreeContext.onSelect}
105
+ onItemHover={navTreeContext.onItemHover}
106
+ />
129
107
  </ScrollArea.Viewport>
130
108
  </ScrollArea.Root>
131
109
  </DensityProvider>
@@ -144,40 +122,30 @@ const L1PanelHeader = ({ item, path, onBack }: Pick<L1PanelProps, 'item' | 'path
144
122
  const { primaryAction, groupedActions, menuActions, onAction } = useL1MenuActions({ item, path });
145
123
  useLoadDescendents(item);
146
124
 
147
- // TODO(wittjosiah): Hack to only show containsAttended when attended item is within alternate tree (e.g. settings).
148
- // Since every object in the space is a descendant of the space node, we'd otherwise always show containsAttended.
149
- const alternateTree = useAlternateTreeItem(item);
150
- const attended = useAttended();
151
- const alternateTreeOpen = useMemo(() => {
152
- if (!alternateTree) {
153
- return true;
154
- }
155
- const hasAlternateAttended = attended.some(
156
- (attendedId) => attendedId === alternateTree.id || attendedId.startsWith(alternateTree.id + '/'),
157
- );
158
- return !hasAlternateAttended;
159
- }, [alternateTree, attended]);
160
-
161
125
  return (
162
- <div className='flex w-full items-center border-b border-subdued-separator dx-app-drag dx-density-coarse pe-1'>
126
+ <div
127
+ data-tauri-drag-region
128
+ className='flex w-full items-center border-b border-subdued-separator dx-app-drag dx-density-coarse pe-1'
129
+ >
163
130
  {backCapableWorkspace ? (
164
131
  <IconButton
165
- size={5}
166
132
  density='coarse'
167
133
  classNames={['shrink-0 px-2 pointer-fine:px-1', hoverableControlItem, hoverableOpenControlItem]}
168
134
  variant='ghost'
169
135
  icon='ph--caret-left--regular'
170
136
  iconOnly
171
- label={t('button back')}
137
+ label={t('button-back.button')}
172
138
  data-testid='treeView.primaryTreeButton'
173
139
  onClick={() => onBack?.()}
174
140
  />
175
141
  ) : (
176
- <div className='w-6' />
142
+ <div data-tauri-drag-region className='w-6' />
177
143
  )}
178
- <h2 className='flex-1 truncate min-w-0'>{title}</h2>
144
+ <h2 data-tauri-drag-region className='flex-1 truncate min-w-0'>
145
+ {title}
146
+ </h2>
179
147
  {/* TODO(wittjosiah): Reconcile with NavTreeItemColumns. */}
180
- <div role='none' className='contents dx-app-no-drag'>
148
+ <div className='contents dx-app-no-drag'>
181
149
  {primaryAction?.properties?.disposition === 'list-item-primary' && !primaryAction?.properties?.disabled && (
182
150
  <NavTreeItemAction
183
151
  testId={primaryAction.properties?.testId}
@@ -191,115 +159,94 @@ const L1PanelHeader = ({ item, path, onBack }: Pick<L1PanelProps, 'item' | 'path
191
159
  caller={NAV_TREE_ITEM}
192
160
  />
193
161
  )}
194
- {menuActions.length === 1 && (
195
- <IconButton
196
- size={5}
197
- density='coarse'
198
- classNames={['shrink-0 px-2 pointer-fine:px-1', hoverableControlItem, hoverableOpenControlItem]}
199
- variant='ghost'
200
- icon={menuActions[0].properties?.icon ?? 'ph--placeholder--regular'}
201
- iconOnly
202
- label={toLocalizedString(menuActions[0].properties?.label, t)}
203
- data-testid={menuActions[0].properties?.testId}
204
- onClick={() => onAction(menuActions[0] as Node.Action)}
205
- />
206
- )}
207
- {menuActions.length > 1 && (
208
- <Menu.Root caller={NAV_TREE_ITEM} onAction={onAction}>
209
- <Menu.Trigger asChild>
210
- <IconButton
211
- size={5}
212
- density='coarse'
213
- classNames={['shrink-0 px-2 pointer-fine:px-1', hoverableControlItem, hoverableOpenControlItem]}
214
- variant='ghost'
215
- icon='ph--dots-three-vertical--regular'
216
- iconOnly
217
- label={t('tree item actions label')}
218
- data-testid='navtree.treeItem.actionsLevel0'
219
- />
220
- </Menu.Trigger>
221
- <Menu.Content group={item} items={menuActions as MenuItem[]} />
222
- </Menu.Root>
223
- )}
224
- {ItemEnd && <ItemEnd node={item} open={alternateTreeOpen} />}
162
+ <MenuActions item={item} menuActions={menuActions} onAction={onAction} />
163
+ {ItemEnd && <ItemEnd node={item} open />}
225
164
  </div>
226
165
  </div>
227
166
  );
228
167
  };
229
168
 
169
+ type L1MenuActions = {
170
+ primaryAction: Node.ActionLike;
171
+ groupedActions: Record<string, Node.Action[]>;
172
+ menuActions: Node.Action[];
173
+ onAction: (action: Node.Action, params?: Node.InvokeProps) => void;
174
+ };
175
+
230
176
  /**
231
- * Builds the menu actions for the L1 panel header, combining graph actions with a synthetic settings/back action.
177
+ * Header menu actions for an L1 workspace tab. Renders nothing for an empty
178
+ * `menuActions`, a single inline icon button for one action, and a
179
+ * `…`-menu trigger for multiple.
232
180
  */
233
- const useL1MenuActions = ({ item, path }: Pick<L1PanelProps, 'item' | 'path'>) => {
181
+ const MenuActions = ({
182
+ item,
183
+ menuActions,
184
+ onAction,
185
+ }: {
186
+ item: Node.Node;
187
+ } & Pick<L1MenuActions, 'menuActions' | 'onAction'>) => {
234
188
  const { t } = useTranslation(meta.id);
235
- const { setAlternateTree } = useNavTreeContext();
236
- const { graph } = useAppGraph();
237
- const runAction = useActionRunner();
238
189
 
239
- const alternateTree = useAlternateTreeItem(item);
240
- const alternatePath = useMemo(() => [...path, item.id], [item.id, path]);
241
- const isAlternate = useIsAlternateTree(alternatePath, item);
190
+ if (menuActions.length === 0) {
191
+ return null;
192
+ }
193
+
194
+ if (menuActions.length === 1) {
195
+ return (
196
+ <IconButton
197
+ density='coarse'
198
+ classNames={['shrink-0 px-2 pointer-fine:px-1', hoverableControlItem, hoverableOpenControlItem]}
199
+ variant='ghost'
200
+ icon={menuActions[0].properties?.icon ?? 'ph--placeholder--regular'}
201
+ iconOnly
202
+ label={toLocalizedString(menuActions[0].properties?.label, t)}
203
+ data-testid={menuActions[0].properties?.testId}
204
+ onClick={() => onAction(menuActions[0] as Node.Action)}
205
+ />
206
+ );
207
+ }
208
+
209
+ return (
210
+ <Menu.Root caller={NAV_TREE_ITEM} onAction={onAction}>
211
+ <Menu.Trigger asChild>
212
+ <IconButton
213
+ density='coarse'
214
+ classNames={['shrink-0 px-2 pointer-fine:px-1', hoverableControlItem, hoverableOpenControlItem]}
215
+ variant='ghost'
216
+ icon='ph--dots-three-vertical--regular'
217
+ iconOnly
218
+ label={t('tree-item-actions.label')}
219
+ data-testid='navtree.treeItem.actionsLevel0'
220
+ />
221
+ </Menu.Trigger>
222
+ <Menu.Content group={item} items={menuActions as MenuItem[]} />
223
+ </Menu.Root>
224
+ );
225
+ };
226
+
227
+ /**
228
+ * Builds the menu actions for the L1 panel header.
229
+ */
230
+ const useL1MenuActions = ({ item, path }: Pick<L1PanelProps, 'item' | 'path'>): L1MenuActions => {
231
+ const runAction = useActionRunner();
242
232
 
243
- // Graph actions.
244
233
  const { actions: actionsProp, groupedActions } = useActions(item);
245
234
  const [primaryAction, ...secondaryActions] = actionsProp.toSorted((a, _b) =>
246
235
  a.properties?.disposition === 'list-item-primary' ? -1 : 1,
247
236
  );
248
237
 
249
- const graphMenuActions = (
250
- primaryAction?.properties?.disposition === 'list-item-primary' ? secondaryActions : actionsProp
251
- )
238
+ const menuActions = (primaryAction?.properties?.disposition === 'list-item-primary' ? secondaryActions : actionsProp)
252
239
  .flatMap((action) => (Node.isAction(action) ? [action] : []))
253
240
  .filter((a) => ['list-item', 'list-item-primary'].includes(a.properties?.disposition));
254
241
 
255
- // Synthetic settings/back action.
256
- const settingsActionId = `${item.id}~settings`;
257
- const settingsAction = useMemo((): Node.Action | undefined => {
258
- if (!alternateTree) {
259
- return undefined;
260
- }
261
- return {
262
- id: settingsActionId,
263
- type: Node.ActionType,
264
- data: () => Effect.void,
265
- properties: {
266
- label: isAlternate ? ['button back', { ns: meta.id }] : (alternateTree.properties.label ?? alternateTree.id),
267
- icon: isAlternate
268
- ? 'ph--arrow-u-down-left--regular'
269
- : (alternateTree.properties.icon ?? 'ph--placeholder--regular'),
270
- disposition: 'list-item',
271
- testId: isAlternate ? 'navtree.backToSpace' : 'navtree.spaceSettings',
272
- },
273
- } as Node.Action;
274
- }, [alternateTree, isAlternate, settingsActionId, t]);
275
-
276
- const menuActions = useMemo(
277
- () => [...graphMenuActions, ...(settingsAction ? [settingsAction] : [])],
278
- [graphMenuActions, settingsAction],
279
- );
280
-
281
242
  const onAction = useCallback(
282
243
  (action: Node.Action, params?: Node.InvokeProps) => {
283
- if (action.id === settingsActionId) {
284
- if (alternateTree && !isAlternate) {
285
- Graph.expand(graph, alternateTree.id, 'child');
286
- }
287
- setAlternateTree?.(alternatePath, !isAlternate);
288
- } else {
289
- void runAction(action, { ...params, path });
290
- }
244
+ void runAction(action, { ...params, path });
291
245
  },
292
- [settingsActionId, setAlternateTree, alternatePath, isAlternate, runAction, graph, alternateTree, path],
246
+ [runAction, path],
293
247
  );
294
248
 
295
249
  return { primaryAction, groupedActions, menuActions, onAction };
296
250
  };
297
251
 
298
- /** Finds the first child with disposition 'alternate-tree' using graph connections directly. */
299
- const useAlternateTreeItem = (item: Node.Node): Node.Node | undefined => {
300
- const { graph } = useAppGraph();
301
- const connections = useConnections(graph, item.id, 'child');
302
- return connections.find((node) => node.properties.disposition === 'alternate-tree');
303
- };
304
-
305
252
  export const L1Panel = memo(L1Panel$);
@@ -7,7 +7,6 @@ import React from 'react';
7
7
  import { type Node } from '@dxos/app-graph';
8
8
 
9
9
  import { l0ItemType } from '../../util';
10
-
11
10
  import { L1Panel, type L1PanelProps } from './L1Panel';
12
11
 
13
12
  export type L1TabsProps = Pick<L1PanelProps, 'open' | 'onBack'> & {
@@ -8,7 +8,7 @@ import { type Node } from '@dxos/app-graph';
8
8
  import type { Density } from '@dxos/react-ui';
9
9
  import type { TreeModel, TreeProps } from '@dxos/react-ui-list';
10
10
 
11
- import type { NavTreeItemGraphNode } from '../types';
11
+ import type { NavTreeItemGraphNode } from '#types';
12
12
 
13
13
  import type { L1PanelProps } from './Sidebar';
14
14
 
@@ -16,7 +16,6 @@ export type NavTreeContextValue = {
16
16
  model: TreeModel<NavTreeItemGraphNode>;
17
17
  popoverAnchorId?: string;
18
18
  renderItemEnd?: FC<{ node: Node.Node; open: boolean }>;
19
- setAlternateTree?: (path: string[], open: boolean) => void;
20
19
  onTabChange?: (node: NavTreeItemGraphNode) => void;
21
20
  } & Pick<
22
21
  TreeProps<NavTreeItemGraphNode>,
@@ -0,0 +1,46 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import React from 'react';
7
+
8
+ import { withPluginManager } from '@dxos/app-framework/testing';
9
+ import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
10
+ import { Dialog } from '@dxos/react-ui';
11
+ import { withLayout } from '@dxos/react-ui/testing';
12
+
13
+ import { translations } from '#translations';
14
+
15
+ import { NavTreePlugin } from '../../NavTreePlugin';
16
+ import { CommandsDialogContent } from './CommandsDialogContent';
17
+
18
+ const DefaultStory = () => (
19
+ <Dialog.Root defaultOpen>
20
+ <Dialog.Overlay>
21
+ <CommandsDialogContent />
22
+ </Dialog.Overlay>
23
+ </Dialog.Root>
24
+ );
25
+
26
+ const meta = {
27
+ title: 'plugins/plugin-navtree/containers/CommandsDialogContent',
28
+ component: CommandsDialogContent,
29
+ render: DefaultStory,
30
+ decorators: [
31
+ withLayout({ layout: 'fullscreen' }),
32
+ withPluginManager({
33
+ plugins: [...corePlugins(), StorybookPlugin({}), NavTreePlugin()],
34
+ }),
35
+ ],
36
+ parameters: {
37
+ layout: 'fullscreen',
38
+ translations,
39
+ },
40
+ } satisfies Meta<typeof CommandsDialogContent>;
41
+
42
+ export default meta;
43
+
44
+ type Story = StoryObj<typeof meta>;
45
+
46
+ export const Default: Story = {};
@@ -11,11 +11,11 @@ import { Keyboard, keySymbols } from '@dxos/keyboard';
11
11
  import { Graph, Node, useActionRunner } from '@dxos/plugin-graph';
12
12
  import { useActions } from '@dxos/plugin-graph';
13
13
  import { Button, Dialog, toLocalizedString, useTranslation } from '@dxos/react-ui';
14
- import { SearchList, useSearchListResults } from '@dxos/react-ui-searchlist';
14
+ import { SearchList, useSearchListResults } from '@dxos/react-ui-search';
15
15
  import { osTranslations } from '@dxos/ui-theme';
16
16
  import { getHostPlatform } from '@dxos/util';
17
17
 
18
- import { KEY_BINDING, meta } from '../../meta';
18
+ import { KEY_BINDING, meta } from '#meta';
19
19
 
20
20
  export type CommandsDialogContentProps = {
21
21
  selected?: string;
@@ -25,7 +25,7 @@ export type CommandsDialogContentProps = {
25
25
  export const CommandsDialogContent = forwardRef<HTMLDivElement, CommandsDialogContentProps>(
26
26
  ({ selected: initial }, forwardedRef) => {
27
27
  const { t } = useTranslation(meta.id);
28
- const { invokeSync } = useOperationInvoker();
28
+ const { invokePromise } = useOperationInvoker();
29
29
  const runAction = useActionRunner();
30
30
  const { graph } = useAppGraph();
31
31
  const [selected, setSelected] = useState<string | undefined>(initial);
@@ -40,11 +40,10 @@ export const CommandsDialogContent = forwardRef<HTMLDivElement, CommandsDialogCo
40
40
  Graph.traverse(graph, {
41
41
  relation: ['child', 'action'],
42
42
  visitor: (node, path) => {
43
- if (
44
- (Node.isAction(node) || Node.isActionGroup(node)) &&
45
- !actionMap.has(node.id) &&
46
- current.startsWith(path.slice(0, -1).join('/'))
47
- ) {
43
+ const isActionLike = Node.isAction(node) || Node.isActionGroup(node);
44
+ const parentId = path.at(-2) ?? '';
45
+ const matches = current === parentId || current.startsWith(parentId + '/');
46
+ if (isActionLike && !actionMap.has(node.id) && matches) {
48
47
  actionMap.add(node.id);
49
48
  actions.push(node);
50
49
  }
@@ -71,62 +70,55 @@ export const CommandsDialogContent = forwardRef<HTMLDivElement, CommandsDialogCo
71
70
 
72
71
  return (
73
72
  <Dialog.Content ref={forwardedRef}>
74
- <Dialog.Title srOnly>{t('commands dialog title', { ns: meta.id })}</Dialog.Title>
75
- <Dialog.Body>
76
- <SearchList.Root onSearch={handleSearch}>
77
- <SearchList.Content>
78
- <SearchList.Input placeholder={t('command list input placeholder')} />
79
- <SearchList.Viewport>
80
- {results.map((action) => {
81
- const shortcut =
82
- typeof action.properties.keyBinding === 'string'
83
- ? action.properties.keyBinding
84
- : action.properties.keyBinding?.[getHostPlatform()];
73
+ <Dialog.Title srOnly>{t('commands-dialog.title', { ns: meta.id })}</Dialog.Title>
74
+ <SearchList.Root onSearch={handleSearch}>
75
+ <SearchList.Input placeholder={t('command-list-input.placeholder')} />
76
+ <SearchList.Viewport>
77
+ {results.map((action) => {
78
+ const shortcut =
79
+ typeof action.properties.keyBinding === 'string'
80
+ ? action.properties.keyBinding
81
+ : action.properties.keyBinding?.[getHostPlatform()];
85
82
 
86
- return (
87
- <SearchList.Item
88
- value={action.id}
89
- key={action.id}
90
- label={toLocalizedString(action.properties.label, t)}
91
- icon={action.properties.icon}
92
- suffix={shortcut ? keySymbols(shortcut).join('') : undefined}
93
- onSelect={() => {
94
- if (action.properties.disabled) {
95
- return;
96
- }
83
+ return (
84
+ <SearchList.Item
85
+ value={action.id}
86
+ key={action.id}
87
+ label={toLocalizedString(action.properties.label, t)}
88
+ icon={action.properties.icon}
89
+ suffix={shortcut ? keySymbols(shortcut).join('') : undefined}
90
+ onSelect={() => {
91
+ if (action.properties.disabled) {
92
+ return;
93
+ }
97
94
 
98
- if (Node.isActionGroup(action)) {
99
- setSelected(action.id);
100
- return;
101
- }
95
+ if (Node.isActionGroup(action)) {
96
+ setSelected(action.id);
97
+ return;
98
+ }
102
99
 
103
- invokeSync(LayoutOperation.UpdateDialog, { state: false });
104
- setTimeout(() => {
105
- const node = Graph.getConnections(
106
- graph,
107
- group?.id ?? action.id,
108
- Node.childRelation('inbound'),
109
- )[0];
110
- if (node && Node.isAction(action)) {
111
- void runAction(action, { parent: node, caller: KEY_BINDING });
112
- }
113
- });
114
- }}
115
- classNames='flex items-center gap-2'
116
- disabled={action.properties.disabled}
117
- {...(action.properties?.testId && {
118
- 'data-testid': action.properties.testId,
119
- })}
120
- />
121
- );
122
- })}
123
- </SearchList.Viewport>
124
- </SearchList.Content>
125
- </SearchList.Root>
126
- </Dialog.Body>
100
+ void invokePromise(LayoutOperation.UpdateDialog, { state: false });
101
+ setTimeout(() => {
102
+ const lookupId = group?.id ?? action.id;
103
+ const node = Graph.getConnections(graph, lookupId, Node.actionRelation('inbound'))[0];
104
+ if (node && Node.isAction(action)) {
105
+ void runAction(action, { parent: node, caller: KEY_BINDING });
106
+ }
107
+ });
108
+ }}
109
+ classNames='flex items-center gap-2'
110
+ disabled={action.properties.disabled}
111
+ {...(action.properties?.testId && {
112
+ 'data-testid': action.properties.testId,
113
+ })}
114
+ />
115
+ );
116
+ })}
117
+ </SearchList.Viewport>
118
+ </SearchList.Root>
127
119
  <Dialog.ActionBar>
128
120
  <Dialog.Close asChild>
129
- <Button classNames='w-full'>{t('close label', { ns: osTranslations })}</Button>
121
+ <Button classNames='w-full'>{t('close.label', { ns: osTranslations })}</Button>
130
122
  </Dialog.Close>
131
123
  </Dialog.ActionBar>
132
124
  </Dialog.Content>
@@ -2,6 +2,4 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { CommandsDialogContent } from './CommandsDialogContent';
6
-
7
- export default CommandsDialogContent;
5
+ export { CommandsDialogContent as default } from './CommandsDialogContent';
@@ -8,19 +8,21 @@ import { useOperationInvoker } from '@dxos/app-framework/ui';
8
8
  import { LayoutOperation } from '@dxos/app-toolkit';
9
9
  import { Button, Icon, useTranslation } from '@dxos/react-ui';
10
10
 
11
- import { COMMANDS_DIALOG, meta } from '../../meta';
11
+ import { COMMANDS_DIALOG, meta } from '#meta';
12
12
 
13
13
  // TODO(thure): Refactor to be handled by a more appropriate plugin.
14
14
  export const CommandsTrigger = () => {
15
- const { invokeSync } = useOperationInvoker();
15
+ const { invokePromise } = useOperationInvoker();
16
16
  const { t } = useTranslation(meta.id);
17
17
  return (
18
18
  <Button
19
19
  classNames='m-1 px-1 lg:px-2'
20
- onClick={() => invokeSync(LayoutOperation.UpdateDialog, { subject: COMMANDS_DIALOG, blockAlign: 'start' })}
20
+ onClick={() =>
21
+ void invokePromise(LayoutOperation.UpdateDialog, { subject: COMMANDS_DIALOG, blockAlign: 'start' })
22
+ }
21
23
  >
22
- <span className='text-description font-normal grow text-start'>{t('command list input placeholder')}</span>
23
- <Icon icon='ph--magnifying-glass--regular' size={5} />
24
+ <span className='text-description font-normal grow text-start'>{t('command-list-input.placeholder')}</span>
25
+ <Icon icon='ph--magnifying-glass--regular' />
24
26
  </Button>
25
27
  );
26
28
  };
@@ -2,6 +2,4 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { CommandsTrigger } from './CommandsTrigger';
6
-
7
- export default CommandsTrigger;
5
+ export { CommandsTrigger as default } from './CommandsTrigger';