@dxos/plugin-navtree 0.8.4-main.c4373fc → 0.8.4-main.c85a9c8dae

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 (226) hide show
  1. package/dist/lib/browser/CommandsDialogContent-A27AKTHH.mjs +111 -0
  2. package/dist/lib/browser/CommandsDialogContent-A27AKTHH.mjs.map +7 -0
  3. package/dist/lib/browser/CommandsTrigger-HBZYQCGV.mjs +33 -0
  4. package/dist/lib/browser/CommandsTrigger-HBZYQCGV.mjs.map +7 -0
  5. package/dist/lib/browser/NavTreeContainer-B5K4FUJS.mjs +12 -0
  6. package/dist/lib/browser/NavTreeContainer-B5K4FUJS.mjs.map +7 -0
  7. package/dist/lib/browser/NavTreeDocumentTitle-GEE3URWH.mjs +26 -0
  8. package/dist/lib/browser/NavTreeDocumentTitle-GEE3URWH.mjs.map +7 -0
  9. package/dist/lib/browser/app-graph-builder-XGA4GPGL.mjs +44 -0
  10. package/dist/lib/browser/app-graph-builder-XGA4GPGL.mjs.map +7 -0
  11. package/dist/lib/browser/chunk-DQCYMXEU.mjs +1426 -0
  12. package/dist/lib/browser/chunk-DQCYMXEU.mjs.map +7 -0
  13. package/dist/lib/browser/chunk-H3OJ4WLF.mjs +14 -0
  14. package/dist/lib/browser/chunk-H3OJ4WLF.mjs.map +7 -0
  15. package/dist/lib/browser/chunk-X2HDG62A.mjs +23 -0
  16. package/dist/lib/browser/chunk-X2HDG62A.mjs.map +7 -0
  17. package/dist/lib/browser/chunk-YKTYPINZ.mjs +19 -0
  18. package/dist/lib/browser/chunk-YKTYPINZ.mjs.map +7 -0
  19. package/dist/lib/browser/index.mjs +86 -91
  20. package/dist/lib/browser/index.mjs.map +4 -4
  21. package/dist/lib/browser/{keyboard-AHSAWKXA.mjs → keyboard-OJZC54PK.mjs} +22 -19
  22. package/dist/lib/browser/keyboard-OJZC54PK.mjs.map +7 -0
  23. package/dist/lib/browser/meta.json +1 -1
  24. package/dist/lib/browser/operation-resolver-BNAMYOHF.mjs +53 -0
  25. package/dist/lib/browser/operation-resolver-BNAMYOHF.mjs.map +7 -0
  26. package/dist/lib/browser/react-surface-AYHOAJ4I.mjs +59 -0
  27. package/dist/lib/browser/react-surface-AYHOAJ4I.mjs.map +7 -0
  28. package/dist/lib/browser/state-AC3EETN7.mjs +131 -0
  29. package/dist/lib/browser/state-AC3EETN7.mjs.map +7 -0
  30. package/dist/types/src/NavTreePlugin.d.ts +2 -1
  31. package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
  32. package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts +6 -0
  33. package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts.map +1 -0
  34. package/dist/types/src/capabilities/app-graph-builder/index.d.ts +3 -0
  35. package/dist/types/src/capabilities/app-graph-builder/index.d.ts.map +1 -0
  36. package/dist/types/src/capabilities/index.d.ts +5 -108
  37. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  38. package/dist/types/src/capabilities/keyboard/index.d.ts +3 -0
  39. package/dist/types/src/capabilities/keyboard/index.d.ts.map +1 -0
  40. package/dist/types/src/capabilities/keyboard/keyboard.d.ts +5 -0
  41. package/dist/types/src/capabilities/keyboard/keyboard.d.ts.map +1 -0
  42. package/dist/types/src/capabilities/operation-resolver/index.d.ts +3 -0
  43. package/dist/types/src/capabilities/operation-resolver/index.d.ts.map +1 -0
  44. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +5 -0
  45. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +1 -0
  46. package/dist/types/src/capabilities/react-surface/index.d.ts +3 -0
  47. package/dist/types/src/capabilities/react-surface/index.d.ts.map +1 -0
  48. package/dist/types/src/capabilities/react-surface/react-surface.d.ts +5 -0
  49. package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +1 -0
  50. package/dist/types/src/capabilities/state/index.d.ts +3 -0
  51. package/dist/types/src/capabilities/state/index.d.ts.map +1 -0
  52. package/dist/types/src/capabilities/state/state.d.ts +6 -0
  53. package/dist/types/src/capabilities/state/state.d.ts.map +1 -0
  54. package/dist/types/src/components/NavTree/NavTree.d.ts +11 -4
  55. package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
  56. package/dist/types/src/components/NavTree/NavTree.stories.d.ts +2 -2
  57. package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -1
  58. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts +6 -6
  59. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -1
  60. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +2 -1
  61. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts.map +1 -1
  62. package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts.map +1 -1
  63. package/dist/types/src/components/Sidebar/L0Menu.d.ts +8 -5
  64. package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
  65. package/dist/types/src/components/Sidebar/L1Panel.d.ts +4 -7
  66. package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
  67. package/dist/types/src/components/Sidebar/L1Tabs.d.ts +5 -4
  68. package/dist/types/src/components/Sidebar/L1Tabs.d.ts.map +1 -1
  69. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts.map +1 -1
  70. package/dist/types/src/components/index.d.ts +0 -3
  71. package/dist/types/src/components/index.d.ts.map +1 -1
  72. package/dist/types/src/components/types.d.ts +8 -16
  73. package/dist/types/src/components/types.d.ts.map +1 -1
  74. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.d.ts +6 -0
  75. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.d.ts.map +1 -0
  76. package/dist/types/src/containers/CommandsDialogContent/index.d.ts +3 -0
  77. package/dist/types/src/containers/CommandsDialogContent/index.d.ts.map +1 -0
  78. package/dist/types/src/containers/CommandsTrigger/CommandsTrigger.d.ts.map +1 -0
  79. package/dist/types/src/containers/CommandsTrigger/index.d.ts +3 -0
  80. package/dist/types/src/containers/CommandsTrigger/index.d.ts.map +1 -0
  81. package/dist/types/src/containers/NavTreeContainer/NavTreeContainer.d.ts +9 -0
  82. package/dist/types/src/containers/NavTreeContainer/NavTreeContainer.d.ts.map +1 -0
  83. package/dist/types/src/containers/NavTreeContainer/index.d.ts +3 -0
  84. package/dist/types/src/containers/NavTreeContainer/index.d.ts.map +1 -0
  85. package/dist/types/src/{components → containers/NavTreeDocumentTitle}/NavTreeDocumentTitle.d.ts +1 -1
  86. package/dist/types/src/containers/NavTreeDocumentTitle/NavTreeDocumentTitle.d.ts.map +1 -0
  87. package/dist/types/src/containers/NavTreeDocumentTitle/index.d.ts +3 -0
  88. package/dist/types/src/containers/NavTreeDocumentTitle/index.d.ts.map +1 -0
  89. package/dist/types/src/containers/index.d.ts +7 -0
  90. package/dist/types/src/containers/index.d.ts.map +1 -0
  91. package/dist/types/src/experimental/Tree.stories.d.ts +15 -4
  92. package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
  93. package/dist/types/src/hooks/index.d.ts +5 -0
  94. package/dist/types/src/hooks/index.d.ts.map +1 -0
  95. package/dist/types/src/hooks/useActions.d.ts +7 -0
  96. package/dist/types/src/hooks/useActions.d.ts.map +1 -0
  97. package/dist/types/src/hooks/useLoadDescendents.d.ts +6 -0
  98. package/dist/types/src/hooks/useLoadDescendents.d.ts.map +1 -0
  99. package/dist/types/src/hooks/useNavTreeModel.d.ts +7 -0
  100. package/dist/types/src/hooks/useNavTreeModel.d.ts.map +1 -0
  101. package/dist/types/src/hooks/useNavTreeState.d.ts +22 -0
  102. package/dist/types/src/hooks/useNavTreeState.d.ts.map +1 -0
  103. package/dist/types/src/index.d.ts +1 -2
  104. package/dist/types/src/index.d.ts.map +1 -1
  105. package/dist/types/src/meta.d.ts +2 -2
  106. package/dist/types/src/meta.d.ts.map +1 -1
  107. package/dist/types/src/testing/app-graph-builder.d.ts +2 -3
  108. package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -1
  109. package/dist/types/src/testing/generator.d.ts +5 -6
  110. package/dist/types/src/testing/generator.d.ts.map +1 -1
  111. package/dist/types/src/testing/update-state.d.ts +2 -2
  112. package/dist/types/src/testing/update-state.d.ts.map +1 -1
  113. package/dist/types/src/types/capabilities.d.ts +16 -0
  114. package/dist/types/src/types/capabilities.d.ts.map +1 -0
  115. package/dist/types/src/types/events.d.ts +5 -0
  116. package/dist/types/src/types/events.d.ts.map +1 -0
  117. package/dist/types/src/types/index.d.ts +4 -0
  118. package/dist/types/src/types/index.d.ts.map +1 -0
  119. package/dist/types/src/{types.d.ts → types/schema.d.ts} +7 -7
  120. package/dist/types/src/types/schema.d.ts.map +1 -0
  121. package/dist/types/src/util.d.ts +11 -7
  122. package/dist/types/src/util.d.ts.map +1 -1
  123. package/dist/types/tsconfig.tsbuildinfo +1 -1
  124. package/package.json +55 -53
  125. package/src/NavTreePlugin.tsx +54 -73
  126. package/src/capabilities/app-graph-builder/app-graph-builder.ts +42 -0
  127. package/src/capabilities/app-graph-builder/index.ts +7 -0
  128. package/src/capabilities/index.ts +5 -9
  129. package/src/capabilities/keyboard/index.ts +7 -0
  130. package/src/capabilities/keyboard/keyboard.ts +66 -0
  131. package/src/capabilities/operation-resolver/index.ts +7 -0
  132. package/src/capabilities/operation-resolver/operation-resolver.ts +44 -0
  133. package/src/capabilities/react-surface/index.ts +7 -0
  134. package/src/capabilities/react-surface/react-surface.tsx +53 -0
  135. package/src/capabilities/state/index.ts +7 -0
  136. package/src/capabilities/state/state.ts +149 -0
  137. package/src/components/NavTree/NavTree.stories.tsx +44 -51
  138. package/src/components/NavTree/NavTree.tsx +63 -32
  139. package/src/components/NavTreeItem/NavTreeItemAction.stories.tsx +18 -8
  140. package/src/components/NavTreeItem/NavTreeItemAction.tsx +33 -28
  141. package/src/components/NavTreeItem/NavTreeItemColumns.tsx +39 -15
  142. package/src/components/Sidebar/L0Menu.tsx +145 -155
  143. package/src/components/Sidebar/L1Panel.tsx +231 -94
  144. package/src/components/Sidebar/L1Tabs.tsx +18 -33
  145. package/src/components/UserAccountAvatar/UserAccountAvatar.stories.tsx +2 -3
  146. package/src/components/UserAccountAvatar/UserAccountAvatar.tsx +1 -1
  147. package/src/components/index.ts +1 -3
  148. package/src/components/types.ts +14 -19
  149. package/src/containers/CommandsDialogContent/CommandsDialogContent.tsx +133 -0
  150. package/src/containers/CommandsDialogContent/index.ts +7 -0
  151. package/src/{components/CommandsDialog → containers/CommandsTrigger}/CommandsTrigger.tsx +5 -14
  152. package/src/containers/CommandsTrigger/index.ts +7 -0
  153. package/src/containers/NavTreeContainer/NavTreeContainer.tsx +270 -0
  154. package/src/containers/NavTreeContainer/index.ts +7 -0
  155. package/src/{components → containers/NavTreeDocumentTitle}/NavTreeDocumentTitle.tsx +4 -3
  156. package/src/containers/NavTreeDocumentTitle/index.ts +7 -0
  157. package/src/containers/index.ts +12 -0
  158. package/src/experimental/Tree.stories.tsx +54 -60
  159. package/src/experimental/Tree.tsx +3 -3
  160. package/src/hooks/index.ts +8 -0
  161. package/src/hooks/useActions.ts +38 -0
  162. package/src/hooks/useLoadDescendents.ts +23 -0
  163. package/src/hooks/useNavTreeModel.ts +112 -0
  164. package/src/hooks/useNavTreeState.ts +42 -0
  165. package/src/index.ts +1 -2
  166. package/src/meta.ts +7 -2
  167. package/src/testing/app-graph-builder.ts +159 -235
  168. package/src/testing/generator.ts +19 -17
  169. package/src/testing/update-state.ts +6 -6
  170. package/src/types/capabilities.ts +21 -0
  171. package/src/types/events.ts +12 -0
  172. package/src/types/index.ts +7 -0
  173. package/src/{types.ts → types/schema.ts} +6 -6
  174. package/src/util.ts +27 -16
  175. package/dist/lib/browser/app-graph-builder-YAPMENYT.mjs +0 -46
  176. package/dist/lib/browser/app-graph-builder-YAPMENYT.mjs.map +0 -7
  177. package/dist/lib/browser/chunk-IMFXKPSB.mjs +0 -1482
  178. package/dist/lib/browser/chunk-IMFXKPSB.mjs.map +0 -7
  179. package/dist/lib/browser/chunk-LPAREX6J.mjs +0 -15
  180. package/dist/lib/browser/chunk-LPAREX6J.mjs.map +0 -7
  181. package/dist/lib/browser/chunk-ZENLR72O.mjs +0 -14
  182. package/dist/lib/browser/chunk-ZENLR72O.mjs.map +0 -7
  183. package/dist/lib/browser/intent-resolver-TCQXGF6U.mjs +0 -47
  184. package/dist/lib/browser/intent-resolver-TCQXGF6U.mjs.map +0 -7
  185. package/dist/lib/browser/keyboard-AHSAWKXA.mjs.map +0 -7
  186. package/dist/lib/browser/react-surface-AH2TP73M.mjs +0 -51
  187. package/dist/lib/browser/react-surface-AH2TP73M.mjs.map +0 -7
  188. package/dist/lib/browser/state-2Q623V7U.mjs +0 -117
  189. package/dist/lib/browser/state-2Q623V7U.mjs.map +0 -7
  190. package/dist/types/src/capabilities/app-graph-builder.d.ts +0 -4
  191. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +0 -1
  192. package/dist/types/src/capabilities/capabilities.d.ts +0 -21
  193. package/dist/types/src/capabilities/capabilities.d.ts.map +0 -1
  194. package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
  195. package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
  196. package/dist/types/src/capabilities/keyboard.d.ts +0 -4
  197. package/dist/types/src/capabilities/keyboard.d.ts.map +0 -1
  198. package/dist/types/src/capabilities/react-surface.d.ts +0 -4
  199. package/dist/types/src/capabilities/react-surface.d.ts.map +0 -1
  200. package/dist/types/src/capabilities/state.d.ts +0 -20
  201. package/dist/types/src/capabilities/state.d.ts.map +0 -1
  202. package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts +0 -5
  203. package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts.map +0 -1
  204. package/dist/types/src/components/CommandsDialog/CommandsTrigger.d.ts.map +0 -1
  205. package/dist/types/src/components/CommandsDialog/index.d.ts +0 -3
  206. package/dist/types/src/components/CommandsDialog/index.d.ts.map +0 -1
  207. package/dist/types/src/components/NavTreeContainer.d.ts +0 -9
  208. package/dist/types/src/components/NavTreeContainer.d.ts.map +0 -1
  209. package/dist/types/src/components/NavTreeDocumentTitle.d.ts.map +0 -1
  210. package/dist/types/src/events.d.ts +0 -4
  211. package/dist/types/src/events.d.ts.map +0 -1
  212. package/dist/types/src/hooks.d.ts +0 -3
  213. package/dist/types/src/hooks.d.ts.map +0 -1
  214. package/dist/types/src/types.d.ts.map +0 -1
  215. package/src/capabilities/app-graph-builder.ts +0 -53
  216. package/src/capabilities/capabilities.ts +0 -20
  217. package/src/capabilities/intent-resolver.ts +0 -37
  218. package/src/capabilities/keyboard.ts +0 -59
  219. package/src/capabilities/react-surface.tsx +0 -45
  220. package/src/capabilities/state.ts +0 -105
  221. package/src/components/CommandsDialog/CommandsDialog.tsx +0 -118
  222. package/src/components/CommandsDialog/index.ts +0 -6
  223. package/src/components/NavTreeContainer.tsx +0 -346
  224. package/src/events.ts +0 -11
  225. package/src/hooks.ts +0 -21
  226. /package/dist/types/src/{components/CommandsDialog → containers/CommandsTrigger}/CommandsTrigger.d.ts +0 -0
@@ -0,0 +1,112 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Atom } from '@effect-atom/atom-react';
6
+ import * as Option from 'effect/Option';
7
+ import { useMemo } from 'react';
8
+
9
+ import { useAppGraph } from '@dxos/app-toolkit/ui';
10
+ import { PLANK_COMPANION_TYPE } from '@dxos/plugin-deck/types';
11
+ import { type Node } from '@dxos/plugin-graph';
12
+ import { Path, type TreeModel } from '@dxos/react-ui-list';
13
+ import { mx } from '@dxos/ui-theme';
14
+
15
+ import { type NavTreeItemGraphNode } from '../types';
16
+ import { filterItems } from '../util';
17
+
18
+ import { useNavTreeState } from './useNavTreeState';
19
+
20
+ // TODO(wittjosiah): Move companion/hidden nodes to their own edge categories so this filter is unnecessary.
21
+ const isVisibleChild = (node: Node.Node): boolean =>
22
+ node.type !== PLANK_COMPANION_TYPE && node.properties.disposition !== 'hidden';
23
+
24
+ /** Create an atom family for item display props keyed by path. */
25
+ const createItemPropsFamily = (graph: ReturnType<typeof useAppGraph>['graph']) =>
26
+ Atom.family((pathKey: string) => {
27
+ const path = Path.parts(pathKey);
28
+ const id = Path.last(pathKey);
29
+ return Atom.make((get) => {
30
+ const nodeOpt = get(graph.node(id));
31
+ const node = Option.getOrElse(nodeOpt, () => undefined);
32
+ if (!node) {
33
+ return { id, label: id };
34
+ }
35
+ const safeChildren = get(graph.connections(node.id, 'child')).filter((child) => !path.includes(child.id));
36
+ const visibleChildren = safeChildren.filter(isVisibleChild);
37
+ const parentOf =
38
+ visibleChildren.length > 0
39
+ ? visibleChildren.map((child) => child.id)
40
+ : node.properties.role === 'branch'
41
+ ? []
42
+ : undefined;
43
+ return {
44
+ id: node.id,
45
+ parentOf,
46
+ disabled: node.properties.disabled,
47
+ label: node.properties.label ?? node.id,
48
+ className: mx(node.properties.className, node.properties.modified && 'italic'),
49
+ headingClassName: node.properties.headingClassName,
50
+ icon: node.properties.icon,
51
+ iconHue: node.properties.iconHue,
52
+ testId: node.properties.testId,
53
+ };
54
+ }).pipe(Atom.keepAlive);
55
+ });
56
+
57
+ /** Create an atom family for outbound child IDs keyed by parent ID (pre-sorted by position on write). */
58
+ const createChildIdsFamily = (graph: ReturnType<typeof useAppGraph>['graph']) =>
59
+ Atom.family((id: string) =>
60
+ Atom.make((get) => get(graph.connections(id, 'child')).map((child) => child.id)).pipe(Atom.keepAlive),
61
+ );
62
+
63
+ /** Create an atom family for item resolution keyed by ID. */
64
+ const createItemFamily = (graph: ReturnType<typeof useAppGraph>['graph']) =>
65
+ Atom.family((id: string) =>
66
+ Atom.make((get) => {
67
+ const node = Option.getOrElse(get(graph.node(id)), () => undefined);
68
+ const passed = node ? filterItems(node) : false;
69
+ return node && passed ? node : undefined;
70
+ }).pipe(Atom.keepAlive),
71
+ );
72
+
73
+ /** Create an atom family for open state keyed by path. */
74
+ const createItemOpenFamily = (getItemAtom: ReturnType<typeof useNavTreeState>['getItemAtom']) =>
75
+ Atom.family((pathKey: string) => {
76
+ const path = Path.parts(pathKey);
77
+ const stateAtom = getItemAtom(path);
78
+ return Atom.make((get) => get(stateAtom).open).pipe(Atom.keepAlive);
79
+ });
80
+
81
+ /** Create an atom family for current (selected) state keyed by path. */
82
+ const createItemCurrentFamily = (getItemAtom: ReturnType<typeof useNavTreeState>['getItemAtom']) =>
83
+ Atom.family((pathKey: string) => {
84
+ const path = Path.parts(pathKey);
85
+ const stateAtom = getItemAtom(path);
86
+ return Atom.make((get) => get(stateAtom).current).pipe(Atom.keepAlive);
87
+ });
88
+
89
+ /**
90
+ * Creates a TreeModel backed by the app graph and navtree state.
91
+ */
92
+ export const useNavTreeModel = (rootId: string): TreeModel<NavTreeItemGraphNode> => {
93
+ const { graph } = useAppGraph();
94
+ const { getItemAtom } = useNavTreeState();
95
+
96
+ const itemPropsFamily = useMemo(() => createItemPropsFamily(graph), [graph]);
97
+ const childIdsFamily = useMemo(() => createChildIdsFamily(graph), [graph]);
98
+ const itemFamily = useMemo(() => createItemFamily(graph), [graph]);
99
+ const itemOpenFamily = useMemo(() => createItemOpenFamily(getItemAtom), [getItemAtom]);
100
+ const itemCurrentFamily = useMemo(() => createItemCurrentFamily(getItemAtom), [getItemAtom]);
101
+
102
+ return useMemo(
103
+ () => ({
104
+ item: (id: string) => itemFamily(id),
105
+ itemProps: (path: string[]) => itemPropsFamily(Path.create(...path)),
106
+ itemOpen: (path: string[]) => itemOpenFamily(Path.create(...path)),
107
+ itemCurrent: (path: string[]) => itemCurrentFamily(Path.create(...path)),
108
+ childIds: (parentId?: string) => childIdsFamily(parentId ?? rootId),
109
+ }),
110
+ [itemFamily, itemPropsFamily, itemOpenFamily, itemCurrentFamily, childIdsFamily, rootId],
111
+ );
112
+ };
@@ -0,0 +1,42 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type Atom, useAtomValue } from '@effect-atom/atom-react';
6
+ import { useMemo } from 'react';
7
+
8
+ import { useCapability } from '@dxos/app-framework/ui';
9
+
10
+ import { NavTreeCapabilities } from '../types';
11
+
12
+ export type UseNavTreeStateResult = {
13
+ getItem: (path: string[]) => NavTreeCapabilities.NavTreeItemState;
14
+ getItemAtom: (path: string[]) => Atom.Atom<NavTreeCapabilities.NavTreeItemState>;
15
+ setItem: (path: string[], key: 'open' | 'current' | 'alternateTree', next: boolean) => void;
16
+ };
17
+
18
+ /**
19
+ * Hook that provides access to navtree state functions.
20
+ * For reactive subscriptions, use `useNavTreeItemState` or `useAtomValue(getItemAtom(path))`.
21
+ */
22
+ export const useNavTreeState = (): UseNavTreeStateResult => {
23
+ return useCapability(NavTreeCapabilities.State);
24
+ };
25
+
26
+ /**
27
+ * Hook that subscribes to a single navtree item's state.
28
+ * Use this for fine-grained reactivity.
29
+ */
30
+ export const useNavTreeItemState = (path: string[]): NavTreeCapabilities.NavTreeItemState => {
31
+ const { getItemAtom } = useCapability(NavTreeCapabilities.State);
32
+ const pathKey = useMemo(() => path.join('~'), [path]);
33
+ const atom = useMemo(() => getItemAtom(path), [getItemAtom, pathKey]);
34
+ return useAtomValue(atom);
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
+ };
package/src/index.ts CHANGED
@@ -2,8 +2,7 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- export { NavTreeCapabilities } from './capabilities';
6
- export { NavTreeEvents } from './events';
5
+ export { NavTreeCapabilities, NavTreeEvents } from './types';
7
6
  export * from './NavTreePlugin';
8
7
  export * from './meta';
9
8
  export * from './util';
package/src/meta.ts CHANGED
@@ -2,11 +2,16 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { type PluginMeta } from '@dxos/app-framework';
5
+ import { type Plugin } from '@dxos/app-framework';
6
+ import { trim } from '@dxos/util';
6
7
 
7
- export const meta: PluginMeta = {
8
+ export const meta: Plugin.Meta = {
8
9
  id: 'dxos.org/plugin/navtree',
9
10
  name: 'Navtree',
11
+ description: trim`
12
+ Hierarchical navigation tree for browsing spaces, folders, and objects.
13
+ Provides sidebar navigation with collapsible sections and quick access to workspace content.
14
+ `,
10
15
  };
11
16
 
12
17
  // TODO(wittjosiah): Factor out.
@@ -2,16 +2,15 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Rx } from '@effect-rx/rx-react';
6
- import * as Function from 'effect/Function';
7
- import * as Option from 'effect/Option';
5
+ import { Atom } from '@effect-atom/atom-react';
6
+ import * as Effect from 'effect/Effect';
8
7
 
9
- import { type PluginContext } from '@dxos/app-framework';
8
+ import { type BuilderExtensions } from '@dxos/app-graph';
10
9
  import { log } from '@dxos/log';
11
- import { type BuilderExtensions, ROOT_ID, createExtension } from '@dxos/plugin-graph';
10
+ import { GraphBuilder, NodeMatcher } from '@dxos/plugin-graph';
12
11
  import { faker } from '@dxos/random';
13
12
 
14
- export const storybookGraphBuilders = (context: PluginContext): BuilderExtensions => {
13
+ export const storybookGraphBuilders = (): BuilderExtensions => {
15
14
  const propertiesCache = new Map<string, Record<string, unknown>>();
16
15
  const getProperties = (id: string, defaults: Record<string, unknown>) => {
17
16
  const cached = propertiesCache.get(id);
@@ -23,249 +22,174 @@ export const storybookGraphBuilders = (context: PluginContext): BuilderExtension
23
22
  return defaults;
24
23
  };
25
24
 
26
- return [
27
- // Create app menu actions.
28
- createExtension({
29
- id: 'app-menu',
30
- actions: (node) =>
31
- Rx.make((get) =>
32
- Function.pipe(
33
- get(node),
34
- Option.flatMap((node) => (node.id === ROOT_ID ? Option.some(node) : Option.none())),
35
- Option.map((node) => {
36
- return [
37
- ...Array.from({ length: 5 }, (_, i) => ({
38
- id: `${node.id}/action-${i}`,
39
- data: () => {
40
- log.info('action', { id: node.id, index: i });
41
- },
42
- properties: {
43
- label: `Action ${i}`,
44
- icon: faker.properties.icon(),
45
- disposition: 'menu',
46
- },
47
- })),
48
- ];
49
- }),
50
- Option.getOrElse(() => []),
25
+ return Effect.runSync(
26
+ Effect.all([
27
+ // Create app menu actions.
28
+ GraphBuilder.createExtension({
29
+ id: 'app-menu',
30
+ match: NodeMatcher.whenRoot,
31
+ actions: () =>
32
+ Effect.succeed(
33
+ Array.from({ length: 5 }, (_, i) => ({
34
+ id: `app-menu/action-${i}`,
35
+ data: Effect.fnUntraced(function* () {
36
+ log.info('action', { id: 'app-menu', index: i });
37
+ }),
38
+ properties: {
39
+ label: `Action ${i}`,
40
+ icon: faker.properties.icon(),
41
+ disposition: 'menu',
42
+ },
43
+ })),
51
44
  ),
52
- ),
53
- }),
54
- // Create user account node.
55
- createExtension({
56
- id: 'user-account',
57
- connector: (node) =>
58
- Rx.make((get) =>
59
- Function.pipe(
60
- get(node),
61
- Option.flatMap((node) => (node.id === ROOT_ID ? Option.some(node) : Option.none())),
62
- Option.map(() => {
63
- return [
45
+ }),
46
+ // Create user account node.
47
+ GraphBuilder.createExtension({
48
+ id: 'user-account',
49
+ match: NodeMatcher.whenRoot,
50
+ connector: () =>
51
+ Effect.succeed([
52
+ {
53
+ id: 'user-account',
54
+ type: 'user-account',
55
+ properties: {
56
+ label: 'User profile',
57
+ icon: 'ph--user--regular',
58
+ disposition: 'user-account',
59
+ userId: '1234567890ABCDEF',
60
+ hue: faker.properties.hue(),
61
+ emoji: faker.properties.emoji(),
62
+ status: 'active',
63
+ },
64
+ nodes: [
64
65
  {
65
- id: 'user-account',
66
- type: 'user-account',
66
+ id: 'profile',
67
+ type: 'profile',
67
68
  properties: {
68
- label: 'User Account',
69
+ label: 'Profile',
69
70
  icon: 'ph--user--regular',
70
- disposition: 'user-account',
71
- userId: '1234567890ABCDEF',
72
- hue: faker.properties.hue(),
73
- emoji: faker.properties.emoji(),
74
- status: 'active',
75
71
  },
76
- nodes: [
77
- {
78
- id: 'profile',
79
- type: 'profile',
80
- properties: {
81
- label: 'Profile',
82
- icon: 'ph--user--regular',
83
- },
84
- },
85
- {
86
- id: 'devices',
87
- type: 'devices',
88
- properties: {
89
- label: 'Devices',
90
- icon: 'ph--devices--regular',
91
- },
92
- },
93
- {
94
- id: 'security',
95
- type: 'security',
96
- properties: {
97
- label: 'Security',
98
- icon: 'ph--key--regular',
99
- },
100
- },
101
- ],
102
72
  },
103
- ];
104
- }),
105
- Option.getOrElse(() => []),
106
- ),
107
- ),
108
- }),
109
- // TODO(wittjosiah): This group node probably is unnecessary now with the flat L0 structure.
110
- // Create spaces group node.
111
- createExtension({
112
- id: 'spaces-root',
113
- position: 'hoist',
114
- connector: (node) =>
115
- Rx.make((get) =>
116
- Function.pipe(
117
- get(node),
118
- Option.flatMap((node) => (node.id === ROOT_ID ? Option.some(node) : Option.none())),
119
- Option.map(() => {
120
- return [
121
73
  {
122
- id: 'spaces-root',
123
- type: 'spaces-root',
74
+ id: 'devices',
75
+ type: 'devices',
124
76
  properties: {
125
- label: 'Spaces',
126
- icon: 'ph--planet--regular',
127
- role: 'branch',
128
- disposition: 'collection',
77
+ label: 'Devices',
78
+ icon: 'ph--devices--regular',
129
79
  },
130
80
  },
131
- ];
132
- }),
133
- Option.getOrElse(() => []),
134
- ),
135
- ),
136
- }),
137
- // Create space nodes.
138
- createExtension({
139
- id: 'spaces',
140
- connector: (node) => {
141
- const count = Rx.make((get) => {
142
- let value = 3;
143
- const interval = setInterval(() => {
144
- if (value >= 10) {
145
- clearInterval(interval);
146
- return;
147
- }
81
+ {
82
+ id: 'security',
83
+ type: 'security',
84
+ properties: {
85
+ label: 'Security',
86
+ icon: 'ph--key--regular',
87
+ },
88
+ },
89
+ ],
90
+ },
91
+ ]),
92
+ }),
93
+ // Create space (workspace) nodes directly under root.
94
+ GraphBuilder.createExtension({
95
+ id: 'spaces',
96
+ match: NodeMatcher.whenRoot,
97
+ connector: (_, get) =>
98
+ Effect.sync(() => {
99
+ const count = Atom.make((get) => {
100
+ let value = 3;
101
+ const interval = setInterval(() => {
102
+ if (value >= 10) {
103
+ clearInterval(interval);
104
+ return;
105
+ }
148
106
 
149
- value++;
150
- get.setSelf(value);
151
- }, 5000);
152
- get.addFinalizer(() => clearInterval(interval));
153
- return value;
154
- });
107
+ value++;
108
+ get.setSelf(value);
109
+ }, 5000);
110
+ get.addFinalizer(() => clearInterval(interval));
111
+ return value;
112
+ });
155
113
 
156
- return Rx.make((get) =>
157
- Function.pipe(
158
- get(node),
159
- Option.flatMap((node) => (node.id === 'spaces-root' ? Option.some(node) : Option.none())),
160
- Option.map(() => {
161
- return [
162
- ...Array.from({ length: get(count) }, (_, i) => ({
163
- id: `space-${i}`,
164
- type: 'space',
165
- properties: getProperties(`space-${i}`, {
166
- label: `Space ${i}`,
167
- icon: faker.properties.icon(),
168
- hue: faker.properties.hue(),
169
- }),
170
- })),
171
- ];
172
- }),
173
- Option.getOrElse(() => []),
174
- ),
175
- );
176
- },
177
- }),
178
- // Create space actions.
179
- createExtension({
180
- id: 'space-actions',
181
- actions: (node) =>
182
- Rx.make((get) =>
183
- Function.pipe(
184
- get(node),
185
- Option.flatMap((node) => (node.type === 'space' ? Option.some(node) : Option.none())),
186
- Option.map((node) => {
187
- return [
188
- ...Array.from({ length: 5 }, (_, i) => ({
189
- id: `${node.id}/action-${i}`,
190
- data: () => {
191
- log.info('action', { id: node.id, index: i });
192
- },
193
- properties: getProperties(`${node.id}/action-${i}`, {
194
- label: `Action ${i}`,
195
- icon: faker.properties.icon(),
196
- }),
197
- })),
198
- ];
199
- }),
200
- Option.getOrElse(() => []),
114
+ return Array.from({ length: get(count) }, (_, i) => ({
115
+ id: `space-${i}`,
116
+ type: 'space',
117
+ properties: getProperties(`space-${i}`, {
118
+ label: `Space ${i}`,
119
+ icon: faker.properties.icon(),
120
+ hue: faker.properties.hue(),
121
+ disposition: 'workspace',
122
+ }),
123
+ }));
124
+ }),
125
+ }),
126
+ // Create space actions.
127
+ GraphBuilder.createExtension({
128
+ id: 'space-actions',
129
+ match: NodeMatcher.whenNodeType('space'),
130
+ actions: () =>
131
+ Effect.succeed(
132
+ Array.from({ length: 5 }, (_, i) => ({
133
+ id: `space-actions/action-${i}`,
134
+ data: Effect.fnUntraced(function* () {
135
+ log.info('action', { id: 'space-actions', index: i });
136
+ }),
137
+ properties: getProperties(`space-actions/action-${i}`, {
138
+ label: `Action ${i}`,
139
+ icon: faker.properties.icon(),
140
+ }),
141
+ })),
201
142
  ),
202
- ),
203
- }),
204
- // Create object nodes.
205
- createExtension({
206
- id: 'objects',
207
- connector: (node) => {
208
- const count = Rx.make((get) => {
209
- let value = 3;
210
- const interval = setInterval(() => {
211
- if (value >= 20) {
212
- clearInterval(interval);
213
- return;
214
- }
143
+ }),
144
+ // Create object nodes.
145
+ GraphBuilder.createExtension({
146
+ id: 'objects',
147
+ match: NodeMatcher.whenNodeType('space'),
148
+ connector: (_, get) =>
149
+ Effect.sync(() => {
150
+ const count = Atom.make((get) => {
151
+ let value = 3;
152
+ const interval = setInterval(() => {
153
+ if (value >= 20) {
154
+ clearInterval(interval);
155
+ return;
156
+ }
215
157
 
216
- value++;
217
- get.setSelf(value);
218
- }, 5000);
219
- get.addFinalizer(() => clearInterval(interval));
220
- return value;
221
- });
158
+ value++;
159
+ get.setSelf(value);
160
+ }, 5000);
161
+ get.addFinalizer(() => clearInterval(interval));
162
+ return value;
163
+ });
222
164
 
223
- return Rx.make((get) =>
224
- Function.pipe(
225
- get(node),
226
- Option.flatMap((node) => (node.type === 'space' ? Option.some(node) : Option.none())),
227
- Option.map((node) => {
228
- return [
229
- ...Array.from({ length: get(count) }, (_, i) => ({
230
- id: `${node.id}/object-${i}`,
231
- type: 'object',
232
- properties: getProperties(`${node.id}/object-${i}`, {
233
- label: `Object ${i}`,
234
- icon: faker.properties.icon(),
235
- }),
236
- })),
237
- ];
238
- }),
239
- Option.getOrElse(() => []),
240
- ),
241
- );
242
- },
243
- }),
244
- // Create object actions.
245
- createExtension({
246
- id: 'object-actions',
247
- actions: (node) =>
248
- Rx.make((get) =>
249
- Function.pipe(
250
- get(node),
251
- Option.flatMap((node) => (node.type === 'object' ? Option.some(node) : Option.none())),
252
- Option.map((node) => {
253
- return [
254
- ...Array.from({ length: 5 }, (_, i) => ({
255
- id: `${node.id}/action-${i}`,
256
- data: () => {
257
- log.info('action', { id: node.id, index: i });
258
- },
259
- properties: getProperties(`${node.id}/action-${i}`, {
260
- label: `Action ${i}`,
261
- icon: faker.properties.icon(),
262
- }),
263
- })),
264
- ];
265
- }),
266
- Option.getOrElse(() => []),
165
+ return Array.from({ length: get(count) }, (_, i) => ({
166
+ id: `object-${i}`,
167
+ type: 'object',
168
+ properties: getProperties(`object-${i}`, {
169
+ label: `Object ${i}`,
170
+ icon: faker.properties.icon(),
171
+ }),
172
+ }));
173
+ }),
174
+ }),
175
+ // Create object actions.
176
+ GraphBuilder.createExtension({
177
+ id: 'object-actions',
178
+ match: NodeMatcher.whenNodeType('object'),
179
+ actions: () =>
180
+ Effect.succeed(
181
+ Array.from({ length: 5 }, (_, i) => ({
182
+ id: `object-actions/action-${i}`,
183
+ data: Effect.fnUntraced(function* () {
184
+ log.info('action', { id: 'object-actions', index: i });
185
+ }),
186
+ properties: getProperties(`object-actions/action-${i}`, {
187
+ label: `Action ${i}`,
188
+ icon: faker.properties.icon(),
189
+ }),
190
+ })),
267
191
  ),
268
- ),
269
- }),
270
- ];
192
+ }),
193
+ ]),
194
+ );
271
195
  };