@dxos/plugin-navtree 0.8.4-main.7ace549 → 0.8.4-main.937b3ca

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 (166) hide show
  1. package/dist/lib/browser/app-graph-builder-RSMA6I22.mjs +43 -0
  2. package/dist/lib/browser/app-graph-builder-RSMA6I22.mjs.map +7 -0
  3. package/dist/lib/browser/chunk-6YPQ625I.mjs +1293 -0
  4. package/dist/lib/browser/chunk-6YPQ625I.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-AGQDZBPL.mjs +23 -0
  6. package/dist/lib/browser/chunk-AGQDZBPL.mjs.map +7 -0
  7. package/dist/lib/browser/{chunk-E6MZBCU5.mjs → chunk-YKTYPINZ.mjs} +1 -1
  8. package/dist/lib/browser/chunk-YKTYPINZ.mjs.map +7 -0
  9. package/dist/lib/browser/index.mjs +82 -91
  10. package/dist/lib/browser/index.mjs.map +4 -4
  11. package/dist/lib/browser/{keyboard-YJEK2HTH.mjs → keyboard-32EH3N4K.mjs} +17 -14
  12. package/dist/lib/browser/keyboard-32EH3N4K.mjs.map +7 -0
  13. package/dist/lib/browser/meta.json +1 -1
  14. package/dist/lib/browser/operation-resolver-IB75DC5O.mjs +52 -0
  15. package/dist/lib/browser/operation-resolver-IB75DC5O.mjs.map +7 -0
  16. package/dist/lib/browser/react-surface-F6CVMS3Y.mjs +58 -0
  17. package/dist/lib/browser/react-surface-F6CVMS3Y.mjs.map +7 -0
  18. package/dist/lib/browser/state-EVYQOUI3.mjs +120 -0
  19. package/dist/lib/browser/state-EVYQOUI3.mjs.map +7 -0
  20. package/dist/types/src/NavTreePlugin.d.ts +2 -1
  21. package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
  22. package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts +6 -0
  23. package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts.map +1 -0
  24. package/dist/types/src/capabilities/app-graph-builder/index.d.ts +3 -0
  25. package/dist/types/src/capabilities/app-graph-builder/index.d.ts.map +1 -0
  26. package/dist/types/src/capabilities/index.d.ts +5 -108
  27. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  28. package/dist/types/src/capabilities/keyboard/index.d.ts +3 -0
  29. package/dist/types/src/capabilities/keyboard/index.d.ts.map +1 -0
  30. package/dist/types/src/capabilities/keyboard/keyboard.d.ts +5 -0
  31. package/dist/types/src/capabilities/keyboard/keyboard.d.ts.map +1 -0
  32. package/dist/types/src/capabilities/operation-resolver/index.d.ts +3 -0
  33. package/dist/types/src/capabilities/operation-resolver/index.d.ts.map +1 -0
  34. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +5 -0
  35. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +1 -0
  36. package/dist/types/src/capabilities/react-surface/index.d.ts +3 -0
  37. package/dist/types/src/capabilities/react-surface/index.d.ts.map +1 -0
  38. package/dist/types/src/capabilities/react-surface/react-surface.d.ts +5 -0
  39. package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +1 -0
  40. package/dist/types/src/capabilities/state/index.d.ts +3 -0
  41. package/dist/types/src/capabilities/state/index.d.ts.map +1 -0
  42. package/dist/types/src/capabilities/state/state.d.ts +6 -0
  43. package/dist/types/src/capabilities/state/state.d.ts.map +1 -0
  44. package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts +3 -2
  45. package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts.map +1 -1
  46. package/dist/types/src/components/CommandsDialog/CommandsTrigger.d.ts.map +1 -1
  47. package/dist/types/src/components/NavTree/NavTree.d.ts +30 -1
  48. package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
  49. package/dist/types/src/components/NavTree/NavTree.stories.d.ts +4 -1
  50. package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -1
  51. package/dist/types/src/components/NavTreeContainer.d.ts +8 -1
  52. package/dist/types/src/components/NavTreeContainer.d.ts.map +1 -1
  53. package/dist/types/src/components/NavTreeDocumentTitle.d.ts +1 -1
  54. package/dist/types/src/components/NavTreeDocumentTitle.d.ts.map +1 -1
  55. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts +6 -6
  56. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -1
  57. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +2 -1
  58. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts.map +1 -1
  59. package/dist/types/src/components/Sidebar/L0Menu.d.ts +4 -4
  60. package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
  61. package/dist/types/src/components/Sidebar/L1Panel.d.ts +1 -1
  62. package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
  63. package/dist/types/src/components/Sidebar/L1Tabs.d.ts +2 -2
  64. package/dist/types/src/components/Sidebar/L1Tabs.d.ts.map +1 -1
  65. package/dist/types/src/components/index.d.ts.map +1 -1
  66. package/dist/types/src/components/types.d.ts +7 -7
  67. package/dist/types/src/components/types.d.ts.map +1 -1
  68. package/dist/types/src/hooks.d.ts +32 -1
  69. package/dist/types/src/hooks.d.ts.map +1 -1
  70. package/dist/types/src/index.d.ts +1 -2
  71. package/dist/types/src/index.d.ts.map +1 -1
  72. package/dist/types/src/meta.d.ts +2 -2
  73. package/dist/types/src/meta.d.ts.map +1 -1
  74. package/dist/types/src/testing/app-graph-builder.d.ts +2 -3
  75. package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -1
  76. package/dist/types/src/testing/generator.d.ts +5 -6
  77. package/dist/types/src/testing/generator.d.ts.map +1 -1
  78. package/dist/types/src/testing/update-state.d.ts +2 -2
  79. package/dist/types/src/testing/update-state.d.ts.map +1 -1
  80. package/dist/types/src/types/capabilities.d.ts +16 -0
  81. package/dist/types/src/types/capabilities.d.ts.map +1 -0
  82. package/dist/types/src/types/events.d.ts +5 -0
  83. package/dist/types/src/types/events.d.ts.map +1 -0
  84. package/dist/types/src/types/index.d.ts +4 -0
  85. package/dist/types/src/types/index.d.ts.map +1 -0
  86. package/dist/types/src/{types.d.ts → types/schema.d.ts} +6 -6
  87. package/dist/types/src/types/schema.d.ts.map +1 -0
  88. package/dist/types/src/util.d.ts +7 -7
  89. package/dist/types/src/util.d.ts.map +1 -1
  90. package/dist/types/tsconfig.tsbuildinfo +1 -1
  91. package/package.json +53 -52
  92. package/src/NavTreePlugin.tsx +53 -70
  93. package/src/capabilities/app-graph-builder/app-graph-builder.ts +41 -0
  94. package/src/capabilities/app-graph-builder/index.ts +7 -0
  95. package/src/capabilities/index.ts +5 -9
  96. package/src/capabilities/keyboard/index.ts +7 -0
  97. package/src/capabilities/keyboard/keyboard.ts +65 -0
  98. package/src/capabilities/operation-resolver/index.ts +7 -0
  99. package/src/capabilities/operation-resolver/operation-resolver.ts +41 -0
  100. package/src/capabilities/react-surface/index.ts +7 -0
  101. package/src/capabilities/react-surface/react-surface.tsx +54 -0
  102. package/src/capabilities/state/index.ts +7 -0
  103. package/src/capabilities/state/state.ts +121 -0
  104. package/src/components/CommandsDialog/CommandsDialog.tsx +107 -104
  105. package/src/components/CommandsDialog/CommandsTrigger.tsx +4 -14
  106. package/src/components/NavTree/NavTree.stories.tsx +32 -41
  107. package/src/components/NavTree/NavTree.tsx +5 -15
  108. package/src/components/NavTreeContainer.tsx +251 -292
  109. package/src/components/NavTreeDocumentTitle.tsx +3 -2
  110. package/src/components/NavTreeItem/NavTreeItemAction.stories.tsx +17 -7
  111. package/src/components/NavTreeItem/NavTreeItemAction.tsx +18 -12
  112. package/src/components/NavTreeItem/NavTreeItemColumns.tsx +5 -5
  113. package/src/components/Sidebar/L0Menu.tsx +65 -91
  114. package/src/components/Sidebar/L1Panel.tsx +9 -7
  115. package/src/components/Sidebar/L1Tabs.tsx +6 -31
  116. package/src/components/index.ts +1 -0
  117. package/src/components/types.ts +7 -7
  118. package/src/experimental/Tree.stories.tsx +2 -2
  119. package/src/experimental/Tree.tsx +1 -1
  120. package/src/hooks.ts +54 -2
  121. package/src/index.ts +1 -2
  122. package/src/meta.ts +2 -2
  123. package/src/testing/app-graph-builder.ts +158 -234
  124. package/src/testing/generator.ts +12 -12
  125. package/src/testing/update-state.ts +6 -6
  126. package/src/types/capabilities.ts +21 -0
  127. package/src/types/events.ts +11 -0
  128. package/src/types/index.ts +7 -0
  129. package/src/{types.ts → types/schema.ts} +5 -5
  130. package/src/util.ts +13 -16
  131. package/dist/lib/browser/app-graph-builder-OHSCN3GX.mjs +0 -46
  132. package/dist/lib/browser/app-graph-builder-OHSCN3GX.mjs.map +0 -7
  133. package/dist/lib/browser/chunk-5VLWX4PK.mjs +0 -15
  134. package/dist/lib/browser/chunk-5VLWX4PK.mjs.map +0 -7
  135. package/dist/lib/browser/chunk-E6MZBCU5.mjs.map +0 -7
  136. package/dist/lib/browser/chunk-UDAINHB6.mjs +0 -1488
  137. package/dist/lib/browser/chunk-UDAINHB6.mjs.map +0 -7
  138. package/dist/lib/browser/intent-resolver-YUVXVPHG.mjs +0 -47
  139. package/dist/lib/browser/intent-resolver-YUVXVPHG.mjs.map +0 -7
  140. package/dist/lib/browser/keyboard-YJEK2HTH.mjs.map +0 -7
  141. package/dist/lib/browser/react-surface-V6LS3R4L.mjs +0 -51
  142. package/dist/lib/browser/react-surface-V6LS3R4L.mjs.map +0 -7
  143. package/dist/lib/browser/state-BFD5KJCV.mjs +0 -117
  144. package/dist/lib/browser/state-BFD5KJCV.mjs.map +0 -7
  145. package/dist/types/src/capabilities/app-graph-builder.d.ts +0 -4
  146. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +0 -1
  147. package/dist/types/src/capabilities/capabilities.d.ts +0 -21
  148. package/dist/types/src/capabilities/capabilities.d.ts.map +0 -1
  149. package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
  150. package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
  151. package/dist/types/src/capabilities/keyboard.d.ts +0 -4
  152. package/dist/types/src/capabilities/keyboard.d.ts.map +0 -1
  153. package/dist/types/src/capabilities/react-surface.d.ts +0 -4
  154. package/dist/types/src/capabilities/react-surface.d.ts.map +0 -1
  155. package/dist/types/src/capabilities/state.d.ts +0 -20
  156. package/dist/types/src/capabilities/state.d.ts.map +0 -1
  157. package/dist/types/src/events.d.ts +0 -4
  158. package/dist/types/src/events.d.ts.map +0 -1
  159. package/dist/types/src/types.d.ts.map +0 -1
  160. package/src/capabilities/app-graph-builder.ts +0 -53
  161. package/src/capabilities/capabilities.ts +0 -20
  162. package/src/capabilities/intent-resolver.ts +0 -37
  163. package/src/capabilities/keyboard.ts +0 -57
  164. package/src/capabilities/react-surface.tsx +0 -45
  165. package/src/capabilities/state.ts +0 -104
  166. package/src/events.ts +0 -11
package/src/hooks.ts CHANGED
@@ -2,13 +2,16 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { useEffect } from 'react';
5
+ import { type Atom, useAtomValue } from '@effect-atom/atom-react';
6
+ import { useEffect, useMemo } from 'react';
6
7
 
8
+ import { useCapability } from '@dxos/app-framework/react';
7
9
  import { type Node } from '@dxos/app-graph';
8
10
 
9
11
  import { useNavTreeContext } from './components';
12
+ import { NavTreeCapabilities } from './types';
10
13
 
11
- export const useLoadDescendents = (root?: Node) => {
14
+ export const useLoadDescendents = (root?: Node.Node) => {
12
15
  const { loadDescendents } = useNavTreeContext();
13
16
  useEffect(() => {
14
17
  const frame = requestAnimationFrame(() => {
@@ -19,3 +22,52 @@ export const useLoadDescendents = (root?: Node) => {
19
22
  return () => cancelAnimationFrame(frame);
20
23
  }, [root]);
21
24
  };
25
+
26
+ export type UseNavTreeStateResult = {
27
+ getItem: (path: string[]) => NavTreeCapabilities.NavTreeItemState;
28
+ getItemAtom: (path: string[]) => Atom.Atom<NavTreeCapabilities.NavTreeItemState>;
29
+ setItem: (path: string[], key: 'open' | 'current' | 'alternateTree', next: boolean) => void;
30
+ };
31
+
32
+ /**
33
+ * Hook that provides access to navtree state functions.
34
+ * For reactive subscriptions, use `useNavTreeItemState` or `useAtomValue(getItemAtom(path))`.
35
+ */
36
+ export const useNavTreeState = (): UseNavTreeStateResult => {
37
+ return useCapability(NavTreeCapabilities.State);
38
+ };
39
+
40
+ /**
41
+ * Hook that subscribes to a single navtree item's state.
42
+ * Use this for fine-grained reactivity.
43
+ */
44
+ export const useNavTreeItemState = (path: string[]): NavTreeCapabilities.NavTreeItemState => {
45
+ const { getItemAtom } = useCapability(NavTreeCapabilities.State);
46
+ // Memoize the path to avoid creating new atoms on every render.
47
+ const pathKey = useMemo(() => path.join('~'), [path]);
48
+ const atom = useMemo(() => getItemAtom(path), [getItemAtom, pathKey]);
49
+ return useAtomValue(atom);
50
+ };
51
+
52
+ /**
53
+ * Hook that subscribes to the open state for a tree item.
54
+ * Designed to be passed to Tree component as useIsOpen prop.
55
+ */
56
+ export const useIsOpen = (path: string[], _item?: any): boolean => {
57
+ return useNavTreeItemState(path).open;
58
+ };
59
+
60
+ /**
61
+ * Hook that subscribes to the current state for a tree item.
62
+ * Designed to be passed to Tree component as useIsCurrent prop.
63
+ */
64
+ export const useIsCurrent = (path: string[], _item?: any): boolean => {
65
+ return useNavTreeItemState(path).current;
66
+ };
67
+
68
+ /**
69
+ * Hook that subscribes to the alternateTree state for a tree item.
70
+ */
71
+ export const useIsAlternateTree = (path: string[], _item?: any): boolean => {
72
+ return useNavTreeItemState(path).alternateTree ?? false;
73
+ };
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,10 +2,10 @@
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
6
  import { trim } from '@dxos/util';
7
7
 
8
- export const meta: PluginMeta = {
8
+ export const meta: Plugin.Meta = {
9
9
  id: 'dxos.org/plugin/navtree',
10
10
  name: 'Navtree',
11
11
  description: trim`
@@ -3,15 +3,14 @@
3
3
  //
4
4
 
5
5
  import { Atom } from '@effect-atom/atom-react';
6
- import * as Function from 'effect/Function';
7
- import * as Option from 'effect/Option';
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
- Atom.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
- Atom.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
- Atom.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 = Atom.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 Atom.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
- Atom.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 = Atom.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 Atom.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
- Atom.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
  };
@@ -4,9 +4,9 @@
4
4
 
5
5
  import * as Schema from 'effect/Schema';
6
6
 
7
- import { type NodeArg } from '@dxos/app-graph';
7
+ import { type Node } from '@dxos/app-graph';
8
8
  import { Obj, Type } from '@dxos/echo';
9
- import { type Live } from '@dxos/live-object';
9
+ import { TestSchema } from '@dxos/echo/testing';
10
10
  import { faker } from '@dxos/random';
11
11
  import { range } from '@dxos/util';
12
12
 
@@ -17,12 +17,12 @@ import { range } from '@dxos/util';
17
17
  export type TestItem = { id: string; type: string } & Record<string, any>;
18
18
 
19
19
  type ObjectDataGenerator = {
20
- createSchema?: () => Schema.Schema.AnyNoContext;
20
+ createSchema?: () => Type.Obj.Any;
21
21
  createData: () => any;
22
22
  };
23
23
 
24
- type ObjectFactory<T extends Live<any>> = {
25
- schema?: Schema.Schema.AnyNoContext; // TODO(burdon): Support both typed and expando schema.
24
+ type ObjectFactory<T> = {
25
+ schema?: Type.Obj.Any; // TODO(burdon): Support both typed and expando schema.
26
26
  createObject: () => T;
27
27
  };
28
28
 
@@ -32,7 +32,7 @@ const createFactory = ({ createSchema, createData }: ObjectDataGenerator) => {
32
32
  const schema = createSchema?.();
33
33
  return {
34
34
  schema,
35
- createObject: () => (schema ? Obj.make(schema, createData()) : Obj.make(Type.Expando, createData())),
35
+ createObject: () => (schema ? Obj.make(schema, createData()) : Obj.make(TestSchema.Expando, createData())),
36
36
  };
37
37
  };
38
38
 
@@ -64,7 +64,7 @@ export const defaultGenerators: { [type: string]: ObjectDataGenerator } = {
64
64
  status: Schema.String,
65
65
  priority: Schema.Number,
66
66
  }).pipe(
67
- Type.Obj({
67
+ Type.object({
68
68
  typename: 'example.com/type/Project',
69
69
  version: '0.1.0',
70
70
  }),
@@ -94,7 +94,7 @@ export class TestObjectGenerator {
94
94
  }, {});
95
95
  }
96
96
 
97
- get schema(): Schema.Schema.AnyNoContext[] {
97
+ get schema(): Type.Obj.Any[] {
98
98
  return Object.values(this.factories).map((f) => f.schema!);
99
99
  }
100
100
 
@@ -142,7 +142,7 @@ export const createTree = () => {
142
142
  properties: {
143
143
  label: l0.title,
144
144
  icon: 'ph--horse--regular',
145
- disposition: 'collection',
145
+ disposition: 'workspace',
146
146
  },
147
147
  nodes: [
148
148
  ...[...Array(4)].map(() => {
@@ -175,7 +175,7 @@ export const createTree = () => {
175
175
  },
176
176
  },
177
177
  ],
178
- } satisfies NodeArg<any>;
178
+ } satisfies Node.NodeArg<any>;
179
179
  }),
180
180
  {
181
181
  id: `${faker.string.uuid()}__a1`,
@@ -196,9 +196,9 @@ export const createTree = () => {
196
196
  },
197
197
  },
198
198
  ],
199
- } satisfies NodeArg<any>;
199
+ } satisfies Node.NodeArg<any>;
200
200
  }),
201
- } satisfies NodeArg<any>;
201
+ } satisfies Node.NodeArg<any>;
202
202
 
203
203
  return initialContent;
204
204
  };
@@ -4,22 +4,22 @@
4
4
 
5
5
  import { type Instruction } from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item';
6
6
 
7
- import { type NodeArg } from '@dxos/app-graph';
7
+ import { type Node } from '@dxos/app-graph';
8
8
  import { log } from '@dxos/log';
9
9
  import { type TreeData } from '@dxos/react-ui-list';
10
10
 
11
- const removeItem = (tree: NodeArg<any>, source: TreeData) => {
11
+ const removeItem = (tree: Node.NodeArg<any>, source: TreeData) => {
12
12
  const parent = getNode(tree, source.path.slice(1, -1));
13
- const index = parent.nodes!.findIndex(({ id }) => id === source.id);
13
+ const index = parent.nodes!.findIndex((node: Node.NodeArg<any>) => node.id === source.id);
14
14
  const item = parent.nodes![index];
15
15
  parent.nodes!.splice(index, 1);
16
16
  return item;
17
17
  };
18
18
 
19
- const getNode = (tree: NodeArg<any>, path: string[]) => {
19
+ const getNode = (tree: Node.NodeArg<any>, path: string[]) => {
20
20
  let node = tree;
21
21
  for (const part of path) {
22
- node = node.nodes!.find(({ id }) => id === part)!;
22
+ node = node.nodes!.find((n: Node.NodeArg<any>) => n.id === part)!;
23
23
  }
24
24
  return node;
25
25
  };
@@ -31,7 +31,7 @@ export const updateState = ({
31
31
  source,
32
32
  target,
33
33
  }: {
34
- state: NodeArg<any>;
34
+ state: Node.NodeArg<any>;
35
35
  instruction: Instruction;
36
36
  source: TreeData;
37
37
  target: TreeData;
@@ -0,0 +1,21 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type Atom } from '@effect-atom/atom-react';
6
+
7
+ import { Capability } from '@dxos/app-framework';
8
+
9
+ import { meta } from '../meta';
10
+
11
+ export namespace NavTreeCapabilities {
12
+ export type NavTreeItemState = { open: boolean; current: boolean; alternateTree?: boolean };
13
+
14
+ export type NavTreeStateStore = {
15
+ getItem: (path: string[]) => NavTreeItemState;
16
+ getItemAtom: (path: string[]) => Atom.Atom<NavTreeItemState>;
17
+ setItem: (path: string[], key: 'open' | 'current' | 'alternateTree', next: boolean) => void;
18
+ };
19
+
20
+ export const State = Capability.make<NavTreeStateStore>(`${meta.id}/capability/state`);
21
+ }