@dxos/plugin-navtree 0.8.4-main.d05673bc65 → 0.8.4-main.d9fc60f731

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 (243) hide show
  1. package/LICENSE +102 -5
  2. package/PLUGIN.mdl +376 -0
  3. package/README.md +1 -1
  4. package/dist/lib/{browser/CommandsDialogContent-2DKIOLJL.mjs → neutral/CommandsDialogContent-UBGIQ5FI.mjs} +19 -20
  5. package/dist/lib/neutral/CommandsDialogContent-UBGIQ5FI.mjs.map +7 -0
  6. package/dist/lib/{browser/CommandsTrigger-ABODVVMP.mjs → neutral/CommandsTrigger-BH5WLMHK.mjs} +8 -14
  7. package/dist/lib/neutral/CommandsTrigger-BH5WLMHK.mjs.map +7 -0
  8. package/dist/lib/neutral/NavTreeContainer-6AR4BFDD.mjs +12 -0
  9. package/dist/lib/{browser/NavTreeDocumentTitle-L6NLLWLD.mjs → neutral/NavTreeDocumentTitle-H3E6ZLYE.mjs} +5 -9
  10. package/dist/lib/neutral/NavTreeDocumentTitle-H3E6ZLYE.mjs.map +7 -0
  11. package/dist/lib/neutral/NavTreePlugin.mjs +68 -0
  12. package/dist/lib/neutral/NavTreePlugin.mjs.map +7 -0
  13. package/dist/lib/neutral/NavTreePlugin.node.mjs +18 -0
  14. package/dist/lib/neutral/NavTreePlugin.node.mjs.map +7 -0
  15. package/dist/lib/neutral/NavTreePlugin.workerd.mjs +16 -0
  16. package/dist/lib/neutral/NavTreePlugin.workerd.mjs.map +7 -0
  17. package/dist/lib/{browser/app-graph-builder-Z43OTNI2.mjs → neutral/app-graph-builder-Z347LKSF.mjs} +10 -12
  18. package/dist/lib/neutral/app-graph-builder-Z347LKSF.mjs.map +7 -0
  19. package/dist/lib/neutral/capabilities/index.mjs +17 -0
  20. package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
  21. package/dist/lib/neutral/chunk-5OPDKH6B.mjs +273 -0
  22. package/dist/lib/neutral/chunk-5OPDKH6B.mjs.map +7 -0
  23. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
  24. package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +7 -0
  25. package/dist/lib/neutral/chunk-JOSNCOV4.mjs +79 -0
  26. package/dist/lib/neutral/chunk-JOSNCOV4.mjs.map +7 -0
  27. package/dist/lib/neutral/chunk-MDGKDQMZ.mjs +37 -0
  28. package/dist/lib/neutral/chunk-MDGKDQMZ.mjs.map +7 -0
  29. package/dist/lib/neutral/chunk-T7QS2EFI.mjs +8 -0
  30. package/dist/lib/neutral/chunk-T7QS2EFI.mjs.map +7 -0
  31. package/dist/lib/neutral/chunk-ZSWYB7JW.mjs +27 -0
  32. package/dist/lib/neutral/chunk-ZSWYB7JW.mjs.map +7 -0
  33. package/dist/lib/neutral/components/index.mjs +807 -0
  34. package/dist/lib/neutral/components/index.mjs.map +7 -0
  35. package/dist/lib/neutral/containers/index.mjs +20 -0
  36. package/dist/lib/{browser/chunk-AYF2TX65.mjs.map → neutral/containers/index.mjs.map} +3 -3
  37. package/dist/lib/neutral/expose-26FV4W5D.mjs +38 -0
  38. package/dist/lib/neutral/expose-26FV4W5D.mjs.map +7 -0
  39. package/dist/lib/neutral/hooks/index.mjs +178 -0
  40. package/dist/lib/neutral/hooks/index.mjs.map +7 -0
  41. package/dist/lib/neutral/index.mjs +34 -0
  42. package/dist/lib/neutral/index.mjs.map +7 -0
  43. package/dist/lib/{browser/keyboard-HYGFQO7T.mjs → neutral/keyboard-EJ65BVTC.mjs} +4 -5
  44. package/dist/lib/neutral/keyboard-EJ65BVTC.mjs.map +7 -0
  45. package/dist/lib/neutral/meta.json +1 -0
  46. package/dist/lib/neutral/meta.mjs +12 -0
  47. package/dist/lib/neutral/meta.mjs.map +7 -0
  48. package/dist/lib/neutral/operation-handler-ORDOJUL2.mjs +13 -0
  49. package/dist/lib/neutral/operation-handler-ORDOJUL2.mjs.map +7 -0
  50. package/dist/lib/neutral/operations/index.mjs +8 -0
  51. package/dist/lib/neutral/operations/index.mjs.map +7 -0
  52. package/dist/lib/neutral/plugin.mjs +16 -0
  53. package/dist/lib/neutral/plugin.mjs.map +7 -0
  54. package/dist/lib/{browser/react-surface-D2RLMV4Z.mjs → neutral/react-surface-Q5XMAG7Z.mjs} +11 -20
  55. package/dist/lib/neutral/react-surface-Q5XMAG7Z.mjs.map +7 -0
  56. package/dist/lib/{browser/state-CWMNNF7K.mjs → neutral/state-VVITVXWM.mjs} +6 -10
  57. package/dist/lib/neutral/state-VVITVXWM.mjs.map +7 -0
  58. package/dist/lib/neutral/testing/index.mjs +439 -0
  59. package/dist/lib/neutral/testing/index.mjs.map +7 -0
  60. package/dist/lib/neutral/translations.mjs +23 -0
  61. package/dist/lib/neutral/translations.mjs.map +7 -0
  62. package/dist/lib/neutral/types/index.mjs +10 -0
  63. package/dist/lib/neutral/types/index.mjs.map +7 -0
  64. package/dist/types/src/NavTreePlugin.d.ts +1 -0
  65. package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
  66. package/dist/types/src/NavTreePlugin.node.d.ts +4 -0
  67. package/dist/types/src/NavTreePlugin.node.d.ts.map +1 -0
  68. package/dist/types/src/NavTreePlugin.test.d.ts +2 -0
  69. package/dist/types/src/NavTreePlugin.test.d.ts.map +1 -0
  70. package/dist/types/src/NavTreePlugin.workerd.d.ts +4 -0
  71. package/dist/types/src/NavTreePlugin.workerd.d.ts.map +1 -0
  72. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -0
  73. package/dist/types/src/capabilities/index.d.ts +7 -5
  74. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  75. package/dist/types/src/capabilities/keyboard.d.ts.map +1 -0
  76. package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
  77. package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
  78. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
  79. package/dist/types/src/capabilities/state.d.ts +6 -0
  80. package/dist/types/src/capabilities/state.d.ts.map +1 -0
  81. package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
  82. package/dist/types/src/components/NavTree/NavTree.stories.d.ts +9 -11
  83. package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -1
  84. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts +5 -3
  85. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -1
  86. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +1 -1
  87. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts.map +1 -1
  88. package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts.map +1 -1
  89. package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
  90. package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
  91. package/dist/types/src/components/Sidebar/L1Tabs.d.ts.map +1 -1
  92. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.d.ts.map +1 -1
  93. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts.map +1 -1
  94. package/dist/types/src/components/types.d.ts +1 -2
  95. package/dist/types/src/components/types.d.ts.map +1 -1
  96. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.d.ts.map +1 -1
  97. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.stories.d.ts +28 -0
  98. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.stories.d.ts.map +1 -0
  99. package/dist/types/src/containers/CommandsDialogContent/index.d.ts +1 -2
  100. package/dist/types/src/containers/CommandsDialogContent/index.d.ts.map +1 -1
  101. package/dist/types/src/containers/CommandsTrigger/CommandsTrigger.d.ts.map +1 -1
  102. package/dist/types/src/containers/CommandsTrigger/index.d.ts +1 -2
  103. package/dist/types/src/containers/CommandsTrigger/index.d.ts.map +1 -1
  104. package/dist/types/src/containers/NavTreeContainer/NavTreeContainer.d.ts.map +1 -1
  105. package/dist/types/src/containers/NavTreeDocumentTitle/NavTreeDocumentTitle.d.ts.map +1 -1
  106. package/dist/types/src/containers/NavTreeDocumentTitle/index.d.ts +1 -2
  107. package/dist/types/src/containers/NavTreeDocumentTitle/index.d.ts.map +1 -1
  108. package/dist/types/src/experimental/Tree.d.ts +2 -2
  109. package/dist/types/src/experimental/Tree.d.ts.map +1 -1
  110. package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
  111. package/dist/types/src/hooks/useActions.d.ts +7 -1
  112. package/dist/types/src/hooks/useActions.d.ts.map +1 -1
  113. package/dist/types/src/hooks/useLoadDescendents.d.ts.map +1 -1
  114. package/dist/types/src/hooks/useNavTreeModel.d.ts +1 -1
  115. package/dist/types/src/hooks/useNavTreeModel.d.ts.map +1 -1
  116. package/dist/types/src/hooks/useNavTreeState.d.ts +2 -6
  117. package/dist/types/src/hooks/useNavTreeState.d.ts.map +1 -1
  118. package/dist/types/src/index.d.ts +1 -2
  119. package/dist/types/src/index.d.ts.map +1 -1
  120. package/dist/types/src/meta.d.ts +3 -2
  121. package/dist/types/src/meta.d.ts.map +1 -1
  122. package/dist/types/src/operations/expose.d.ts +5 -0
  123. package/dist/types/src/operations/expose.d.ts.map +1 -0
  124. package/dist/types/src/operations/index.d.ts +3 -0
  125. package/dist/types/src/operations/index.d.ts.map +1 -0
  126. package/dist/types/src/plugin.d.ts +4 -0
  127. package/dist/types/src/plugin.d.ts.map +1 -0
  128. package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -1
  129. package/dist/types/src/testing/generator.d.ts.map +1 -1
  130. package/dist/types/src/testing/update-state.d.ts.map +1 -1
  131. package/dist/types/src/translations.d.ts +8 -11
  132. package/dist/types/src/translations.d.ts.map +1 -1
  133. package/dist/types/src/types/NavTreeCapabilities.d.ts +13 -0
  134. package/dist/types/src/types/NavTreeCapabilities.d.ts.map +1 -0
  135. package/dist/types/src/types/NavTreeEvents.d.ts +3 -0
  136. package/dist/types/src/types/NavTreeEvents.d.ts.map +1 -0
  137. package/dist/types/src/types/index.d.ts +2 -2
  138. package/dist/types/src/types/index.d.ts.map +1 -1
  139. package/dist/types/src/types/schema.d.ts +4 -0
  140. package/dist/types/src/types/schema.d.ts.map +1 -1
  141. package/dist/types/src/util.d.ts +2 -2
  142. package/dist/types/src/util.d.ts.map +1 -1
  143. package/dist/types/tsconfig.tsbuildinfo +1 -1
  144. package/package.json +110 -50
  145. package/src/NavTreePlugin.node.ts +17 -0
  146. package/src/NavTreePlugin.test.ts +27 -0
  147. package/src/NavTreePlugin.tsx +16 -27
  148. package/src/NavTreePlugin.workerd.ts +16 -0
  149. package/src/capabilities/{app-graph-builder/app-graph-builder.ts → app-graph-builder.ts} +7 -7
  150. package/src/capabilities/index.ts +11 -5
  151. package/src/capabilities/{keyboard/keyboard.ts → keyboard.ts} +1 -1
  152. package/src/capabilities/operation-handler.ts +16 -0
  153. package/src/capabilities/{react-surface/react-surface.tsx → react-surface.tsx} +9 -9
  154. package/src/capabilities/{state/state.ts → state.ts} +13 -9
  155. package/src/components/NavTree/NavTree.stories.tsx +22 -23
  156. package/src/components/NavTree/NavTree.tsx +5 -4
  157. package/src/components/NavTreeItem/NavTreeItemAction.stories.tsx +8 -10
  158. package/src/components/NavTreeItem/NavTreeItemAction.tsx +50 -50
  159. package/src/components/NavTreeItem/NavTreeItemColumns.tsx +20 -63
  160. package/src/components/Sidebar/L0Menu.tsx +61 -80
  161. package/src/components/Sidebar/L1Panel.tsx +98 -174
  162. package/src/components/Sidebar/L1Tabs.tsx +0 -1
  163. package/src/components/types.ts +1 -2
  164. package/src/containers/CommandsDialogContent/CommandsDialogContent.stories.tsx +46 -0
  165. package/src/containers/CommandsDialogContent/CommandsDialogContent.tsx +51 -59
  166. package/src/containers/CommandsDialogContent/index.ts +1 -3
  167. package/src/containers/CommandsTrigger/CommandsTrigger.tsx +7 -5
  168. package/src/containers/CommandsTrigger/index.ts +1 -3
  169. package/src/containers/NavTreeContainer/NavTreeContainer.tsx +24 -24
  170. package/src/containers/NavTreeDocumentTitle/NavTreeDocumentTitle.tsx +2 -2
  171. package/src/containers/NavTreeDocumentTitle/index.ts +1 -3
  172. package/src/experimental/Tree.stories.tsx +27 -27
  173. package/src/experimental/Tree.tsx +2 -2
  174. package/src/hooks/useActions.ts +11 -1
  175. package/src/hooks/useNavTreeModel.ts +5 -3
  176. package/src/hooks/useNavTreeState.ts +2 -9
  177. package/src/index.ts +1 -2
  178. package/src/meta.ts +22 -7
  179. package/src/operations/expose.ts +42 -0
  180. package/src/operations/index.ts +7 -0
  181. package/src/plugin.ts +11 -0
  182. package/src/testing/app-graph-builder.ts +42 -36
  183. package/src/testing/generator.ts +23 -28
  184. package/src/translations.ts +8 -10
  185. package/src/types/NavTreeCapabilities.ts +21 -0
  186. package/src/types/NavTreeEvents.ts +12 -0
  187. package/src/types/index.ts +3 -2
  188. package/src/types/schema.ts +4 -0
  189. package/src/util.ts +2 -2
  190. package/src/vite-env.d.ts +10 -0
  191. package/dist/lib/browser/CommandsDialogContent-2DKIOLJL.mjs.map +0 -7
  192. package/dist/lib/browser/CommandsTrigger-ABODVVMP.mjs.map +0 -7
  193. package/dist/lib/browser/NavTreeContainer-TLSYS4KK.mjs +0 -12
  194. package/dist/lib/browser/NavTreeDocumentTitle-L6NLLWLD.mjs.map +0 -7
  195. package/dist/lib/browser/app-graph-builder-Z43OTNI2.mjs.map +0 -7
  196. package/dist/lib/browser/chunk-A4VDCIJC.mjs +0 -19
  197. package/dist/lib/browser/chunk-A4VDCIJC.mjs.map +0 -7
  198. package/dist/lib/browser/chunk-AYF2TX65.mjs +0 -14
  199. package/dist/lib/browser/chunk-JT37R5HS.mjs +0 -1468
  200. package/dist/lib/browser/chunk-JT37R5HS.mjs.map +0 -7
  201. package/dist/lib/browser/chunk-KF26WA4O.mjs +0 -23
  202. package/dist/lib/browser/chunk-KF26WA4O.mjs.map +0 -7
  203. package/dist/lib/browser/index.mjs +0 -141
  204. package/dist/lib/browser/index.mjs.map +0 -7
  205. package/dist/lib/browser/keyboard-HYGFQO7T.mjs.map +0 -7
  206. package/dist/lib/browser/meta.json +0 -1
  207. package/dist/lib/browser/operation-resolver-VDWVUII5.mjs +0 -49
  208. package/dist/lib/browser/operation-resolver-VDWVUII5.mjs.map +0 -7
  209. package/dist/lib/browser/react-surface-D2RLMV4Z.mjs.map +0 -7
  210. package/dist/lib/browser/state-CWMNNF7K.mjs.map +0 -7
  211. package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts.map +0 -1
  212. package/dist/types/src/capabilities/app-graph-builder/index.d.ts +0 -3
  213. package/dist/types/src/capabilities/app-graph-builder/index.d.ts.map +0 -1
  214. package/dist/types/src/capabilities/keyboard/index.d.ts +0 -3
  215. package/dist/types/src/capabilities/keyboard/index.d.ts.map +0 -1
  216. package/dist/types/src/capabilities/keyboard/keyboard.d.ts.map +0 -1
  217. package/dist/types/src/capabilities/operation-resolver/index.d.ts +0 -3
  218. package/dist/types/src/capabilities/operation-resolver/index.d.ts.map +0 -1
  219. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +0 -5
  220. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +0 -1
  221. package/dist/types/src/capabilities/react-surface/index.d.ts +0 -3
  222. package/dist/types/src/capabilities/react-surface/index.d.ts.map +0 -1
  223. package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +0 -1
  224. package/dist/types/src/capabilities/state/index.d.ts +0 -3
  225. package/dist/types/src/capabilities/state/index.d.ts.map +0 -1
  226. package/dist/types/src/capabilities/state/state.d.ts +0 -6
  227. package/dist/types/src/capabilities/state/state.d.ts.map +0 -1
  228. package/dist/types/src/types/capabilities.d.ts +0 -16
  229. package/dist/types/src/types/capabilities.d.ts.map +0 -1
  230. package/dist/types/src/types/events.d.ts +0 -5
  231. package/dist/types/src/types/events.d.ts.map +0 -1
  232. package/src/capabilities/app-graph-builder/index.ts +0 -7
  233. package/src/capabilities/keyboard/index.ts +0 -7
  234. package/src/capabilities/operation-resolver/index.ts +0 -7
  235. package/src/capabilities/operation-resolver/operation-resolver.ts +0 -45
  236. package/src/capabilities/react-surface/index.ts +0 -7
  237. package/src/capabilities/state/index.ts +0 -7
  238. package/src/types/capabilities.ts +0 -21
  239. package/src/types/events.ts +0 -12
  240. /package/dist/lib/{browser/NavTreeContainer-TLSYS4KK.mjs.map → neutral/NavTreeContainer-6AR4BFDD.mjs.map} +0 -0
  241. /package/dist/types/src/capabilities/{app-graph-builder/app-graph-builder.d.ts → app-graph-builder.d.ts} +0 -0
  242. /package/dist/types/src/capabilities/{keyboard/keyboard.d.ts → keyboard.d.ts} +0 -0
  243. /package/dist/types/src/capabilities/{react-surface/react-surface.d.ts → react-surface.d.ts} +0 -0
@@ -2,26 +2,25 @@
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 { getListActions, 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
- import { NavTreeItemAction, NavTreeItemColumns } from '../NavTreeItem';
23
+ import { NavTreeItemColumns } from '../NavTreeItem/NavTreeItemColumns';
25
24
 
26
25
  export type L1PanelProps = {
27
26
  open?: boolean;
@@ -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
- <DensityProvider density='fine'>
86
+ <DensityProvider density='md'>
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] minmax(0, 1fr) 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>
@@ -141,165 +119,111 @@ const L1PanelHeader = ({ item, path, onBack }: Pick<L1PanelProps, 'item' | 'path
141
119
  const title = toLocalizedString(item.properties.label, t);
142
120
  const backCapableWorkspace = isPinnedWorkspace(item.id);
143
121
 
144
- const { primaryAction, groupedActions, menuActions, onAction } = useL1MenuActions({ item, path });
122
+ const { 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='grid grid-cols-[28px_1fr_min-content_min-content] w-full items-center dx-app-drag dx-density-lg'
129
+ >
163
130
  {backCapableWorkspace ? (
164
131
  <IconButton
165
- size={5}
166
- density='coarse'
167
- classNames={['shrink-0 px-2 pointer-fine:px-1', hoverableControlItem, hoverableOpenControlItem]}
132
+ classNames={[hoverableControlItem, hoverableOpenControlItem]}
168
133
  variant='ghost'
169
134
  icon='ph--caret-left--regular'
170
135
  iconOnly
171
- label={t('button back')}
136
+ size={4}
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 />
177
143
  )}
178
- <h2 className='flex-1 truncate min-w-0'>{title}</h2>
179
- {/* TODO(wittjosiah): Reconcile with NavTreeItemColumns. */}
180
- <div role='none' className='contents dx-app-no-drag'>
181
- {primaryAction?.properties?.disposition === 'list-item-primary' && !primaryAction?.properties?.disabled && (
182
- <NavTreeItemAction
183
- testId={primaryAction.properties?.testId}
184
- label={toLocalizedString(primaryAction.properties?.label, t)}
185
- icon={primaryAction.properties?.icon ?? 'ph--placeholder--regular'}
186
- parent={item}
187
- path={path}
188
- monolithic={Node.isAction(primaryAction)}
189
- menuActions={Node.isAction(primaryAction) ? [primaryAction] : groupedActions[primaryAction?.id ?? '']}
190
- menuType={primaryAction.properties?.menuType}
191
- caller={NAV_TREE_ITEM}
192
- />
193
- )}
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} />}
144
+ <h2 data-tauri-drag-region className='flex-1 truncate min-w-0'>
145
+ {title}
146
+ </h2>
147
+ <div className='contents dx-app-no-drag'>
148
+ <MenuActions item={item} menuActions={menuActions} onAction={onAction} />
149
+ {ItemEnd && <ItemEnd node={item} open />}
225
150
  </div>
226
151
  </div>
227
152
  );
228
153
  };
229
154
 
155
+ type L1MenuActions = {
156
+ menuActions: Node.Action[];
157
+ onAction: (action: Node.Action, params?: Node.InvokeProps) => void;
158
+ };
159
+
230
160
  /**
231
- * Builds the menu actions for the L1 panel header, combining graph actions with a synthetic settings/back action.
161
+ * Header menu actions for an L1 workspace tab. Renders nothing for an empty
162
+ * `menuActions`, a single inline icon button for one action, and a
163
+ * `…`-menu trigger for multiple.
232
164
  */
233
- const useL1MenuActions = ({ item, path }: Pick<L1PanelProps, 'item' | 'path'>) => {
165
+ const MenuActions = ({
166
+ item,
167
+ menuActions,
168
+ onAction,
169
+ }: {
170
+ item: Node.Node;
171
+ } & Pick<L1MenuActions, 'menuActions' | 'onAction'>) => {
234
172
  const { t } = useTranslation(meta.id);
235
- const { setAlternateTree } = useNavTreeContext();
236
- const { graph } = useAppGraph();
237
- const runAction = useActionRunner();
238
173
 
239
- const alternateTree = useAlternateTreeItem(item);
240
- const alternatePath = useMemo(() => [...path, item.id], [item.id, path]);
241
- const isAlternate = useIsAlternateTree(alternatePath, item);
174
+ if (menuActions.length === 0) {
175
+ return null;
176
+ }
177
+
178
+ if (menuActions.length === 1) {
179
+ return (
180
+ <IconButton
181
+ classNames={['shrink-0 px-2 pointer-fine:px-1', hoverableControlItem, hoverableOpenControlItem]}
182
+ variant='ghost'
183
+ icon={menuActions[0].properties?.icon ?? 'ph--circle-dashed--regular'}
184
+ iconOnly
185
+ size={4}
186
+ label={toLocalizedString(menuActions[0].properties?.label, t)}
187
+ data-testid={menuActions[0].properties?.testId}
188
+ onClick={() => onAction(menuActions[0] as Node.Action)}
189
+ />
190
+ );
191
+ }
242
192
 
243
- // Graph actions.
244
- const { actions: actionsProp, groupedActions } = useActions(item);
245
- const [primaryAction, ...secondaryActions] = actionsProp.toSorted((a, _b) =>
246
- a.properties?.disposition === 'list-item-primary' ? -1 : 1,
193
+ return (
194
+ <Menu.Root caller={NAV_TREE_ITEM} onAction={onAction}>
195
+ <Menu.Trigger asChild>
196
+ <IconButton
197
+ classNames={['shrink-0 px-2 pointer-fine:px-1', hoverableControlItem, hoverableOpenControlItem]}
198
+ variant='ghost'
199
+ icon='ph--dots-three-vertical--regular'
200
+ iconOnly
201
+ size={4}
202
+ label={t('tree-item-actions.label')}
203
+ data-testid='navtree.treeItem.actionsLevel0'
204
+ />
205
+ </Menu.Trigger>
206
+ <Menu.Content group={item} items={menuActions as MenuItem[]} />
207
+ </Menu.Root>
247
208
  );
209
+ };
248
210
 
249
- const graphMenuActions = (
250
- primaryAction?.properties?.disposition === 'list-item-primary' ? secondaryActions : actionsProp
251
- )
252
- .flatMap((action) => (Node.isAction(action) ? [action] : []))
253
- .filter((a) => ['list-item', 'list-item-primary'].includes(a.properties?.disposition));
254
-
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]);
211
+ /**
212
+ * Builds the menu actions for the L1 panel header.
213
+ */
214
+ const useL1MenuActions = ({ item, path }: Pick<L1PanelProps, 'item' | 'path'>): L1MenuActions => {
215
+ const runAction = useActionRunner();
275
216
 
276
- const menuActions = useMemo(
277
- () => [...graphMenuActions, ...(settingsAction ? [settingsAction] : [])],
278
- [graphMenuActions, settingsAction],
279
- );
217
+ const menuActions = getListActions(useActions(item));
280
218
 
281
219
  const onAction = useCallback(
282
220
  (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
- }
221
+ void runAction(action, { ...params, path });
291
222
  },
292
- [settingsActionId, setAlternateTree, alternatePath, isAlternate, runAction, graph, alternateTree, path],
223
+ [runAction, path],
293
224
  );
294
225
 
295
- return { primaryAction, groupedActions, menuActions, onAction };
296
- };
297
-
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');
226
+ return { menuActions, onAction };
303
227
  };
304
228
 
305
229
  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';