@dxos/plugin-navtree 0.8.4-main.422d1c7879 → 0.8.4-main.51f1e5ca51

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 (71) hide show
  1. package/dist/lib/browser/index.mjs +7 -27
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/browser/translations.mjs +21 -0
  5. package/dist/lib/browser/translations.mjs.map +7 -0
  6. package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
  7. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  8. package/dist/types/src/capabilities/index.d.ts +1 -1
  9. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  10. package/dist/types/src/capabilities/keyboard.d.ts.map +1 -1
  11. package/dist/types/src/capabilities/operation-handler.d.ts +1 -1
  12. package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -1
  13. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  14. package/dist/types/src/capabilities/state.d.ts.map +1 -1
  15. package/dist/types/src/components/NavTree/NavTree.stories.d.ts +8 -9
  16. package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -1
  17. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts +2 -2
  18. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -1
  19. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +1 -1
  20. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts.map +1 -1
  21. package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
  22. package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
  23. package/dist/types/src/components/Sidebar/L1Tabs.d.ts.map +1 -1
  24. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.d.ts.map +1 -1
  25. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts.map +1 -1
  26. package/dist/types/src/components/types.d.ts +0 -1
  27. package/dist/types/src/components/types.d.ts.map +1 -1
  28. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.stories.d.ts +8 -9
  29. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.stories.d.ts.map +1 -1
  30. package/dist/types/src/containers/NavTreeContainer/NavTreeContainer.d.ts.map +1 -1
  31. package/dist/types/src/containers/NavTreeDocumentTitle/NavTreeDocumentTitle.d.ts.map +1 -1
  32. package/dist/types/src/experimental/Tree.d.ts +2 -2
  33. package/dist/types/src/experimental/Tree.d.ts.map +1 -1
  34. package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
  35. package/dist/types/src/hooks/useActions.d.ts.map +1 -1
  36. package/dist/types/src/hooks/useLoadDescendents.d.ts.map +1 -1
  37. package/dist/types/src/hooks/useNavTreeModel.d.ts.map +1 -1
  38. package/dist/types/src/hooks/useNavTreeState.d.ts +1 -5
  39. package/dist/types/src/hooks/useNavTreeState.d.ts.map +1 -1
  40. package/dist/types/src/operations/expose.d.ts +1 -1
  41. package/dist/types/src/operations/expose.d.ts.map +1 -1
  42. package/dist/types/src/operations/index.d.ts +1 -1
  43. package/dist/types/src/operations/index.d.ts.map +1 -1
  44. package/dist/types/src/testing/generator.d.ts.map +1 -1
  45. package/dist/types/src/testing/update-state.d.ts.map +1 -1
  46. package/dist/types/src/translations.d.ts +8 -10
  47. package/dist/types/src/translations.d.ts.map +1 -1
  48. package/dist/types/src/types/capabilities.d.ts +1 -2
  49. package/dist/types/src/types/capabilities.d.ts.map +1 -1
  50. package/dist/types/src/types/events.d.ts.map +1 -1
  51. package/dist/types/src/util.d.ts +1 -1
  52. package/dist/types/src/util.d.ts.map +1 -1
  53. package/dist/types/tsconfig.tsbuildinfo +1 -1
  54. package/package.json +44 -37
  55. package/src/NavTreePlugin.tsx +1 -2
  56. package/src/capabilities/app-graph-builder.ts +1 -1
  57. package/src/capabilities/index.ts +1 -1
  58. package/src/capabilities/operation-handler.ts +1 -1
  59. package/src/capabilities/state.ts +2 -2
  60. package/src/components/NavTree/NavTree.stories.tsx +2 -2
  61. package/src/components/NavTreeItem/NavTreeItemAction.tsx +3 -4
  62. package/src/components/Sidebar/L1Panel.tsx +82 -141
  63. package/src/components/types.ts +0 -1
  64. package/src/containers/CommandsDialogContent/CommandsDialogContent.stories.tsx +2 -1
  65. package/src/containers/NavTreeContainer/NavTreeContainer.tsx +0 -9
  66. package/src/hooks/useNavTreeState.ts +1 -8
  67. package/src/operations/expose.ts +1 -1
  68. package/src/operations/index.ts +1 -1
  69. package/src/translations.ts +0 -1
  70. package/src/types/capabilities.ts +2 -2
  71. package/src/util.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/plugin-navtree",
3
- "version": "0.8.4-main.422d1c7879",
3
+ "version": "0.8.4-main.51f1e5ca51",
4
4
  "description": "DXOS Surface plugin for a tree view visualization of the graph.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -20,6 +20,7 @@
20
20
  "#meta": "./src/meta.ts",
21
21
  "#operations": "./src/operations/index.ts",
22
22
  "#testing": "./src/testing/index.ts",
23
+ "#translations": "./src/translations.ts",
23
24
  "#types": "./src/types/index.ts"
24
25
  },
25
26
  "exports": {
@@ -27,6 +28,12 @@
27
28
  "source": "./src/index.ts",
28
29
  "types": "./dist/types/src/index.d.ts",
29
30
  "browser": "./dist/lib/browser/index.mjs"
31
+ },
32
+ "./translations": {
33
+ "source": "./src/translations.ts",
34
+ "types": "./dist/types/src/translations.d.ts",
35
+ "browser": "./dist/lib/browser/translations.mjs",
36
+ "node": "./dist/lib/node-esm/translations.mjs"
30
37
  }
31
38
  },
32
39
  "types": "dist/types/src/index.d.ts",
@@ -40,30 +47,30 @@
40
47
  "@radix-ui/react-dismissable-layer": "1.1.5",
41
48
  "@radix-ui/react-use-controllable-state": "1.1.0",
42
49
  "date-fns": "^3.6.0",
43
- "@dxos/app-framework": "0.8.4-main.422d1c7879",
44
- "@dxos/app-graph": "0.8.4-main.422d1c7879",
45
- "@dxos/app-toolkit": "0.8.4-main.422d1c7879",
46
- "@dxos/async": "0.8.4-main.422d1c7879",
47
- "@dxos/echo": "0.8.4-main.422d1c7879",
48
- "@dxos/invariant": "0.8.4-main.422d1c7879",
49
- "@dxos/keyboard": "0.8.4-main.422d1c7879",
50
- "@dxos/debug": "0.8.4-main.422d1c7879",
51
- "@dxos/lit-ui": "0.8.4-main.422d1c7879",
52
- "@dxos/log": "0.8.4-main.422d1c7879",
53
- "@dxos/plugin-deck": "0.8.4-main.422d1c7879",
54
- "@dxos/operation": "0.8.4-main.422d1c7879",
55
- "@dxos/plugin-graph": "0.8.4-main.422d1c7879",
56
- "@dxos/plugin-observability": "0.8.4-main.422d1c7879",
57
- "@dxos/react-client": "0.8.4-main.422d1c7879",
58
- "@dxos/react-ui-attention": "0.8.4-main.422d1c7879",
59
- "@dxos/protocols": "0.8.4-main.422d1c7879",
60
- "@dxos/react-ui-menu": "0.8.4-main.422d1c7879",
61
- "@dxos/react-ui-list": "0.8.4-main.422d1c7879",
62
- "@dxos/react-ui-search": "0.8.4-main.422d1c7879",
63
- "@dxos/react-ui-tabs": "0.8.4-main.422d1c7879",
64
- "@dxos/react-ui-stack": "0.8.4-main.422d1c7879",
65
- "@dxos/util": "0.8.4-main.422d1c7879",
66
- "@dxos/react-ui-mosaic": "0.8.4-main.422d1c7879"
50
+ "@dxos/app-graph": "0.8.4-main.51f1e5ca51",
51
+ "@dxos/app-framework": "0.8.4-main.51f1e5ca51",
52
+ "@dxos/app-toolkit": "0.8.4-main.51f1e5ca51",
53
+ "@dxos/async": "0.8.4-main.51f1e5ca51",
54
+ "@dxos/compute": "0.8.4-main.51f1e5ca51",
55
+ "@dxos/debug": "0.8.4-main.51f1e5ca51",
56
+ "@dxos/echo": "0.8.4-main.51f1e5ca51",
57
+ "@dxos/invariant": "0.8.4-main.51f1e5ca51",
58
+ "@dxos/keyboard": "0.8.4-main.51f1e5ca51",
59
+ "@dxos/lit-ui": "0.8.4-main.51f1e5ca51",
60
+ "@dxos/plugin-graph": "0.8.4-main.51f1e5ca51",
61
+ "@dxos/log": "0.8.4-main.51f1e5ca51",
62
+ "@dxos/protocols": "0.8.4-main.51f1e5ca51",
63
+ "@dxos/react-client": "0.8.4-main.51f1e5ca51",
64
+ "@dxos/plugin-observability": "0.8.4-main.51f1e5ca51",
65
+ "@dxos/react-ui-list": "0.8.4-main.51f1e5ca51",
66
+ "@dxos/react-ui-menu": "0.8.4-main.51f1e5ca51",
67
+ "@dxos/react-ui-attention": "0.8.4-main.51f1e5ca51",
68
+ "@dxos/react-ui-search": "0.8.4-main.51f1e5ca51",
69
+ "@dxos/react-ui-mosaic": "0.8.4-main.51f1e5ca51",
70
+ "@dxos/react-ui-stack": "0.8.4-main.51f1e5ca51",
71
+ "@dxos/react-ui-tabs": "0.8.4-main.51f1e5ca51",
72
+ "@dxos/plugin-deck": "0.8.4-main.51f1e5ca51",
73
+ "@dxos/util": "0.8.4-main.51f1e5ca51"
67
74
  },
68
75
  "devDependencies": {
69
76
  "@effect-atom/atom-react": "^0.5.0",
@@ -73,16 +80,16 @@
73
80
  "effect": "3.20.0",
74
81
  "react": "~19.2.3",
75
82
  "react-dom": "~19.2.3",
76
- "vite": "^7.1.11",
77
- "@dxos/plugin-attention": "0.8.4-main.422d1c7879",
78
- "@dxos/plugin-client": "0.8.4-main.422d1c7879",
79
- "@dxos/plugin-space": "0.8.4-main.422d1c7879",
80
- "@dxos/random": "0.8.4-main.422d1c7879",
81
- "@dxos/plugin-testing": "0.8.4-main.422d1c7879",
82
- "@dxos/react-ui": "0.8.4-main.422d1c7879",
83
- "@dxos/plugin-theme": "0.8.4-main.422d1c7879",
84
- "@dxos/storybook-utils": "0.8.4-main.422d1c7879",
85
- "@dxos/ui-theme": "0.8.4-main.422d1c7879"
83
+ "vite": "^8.0.10",
84
+ "@dxos/plugin-attention": "0.8.4-main.51f1e5ca51",
85
+ "@dxos/plugin-space": "0.8.4-main.51f1e5ca51",
86
+ "@dxos/plugin-testing": "0.8.4-main.51f1e5ca51",
87
+ "@dxos/random": "0.8.4-main.51f1e5ca51",
88
+ "@dxos/plugin-client": "0.8.4-main.51f1e5ca51",
89
+ "@dxos/plugin-theme": "0.8.4-main.51f1e5ca51",
90
+ "@dxos/react-ui": "0.8.4-main.51f1e5ca51",
91
+ "@dxos/storybook-utils": "0.8.4-main.51f1e5ca51",
92
+ "@dxos/ui-theme": "0.8.4-main.51f1e5ca51"
86
93
  },
87
94
  "peerDependencies": {
88
95
  "@effect-atom/atom-react": "^0.5.0",
@@ -90,8 +97,8 @@
90
97
  "effect": "3.20.0",
91
98
  "react": "~19.2.3",
92
99
  "react-dom": "~19.2.3",
93
- "@dxos/react-ui": "0.8.4-main.422d1c7879",
94
- "@dxos/ui-theme": "0.8.4-main.422d1c7879"
100
+ "@dxos/react-ui": "0.8.4-main.51f1e5ca51",
101
+ "@dxos/ui-theme": "0.8.4-main.51f1e5ca51"
95
102
  },
96
103
  "publishConfig": {
97
104
  "access": "public"
@@ -12,10 +12,9 @@ import { type TreeData } from '@dxos/react-ui-list';
12
12
  import { AppGraphBuilder, Keyboard, OperationHandler, ReactSurface, State } from '#capabilities';
13
13
  import { NODE_TYPE } from '#containers';
14
14
  import { meta } from '#meta';
15
+ import { translations } from '#translations';
15
16
  import { NavTreeEvents } from '#types';
16
17
 
17
- import { translations } from './translations';
18
-
19
18
  export const NavTreePlugin = Plugin.define(meta).pipe(
20
19
  AppPlugin.addAppGraphModule({ activate: AppGraphBuilder }),
21
20
  AppPlugin.addMetadataModule({
@@ -6,7 +6,7 @@ import * as Effect from 'effect/Effect';
6
6
 
7
7
  import { Capability } from '@dxos/app-framework';
8
8
  import { AppCapabilities, LayoutOperation } from '@dxos/app-toolkit';
9
- import { Operation } from '@dxos/operation';
9
+ import { Operation } from '@dxos/compute';
10
10
  import { GraphBuilder, Node, NodeMatcher } from '@dxos/plugin-graph';
11
11
 
12
12
  import { COMMANDS_DIALOG, meta } from '#meta';
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import { Capability } from '@dxos/app-framework';
6
- import { OperationHandlerSet } from '@dxos/operation';
6
+ import { OperationHandlerSet } from '@dxos/compute';
7
7
 
8
8
  export const AppGraphBuilder = Capability.lazy('AppGraphBuilder', () => import('./app-graph-builder'));
9
9
  export const Keyboard = Capability.lazy('Keyboard', () => import('./keyboard'));
@@ -5,7 +5,7 @@
5
5
  import * as Effect from 'effect/Effect';
6
6
 
7
7
  import { Capabilities, Capability } from '@dxos/app-framework';
8
- import type { OperationHandlerSet } from '@dxos/operation';
8
+ import type { OperationHandlerSet } from '@dxos/compute';
9
9
 
10
10
  import { NavTreeOperationHandlerSet } from '#operations';
11
11
 
@@ -16,7 +16,7 @@ import { type NavTreeCapabilities as NC, NavTreeCapabilities } from '#types';
16
16
  const KEY = `${meta.id}.state.v1`;
17
17
 
18
18
  /** Default item state for new entries. */
19
- const defaultItemState: NC.NavTreeItemState = { open: false, current: false, alternateTree: false };
19
+ const defaultItemState: NC.NavTreeItemState = { open: false, current: false };
20
20
 
21
21
  /** L0 (top-level workspace) paths are direct children of root — not part of the expandable tree model. */
22
22
  const isTopLevelPath = (path: string[]): boolean => path.length === 2 && path[0] === Node.RootId;
@@ -66,7 +66,7 @@ export default Capability.makeModule(
66
66
  return registry.get(getItemAtom(path));
67
67
  };
68
68
 
69
- const setItem = (path: string[], key: 'open' | 'current' | 'alternateTree', next: boolean) => {
69
+ const setItem = (path: string[], key: 'open' | 'current', next: boolean) => {
70
70
  const pathString = Path.create(...path);
71
71
  const atom = itemAtomFamily(pathString);
72
72
  const currentValue = registry.get(atom);
@@ -12,7 +12,7 @@ import { Capabilities, Capability } from '@dxos/app-framework';
12
12
  import { withPluginManager } from '@dxos/app-framework/testing';
13
13
  import { useAtomCapability } from '@dxos/app-framework/ui';
14
14
  import { AppCapabilities, LayoutOperation } from '@dxos/app-toolkit';
15
- import { Operation, OperationHandlerSet } from '@dxos/operation';
15
+ import { Operation, OperationHandlerSet } from '@dxos/compute';
16
16
  import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
17
17
  import { random } from '@dxos/random';
18
18
  import { IconButton, Input, Main, Toolbar } from '@dxos/react-ui';
@@ -23,9 +23,9 @@ import { mx } from '@dxos/ui-theme';
23
23
 
24
24
  import { NavTreeContainer } from '#containers';
25
25
  import { storybookGraphBuilders } from '#testing';
26
+ import { translations } from '#translations';
26
27
 
27
28
  import { NavTreePlugin } from '../../NavTreePlugin';
28
- import { translations } from '../../translations';
29
29
 
30
30
  random.seed(1234);
31
31
 
@@ -113,7 +113,7 @@ export const NavTreeItemAction = ({
113
113
  monolithic,
114
114
  menuActions,
115
115
  menuType,
116
- parent: node,
116
+ parent,
117
117
  path,
118
118
  ...props
119
119
  }: NavTreeItemActionMenuProps) => {
@@ -121,10 +121,9 @@ export const NavTreeItemAction = ({
121
121
 
122
122
  const monolithicAction = menuActions?.length === 1 && menuActions[0];
123
123
  const baseLabel = toLocalizedString(monolithicAction ? monolithicAction.properties!.label : props.label, t);
124
-
125
124
  return monolithic && menuActions?.length === 1 ? (
126
- <NavTreeItemMonolithicAction baseLabel={baseLabel} parent={node} path={path} {...menuActions[0]} />
125
+ <NavTreeItemMonolithicAction baseLabel={baseLabel} parent={parent} path={path} {...menuActions[0]} />
127
126
  ) : (
128
- <NavTreeItemActionDropdownMenu {...props} label={baseLabel} parent={node} path={path} menuActions={menuActions} />
127
+ <NavTreeItemActionDropdownMenu {...props} label={baseLabel} parent={parent} path={path} menuActions={menuActions} />
129
128
  );
130
129
  };
@@ -2,22 +2,20 @@
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';
18
+ import { useActions, useLoadDescendents } from '#hooks';
21
19
  import { meta } from '#meta';
22
20
 
23
21
  import { NAV_TREE_ITEM } from '../NavTree';
@@ -84,49 +82,27 @@ const useIsActivatedWorkspace = (item: Node.Node): boolean => {
84
82
  const L1PanelContent = ({ path, item, onBack }: Pick<L1PanelProps, 'open' | 'path' | 'item' | 'onBack'>) => {
85
83
  const navTreeContext = useNavTreeContext();
86
84
 
87
- const alternateTree = useAlternateTreeItem(item);
88
- const alternatePath = useMemo(() => [...path, item.id], [item.id, path]);
89
- const isAlternate = useIsAlternateTree(alternatePath, item);
90
-
91
85
  return (
92
86
  <DensityProvider density='fine'>
93
87
  <L1PanelHeader path={path} item={item} onBack={onBack} />
94
88
  <ScrollArea.Root thin orientation='vertical'>
95
89
  <ScrollArea.Viewport>
96
- {isAlternate && alternateTree ? (
97
- <Tree
98
- model={navTreeContext.model}
99
- id={alternateTree.id}
100
- rootId={alternateTree.id}
101
- path={alternatePath}
102
- levelOffset={5}
103
- gridTemplateColumns='[tree-row-start] 1fr min-content min-content min-content [tree-row-end]'
104
- renderColumns={NavTreeItemColumns}
105
- blockInstruction={navTreeContext.blockInstruction}
106
- canDrop={navTreeContext.canDrop}
107
- canSelect={navTreeContext.canSelect}
108
- onOpenChange={navTreeContext.onOpenChange}
109
- onSelect={navTreeContext.onSelect}
110
- onItemHover={navTreeContext.onItemHover}
111
- />
112
- ) : (
113
- <Tree
114
- model={navTreeContext.model}
115
- id={item.id}
116
- rootId={item.id}
117
- path={path}
118
- levelOffset={5}
119
- gridTemplateColumns='[tree-row-start] 1fr min-content min-content min-content [tree-row-end]'
120
- draggable
121
- renderColumns={NavTreeItemColumns}
122
- blockInstruction={navTreeContext.blockInstruction}
123
- canDrop={navTreeContext.canDrop}
124
- canSelect={navTreeContext.canSelect}
125
- onOpenChange={navTreeContext.onOpenChange}
126
- onSelect={navTreeContext.onSelect}
127
- onItemHover={navTreeContext.onItemHover}
128
- />
129
- )}
90
+ <Tree
91
+ model={navTreeContext.model}
92
+ id={item.id}
93
+ rootId={item.id}
94
+ path={path}
95
+ levelOffset={5}
96
+ gridTemplateColumns='[tree-row-start] 1fr min-content min-content min-content [tree-row-end]'
97
+ draggable
98
+ renderColumns={NavTreeItemColumns}
99
+ blockInstruction={navTreeContext.blockInstruction}
100
+ canDrop={navTreeContext.canDrop}
101
+ canSelect={navTreeContext.canSelect}
102
+ onOpenChange={navTreeContext.onOpenChange}
103
+ onSelect={navTreeContext.onSelect}
104
+ onItemHover={navTreeContext.onItemHover}
105
+ />
130
106
  </ScrollArea.Viewport>
131
107
  </ScrollArea.Root>
132
108
  </DensityProvider>
@@ -145,20 +121,6 @@ const L1PanelHeader = ({ item, path, onBack }: Pick<L1PanelProps, 'item' | 'path
145
121
  const { primaryAction, groupedActions, menuActions, onAction } = useL1MenuActions({ item, path });
146
122
  useLoadDescendents(item);
147
123
 
148
- // TODO(wittjosiah): Hack to only show containsAttended when attended item is within alternate tree (e.g. settings).
149
- // Since every object in the space is a descendant of the space node, we'd otherwise always show containsAttended.
150
- const alternateTree = useAlternateTreeItem(item);
151
- const attended = useAttended();
152
- const alternateTreeOpen = useMemo(() => {
153
- if (!alternateTree) {
154
- return true;
155
- }
156
- const hasAlternateAttended = attended.some(
157
- (attendedId) => attendedId === alternateTree.id || attendedId.startsWith(alternateTree.id + '/'),
158
- );
159
- return !hasAlternateAttended;
160
- }, [alternateTree, attended]);
161
-
162
124
  return (
163
125
  <div
164
126
  data-tauri-drag-region
@@ -196,115 +158,94 @@ const L1PanelHeader = ({ item, path, onBack }: Pick<L1PanelProps, 'item' | 'path
196
158
  caller={NAV_TREE_ITEM}
197
159
  />
198
160
  )}
199
- {menuActions.length === 1 && (
200
- <IconButton
201
- density='coarse'
202
- classNames={['shrink-0 px-2 pointer-fine:px-1', hoverableControlItem, hoverableOpenControlItem]}
203
- variant='ghost'
204
- icon={menuActions[0].properties?.icon ?? 'ph--placeholder--regular'}
205
- iconOnly
206
- label={toLocalizedString(menuActions[0].properties?.label, t)}
207
- data-testid={menuActions[0].properties?.testId}
208
- onClick={() => onAction(menuActions[0] as Node.Action)}
209
- />
210
- )}
211
- {menuActions.length > 1 && (
212
- <Menu.Root caller={NAV_TREE_ITEM} onAction={onAction}>
213
- <Menu.Trigger asChild>
214
- <IconButton
215
- density='coarse'
216
- classNames={['shrink-0 px-2 pointer-fine:px-1', hoverableControlItem, hoverableOpenControlItem]}
217
- variant='ghost'
218
- icon='ph--dots-three-vertical--regular'
219
- iconOnly
220
- label={t('tree-item-actions.label')}
221
- data-testid='navtree.treeItem.actionsLevel0'
222
- />
223
- </Menu.Trigger>
224
- <Menu.Content group={item} items={menuActions as MenuItem[]} />
225
- </Menu.Root>
226
- )}
227
- {ItemEnd && <ItemEnd node={item} open={alternateTreeOpen} />}
161
+ <MenuActions item={item} menuActions={menuActions} onAction={onAction} />
162
+ {ItemEnd && <ItemEnd node={item} open />}
228
163
  </div>
229
164
  </div>
230
165
  );
231
166
  };
232
167
 
168
+ type L1MenuActions = {
169
+ primaryAction: Node.ActionLike;
170
+ groupedActions: Record<string, Node.Action[]>;
171
+ menuActions: Node.Action[];
172
+ onAction: (action: Node.Action, params?: Node.InvokeProps) => void;
173
+ };
174
+
233
175
  /**
234
- * Builds the menu actions for the L1 panel header, combining graph actions with a synthetic settings/back action.
176
+ * Header menu actions for an L1 workspace tab. Renders nothing for an empty
177
+ * `menuActions`, a single inline icon button for one action, and a
178
+ * `…`-menu trigger for multiple.
235
179
  */
236
- const useL1MenuActions = ({ item, path }: Pick<L1PanelProps, 'item' | 'path'>) => {
180
+ const MenuActions = ({
181
+ item,
182
+ menuActions,
183
+ onAction,
184
+ }: {
185
+ item: Node.Node;
186
+ } & Pick<L1MenuActions, 'menuActions' | 'onAction'>) => {
237
187
  const { t } = useTranslation(meta.id);
238
- const { setAlternateTree } = useNavTreeContext();
239
- const { graph } = useAppGraph();
240
- const runAction = useActionRunner();
241
188
 
242
- const alternateTree = useAlternateTreeItem(item);
243
- const alternatePath = useMemo(() => [...path, item.id], [item.id, path]);
244
- const isAlternate = useIsAlternateTree(alternatePath, item);
189
+ if (menuActions.length === 0) {
190
+ return null;
191
+ }
192
+
193
+ if (menuActions.length === 1) {
194
+ return (
195
+ <IconButton
196
+ density='coarse'
197
+ classNames={['shrink-0 px-2 pointer-fine:px-1', hoverableControlItem, hoverableOpenControlItem]}
198
+ variant='ghost'
199
+ icon={menuActions[0].properties?.icon ?? 'ph--placeholder--regular'}
200
+ iconOnly
201
+ label={toLocalizedString(menuActions[0].properties?.label, t)}
202
+ data-testid={menuActions[0].properties?.testId}
203
+ onClick={() => onAction(menuActions[0] as Node.Action)}
204
+ />
205
+ );
206
+ }
207
+
208
+ return (
209
+ <Menu.Root caller={NAV_TREE_ITEM} onAction={onAction}>
210
+ <Menu.Trigger asChild>
211
+ <IconButton
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
+ };
225
+
226
+ /**
227
+ * Builds the menu actions for the L1 panel header.
228
+ */
229
+ const useL1MenuActions = ({ item, path }: Pick<L1PanelProps, 'item' | 'path'>): L1MenuActions => {
230
+ const runAction = useActionRunner();
245
231
 
246
- // Graph actions.
247
232
  const { actions: actionsProp, groupedActions } = useActions(item);
248
233
  const [primaryAction, ...secondaryActions] = actionsProp.toSorted((a, _b) =>
249
234
  a.properties?.disposition === 'list-item-primary' ? -1 : 1,
250
235
  );
251
236
 
252
- const graphMenuActions = (
253
- primaryAction?.properties?.disposition === 'list-item-primary' ? secondaryActions : actionsProp
254
- )
237
+ const menuActions = (primaryAction?.properties?.disposition === 'list-item-primary' ? secondaryActions : actionsProp)
255
238
  .flatMap((action) => (Node.isAction(action) ? [action] : []))
256
239
  .filter((a) => ['list-item', 'list-item-primary'].includes(a.properties?.disposition));
257
240
 
258
- // Synthetic settings/back action.
259
- const settingsActionId = `${item.id}~settings`;
260
- const settingsAction = useMemo((): Node.Action | undefined => {
261
- if (!alternateTree) {
262
- return undefined;
263
- }
264
- return {
265
- id: settingsActionId,
266
- type: Node.ActionType,
267
- data: () => Effect.void,
268
- properties: {
269
- label: isAlternate
270
- ? ['button-back.button', { ns: meta.id }]
271
- : (alternateTree.properties.label ?? alternateTree.id),
272
- icon: isAlternate
273
- ? 'ph--arrow-u-down-left--regular'
274
- : (alternateTree.properties.icon ?? 'ph--placeholder--regular'),
275
- disposition: 'list-item',
276
- testId: isAlternate ? 'navtree.backToSpace' : 'navtree.spaceSettings',
277
- },
278
- } as Node.Action;
279
- }, [alternateTree, isAlternate, settingsActionId, t]);
280
-
281
- const menuActions = useMemo(
282
- () => [...graphMenuActions, ...(settingsAction ? [settingsAction] : [])],
283
- [graphMenuActions, settingsAction],
284
- );
285
-
286
241
  const onAction = useCallback(
287
242
  (action: Node.Action, params?: Node.InvokeProps) => {
288
- if (action.id === settingsActionId) {
289
- if (alternateTree && !isAlternate) {
290
- Graph.expand(graph, alternateTree.id, 'child');
291
- }
292
- setAlternateTree?.(alternatePath, !isAlternate);
293
- } else {
294
- void runAction(action, { ...params, path });
295
- }
243
+ void runAction(action, { ...params, path });
296
244
  },
297
- [settingsActionId, setAlternateTree, alternatePath, isAlternate, runAction, graph, alternateTree, path],
245
+ [runAction, path],
298
246
  );
299
247
 
300
248
  return { primaryAction, groupedActions, menuActions, onAction };
301
249
  };
302
250
 
303
- /** Finds the first child with disposition 'alternate-tree' using graph connections directly. */
304
- const useAlternateTreeItem = (item: Node.Node): Node.Node | undefined => {
305
- const { graph } = useAppGraph();
306
- const connections = useConnections(graph, item.id, 'child');
307
- return connections.find((node) => node.properties.disposition === 'alternate-tree');
308
- };
309
-
310
251
  export const L1Panel = memo(L1Panel$);
@@ -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>,
@@ -10,8 +10,9 @@ import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
10
10
  import { Dialog } from '@dxos/react-ui';
11
11
  import { withLayout } from '@dxos/react-ui/testing';
12
12
 
13
+ import { translations } from '#translations';
14
+
13
15
  import { NavTreePlugin } from '../../NavTreePlugin';
14
- import { translations } from '../../translations';
15
16
  import { CommandsDialogContent } from './CommandsDialogContent';
16
17
 
17
18
  const DefaultStory = () => (
@@ -230,13 +230,6 @@ export const NavTreeContainer$ = forwardRef<HTMLDivElement, NavTreeContainerProp
230
230
  });
231
231
  }, [graph]);
232
232
 
233
- const setAlternateTree = useCallback(
234
- (path: string[], open: boolean) => {
235
- setItem(path, 'alternateTree', open);
236
- },
237
- [setItem],
238
- );
239
-
240
233
  const onItemHover = useCallback(({ item }: { item: Node.Node }) => Graph.expand(graph, item.id, 'child'), [graph]);
241
234
 
242
235
  const navTreeContextValue = useMemo(
@@ -247,7 +240,6 @@ export const NavTreeContainer$ = forwardRef<HTMLDivElement, NavTreeContainerProp
247
240
  blockInstruction,
248
241
  canDrop,
249
242
  canSelect,
250
- setAlternateTree,
251
243
  onBack: handleBack,
252
244
  onOpenChange: handleOpenChange,
253
245
  onSelect: handleSelect,
@@ -260,7 +252,6 @@ export const NavTreeContainer$ = forwardRef<HTMLDivElement, NavTreeContainerProp
260
252
  blockInstruction,
261
253
  canDrop,
262
254
  canSelect,
263
- setAlternateTree,
264
255
  handleBack,
265
256
  handleOpenChange,
266
257
  handleSelect,
@@ -12,7 +12,7 @@ import { NavTreeCapabilities } from '#types';
12
12
  export type UseNavTreeStateResult = {
13
13
  getItem: (path: string[]) => NavTreeCapabilities.NavTreeItemState;
14
14
  getItemAtom: (path: string[]) => Atom.Atom<NavTreeCapabilities.NavTreeItemState>;
15
- setItem: (path: string[], key: 'open' | 'current' | 'alternateTree', next: boolean) => void;
15
+ setItem: (path: string[], key: 'open' | 'current', next: boolean) => void;
16
16
  };
17
17
 
18
18
  /**
@@ -33,10 +33,3 @@ export const useNavTreeItemState = (path: string[]): NavTreeCapabilities.NavTree
33
33
  const atom = useMemo(() => getItemAtom(path), [getItemAtom, pathKey]);
34
34
  return useAtomValue(atom);
35
35
  };
36
-
37
- /**
38
- * Hook that subscribes to the alternateTree state for a tree item.
39
- */
40
- export const useIsAlternateTree = (path: string[], _item?: any): boolean => {
41
- return useNavTreeItemState(path).alternateTree ?? false;
42
- };
@@ -7,8 +7,8 @@ import * as Option from 'effect/Option';
7
7
 
8
8
  import { Capability } from '@dxos/app-framework';
9
9
  import { AppCapabilities, LayoutOperation } from '@dxos/app-toolkit';
10
+ import { Operation } from '@dxos/compute';
10
11
  import { log } from '@dxos/log';
11
- import { Operation } from '@dxos/operation';
12
12
  import { Graph } from '@dxos/plugin-graph';
13
13
  import { expandAttendableId } from '@dxos/react-ui-attention';
14
14
 
@@ -2,6 +2,6 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { OperationHandlerSet } from '@dxos/operation';
5
+ import { OperationHandlerSet } from '@dxos/compute';
6
6
 
7
7
  export const NavTreeOperationHandlerSet = OperationHandlerSet.lazy(() => import('./expose'));
@@ -11,7 +11,6 @@ export const translations = [
11
11
  'en-US': {
12
12
  [meta.id]: {
13
13
  'app-menu.label': 'App menu',
14
- 'open-settings.label': 'Settings',
15
14
  'open-commands.label': 'Search commands',
16
15
  'commands-dialog.title': 'Commands',
17
16
  'command-list-input.placeholder': 'Search for commands…',
@@ -9,12 +9,12 @@ import { Capability } from '@dxos/app-framework';
9
9
  import { meta } from '#meta';
10
10
 
11
11
  export namespace NavTreeCapabilities {
12
- export type NavTreeItemState = { open: boolean; current: boolean; alternateTree?: boolean };
12
+ export type NavTreeItemState = { open: boolean; current: boolean };
13
13
 
14
14
  export type NavTreeStateStore = {
15
15
  getItem: (path: string[]) => NavTreeItemState;
16
16
  getItemAtom: (path: string[]) => Atom.Atom<NavTreeItemState>;
17
- setItem: (path: string[], key: 'open' | 'current' | 'alternateTree', next: boolean) => void;
17
+ setItem: (path: string[], key: 'open' | 'current', next: boolean) => void;
18
18
  };
19
19
 
20
20
  export const State = Capability.make<NavTreeStateStore>(`${meta.id}.capability.state`);
package/src/util.ts CHANGED
@@ -93,7 +93,7 @@ export const getChildren = (
93
93
  * Determines whether a node should be visible based on its disposition.
94
94
  */
95
95
  export const filterItems = (node: Node.Node, disposition?: string) => {
96
- if (!disposition && (node.properties.disposition === 'hidden' || node.properties.disposition === 'alternate-tree')) {
96
+ if (!disposition && node.properties.disposition === 'hidden') {
97
97
  return false;
98
98
  } else if (!disposition) {
99
99
  const action = Node.isAction(node);