@dxos/plugin-navtree 0.8.4-main.3c1ae3b → 0.8.4-main.3eb6e50203

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 (163) hide show
  1. package/dist/lib/browser/app-graph-builder-XGA4GPGL.mjs +44 -0
  2. package/dist/lib/browser/app-graph-builder-XGA4GPGL.mjs.map +7 -0
  3. package/dist/lib/browser/chunk-63YLDLLE.mjs +1373 -0
  4. package/dist/lib/browser/chunk-63YLDLLE.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-X2HDG62A.mjs +23 -0
  6. package/dist/lib/browser/chunk-X2HDG62A.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 +83 -91
  10. package/dist/lib/browser/index.mjs.map +4 -4
  11. package/dist/lib/browser/{keyboard-YJEK2HTH.mjs → keyboard-46XQHY2O.mjs} +18 -14
  12. package/dist/lib/browser/keyboard-46XQHY2O.mjs.map +7 -0
  13. package/dist/lib/browser/meta.json +1 -1
  14. package/dist/lib/browser/operation-resolver-NNNDII45.mjs +53 -0
  15. package/dist/lib/browser/operation-resolver-NNNDII45.mjs.map +7 -0
  16. package/dist/lib/browser/{react-surface-RPXGIV45.mjs → react-surface-APSEG23H.mjs} +16 -14
  17. package/dist/lib/browser/react-surface-APSEG23H.mjs.map +7 -0
  18. package/dist/lib/browser/state-M5YUCIQU.mjs +121 -0
  19. package/dist/lib/browser/state-M5YUCIQU.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.map +1 -1
  45. package/dist/types/src/components/CommandsDialog/CommandsTrigger.d.ts.map +1 -1
  46. package/dist/types/src/components/NavTree/NavTree.d.ts +2 -2
  47. package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
  48. package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -1
  49. package/dist/types/src/components/NavTreeContainer.d.ts.map +1 -1
  50. package/dist/types/src/components/NavTreeDocumentTitle.d.ts +1 -1
  51. package/dist/types/src/components/NavTreeDocumentTitle.d.ts.map +1 -1
  52. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts +6 -6
  53. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -1
  54. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +2 -1
  55. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts.map +1 -1
  56. package/dist/types/src/components/Sidebar/L0Menu.d.ts +4 -4
  57. package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
  58. package/dist/types/src/components/Sidebar/L1Panel.d.ts +2 -2
  59. package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
  60. package/dist/types/src/components/Sidebar/L1Tabs.d.ts +2 -2
  61. package/dist/types/src/components/Sidebar/L1Tabs.d.ts.map +1 -1
  62. package/dist/types/src/components/types.d.ts +7 -7
  63. package/dist/types/src/components/types.d.ts.map +1 -1
  64. package/dist/types/src/experimental/Tree.stories.d.ts +18 -4
  65. package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
  66. package/dist/types/src/hooks.d.ts +32 -1
  67. package/dist/types/src/hooks.d.ts.map +1 -1
  68. package/dist/types/src/index.d.ts +1 -2
  69. package/dist/types/src/index.d.ts.map +1 -1
  70. package/dist/types/src/meta.d.ts +2 -2
  71. package/dist/types/src/meta.d.ts.map +1 -1
  72. package/dist/types/src/testing/app-graph-builder.d.ts +2 -3
  73. package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -1
  74. package/dist/types/src/testing/generator.d.ts +5 -6
  75. package/dist/types/src/testing/generator.d.ts.map +1 -1
  76. package/dist/types/src/testing/update-state.d.ts +2 -2
  77. package/dist/types/src/testing/update-state.d.ts.map +1 -1
  78. package/dist/types/src/types/capabilities.d.ts +16 -0
  79. package/dist/types/src/types/capabilities.d.ts.map +1 -0
  80. package/dist/types/src/types/events.d.ts +5 -0
  81. package/dist/types/src/types/events.d.ts.map +1 -0
  82. package/dist/types/src/types/index.d.ts +4 -0
  83. package/dist/types/src/types/index.d.ts.map +1 -0
  84. package/dist/types/src/{types.d.ts → types/schema.d.ts} +6 -6
  85. package/dist/types/src/types/schema.d.ts.map +1 -0
  86. package/dist/types/src/util.d.ts +7 -7
  87. package/dist/types/src/util.d.ts.map +1 -1
  88. package/dist/types/tsconfig.tsbuildinfo +1 -1
  89. package/package.json +54 -52
  90. package/src/NavTreePlugin.tsx +53 -72
  91. package/src/capabilities/app-graph-builder/app-graph-builder.ts +42 -0
  92. package/src/capabilities/app-graph-builder/index.ts +7 -0
  93. package/src/capabilities/index.ts +5 -9
  94. package/src/capabilities/keyboard/index.ts +7 -0
  95. package/src/capabilities/keyboard/keyboard.ts +66 -0
  96. package/src/capabilities/operation-resolver/index.ts +7 -0
  97. package/src/capabilities/operation-resolver/operation-resolver.ts +42 -0
  98. package/src/capabilities/react-surface/index.ts +7 -0
  99. package/src/capabilities/react-surface/react-surface.tsx +55 -0
  100. package/src/capabilities/state/index.ts +7 -0
  101. package/src/capabilities/state/state.ts +122 -0
  102. package/src/components/CommandsDialog/CommandsDialog.tsx +62 -64
  103. package/src/components/CommandsDialog/CommandsTrigger.tsx +4 -14
  104. package/src/components/NavTree/NavTree.stories.tsx +35 -43
  105. package/src/components/NavTree/NavTree.tsx +1 -11
  106. package/src/components/NavTreeContainer.tsx +70 -114
  107. package/src/components/NavTreeDocumentTitle.tsx +3 -2
  108. package/src/components/NavTreeItem/NavTreeItemAction.stories.tsx +17 -7
  109. package/src/components/NavTreeItem/NavTreeItemAction.tsx +18 -12
  110. package/src/components/NavTreeItem/NavTreeItemColumns.tsx +5 -5
  111. package/src/components/Sidebar/L0Menu.tsx +67 -96
  112. package/src/components/Sidebar/L1Panel.tsx +151 -70
  113. package/src/components/Sidebar/L1Tabs.tsx +6 -31
  114. package/src/components/UserAccountAvatar/UserAccountAvatar.stories.tsx +1 -1
  115. package/src/components/types.ts +7 -7
  116. package/src/experimental/Tree.stories.tsx +44 -50
  117. package/src/experimental/Tree.tsx +1 -1
  118. package/src/hooks.ts +54 -2
  119. package/src/index.ts +1 -2
  120. package/src/meta.ts +2 -2
  121. package/src/testing/app-graph-builder.ts +158 -234
  122. package/src/testing/generator.ts +12 -12
  123. package/src/testing/update-state.ts +6 -6
  124. package/src/types/capabilities.ts +21 -0
  125. package/src/types/events.ts +12 -0
  126. package/src/types/index.ts +7 -0
  127. package/src/{types.ts → types/schema.ts} +5 -5
  128. package/src/util.ts +13 -16
  129. package/dist/lib/browser/app-graph-builder-OHSCN3GX.mjs +0 -46
  130. package/dist/lib/browser/app-graph-builder-OHSCN3GX.mjs.map +0 -7
  131. package/dist/lib/browser/chunk-5DWDYPGQ.mjs +0 -1492
  132. package/dist/lib/browser/chunk-5DWDYPGQ.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/intent-resolver-YUVXVPHG.mjs +0 -47
  137. package/dist/lib/browser/intent-resolver-YUVXVPHG.mjs.map +0 -7
  138. package/dist/lib/browser/keyboard-YJEK2HTH.mjs.map +0 -7
  139. package/dist/lib/browser/react-surface-RPXGIV45.mjs.map +0 -7
  140. package/dist/lib/browser/state-BFD5KJCV.mjs +0 -117
  141. package/dist/lib/browser/state-BFD5KJCV.mjs.map +0 -7
  142. package/dist/types/src/capabilities/app-graph-builder.d.ts +0 -4
  143. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +0 -1
  144. package/dist/types/src/capabilities/capabilities.d.ts +0 -21
  145. package/dist/types/src/capabilities/capabilities.d.ts.map +0 -1
  146. package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
  147. package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
  148. package/dist/types/src/capabilities/keyboard.d.ts +0 -4
  149. package/dist/types/src/capabilities/keyboard.d.ts.map +0 -1
  150. package/dist/types/src/capabilities/react-surface.d.ts +0 -4
  151. package/dist/types/src/capabilities/react-surface.d.ts.map +0 -1
  152. package/dist/types/src/capabilities/state.d.ts +0 -20
  153. package/dist/types/src/capabilities/state.d.ts.map +0 -1
  154. package/dist/types/src/events.d.ts +0 -4
  155. package/dist/types/src/events.d.ts.map +0 -1
  156. package/dist/types/src/types.d.ts.map +0 -1
  157. package/src/capabilities/app-graph-builder.ts +0 -53
  158. package/src/capabilities/capabilities.ts +0 -20
  159. package/src/capabilities/intent-resolver.ts +0 -37
  160. package/src/capabilities/keyboard.ts +0 -57
  161. package/src/capabilities/react-surface.tsx +0 -48
  162. package/src/capabilities/state.ts +0 -104
  163. package/src/events.ts +0 -11
@@ -4,20 +4,20 @@
4
4
 
5
5
  import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
6
6
  import { type Instruction, extractInstruction } from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item';
7
- import { untracked } from '@preact/signals-core';
8
7
  import React, { forwardRef, memo, useCallback, useEffect, useMemo } from 'react';
9
8
 
10
- import { LayoutAction, createIntent } from '@dxos/app-framework';
11
- import { Surface, useAppGraph, useCapability, useIntentDispatcher, useLayout } from '@dxos/app-framework/react';
12
- import { type Node, ROOT_ID, type ReadableGraph, isAction, isActionLike } from '@dxos/app-graph';
9
+ import { Surface, useOperationInvoker } from '@dxos/app-framework/ui';
10
+ import { LayoutOperation } from '@dxos/app-toolkit';
11
+ import { useAppGraph, useLayout } from '@dxos/app-toolkit/ui';
13
12
  import { PLANK_COMPANION_TYPE } from '@dxos/plugin-deck/types';
13
+ import { Graph, Node, useActionRunner } from '@dxos/plugin-graph';
14
14
  import { useConnections, useActions as useGraphActions } from '@dxos/plugin-graph';
15
15
  import { useMediaQuery, useSidebars } from '@dxos/react-ui';
16
16
  import { type TreeData, type TreeItemDataProps, isTreeData } from '@dxos/react-ui-list';
17
- import { mx } from '@dxos/react-ui-theme';
17
+ import { mx } from '@dxos/ui-theme';
18
18
  import { arrayMove, byPosition } from '@dxos/util';
19
19
 
20
- import { NavTreeCapabilities } from '../capabilities';
20
+ import { useIsAlternateTree, useIsCurrent, useIsOpen, useNavTreeState } from '../hooks';
21
21
  import { meta } from '../meta';
22
22
  import { type FlattenedActions, type NavTreeItemGraphNode } from '../types';
23
23
  import { getChildren, getParent, resolveMigrationOperation } from '../util';
@@ -29,38 +29,38 @@ import { type NavTreeContextValue } from './types';
29
29
  // TODO(thure): Is NavTree truly authoritative in this regard?
30
30
  export const NODE_TYPE = 'dxos/app-graph/node';
31
31
 
32
- const renderItemEnd = ({ node, open }: { node: Node; open: boolean }) => (
33
- <Surface role='navtree-item-end' data={{ id: node.id, subject: node.data, open }} limit={1} />
32
+ const renderItemEnd = ({ node, open }: { node: Node.Node; open: boolean }) => (
33
+ <Surface.Surface role='navtree-item-end' data={{ id: node.id, subject: node.data, open }} limit={1} />
34
34
  );
35
35
 
36
- const getChildrenFilter = (node: Node): node is Node =>
37
- untracked(
38
- () => !isActionLike(node) && node.type !== PLANK_COMPANION_TYPE && node.properties.disposition !== 'hidden',
39
- );
36
+ const getChildrenFilter = (node: Node.Node): node is Node.Node =>
37
+ !Node.isActionLike(node) && node.type !== PLANK_COMPANION_TYPE && node.properties.disposition !== 'hidden';
40
38
 
41
- const filterItems = (node: Node, disposition?: string) => {
39
+ const filterItems = (node: Node.Node, disposition?: string) => {
42
40
  if (!disposition && (node.properties.disposition === 'hidden' || node.properties.disposition === 'alternate-tree')) {
43
41
  return false;
44
42
  } else if (!disposition) {
45
- const action = isAction(node);
43
+ const action = Node.isAction(node);
46
44
  return !action || node.properties.disposition === 'item';
47
45
  } else {
48
46
  return node.properties.disposition === disposition;
49
47
  }
50
48
  };
51
49
 
52
- const getItems = (graph: ReadableGraph, node?: Node, disposition?: string) => {
53
- return graph.getConnections(node?.id ?? ROOT_ID, 'outbound').filter((node) => filterItems(node, disposition));
50
+ const getItems = (graph: Graph.ReadableGraph, node?: Node.Node, disposition?: string) => {
51
+ return Graph.getConnections(graph, node?.id ?? Node.RootId, 'outbound').filter((node) =>
52
+ filterItems(node, disposition),
53
+ );
54
54
  };
55
55
 
56
- const useItems = (node?: Node, options?: { disposition?: string; sort?: boolean }) => {
56
+ const useItems = (node?: Node.Node, options?: { disposition?: string; sort?: boolean }) => {
57
57
  const { graph } = useAppGraph();
58
- const connections = useConnections(graph, node?.id ?? ROOT_ID);
58
+ const connections = useConnections(graph, node?.id ?? Node.RootId);
59
59
  const filtered = connections.filter((node) => filterItems(node, options?.disposition));
60
60
  return options?.sort ? filtered.toSorted((a, b) => byPosition(a.properties, b.properties)) : filtered;
61
61
  };
62
62
 
63
- const useActions = (node: Node): FlattenedActions => {
63
+ const useActions = (node: Node.Node): FlattenedActions => {
64
64
  const { graph } = useAppGraph();
65
65
  const actions = useGraphActions(graph, node.id);
66
66
 
@@ -73,8 +73,8 @@ const useActions = (node: Node): FlattenedActions => {
73
73
  }
74
74
 
75
75
  acc.actions.push(arg);
76
- if (!isAction(arg)) {
77
- const actionGroup = graph.getActions(arg.id);
76
+ if (!Node.isAction(arg)) {
77
+ const actionGroup = Graph.getActions(graph, arg.id);
78
78
  acc.groupedActions[arg.id] = actionGroup;
79
79
  }
80
80
  return acc;
@@ -93,14 +93,15 @@ export type NavTreeContainerProps = {
93
93
  export const NavTreeContainer$ = forwardRef<HTMLDivElement, NavTreeContainerProps>(
94
94
  ({ tab, popoverAnchorId, topbar }, forwardedRef) => {
95
95
  const [isLg] = useMediaQuery('lg');
96
- const { dispatchPromise: dispatch } = useIntentDispatcher();
96
+ const { invokeSync, invokePromise } = useOperationInvoker();
97
+ const runAction = useActionRunner();
97
98
  const { graph } = useAppGraph();
98
- const { isOpen, isCurrent, isAlternateTree, setItem } = useCapability(NavTreeCapabilities.State);
99
+ const { getItem, setItem } = useNavTreeState();
99
100
  const layout = useLayout();
100
101
  const { navigationSidebarState } = useSidebars(meta.id);
101
102
 
102
103
  const getProps = useCallback(
103
- (node: Node, path: string[]): TreeItemDataProps => {
104
+ (node: Node.Node, path: string[]): TreeItemDataProps => {
104
105
  const children = getChildren(graph, node, path).filter(getChildrenFilter);
105
106
  const parentOf =
106
107
  children.length > 0 ? children.map(({ id }) => id) : node.properties.role === 'branch' ? [] : undefined;
@@ -120,64 +121,49 @@ export const NavTreeContainer$ = forwardRef<HTMLDivElement, NavTreeContainerProp
120
121
  );
121
122
 
122
123
  const loadDescendents = useCallback(
123
- (node: Node) => {
124
- graph.expand(node.id, 'outbound');
124
+ (node: Node.Node) => {
125
+ Graph.expand(graph, node.id, 'outbound');
125
126
  // Load one level deeper, which resolves some juddering observed on open/close.
126
- graph.getConnections(node.id, 'outbound').forEach((child) => {
127
- graph.expand(child.id, 'outbound');
127
+ Graph.getConnections(graph, node.id, 'outbound').forEach((child) => {
128
+ Graph.expand(graph, child.id, 'outbound');
128
129
  });
129
130
  },
130
131
  [graph],
131
132
  );
132
133
 
133
134
  const handleOpenChange = useCallback(
134
- ({ item: { id }, path, open }: { item: Node; path: string[]; open: boolean }) => {
135
+ ({ item: { id }, path, open }: { item: Node.Node; path: string[]; open: boolean }) => {
135
136
  // TODO(thure): This might become a localstorage leak; openItemIds that no longer exist should be removed from this map.
136
137
  setItem(path, 'open', open);
137
- graph.expand(id, 'outbound');
138
+ Graph.expand(graph, id, 'outbound');
138
139
  },
139
140
  [graph],
140
141
  );
141
142
 
142
143
  const handleTabChange = useCallback(
143
- async (node: NavTreeItemGraphNode) => {
144
- await dispatch(
145
- createIntent(LayoutAction.UpdateSidebar, {
146
- part: 'sidebar',
147
- options: {
148
- state:
149
- node.id === tab
150
- ? navigationSidebarState === 'expanded'
151
- ? isLg
152
- ? 'collapsed'
153
- : 'closed'
154
- : 'expanded'
155
- : 'expanded',
156
- },
157
- }),
158
- );
144
+ (node: NavTreeItemGraphNode) => {
145
+ invokeSync(LayoutOperation.UpdateSidebar, {
146
+ state:
147
+ node.id === tab
148
+ ? navigationSidebarState === 'expanded'
149
+ ? isLg
150
+ ? 'collapsed'
151
+ : 'closed'
152
+ : 'expanded'
153
+ : 'expanded',
154
+ });
159
155
 
160
- await dispatch(
161
- createIntent(LayoutAction.SwitchWorkspace, {
162
- part: 'workspace',
163
- subject: node.id,
164
- }),
165
- );
156
+ invokeSync(LayoutOperation.SwitchWorkspace, { subject: node.id });
166
157
 
167
158
  // Open the first item if the workspace is empty.
168
159
  if (layout.active.length === 0) {
169
- const [item] = getItems(graph, node).filter((node) => !isActionLike(node));
160
+ const [item] = getItems(graph, node).filter((node) => !Node.isActionLike(node));
170
161
  if (item && item.data) {
171
- await dispatch(
172
- createIntent(LayoutAction.Open, {
173
- part: 'main',
174
- subject: [item.id],
175
- }),
176
- );
162
+ invokeSync(LayoutOperation.Open, { subject: [item.id] });
177
163
  }
178
164
  }
179
165
  },
180
- [dispatch, layout.active, tab, navigationSidebarState, isLg],
166
+ [invokeSync, layout.active, tab, navigationSidebarState, isLg],
181
167
  );
182
168
 
183
169
  const blockInstruction = useCallback(
@@ -191,75 +177,48 @@ export const NavTreeContainer$ = forwardRef<HTMLDivElement, NavTreeContainerProp
191
177
  return target.item.properties.canDrop?.(source) ?? false;
192
178
  }, []);
193
179
 
194
- const canSelect = useCallback(({ item }: { item: Node }) => {
180
+ const canSelect = useCallback(({ item }: { item: Node.Node }) => {
195
181
  return item.properties.selectable ?? true;
196
182
  }, []);
197
183
 
198
184
  const handleSelect = useCallback(
199
- ({ item: node, path, option }: { item: Node; path: string[]; option: boolean }) => {
185
+ ({ item: node, path, option }: { item: Node.Node; path: string[]; option: boolean }) => {
200
186
  if (!node.data) {
201
187
  return;
202
188
  }
203
189
 
204
- if (isAction(node)) {
205
- const [parent] = graph.getConnections(node.id, 'inbound');
206
- void (parent && node.data({ parent, caller: NAV_TREE_ITEM }));
190
+ if (Node.isAction(node)) {
191
+ const [parent] = Graph.getConnections(graph, node.id, 'inbound');
192
+ if (parent) {
193
+ void runAction(node, { parent, caller: NAV_TREE_ITEM });
194
+ }
207
195
  return;
208
196
  }
209
197
 
210
- const current = isCurrent(path, node);
198
+ const current = getItem(path).current;
211
199
  if (!current) {
212
- void dispatch(
213
- createIntent(LayoutAction.Open, {
214
- part: 'main',
215
- subject: [node.id],
216
- options: { key: node.properties.key },
217
- }),
218
- );
200
+ invokeSync(LayoutOperation.Open, { subject: [node.id], key: node.properties.key });
219
201
  } else if (option) {
220
- void dispatch(
221
- createIntent(LayoutAction.Close, {
222
- part: 'main',
223
- subject: [node.id],
224
- options: { state: false },
225
- }),
226
- );
202
+ invokeSync(LayoutOperation.Close, { subject: [node.id] });
227
203
  } else {
228
- void dispatch(
229
- createIntent(LayoutAction.ScrollIntoView, {
230
- part: 'current',
231
- subject: node.id,
232
- }),
233
- );
204
+ void invokePromise(LayoutOperation.ScrollIntoView, { subject: node.id });
234
205
  }
235
206
 
236
- const defaultAction = graph.getActions(node.id).find((action) => action.properties?.disposition === 'default');
237
- if (isAction(defaultAction)) {
238
- void (defaultAction.data as () => void)();
207
+ const defaultAction = Graph.getActions(graph, node.id).find(
208
+ (action) => action.properties?.disposition === 'default',
209
+ );
210
+ if (Node.isAction(defaultAction)) {
211
+ void runAction(defaultAction);
239
212
  }
240
213
 
241
214
  if (!isLg) {
242
- void dispatch(
243
- createIntent(LayoutAction.UpdateSidebar, {
244
- part: 'sidebar',
245
- options: { state: 'closed' },
246
- }),
247
- );
215
+ invokeSync(LayoutOperation.UpdateSidebar, { state: 'closed' });
248
216
  }
249
217
  },
250
- [graph, dispatch, isCurrent, isLg],
218
+ [graph, invokeSync, invokePromise, getItem, isLg, runAction],
251
219
  );
252
220
 
253
- const handleBack = useCallback(
254
- () =>
255
- dispatch(
256
- createIntent(LayoutAction.RevertWorkspace, {
257
- part: 'workspace',
258
- options: { revert: true },
259
- }),
260
- ),
261
- [dispatch],
262
- );
221
+ const handleBack = useCallback(() => invokeSync(LayoutOperation.RevertWorkspace), [invokeSync]);
263
222
 
264
223
  // TODO(wittjosiah): Factor out hook.
265
224
  useEffect(() => {
@@ -297,7 +256,7 @@ export const NavTreeContainer$ = forwardRef<HTMLDivElement, NavTreeContainerProp
297
256
  case 'rearrange': {
298
257
  const nextItems = sourceItems.map(({ data }) => data);
299
258
  arrayMove(nextItems, sourceIndex, targetIndex);
300
- void sourceParent?.properties.onRearrangeChildren?.(nextItems);
259
+ void sourceNode.properties.onRearrange?.(nextItems);
301
260
  break;
302
261
  }
303
262
  case 'copy': {
@@ -333,9 +292,9 @@ export const NavTreeContainer$ = forwardRef<HTMLDivElement, NavTreeContainerProp
333
292
  canDrop,
334
293
  canSelect,
335
294
  getProps,
336
- isAlternateTree,
337
- isCurrent,
338
- isOpen,
295
+ useIsAlternateTree,
296
+ useIsCurrent,
297
+ useIsOpen,
339
298
  loadDescendents,
340
299
  onBack: handleBack,
341
300
  onOpenChange: handleOpenChange,
@@ -358,9 +317,6 @@ export const NavTreeContainer$ = forwardRef<HTMLDivElement, NavTreeContainerProp
358
317
  handleOpenChange,
359
318
  handleSelect,
360
319
  handleTabChange,
361
- isAlternateTree,
362
- isCurrent,
363
- isOpen,
364
320
  loadDescendents,
365
321
  popoverAnchorId,
366
322
  renderItemEnd,
@@ -373,7 +329,7 @@ export const NavTreeContainer$ = forwardRef<HTMLDivElement, NavTreeContainerProp
373
329
 
374
330
  return (
375
331
  <NavTreeContext.Provider value={navTreeContextValue}>
376
- <NavTree id={ROOT_ID} root={graph.root} open={layout.sidebarOpen} ref={forwardedRef} />
332
+ <NavTree id={Node.RootId} root={Graph.getRoot(graph)} open={layout.sidebarOpen} ref={forwardedRef} />
377
333
  </NavTreeContext.Provider>
378
334
  );
379
335
  },
@@ -5,13 +5,14 @@ import { useEffect } from 'react';
5
5
 
6
6
  import { type Node } from '@dxos/app-graph';
7
7
  import { toLocalizedString, useTranslation } from '@dxos/react-ui';
8
+ import { osTranslations } from '@dxos/ui-theme';
8
9
 
9
10
  import { meta } from '../meta';
10
11
 
11
- export const NavTreeDocumentTitle = ({ node }: { node?: Node }) => {
12
+ export const NavTreeDocumentTitle = ({ node }: { node?: Node.Node }) => {
12
13
  const { t } = useTranslation(meta.id);
13
14
  useEffect(() => {
14
- document.title = node ? toLocalizedString(node.properties.label, t) : t('current app name', { ns: 'appkit' });
15
+ document.title = node ? toLocalizedString(node.properties.label, t) : t('current app name', { ns: osTranslations });
15
16
  }, [node?.properties?.label]);
16
17
  return null;
17
18
  };
@@ -3,8 +3,12 @@
3
3
  //
4
4
 
5
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import * as Effect from 'effect/Effect';
6
7
 
7
- import { type NodeArg } from '@dxos/app-graph';
8
+ import { RuntimePlugin } from '@dxos/app-framework';
9
+ import { withPluginManager } from '@dxos/app-framework/testing';
10
+ import { type Node } from '@dxos/app-graph';
11
+ import { corePlugins } from '@dxos/plugin-testing';
8
12
  import { faker } from '@dxos/random';
9
13
  import { withTheme } from '@dxos/react-ui/testing';
10
14
 
@@ -18,7 +22,7 @@ const parent = {
18
22
  label: faker.lorem.words(2),
19
23
  icon: 'ph--circle--regular',
20
24
  },
21
- } satisfies NodeArg<any>;
25
+ } satisfies Node.NodeArg<any>;
22
26
 
23
27
  // TODO(burdon): Factor out across tests.
24
28
  const menuActions = faker.helpers.multiple(
@@ -26,14 +30,15 @@ const menuActions = faker.helpers.multiple(
26
30
  ({
27
31
  id: faker.string.uuid(),
28
32
  type: 'action',
29
- data: () => {
30
- console.log('invoke');
31
- },
33
+ data: () =>
34
+ Effect.sync(() => {
35
+ console.log('invoke');
36
+ }),
32
37
  properties: {
33
38
  label: faker.lorem.words(2),
34
39
  icon: 'ph--circle--regular',
35
40
  },
36
- }) satisfies NodeArg<any>,
41
+ }) satisfies Node.NodeArg<any>,
37
42
  { count: 20 },
38
43
  );
39
44
 
@@ -46,7 +51,12 @@ const meta = {
46
51
  menuActions,
47
52
  label: 'Select action',
48
53
  } satisfies Partial<NavTreeItemActionMenuProps>,
49
- decorators: [withTheme],
54
+ decorators: [
55
+ withTheme(),
56
+ withPluginManager({
57
+ plugins: [...corePlugins(), RuntimePlugin()],
58
+ }),
59
+ ],
50
60
  parameters: {
51
61
  layout: 'centered',
52
62
  },
@@ -4,19 +4,20 @@
4
4
 
5
5
  import React from 'react';
6
6
 
7
- import { type Action, type Node } from '@dxos/app-graph';
7
+ import { type Node } from '@dxos/app-graph';
8
+ import { useActionRunner } from '@dxos/plugin-graph';
8
9
  import { IconButton, toLocalizedString, useDensityContext, useTranslation } from '@dxos/react-ui';
9
10
  import { DropdownMenu, type MenuItem, MenuProvider } from '@dxos/react-ui-menu';
10
- import { hoverableControlItem, hoverableOpenControlItem } from '@dxos/react-ui-theme';
11
+ import { hoverableControlItem, hoverableOpenControlItem } from '@dxos/ui-theme';
11
12
 
12
13
  import { meta } from '../../meta';
13
14
  import { type ActionProperties } from '../../types';
14
15
 
15
16
  export type NavTreeItemActionMenuProps = ActionProperties & {
16
- parent: Node;
17
+ parent: Node.Node;
17
18
  caller?: string;
18
19
  monolithic?: boolean;
19
- menuActions?: Action[];
20
+ menuActions?: Node.Action[];
20
21
  };
21
22
 
22
23
  const fallbackIcon = 'ph--placeholder--regular';
@@ -40,8 +41,10 @@ export const NavTreeItemActionDropdownMenu = ({
40
41
  }: NavTreeItemActionMenuProps) => {
41
42
  const { t } = useTranslation(meta.id);
42
43
  const density = useDensityContext();
44
+ const runAction = useActionRunner();
45
+
43
46
  return (
44
- <MenuProvider>
47
+ <MenuProvider onAction={runAction}>
45
48
  <DropdownMenu.Root group={parent} items={menuActions as MenuItem[]} caller={caller}>
46
49
  <DropdownMenu.Trigger asChild>
47
50
  <IconButton
@@ -59,13 +62,16 @@ export const NavTreeItemActionDropdownMenu = ({
59
62
  );
60
63
  };
61
64
 
62
- export const NavTreeItemMonolithicAction = ({
63
- parent,
64
- properties: { disabled, caller, testId, icon, variant = 'ghost', iconOnly = true } = { label: 'never' },
65
- data: invoke,
66
- baseLabel,
67
- }: Action & { parent: Node; onAction?: (action: Action) => void; baseLabel: string }) => {
65
+ export const NavTreeItemMonolithicAction = (
66
+ props: Node.Action & { parent: Node.Node; onAction?: (action: Node.Action) => void; baseLabel: string },
67
+ ) => {
68
+ const {
69
+ parent,
70
+ properties: { disabled, caller, testId, icon, variant = 'ghost', iconOnly = true } = { label: 'never' },
71
+ baseLabel,
72
+ } = props;
68
73
  const density = useDensityContext();
74
+ const runAction = useActionRunner();
69
75
  return (
70
76
  <IconButton
71
77
  {...(density === 'coarse' ? coarseActionButtonProps : fineActionButtonProps)}
@@ -86,7 +92,7 @@ export const NavTreeItemMonolithicAction = ({
86
92
  return;
87
93
  }
88
94
 
89
- void invoke?.(caller ? { parent, caller } : { parent });
95
+ void runAction(props, caller ? { parent, caller } : { parent });
90
96
  }}
91
97
  data-testid={testId}
92
98
  />
@@ -4,7 +4,7 @@
4
4
 
5
5
  import React, { Fragment, memo } from 'react';
6
6
 
7
- import { isAction } from '@dxos/app-graph';
7
+ import { Node } from '@dxos/app-graph';
8
8
  import { Popover, Treegrid, toLocalizedString, useTranslation } from '@dxos/react-ui';
9
9
 
10
10
  import { useLoadDescendents } from '../../hooks';
@@ -26,11 +26,11 @@ export const NavTreeItemColumns = memo(({ path, item, open }: NavTreeItemColumns
26
26
  );
27
27
 
28
28
  const actions = (primaryAction?.properties?.disposition === 'list-item-primary' ? secondaryActions : _actions)
29
- .flatMap((action) => (isAction(action) ? [action] : []))
29
+ .flatMap((action) => (Node.isAction(action) ? [action] : []))
30
30
  .filter((a) => ['list-item', 'list-item-primary'].includes(a.properties?.disposition));
31
31
 
32
32
  useLoadDescendents(item);
33
- useLoadDescendents(primaryAction && !isAction(primaryAction) ? primaryAction : undefined);
33
+ useLoadDescendents(primaryAction && !Node.isAction(primaryAction) ? (primaryAction as Node.Node) : undefined);
34
34
 
35
35
  const ActionRoot = popoverAnchorId === `dxos.org/ui/${NAV_TREE_ITEM}/${item.id}` ? Popover.Anchor : Fragment;
36
36
 
@@ -43,8 +43,8 @@ export const NavTreeItemColumns = memo(({ path, item, open }: NavTreeItemColumns
43
43
  label={toLocalizedString(primaryAction.properties?.label, t)}
44
44
  icon={primaryAction.properties?.icon ?? 'ph--placeholder--regular'}
45
45
  parent={item}
46
- monolithic={isAction(primaryAction)}
47
- menuActions={isAction(primaryAction) ? [primaryAction] : groupedActions[primaryAction.id]}
46
+ monolithic={Node.isAction(primaryAction)}
47
+ menuActions={Node.isAction(primaryAction) ? [primaryAction] : groupedActions[primaryAction?.id ?? '']}
48
48
  menuType={primaryAction.properties?.menuType}
49
49
  caller={NAV_TREE_ITEM}
50
50
  />