@dxos/plugin-navtree 0.8.4-main.c85a9c8dae → 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 (258) 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-A27AKTHH.mjs → neutral/CommandsDialogContent-UBGIQ5FI.mjs} +24 -23
  5. package/dist/lib/neutral/CommandsDialogContent-UBGIQ5FI.mjs.map +7 -0
  6. package/dist/lib/{browser/CommandsTrigger-HBZYQCGV.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/neutral/NavTreeContainer-6AR4BFDD.mjs.map +7 -0
  10. package/dist/lib/{browser/NavTreeDocumentTitle-GEE3URWH.mjs → neutral/NavTreeDocumentTitle-H3E6ZLYE.mjs} +5 -9
  11. package/dist/lib/neutral/NavTreeDocumentTitle-H3E6ZLYE.mjs.map +7 -0
  12. package/dist/lib/neutral/NavTreePlugin.mjs +68 -0
  13. package/dist/lib/neutral/NavTreePlugin.mjs.map +7 -0
  14. package/dist/lib/neutral/NavTreePlugin.node.mjs +18 -0
  15. package/dist/lib/neutral/NavTreePlugin.node.mjs.map +7 -0
  16. package/dist/lib/neutral/NavTreePlugin.workerd.mjs +16 -0
  17. package/dist/lib/neutral/NavTreePlugin.workerd.mjs.map +7 -0
  18. package/dist/lib/{browser/app-graph-builder-XGA4GPGL.mjs → neutral/app-graph-builder-Z347LKSF.mjs} +10 -12
  19. package/dist/lib/neutral/app-graph-builder-Z347LKSF.mjs.map +7 -0
  20. package/dist/lib/neutral/capabilities/index.mjs +17 -0
  21. package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
  22. package/dist/lib/neutral/chunk-5OPDKH6B.mjs +273 -0
  23. package/dist/lib/neutral/chunk-5OPDKH6B.mjs.map +7 -0
  24. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
  25. package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +7 -0
  26. package/dist/lib/neutral/chunk-JOSNCOV4.mjs +79 -0
  27. package/dist/lib/neutral/chunk-JOSNCOV4.mjs.map +7 -0
  28. package/dist/lib/neutral/chunk-MDGKDQMZ.mjs +37 -0
  29. package/dist/lib/neutral/chunk-MDGKDQMZ.mjs.map +7 -0
  30. package/dist/lib/neutral/chunk-T7QS2EFI.mjs +8 -0
  31. package/dist/lib/neutral/chunk-T7QS2EFI.mjs.map +7 -0
  32. package/dist/lib/neutral/chunk-ZSWYB7JW.mjs +27 -0
  33. package/dist/lib/neutral/chunk-ZSWYB7JW.mjs.map +7 -0
  34. package/dist/lib/neutral/components/index.mjs +807 -0
  35. package/dist/lib/neutral/components/index.mjs.map +7 -0
  36. package/dist/lib/neutral/containers/index.mjs +20 -0
  37. package/dist/lib/neutral/containers/index.mjs.map +7 -0
  38. package/dist/lib/neutral/expose-26FV4W5D.mjs +38 -0
  39. package/dist/lib/neutral/expose-26FV4W5D.mjs.map +7 -0
  40. package/dist/lib/neutral/hooks/index.mjs +178 -0
  41. package/dist/lib/neutral/hooks/index.mjs.map +7 -0
  42. package/dist/lib/neutral/index.mjs +34 -0
  43. package/dist/lib/neutral/index.mjs.map +7 -0
  44. package/dist/lib/{browser/keyboard-OJZC54PK.mjs → neutral/keyboard-EJ65BVTC.mjs} +8 -6
  45. package/dist/lib/neutral/keyboard-EJ65BVTC.mjs.map +7 -0
  46. package/dist/lib/neutral/meta.json +1 -0
  47. package/dist/lib/neutral/meta.mjs +12 -0
  48. package/dist/lib/neutral/meta.mjs.map +7 -0
  49. package/dist/lib/neutral/operation-handler-ORDOJUL2.mjs +13 -0
  50. package/dist/lib/neutral/operation-handler-ORDOJUL2.mjs.map +7 -0
  51. package/dist/lib/neutral/operations/index.mjs +8 -0
  52. package/dist/lib/neutral/operations/index.mjs.map +7 -0
  53. package/dist/lib/neutral/plugin.mjs +16 -0
  54. package/dist/lib/neutral/plugin.mjs.map +7 -0
  55. package/dist/lib/{browser/react-surface-AYHOAJ4I.mjs → neutral/react-surface-Q5XMAG7Z.mjs} +11 -20
  56. package/dist/lib/neutral/react-surface-Q5XMAG7Z.mjs.map +7 -0
  57. package/dist/lib/{browser/state-AC3EETN7.mjs → neutral/state-VVITVXWM.mjs} +7 -11
  58. package/dist/lib/neutral/state-VVITVXWM.mjs.map +7 -0
  59. package/dist/lib/neutral/testing/index.mjs +439 -0
  60. package/dist/lib/neutral/testing/index.mjs.map +7 -0
  61. package/dist/lib/neutral/translations.mjs +23 -0
  62. package/dist/lib/neutral/translations.mjs.map +7 -0
  63. package/dist/lib/neutral/types/index.mjs +10 -0
  64. package/dist/lib/neutral/types/index.mjs.map +7 -0
  65. package/dist/types/src/NavTreePlugin.d.ts +1 -0
  66. package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
  67. package/dist/types/src/NavTreePlugin.node.d.ts +4 -0
  68. package/dist/types/src/NavTreePlugin.node.d.ts.map +1 -0
  69. package/dist/types/src/NavTreePlugin.test.d.ts +2 -0
  70. package/dist/types/src/NavTreePlugin.test.d.ts.map +1 -0
  71. package/dist/types/src/NavTreePlugin.workerd.d.ts +4 -0
  72. package/dist/types/src/NavTreePlugin.workerd.d.ts.map +1 -0
  73. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -0
  74. package/dist/types/src/capabilities/index.d.ts +7 -5
  75. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  76. package/dist/types/src/capabilities/keyboard.d.ts.map +1 -0
  77. package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
  78. package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
  79. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
  80. package/dist/types/src/capabilities/state.d.ts +6 -0
  81. package/dist/types/src/capabilities/state.d.ts.map +1 -0
  82. package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
  83. package/dist/types/src/components/NavTree/NavTree.stories.d.ts +9 -11
  84. package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -1
  85. package/dist/types/src/components/{NavTreeContext.d.ts → NavTreeContext/NavTreeContext.d.ts} +1 -1
  86. package/dist/types/src/components/NavTreeContext/NavTreeContext.d.ts.map +1 -0
  87. package/dist/types/src/components/NavTreeContext/index.d.ts +2 -0
  88. package/dist/types/src/components/NavTreeContext/index.d.ts.map +1 -0
  89. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts +7 -3
  90. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -1
  91. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +1 -1
  92. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts.map +1 -1
  93. package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts.map +1 -1
  94. package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
  95. package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
  96. package/dist/types/src/components/Sidebar/L1Tabs.d.ts.map +1 -1
  97. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.d.ts.map +1 -1
  98. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts.map +1 -1
  99. package/dist/types/src/components/types.d.ts +1 -2
  100. package/dist/types/src/components/types.d.ts.map +1 -1
  101. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.d.ts.map +1 -1
  102. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.stories.d.ts +28 -0
  103. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.stories.d.ts.map +1 -0
  104. package/dist/types/src/containers/CommandsDialogContent/index.d.ts +1 -2
  105. package/dist/types/src/containers/CommandsDialogContent/index.d.ts.map +1 -1
  106. package/dist/types/src/containers/CommandsTrigger/CommandsTrigger.d.ts.map +1 -1
  107. package/dist/types/src/containers/CommandsTrigger/index.d.ts +1 -2
  108. package/dist/types/src/containers/CommandsTrigger/index.d.ts.map +1 -1
  109. package/dist/types/src/containers/NavTreeContainer/NavTreeContainer.d.ts.map +1 -1
  110. package/dist/types/src/containers/NavTreeContainer/index.d.ts +2 -2
  111. package/dist/types/src/containers/NavTreeContainer/index.d.ts.map +1 -1
  112. package/dist/types/src/containers/NavTreeDocumentTitle/NavTreeDocumentTitle.d.ts.map +1 -1
  113. package/dist/types/src/containers/NavTreeDocumentTitle/index.d.ts +1 -2
  114. package/dist/types/src/containers/NavTreeDocumentTitle/index.d.ts.map +1 -1
  115. package/dist/types/src/containers/index.d.ts +1 -1
  116. package/dist/types/src/containers/index.d.ts.map +1 -1
  117. package/dist/types/src/experimental/Tree.d.ts +2 -2
  118. package/dist/types/src/experimental/Tree.d.ts.map +1 -1
  119. package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
  120. package/dist/types/src/hooks/useActions.d.ts +6 -2
  121. package/dist/types/src/hooks/useActions.d.ts.map +1 -1
  122. package/dist/types/src/hooks/useLoadDescendents.d.ts.map +1 -1
  123. package/dist/types/src/hooks/useNavTreeModel.d.ts +1 -1
  124. package/dist/types/src/hooks/useNavTreeModel.d.ts.map +1 -1
  125. package/dist/types/src/hooks/useNavTreeState.d.ts +2 -6
  126. package/dist/types/src/hooks/useNavTreeState.d.ts.map +1 -1
  127. package/dist/types/src/index.d.ts +1 -2
  128. package/dist/types/src/index.d.ts.map +1 -1
  129. package/dist/types/src/meta.d.ts +3 -2
  130. package/dist/types/src/meta.d.ts.map +1 -1
  131. package/dist/types/src/operations/expose.d.ts +5 -0
  132. package/dist/types/src/operations/expose.d.ts.map +1 -0
  133. package/dist/types/src/operations/index.d.ts +3 -0
  134. package/dist/types/src/operations/index.d.ts.map +1 -0
  135. package/dist/types/src/plugin.d.ts +4 -0
  136. package/dist/types/src/plugin.d.ts.map +1 -0
  137. package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -1
  138. package/dist/types/src/testing/generator.d.ts.map +1 -1
  139. package/dist/types/src/testing/update-state.d.ts.map +1 -1
  140. package/dist/types/src/translations.d.ts +8 -11
  141. package/dist/types/src/translations.d.ts.map +1 -1
  142. package/dist/types/src/types/NavTreeCapabilities.d.ts +13 -0
  143. package/dist/types/src/types/NavTreeCapabilities.d.ts.map +1 -0
  144. package/dist/types/src/types/NavTreeEvents.d.ts +3 -0
  145. package/dist/types/src/types/NavTreeEvents.d.ts.map +1 -0
  146. package/dist/types/src/types/index.d.ts +2 -2
  147. package/dist/types/src/types/index.d.ts.map +1 -1
  148. package/dist/types/src/types/schema.d.ts +4 -0
  149. package/dist/types/src/types/schema.d.ts.map +1 -1
  150. package/dist/types/src/util.d.ts +2 -2
  151. package/dist/types/src/util.d.ts.map +1 -1
  152. package/dist/types/tsconfig.tsbuildinfo +1 -1
  153. package/package.json +110 -50
  154. package/src/NavTreePlugin.node.ts +17 -0
  155. package/src/NavTreePlugin.test.ts +27 -0
  156. package/src/NavTreePlugin.tsx +16 -27
  157. package/src/NavTreePlugin.workerd.ts +16 -0
  158. package/src/capabilities/{app-graph-builder/app-graph-builder.ts → app-graph-builder.ts} +7 -7
  159. package/src/capabilities/index.ts +11 -5
  160. package/src/capabilities/{keyboard/keyboard.ts → keyboard.ts} +2 -2
  161. package/src/capabilities/operation-handler.ts +16 -0
  162. package/src/capabilities/{react-surface/react-surface.tsx → react-surface.tsx} +9 -9
  163. package/src/capabilities/{state/state.ts → state.ts} +14 -10
  164. package/src/components/NavTree/NavTree.stories.tsx +23 -24
  165. package/src/components/NavTree/NavTree.tsx +5 -4
  166. package/src/components/{NavTreeContext.tsx → NavTreeContext/NavTreeContext.tsx} +1 -1
  167. package/src/components/NavTreeContext/index.ts +5 -0
  168. package/src/components/NavTreeItem/NavTreeItemAction.stories.tsx +8 -10
  169. package/src/components/NavTreeItem/NavTreeItemAction.tsx +66 -48
  170. package/src/components/NavTreeItem/NavTreeItemColumns.tsx +22 -63
  171. package/src/components/Sidebar/L0Menu.tsx +62 -81
  172. package/src/components/Sidebar/L1Panel.tsx +99 -158
  173. package/src/components/Sidebar/L1Tabs.tsx +0 -1
  174. package/src/components/types.ts +1 -2
  175. package/src/containers/CommandsDialogContent/CommandsDialogContent.stories.tsx +46 -0
  176. package/src/containers/CommandsDialogContent/CommandsDialogContent.tsx +52 -58
  177. package/src/containers/CommandsDialogContent/index.ts +1 -3
  178. package/src/containers/CommandsTrigger/CommandsTrigger.tsx +7 -5
  179. package/src/containers/CommandsTrigger/index.ts +1 -3
  180. package/src/containers/NavTreeContainer/NavTreeContainer.tsx +28 -27
  181. package/src/containers/NavTreeContainer/index.ts +2 -3
  182. package/src/containers/NavTreeDocumentTitle/NavTreeDocumentTitle.tsx +2 -2
  183. package/src/containers/NavTreeDocumentTitle/index.ts +1 -3
  184. package/src/containers/index.ts +1 -1
  185. package/src/experimental/Tree.stories.tsx +27 -27
  186. package/src/experimental/Tree.tsx +2 -2
  187. package/src/hooks/useActions.ts +26 -20
  188. package/src/hooks/useNavTreeModel.ts +13 -5
  189. package/src/hooks/useNavTreeState.ts +2 -9
  190. package/src/index.ts +1 -2
  191. package/src/meta.ts +22 -7
  192. package/src/operations/expose.ts +42 -0
  193. package/src/operations/index.ts +7 -0
  194. package/src/plugin.ts +11 -0
  195. package/src/testing/app-graph-builder.ts +47 -41
  196. package/src/testing/generator.ts +23 -28
  197. package/src/translations.ts +8 -10
  198. package/src/types/NavTreeCapabilities.ts +21 -0
  199. package/src/types/NavTreeEvents.ts +12 -0
  200. package/src/types/index.ts +3 -2
  201. package/src/types/schema.ts +4 -0
  202. package/src/util.ts +2 -2
  203. package/src/vite-env.d.ts +10 -0
  204. package/dist/lib/browser/CommandsDialogContent-A27AKTHH.mjs.map +0 -7
  205. package/dist/lib/browser/CommandsTrigger-HBZYQCGV.mjs.map +0 -7
  206. package/dist/lib/browser/NavTreeContainer-B5K4FUJS.mjs +0 -12
  207. package/dist/lib/browser/NavTreeContainer-B5K4FUJS.mjs.map +0 -7
  208. package/dist/lib/browser/NavTreeDocumentTitle-GEE3URWH.mjs.map +0 -7
  209. package/dist/lib/browser/app-graph-builder-XGA4GPGL.mjs.map +0 -7
  210. package/dist/lib/browser/chunk-DQCYMXEU.mjs +0 -1426
  211. package/dist/lib/browser/chunk-DQCYMXEU.mjs.map +0 -7
  212. package/dist/lib/browser/chunk-H3OJ4WLF.mjs +0 -14
  213. package/dist/lib/browser/chunk-H3OJ4WLF.mjs.map +0 -7
  214. package/dist/lib/browser/chunk-X2HDG62A.mjs +0 -23
  215. package/dist/lib/browser/chunk-X2HDG62A.mjs.map +0 -7
  216. package/dist/lib/browser/chunk-YKTYPINZ.mjs +0 -19
  217. package/dist/lib/browser/chunk-YKTYPINZ.mjs.map +0 -7
  218. package/dist/lib/browser/index.mjs +0 -141
  219. package/dist/lib/browser/index.mjs.map +0 -7
  220. package/dist/lib/browser/keyboard-OJZC54PK.mjs.map +0 -7
  221. package/dist/lib/browser/meta.json +0 -1
  222. package/dist/lib/browser/operation-resolver-BNAMYOHF.mjs +0 -53
  223. package/dist/lib/browser/operation-resolver-BNAMYOHF.mjs.map +0 -7
  224. package/dist/lib/browser/react-surface-AYHOAJ4I.mjs.map +0 -7
  225. package/dist/lib/browser/state-AC3EETN7.mjs.map +0 -7
  226. package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts.map +0 -1
  227. package/dist/types/src/capabilities/app-graph-builder/index.d.ts +0 -3
  228. package/dist/types/src/capabilities/app-graph-builder/index.d.ts.map +0 -1
  229. package/dist/types/src/capabilities/keyboard/index.d.ts +0 -3
  230. package/dist/types/src/capabilities/keyboard/index.d.ts.map +0 -1
  231. package/dist/types/src/capabilities/keyboard/keyboard.d.ts.map +0 -1
  232. package/dist/types/src/capabilities/operation-resolver/index.d.ts +0 -3
  233. package/dist/types/src/capabilities/operation-resolver/index.d.ts.map +0 -1
  234. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +0 -5
  235. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +0 -1
  236. package/dist/types/src/capabilities/react-surface/index.d.ts +0 -3
  237. package/dist/types/src/capabilities/react-surface/index.d.ts.map +0 -1
  238. package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +0 -1
  239. package/dist/types/src/capabilities/state/index.d.ts +0 -3
  240. package/dist/types/src/capabilities/state/index.d.ts.map +0 -1
  241. package/dist/types/src/capabilities/state/state.d.ts +0 -6
  242. package/dist/types/src/capabilities/state/state.d.ts.map +0 -1
  243. package/dist/types/src/components/NavTreeContext.d.ts.map +0 -1
  244. package/dist/types/src/types/capabilities.d.ts +0 -16
  245. package/dist/types/src/types/capabilities.d.ts.map +0 -1
  246. package/dist/types/src/types/events.d.ts +0 -5
  247. package/dist/types/src/types/events.d.ts.map +0 -1
  248. package/src/capabilities/app-graph-builder/index.ts +0 -7
  249. package/src/capabilities/keyboard/index.ts +0 -7
  250. package/src/capabilities/operation-resolver/index.ts +0 -7
  251. package/src/capabilities/operation-resolver/operation-resolver.ts +0 -44
  252. package/src/capabilities/react-surface/index.ts +0 -7
  253. package/src/capabilities/state/index.ts +0 -7
  254. package/src/types/capabilities.ts +0 -21
  255. package/src/types/events.ts +0 -12
  256. /package/dist/types/src/capabilities/{app-graph-builder/app-graph-builder.d.ts → app-graph-builder.d.ts} +0 -0
  257. /package/dist/types/src/capabilities/{keyboard/keyboard.d.ts → keyboard.d.ts} +0 -0
  258. /package/dist/types/src/capabilities/{react-surface/react-surface.d.ts → react-surface.d.ts} +0 -0
@@ -2,15 +2,15 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
6
- import { draggable, dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
7
- import { preserveOffsetOnSource } from '@atlaskit/pragmatic-drag-and-drop/element/preserve-offset-on-source';
8
- import { scrollJustEnoughIntoView } from '@atlaskit/pragmatic-drag-and-drop/element/scroll-just-enough-into-view';
9
5
  import {
10
6
  type Edge,
11
7
  attachClosestEdge,
12
8
  extractClosestEdge,
13
9
  } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
10
+ import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
11
+ import { draggable, dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
12
+ import { preserveOffsetOnSource } from '@atlaskit/pragmatic-drag-and-drop/element/preserve-offset-on-source';
13
+ import { scrollJustEnoughIntoView } from '@atlaskit/pragmatic-drag-and-drop/element/scroll-just-enough-into-view';
14
14
  import { useAtomValue } from '@effect-atom/atom-react';
15
15
  import React, {
16
16
  type MouseEvent,
@@ -29,6 +29,7 @@ import { DxAvatar } from '@dxos/lit-ui/react';
29
29
  import { useActionRunner } from '@dxos/plugin-graph';
30
30
  import {
31
31
  Icon,
32
+ IconButton,
32
33
  ListItem,
33
34
  ScrollArea,
34
35
  type ThemedClassName,
@@ -43,8 +44,9 @@ import { Tabs } from '@dxos/react-ui-tabs';
43
44
  import { mx } from '@dxos/ui-theme';
44
45
  import { arrayMove } from '@dxos/util';
45
46
 
46
- import { useNavTreeState } from '../../hooks';
47
- import { meta } from '../../meta';
47
+ import { useNavTreeState } from '#hooks';
48
+ import { meta } from '#meta';
49
+
48
50
  import { l0ItemType } from '../../util';
49
51
  import { useNavTreeContext } from '../NavTreeContext';
50
52
  import { UserAccountAvatar } from '../UserAccountAvatar';
@@ -85,12 +87,10 @@ const useL0ItemClick = ({ item, parent, path }: L0ItemProps, type: string) => {
85
87
  switch (type) {
86
88
  case 'action': {
87
89
  const { properties: { caller } = {} } = item;
88
- return void runAction(item as Node.Action, caller ? { parent, caller } : { parent });
90
+ return void runAction(item as Node.Action, caller ? { parent, path, caller } : { parent, path });
89
91
  }
90
-
91
92
  case 'tab':
92
93
  return onTabChange?.(item);
93
-
94
94
  case 'link':
95
95
  return onSelect?.({ item, path, current: !getItem(path).current, option: event.altKey });
96
96
  }
@@ -103,36 +103,34 @@ const l0Breakpoints: Record<string, string> = {
103
103
  lg: 'hidden lg:grid',
104
104
  };
105
105
 
106
- const l0ItemRoot =
107
- 'group/l0item flex w-full justify-center items-center relative data[type!="collection"]:cursor-pointer dx-app-no-drag dx-focus-ring-group';
108
-
109
- const l0ItemContent = 'flex justify-center items-center dx-focus-ring-group-indicator transition-colors rounded-sm';
110
-
111
106
  const L0ItemRoot = memo(
112
- forwardRef<HTMLElement, PropsWithChildren<L0ItemRootProps>>(
107
+ forwardRef<HTMLButtonElement, PropsWithChildren<L0ItemRootProps>>(
113
108
  ({ item, parent, path, onMouseEnter, children }, forwardedRef) => {
109
+ const { t } = useTranslation(meta.id);
114
110
  const { model } = useNavTreeContext();
115
111
  const itemPath = useMemo(() => [...path, item.id], [item.id, path]);
116
112
  const { id, testId } = useAtomValue(model.itemProps(itemPath));
117
- const type = l0ItemType(item);
118
-
119
- const { t } = useTranslation(meta.id);
120
113
  const localizedString = toLocalizedString(item.properties.label, t);
121
114
 
115
+ const type = l0ItemType(item);
122
116
  const handleClick = useL0ItemClick({ item, parent, path: itemPath }, type);
123
- const rootProps =
124
- type === 'tab'
125
- ? { value: item.id, tabIndex: 0, onClick: handleClick, 'data-testid': testId, 'data-object-id': id }
126
- : { onClick: handleClick, 'data-testid': testId, 'data-object-id': id };
127
117
 
128
118
  return (
129
119
  <Tooltip.Trigger asChild delayDuration={0} side='right' content={localizedString}>
130
120
  <Tabs.TabPrimitive
131
- {...(rootProps as any)}
121
+ className={mx(
122
+ 'group/l0item flex w-full justify-center items-center relative',
123
+ 'dx-app-no-drag dx-focus-ring-group data[type!="collection"]:cursor-pointer',
124
+ l0Breakpoints[item.properties.l0Breakpoint],
125
+ )}
126
+ tabIndex={type === 'tab' ? 0 : undefined}
132
127
  data-type={type}
133
- className={mx(l0ItemRoot, l0Breakpoints[item.properties.l0Breakpoint])}
134
- ref={forwardedRef}
128
+ data-testid={testId}
129
+ data-object-id={id}
130
+ value={item.id}
131
+ onClick={handleClick}
135
132
  onMouseEnter={onMouseEnter}
133
+ ref={forwardedRef}
136
134
  >
137
135
  {children}
138
136
  </Tabs.TabPrimitive>
@@ -144,9 +142,8 @@ const L0ItemRoot = memo(
144
142
 
145
143
  export const L0ItemActiveTabIndicator = ({ classNames }: ThemedClassName<{}>) => (
146
144
  <div
147
- role='none'
148
145
  className={mx(
149
- 'hidden group-aria-selected/l0item:block absolute start-0 inset-y-2 w-1 bg-accent-surface rounded-ie',
146
+ 'hidden group-aria-selected/l0item:block absolute start-0 h-6 w-1.5 bg-accent-bg rounded-sm',
150
147
  classNames,
151
148
  )}
152
149
  />
@@ -155,13 +152,12 @@ export const L0ItemActiveTabIndicator = ({ classNames }: ThemedClassName<{}>) =>
155
152
  // TODO(burdon): Factor out pinned (non-draggable) items.
156
153
  const L0Item = memo(({ item, parent, path, pinned, onRearrange, onItemHover }: L0ItemProps) => {
157
154
  const { t } = useTranslation(meta.id);
158
- const itemElement = useRef<HTMLElement | null>(null);
155
+ const itemElement = useRef<HTMLButtonElement | null>(null);
159
156
  const [closestEdge, setEdge] = useState<Edge | null>(null);
160
157
  const localizedString = toLocalizedString(item.properties.label, t);
161
158
  const hue = item.properties.hue ?? null;
162
159
 
163
160
  useLayoutEffect(() => {
164
- // NOTE(thure): This is copied from StackItem, perhaps this should be DRYed out.
165
161
  if (!itemElement.current || !onRearrange) {
166
162
  return;
167
163
  }
@@ -221,14 +217,13 @@ const L0Item = memo(({ item, parent, path, pinned, onRearrange, onItemHover }: L
221
217
  return (
222
218
  <L0ItemRoot ref={itemElement} item={item} parent={parent} path={path} onMouseEnter={handleMouseEnter}>
223
219
  <div
224
- role='none'
225
220
  data-frame={true}
226
221
  {...(hue && { style: { background: `var(--color-${hue}-surface)` } })}
227
222
  className={mx(
228
- l0ItemContent,
223
+ 'flex justify-center items-center dx-focus-ring-group-indicator transition-colors rounded-sm',
229
224
  pinned
230
- ? 'p-2 group-hover/l0item:bg-active-surface'
231
- : 'w-(--dx-l0-avatar-size) h-(--dx-l0-avatar-size) bg-active-surface',
225
+ ? 'p-2 group-hover/l0item:bg-current-surface'
226
+ : 'w-(--dx-l0-avatar-size) h-(--dx-l0-avatar-size) bg-current-surface',
232
227
  )}
233
228
  >
234
229
  <ItemAvatar item={item} />
@@ -244,13 +239,15 @@ const L0Item = memo(({ item, parent, path, pinned, onRearrange, onItemHover }: L
244
239
 
245
240
  const ItemAvatar = ({ item }: Pick<L0ItemProps, 'item'>) => {
246
241
  const { t } = useTranslation(meta.id);
247
- const type = l0ItemType(item);
242
+
243
+ // Actions.
248
244
  if (item.properties.icon) {
249
245
  const hue = item.properties.hue ?? null;
250
- const hueFgStyle = hue && { style: { color: `var(--color-${hue}-surface-text)` } };
246
+ const hueFgStyle = hue && { style: { color: `var(--color-${hue}-fg)` } };
251
247
  return <Icon icon={item.properties.icon} size={6} {...hueFgStyle} />;
252
248
  }
253
249
 
250
+ const type = l0ItemType(item);
254
251
  if (type === 'tab' && item.properties.disposition !== 'pin-end') {
255
252
  const hue = item.properties.hue ?? null;
256
253
  const localizedString = toLocalizedString(item.properties.label, t);
@@ -317,74 +314,58 @@ export const L0Menu = ({
317
314
 
318
315
  return (
319
316
  <Tabs.Tablist
317
+ data-tauri-drag-region
320
318
  classNames={[
321
319
  'group/l0 absolute z-[1] inset-y-0 start-0 rounded-is',
322
320
  'grid grid-cols-[var(--dx-l0-size)] grid-rows-[var(--dx-rail-size)_1fr_min-content_var(--dx-l0-size)] dx-contain-layout',
323
- '!w-(--dx-l0-size) bg-toolbar-surface border-e border-subdued-separator dx-app-drag pb-[env(safe-area-inset-bottom)]',
321
+ 'w-(--dx-l0-size) bg-l0-surface dx-app-drag pb-[env(safe-area-inset-bottom)]',
322
+ '[body[data-platform="macos"]_&]:pt-[30px]',
323
+ '[body[data-platform="ios"]_&]:pt-[max(env(safe-area-inset-top),0.25rem)]',
324
324
  ]}
325
325
  >
326
326
  {/* TODO(wittjosiah): Use L0Item trigger. */}
327
327
  <Menu.Root onAction={handleAction}>
328
- <Menu.Trigger
329
- asChild
330
- data-testid='spacePlugin.addSpace'
331
- className={mx(
332
- l0ItemRoot,
333
- 'grid place-items-center dx-focus-ring-group',
334
- '[body[data-platform="macos"]_&]:mt-[30px]',
335
- )}
336
- >
337
- <Tooltip.Trigger content={t('app menu label')} side='right' asChild>
338
- <Tabs.TabPrimitive value='options' asChild role='button'>
339
- <div
340
- role='none'
341
- className={mx(
342
- l0ItemContent,
343
- 'h-(--dx-rail-action) w-(--dx-rail-action) group-hover/l0item:bg-hover-surface',
344
- )}
345
- >
346
- <Icon icon='ph--list--regular' size={5} />
347
- </div>
348
- </Tabs.TabPrimitive>
349
- </Tooltip.Trigger>
328
+ <Menu.Trigger asChild data-testid='spacePlugin.addSpace'>
329
+ <div className='grid place-items-center'>
330
+ <IconButton
331
+ density='lg'
332
+ variant='ghost'
333
+ size={5}
334
+ icon='ph--list--regular'
335
+ iconOnly
336
+ square
337
+ label={t('app-menu.label')}
338
+ />
339
+ </div>
350
340
  </Menu.Trigger>
351
341
  <Menu.Content group={parent} items={menuActions} />
352
342
  </Menu.Root>
353
343
 
354
344
  {/* Space list. */}
355
- <ScrollArea.Root thin orientation='vertical'>
356
- <ScrollArea.Viewport>
357
- <div
358
- role='none'
359
- className={mx([
360
- 'flex flex-col gap-1 pt-1',
361
- '[body[data-platform="macos"]_&]:py-[30px]',
362
- '[body[data-platform="ios"]_&]:py-[max(env(safe-area-inset-top),0.25rem)]',
363
- ])}
364
- >
365
- {topLevelItems.map((item) => (
366
- <L0Item
367
- key={item.id}
368
- item={item}
369
- parent={parent}
370
- path={path}
371
- onItemHover={onItemHover}
372
- {...(hasRearrangeableItems && { onRearrange: handleRearrange })}
373
- />
374
- ))}
375
- </div>
345
+ <ScrollArea.Root centered thin orientation='vertical'>
346
+ <ScrollArea.Viewport classNames='flex flex-col gap-2 py-1'>
347
+ {topLevelItems.map((item) => (
348
+ <L0Item
349
+ key={item.id}
350
+ item={item}
351
+ parent={parent}
352
+ path={path}
353
+ onItemHover={onItemHover}
354
+ {...(hasRearrangeableItems && { onRearrange: handleRearrange })}
355
+ />
356
+ ))}
376
357
  </ScrollArea.Viewport>
377
358
  </ScrollArea.Root>
378
359
 
379
360
  {/* Actions. */}
380
- <div role='none' className='grid grid-cols-1 auto-rows-(--dx-rail-action) pt-2'>
361
+ <div className='grid grid-cols-1 auto-rows-(--dx-rail-action) pt-2'>
381
362
  {pinnedItems.map((item) => (
382
363
  <L0Item key={item.id} item={item} parent={parent} path={path} pinned />
383
364
  ))}
384
365
  </div>
385
366
 
386
367
  {userAccountItem && (
387
- <div role='none' className='grid dx-app-no-drag'>
368
+ <div className='grid dx-app-no-drag'>
388
369
  <L0ItemRoot key={userAccountItem.id} item={userAccountItem} parent={parent} path={path}>
389
370
  <UserAccountAvatar
390
371
  userId={userAccountItem.properties.userId}
@@ -2,24 +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';
9
+ import { isPinnedWorkspace } from '@dxos/app-toolkit';
10
10
  import { useAppGraph } from '@dxos/app-toolkit/ui';
11
- import { Graph, useActionRunner, useConnections, useEdges } from '@dxos/plugin-graph';
11
+ import { Graph, useActionRunner, useEdges } from '@dxos/plugin-graph';
12
12
  import { DensityProvider, IconButton, ScrollArea, toLocalizedString, useTranslation } from '@dxos/react-ui';
13
13
  import { Tree } from '@dxos/react-ui-list';
14
14
  import { Menu, type MenuItem } from '@dxos/react-ui-menu';
15
15
  import { Tabs } from '@dxos/react-ui-tabs';
16
16
  import { hoverableControlItem, hoverableOpenControlItem } from '@dxos/ui-theme';
17
17
 
18
- import { useActions, useIsAlternateTree, useLoadDescendents } from '../../hooks';
19
- import { meta } from '../../meta';
18
+ import { getListActions, useActions, useLoadDescendents } from '#hooks';
19
+ import { meta } from '#meta';
20
+
20
21
  import { NAV_TREE_ITEM } from '../NavTree';
21
22
  import { useNavTreeContext } from '../NavTreeContext';
22
- import { NavTreeItemAction, NavTreeItemColumns } from '../NavTreeItem';
23
+ import { NavTreeItemColumns } from '../NavTreeItem/NavTreeItemColumns';
23
24
 
24
25
  export type L1PanelProps = {
25
26
  open?: boolean;
@@ -39,7 +40,7 @@ const L1Panel$ = ({ open, path, item, isCurrent, onBack }: L1PanelProps) => {
39
40
  const shouldRenderContent = isCurrent || isActivated;
40
41
 
41
42
  return (
42
- <Tabs.Tabpanel
43
+ <Tabs.Panel
43
44
  key={item.id}
44
45
  value={item.id}
45
46
  classNames={[
@@ -54,7 +55,7 @@ const L1Panel$ = ({ open, path, item, isCurrent, onBack }: L1PanelProps) => {
54
55
  {...(!open && { inert: true })}
55
56
  >
56
57
  {shouldRenderContent && <L1PanelContent open={open} path={path} item={item} onBack={onBack} />}
57
- </Tabs.Tabpanel>
58
+ </Tabs.Panel>
58
59
  );
59
60
  };
60
61
 
@@ -81,49 +82,28 @@ const useIsActivatedWorkspace = (item: Node.Node): boolean => {
81
82
  const L1PanelContent = ({ path, item, onBack }: Pick<L1PanelProps, 'open' | 'path' | 'item' | 'onBack'>) => {
82
83
  const navTreeContext = useNavTreeContext();
83
84
 
84
- const alternateTree = useAlternateTreeItem(item);
85
- const alternatePath = useMemo(() => [...path, item.id], [item.id, path]);
86
- const isAlternate = useIsAlternateTree(alternatePath, item);
87
-
88
85
  return (
89
- <DensityProvider density='fine'>
86
+ <DensityProvider density='md'>
90
87
  <L1PanelHeader path={path} item={item} onBack={onBack} />
91
88
  <ScrollArea.Root thin orientation='vertical'>
92
89
  <ScrollArea.Viewport>
93
- {isAlternate && alternateTree ? (
94
- <Tree
95
- model={navTreeContext.model}
96
- id={alternateTree.id}
97
- rootId={alternateTree.id}
98
- path={alternatePath}
99
- levelOffset={5}
100
- gridTemplateColumns='[tree-row-start] 1fr min-content min-content min-content [tree-row-end]'
101
- renderColumns={NavTreeItemColumns}
102
- blockInstruction={navTreeContext.blockInstruction}
103
- canDrop={navTreeContext.canDrop}
104
- canSelect={navTreeContext.canSelect}
105
- onOpenChange={navTreeContext.onOpenChange}
106
- onSelect={navTreeContext.onSelect}
107
- onItemHover={navTreeContext.onItemHover}
108
- />
109
- ) : (
110
- <Tree
111
- model={navTreeContext.model}
112
- id={item.id}
113
- rootId={item.id}
114
- path={path}
115
- levelOffset={5}
116
- gridTemplateColumns='[tree-row-start] 1fr min-content min-content min-content [tree-row-end]'
117
- draggable
118
- renderColumns={NavTreeItemColumns}
119
- blockInstruction={navTreeContext.blockInstruction}
120
- canDrop={navTreeContext.canDrop}
121
- canSelect={navTreeContext.canSelect}
122
- onOpenChange={navTreeContext.onOpenChange}
123
- onSelect={navTreeContext.onSelect}
124
- onItemHover={navTreeContext.onItemHover}
125
- />
126
- )}
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
+ />
127
107
  </ScrollArea.Viewport>
128
108
  </ScrollArea.Root>
129
109
  </DensityProvider>
@@ -137,152 +117,113 @@ const L1PanelHeader = ({ item, path, onBack }: Pick<L1PanelProps, 'item' | 'path
137
117
  const { t } = useTranslation(meta.id);
138
118
  const { renderItemEnd: ItemEnd } = useNavTreeContext();
139
119
  const title = toLocalizedString(item.properties.label, t);
140
- const backCapableWorkspace = item.id.startsWith('!');
120
+ const backCapableWorkspace = isPinnedWorkspace(item.id);
141
121
 
142
- const { primaryAction, groupedActions, menuActions, onAction } = useL1MenuActions({ item, path });
122
+ const { menuActions, onAction } = useL1MenuActions({ item, path });
143
123
  useLoadDescendents(item);
144
124
 
145
125
  return (
146
- <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
+ >
147
130
  {backCapableWorkspace ? (
148
131
  <IconButton
149
- size={5}
150
- density='coarse'
151
- classNames={['shrink-0 px-2 pointer-fine:px-1', hoverableControlItem, hoverableOpenControlItem]}
132
+ classNames={[hoverableControlItem, hoverableOpenControlItem]}
152
133
  variant='ghost'
153
134
  icon='ph--caret-left--regular'
154
135
  iconOnly
155
- label={t('button back')}
136
+ size={4}
137
+ label={t('button-back.button')}
156
138
  data-testid='treeView.primaryTreeButton'
157
139
  onClick={() => onBack?.()}
158
140
  />
159
141
  ) : (
160
- <div className='w-6' />
142
+ <div />
161
143
  )}
162
- <h2 className='flex-1 truncate min-w-0'>{title}</h2>
163
- {/* TODO(wittjosiah): Reconcile with NavTreeItemColumns. */}
164
- <div role='none' className='contents dx-app-no-drag'>
165
- {primaryAction?.properties?.disposition === 'list-item-primary' && !primaryAction?.properties?.disabled && (
166
- <NavTreeItemAction
167
- testId={primaryAction.properties?.testId}
168
- label={toLocalizedString(primaryAction.properties?.label, t)}
169
- icon={primaryAction.properties?.icon ?? 'ph--placeholder--regular'}
170
- parent={item}
171
- monolithic={Node.isAction(primaryAction)}
172
- menuActions={Node.isAction(primaryAction) ? [primaryAction] : groupedActions[primaryAction?.id ?? '']}
173
- menuType={primaryAction.properties?.menuType}
174
- caller={NAV_TREE_ITEM}
175
- />
176
- )}
177
- {menuActions.length === 1 && (
178
- <IconButton
179
- size={5}
180
- density='coarse'
181
- classNames={['shrink-0 px-2 pointer-fine:px-1', hoverableControlItem, hoverableOpenControlItem]}
182
- variant='ghost'
183
- icon={menuActions[0].properties?.icon ?? 'ph--placeholder--regular'}
184
- iconOnly
185
- label={toLocalizedString(menuActions[0].properties?.label, t)}
186
- data-testid={menuActions[0].properties?.testId}
187
- onClick={() => onAction(menuActions[0] as Node.Action)}
188
- />
189
- )}
190
- {menuActions.length > 1 && (
191
- <Menu.Root caller={NAV_TREE_ITEM} onAction={onAction}>
192
- <Menu.Trigger asChild>
193
- <IconButton
194
- size={5}
195
- density='coarse'
196
- classNames={['shrink-0 px-2 pointer-fine:px-1', hoverableControlItem, hoverableOpenControlItem]}
197
- variant='ghost'
198
- icon='ph--dots-three-vertical--regular'
199
- iconOnly
200
- label={t('tree item actions label')}
201
- data-testid='navtree.treeItem.actionsLevel0'
202
- />
203
- </Menu.Trigger>
204
- <Menu.Content group={item} items={menuActions as MenuItem[]} />
205
- </Menu.Root>
206
- )}
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} />
207
149
  {ItemEnd && <ItemEnd node={item} open />}
208
150
  </div>
209
151
  </div>
210
152
  );
211
153
  };
212
154
 
155
+ type L1MenuActions = {
156
+ menuActions: Node.Action[];
157
+ onAction: (action: Node.Action, params?: Node.InvokeProps) => void;
158
+ };
159
+
213
160
  /**
214
- * 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.
215
164
  */
216
- 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'>) => {
217
172
  const { t } = useTranslation(meta.id);
218
- const { setAlternateTree } = useNavTreeContext();
219
- const { graph } = useAppGraph();
220
- const runAction = useActionRunner();
221
173
 
222
- const alternateTree = useAlternateTreeItem(item);
223
- const alternatePath = useMemo(() => [...path, item.id], [item.id, path]);
224
- 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
+ }
225
192
 
226
- // Graph actions.
227
- const { actions: actionsProp, groupedActions } = useActions(item);
228
- const [primaryAction, ...secondaryActions] = actionsProp.toSorted((a, _b) =>
229
- 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>
230
208
  );
209
+ };
231
210
 
232
- const graphMenuActions = (
233
- primaryAction?.properties?.disposition === 'list-item-primary' ? secondaryActions : actionsProp
234
- )
235
- .flatMap((action) => (Node.isAction(action) ? [action] : []))
236
- .filter((a) => ['list-item', 'list-item-primary'].includes(a.properties?.disposition));
237
-
238
- // Synthetic settings/back action.
239
- const settingsActionId = `${item.id}~settings`;
240
- const settingsAction = useMemo((): Node.Action | undefined => {
241
- if (!alternateTree) {
242
- return undefined;
243
- }
244
- return {
245
- id: settingsActionId,
246
- type: Node.ActionType,
247
- data: () => Effect.void,
248
- properties: {
249
- label: isAlternate ? ['button back', { ns: meta.id }] : (alternateTree.properties.label ?? alternateTree.id),
250
- icon: isAlternate
251
- ? 'ph--arrow-u-down-left--regular'
252
- : (alternateTree.properties.icon ?? 'ph--placeholder--regular'),
253
- disposition: 'list-item',
254
- testId: isAlternate ? 'navtree.backToSpace' : 'navtree.spaceSettings',
255
- },
256
- } as Node.Action;
257
- }, [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();
258
216
 
259
- const menuActions = useMemo(
260
- () => [...graphMenuActions, ...(settingsAction ? [settingsAction] : [])],
261
- [graphMenuActions, settingsAction],
262
- );
217
+ const menuActions = getListActions(useActions(item));
263
218
 
264
219
  const onAction = useCallback(
265
220
  (action: Node.Action, params?: Node.InvokeProps) => {
266
- if (action.id === settingsActionId) {
267
- if (alternateTree && !isAlternate) {
268
- Graph.expand(graph, alternateTree.id, 'child');
269
- }
270
- setAlternateTree?.(alternatePath, !isAlternate);
271
- } else {
272
- void runAction(action, params);
273
- }
221
+ void runAction(action, { ...params, path });
274
222
  },
275
- [settingsActionId, setAlternateTree, alternatePath, isAlternate, runAction, graph, alternateTree],
223
+ [runAction, path],
276
224
  );
277
225
 
278
- return { primaryAction, groupedActions, menuActions, onAction };
279
- };
280
-
281
- /** Finds the first child with disposition 'alternate-tree' using graph connections directly. */
282
- const useAlternateTreeItem = (item: Node.Node): Node.Node | undefined => {
283
- const { graph } = useAppGraph();
284
- const connections = useConnections(graph, item.id, 'child');
285
- return connections.find((node) => node.properties.disposition === 'alternate-tree');
226
+ return { menuActions, onAction };
286
227
  };
287
228
 
288
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>,