@dxos/plugin-graph 0.12.0-next-73daef452f1464b62efdbca75fb0067c1066f264 → 0.14.0
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.
- package/dist/lib/chunk-action.mjs +1 -1
- package/dist/lib/chunk-action.mjs.map +1 -1
- package/dist/lib/chunk-graph.mjs +11 -1
- package/dist/lib/chunk-graph.mjs.map +1 -1
- package/dist/lib/chunk-rolldown-runtime.mjs +15 -1
- package/dist/lib/hooks.mjs +84 -5
- package/dist/lib/hooks.mjs.map +1 -1
- package/dist/lib/index.mjs +3 -3
- package/dist/lib/meta.mjs.map +1 -1
- package/dist/lib/ns/GraphAction.mjs +8 -0
- package/dist/lib/ns/GraphAction.mjs.map +1 -0
- package/dist/lib/ns/Hooks.mjs +10 -0
- package/dist/lib/ns/Hooks.mjs.map +1 -0
- package/dist/lib/plugin.mjs +1 -1
- package/dist/lib/plugin.mjs.map +1 -1
- package/dist/types/src/GraphAction.d.ts +2 -0
- package/dist/types/src/GraphAction.d.ts.map +1 -0
- package/dist/types/src/Hooks.d.ts +2 -0
- package/dist/types/src/Hooks.d.ts.map +1 -0
- package/dist/types/src/hooks/graph-tree-model.test.d.ts +2 -0
- package/dist/types/src/hooks/graph-tree-model.test.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +3 -2
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useActionRunner.d.ts.map +1 -1
- package/dist/types/src/hooks/useGraphTreeModel.d.ts +18 -0
- package/dist/types/src/hooks/useGraphTreeModel.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +3 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/testing.d.ts +1 -1
- package/dist/types/src/testing.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +30 -23
- package/src/GraphAction.ts +7 -0
- package/src/Hooks.ts +7 -0
- package/src/action.ts +1 -1
- package/src/graph.ts +14 -2
- package/src/hooks/graph-tree-model.test.ts +73 -0
- package/src/hooks/index.ts +3 -2
- package/src/hooks/useActionRunner.ts +5 -4
- package/src/hooks/useGraphTreeModel.ts +114 -0
- package/src/index.ts +3 -3
- package/src/meta.ts +1 -1
- package/src/plugin.test.ts +2 -2
- package/src/plugin.ts +1 -1
- package/src/testing.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/plugin-graph",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "DXOS Surface plugin for constructing knowledge graphs",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"license": "FSL-1.1-Apache-2.0",
|
|
12
12
|
"author": "DXOS.org",
|
|
13
|
-
"sideEffects":
|
|
13
|
+
"sideEffects": false,
|
|
14
14
|
"type": "module",
|
|
15
15
|
"imports": {
|
|
16
16
|
"#hooks": {
|
|
@@ -35,20 +35,25 @@
|
|
|
35
35
|
"types": "./dist/types/src/index.d.ts",
|
|
36
36
|
"import": "./dist/lib/index.mjs"
|
|
37
37
|
},
|
|
38
|
-
"./
|
|
39
|
-
"source": "./src/
|
|
40
|
-
"types": "./dist/types/src/
|
|
41
|
-
"import": "./dist/lib/
|
|
42
|
-
},
|
|
43
|
-
"./testing": {
|
|
44
|
-
"source": "./src/testing.ts",
|
|
45
|
-
"types": "./dist/types/src/testing.d.ts",
|
|
46
|
-
"import": "./dist/lib/testing.mjs"
|
|
38
|
+
"./GraphAction": {
|
|
39
|
+
"source": "./src/GraphAction.ts",
|
|
40
|
+
"types": "./dist/types/src/GraphAction.d.ts",
|
|
41
|
+
"import": "./dist/lib/ns/GraphAction.mjs"
|
|
47
42
|
},
|
|
48
43
|
"./GraphPlugin": {
|
|
49
44
|
"source": "./src/GraphPlugin.ts",
|
|
50
45
|
"types": "./dist/types/src/GraphPlugin.d.ts",
|
|
51
46
|
"import": "./dist/lib/GraphPlugin.mjs"
|
|
47
|
+
},
|
|
48
|
+
"./Hooks": {
|
|
49
|
+
"source": "./src/Hooks.ts",
|
|
50
|
+
"types": "./dist/types/src/Hooks.d.ts",
|
|
51
|
+
"import": "./dist/lib/ns/Hooks.mjs"
|
|
52
|
+
},
|
|
53
|
+
"./testing": {
|
|
54
|
+
"source": "./src/testing.ts",
|
|
55
|
+
"types": "./dist/types/src/testing.d.ts",
|
|
56
|
+
"import": "./dist/lib/testing.mjs"
|
|
52
57
|
}
|
|
53
58
|
},
|
|
54
59
|
"types": "dist/types/src/index.d.ts",
|
|
@@ -58,26 +63,28 @@
|
|
|
58
63
|
"src"
|
|
59
64
|
],
|
|
60
65
|
"dependencies": {
|
|
61
|
-
"@dxos/app-framework": "0.
|
|
62
|
-
"@dxos/app-graph": "0.
|
|
63
|
-
"@dxos/app-toolkit": "0.
|
|
64
|
-
"@dxos/compute": "0.
|
|
65
|
-
"@dxos/effect": "0.
|
|
66
|
-
"@dxos/graph": "0.
|
|
67
|
-
"@dxos/
|
|
66
|
+
"@dxos/app-framework": "0.14.0",
|
|
67
|
+
"@dxos/app-graph": "0.14.0",
|
|
68
|
+
"@dxos/app-toolkit": "0.14.0",
|
|
69
|
+
"@dxos/compute": "0.14.0",
|
|
70
|
+
"@dxos/effect": "0.14.0",
|
|
71
|
+
"@dxos/graph": "0.14.0",
|
|
72
|
+
"@dxos/react-ui-list": "0.14.0",
|
|
73
|
+
"@dxos/ui-theme": "0.14.0",
|
|
74
|
+
"@dxos/util": "0.14.0"
|
|
68
75
|
},
|
|
69
76
|
"devDependencies": {
|
|
70
|
-
"@effect/atom-react": "4.0.0
|
|
77
|
+
"@effect/atom-react": "4.0.0",
|
|
71
78
|
"@types/react": "~19.2.17",
|
|
72
79
|
"@types/react-dom": "~19.2.3",
|
|
73
|
-
"effect": "4.0.0
|
|
80
|
+
"effect": "4.0.0",
|
|
74
81
|
"react": "~19.2.7",
|
|
75
82
|
"react-dom": "~19.2.7",
|
|
76
|
-
"vite": "^8.2.
|
|
83
|
+
"vite": "^8.2.2"
|
|
77
84
|
},
|
|
78
85
|
"peerDependencies": {
|
|
79
|
-
"@effect/atom-react": "4.0.0
|
|
80
|
-
"effect": "4.0.0
|
|
86
|
+
"@effect/atom-react": "4.0.0",
|
|
87
|
+
"effect": "4.0.0",
|
|
81
88
|
"react": "~19.2.7",
|
|
82
89
|
"react-dom": "~19.2.7"
|
|
83
90
|
},
|
package/src/Hooks.ts
ADDED
package/src/action.ts
CHANGED
|
@@ -9,7 +9,7 @@ import * as Capability from '@dxos/app-framework/Capability';
|
|
|
9
9
|
import type * as CapabilityManager from '@dxos/app-framework/CapabilityManager';
|
|
10
10
|
import type * as AppGraphNode from '@dxos/app-graph/AppGraphNode';
|
|
11
11
|
import * as Operation from '@dxos/compute/Operation';
|
|
12
|
-
import
|
|
12
|
+
import * as EffectEx from '@dxos/effect/EffectEx';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Run an action with required layers: Operation.Service, Capability.Service, and captured context.
|
package/src/graph.ts
CHANGED
|
@@ -10,11 +10,12 @@ import * as Capability from '@dxos/app-framework/Capability';
|
|
|
10
10
|
import * as AppGraph from '@dxos/app-graph/AppGraph';
|
|
11
11
|
import * as AppGraphBuilder from '@dxos/app-graph/AppGraphBuilder';
|
|
12
12
|
import * as AppCapabilities from '@dxos/app-toolkit/AppCapabilities';
|
|
13
|
+
import * as AppNode from '@dxos/app-toolkit/AppNode';
|
|
13
14
|
import * as UrlPath from '@dxos/app-toolkit/UrlPath';
|
|
14
15
|
import * as GraphNode from '@dxos/graph/GraphNode';
|
|
15
16
|
|
|
16
17
|
// TODO(wittjosiah): Remove or restore graph caching.
|
|
17
|
-
// import { meta } from './meta';
|
|
18
|
+
// import { meta } from './meta.ts';
|
|
18
19
|
|
|
19
20
|
// const KEY = `${meta.id}.app-graph`;
|
|
20
21
|
|
|
@@ -30,7 +31,8 @@ export default Capability.makeModule(
|
|
|
30
31
|
// produces their nodes (see `AppGraphBuilder.UrlGrammar`).
|
|
31
32
|
const builder = AppGraphBuilder.from(/* localStorage.getItem(KEY) ?? */ undefined, registry, {
|
|
32
33
|
anchorKey: UrlPath.WORKSPACE_KEY,
|
|
33
|
-
|
|
34
|
+
linked: { key: UrlPath.COMPANION_KEY, relation: AppNode.companion },
|
|
35
|
+
tailSeparator: UrlPath.TAIL_SEPARATOR,
|
|
34
36
|
});
|
|
35
37
|
// const interval = setInterval(() => {
|
|
36
38
|
// localStorage.setItem(KEY, builder.graph.pickle());
|
|
@@ -56,6 +58,13 @@ export default Capability.makeModule(
|
|
|
56
58
|
{ immediate: true },
|
|
57
59
|
);
|
|
58
60
|
|
|
61
|
+
const retentionAtom = yield* Capability.atom(AppCapabilities.AppGraphRetention);
|
|
62
|
+
const unsubscribeRetention = registry.subscribe(
|
|
63
|
+
retentionAtom,
|
|
64
|
+
(retentions) => AppGraphBuilder.setRetention(builder, retentions),
|
|
65
|
+
{ immediate: true },
|
|
66
|
+
);
|
|
67
|
+
|
|
59
68
|
// await builder.initialize();
|
|
60
69
|
void AppGraph.expandSync(builder.graph, GraphNode.RootId, 'child');
|
|
61
70
|
|
|
@@ -65,6 +74,9 @@ export default Capability.makeModule(
|
|
|
65
74
|
Effect.sync(() => {
|
|
66
75
|
// clearInterval(interval);
|
|
67
76
|
unsubscribe();
|
|
77
|
+
unsubscribeRetention();
|
|
78
|
+
AppGraphBuilder.setRetention(builder, []);
|
|
79
|
+
AppGraphBuilder.destroy(builder);
|
|
68
80
|
}),
|
|
69
81
|
);
|
|
70
82
|
return Capability.contribute(AppCapabilities.AppGraph, builder);
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
import * as Atom from 'effect/reactivity/Atom';
|
|
7
|
+
import { describe, test } from 'vitest';
|
|
8
|
+
|
|
9
|
+
import * as AppGraphBuilder from '@dxos/app-graph/AppGraphBuilder';
|
|
10
|
+
import { setupGraphBuilder } from '@dxos/app-graph/testing';
|
|
11
|
+
import * as EffectEx from '@dxos/effect/EffectEx';
|
|
12
|
+
import * as GraphNode from '@dxos/graph/GraphNode';
|
|
13
|
+
import * as GraphNodeMatcher from '@dxos/graph/GraphNodeMatcher';
|
|
14
|
+
import { Path } from '@dxos/react-ui-list';
|
|
15
|
+
|
|
16
|
+
import { createGraphTreeModel } from './useGraphTreeModel.ts';
|
|
17
|
+
|
|
18
|
+
describe('createGraphTreeModel', () => {
|
|
19
|
+
const setup = async () => {
|
|
20
|
+
const extensions = await EffectEx.runPromise(
|
|
21
|
+
AppGraphBuilder.createExtension({
|
|
22
|
+
id: 'test',
|
|
23
|
+
match: GraphNodeMatcher.whenRoot,
|
|
24
|
+
connector: () =>
|
|
25
|
+
Effect.succeed([
|
|
26
|
+
{ id: 'a', type: 'page', data: 'a', properties: { label: 'A' } },
|
|
27
|
+
{ id: 'hidden', type: 'page', data: 'h', properties: { label: 'H', disposition: 'hidden' } },
|
|
28
|
+
{
|
|
29
|
+
id: 'group',
|
|
30
|
+
type: 'group',
|
|
31
|
+
data: null,
|
|
32
|
+
properties: { label: 'G', disposition: 'group' },
|
|
33
|
+
nodes: [{ id: 'b', type: 'page', data: 'b', properties: { label: 'B' } }],
|
|
34
|
+
},
|
|
35
|
+
]),
|
|
36
|
+
}),
|
|
37
|
+
);
|
|
38
|
+
const context = setupGraphBuilder({ extensions });
|
|
39
|
+
await context.expand(GraphNode.RootId);
|
|
40
|
+
await context.expand(`${GraphNode.RootId}/group`);
|
|
41
|
+
const openPaths = new Set<string>();
|
|
42
|
+
const openAtom = Atom.make(openPaths);
|
|
43
|
+
const model = createGraphTreeModel(context.graph, GraphNode.RootId, {
|
|
44
|
+
itemOpen: (path) => Atom.make((get) => get(openAtom).has(Path.create(...path))),
|
|
45
|
+
itemCurrent: (path) => Atom.make(() => Path.last(Path.create(...path)) === `${GraphNode.RootId}/b`),
|
|
46
|
+
});
|
|
47
|
+
return { ...context, model };
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
test('lists visible children of the root, dropping hidden nodes', async ({ expect }) => {
|
|
51
|
+
const { registry, model } = await setup();
|
|
52
|
+
expect(registry.get(model.childIds())).toEqual([`${GraphNode.RootId}/a`, `${GraphNode.RootId}/group`]);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('maps node properties to row props; groups are disabled, undraggable, not branches', async ({ expect }) => {
|
|
56
|
+
const { registry, model } = await setup();
|
|
57
|
+
const group = registry.get(model.itemProps([GraphNode.RootId, `${GraphNode.RootId}/group`]));
|
|
58
|
+
expect(group.disposition).toBe('group');
|
|
59
|
+
expect(group.disabled).toBe(true);
|
|
60
|
+
expect(group.draggable).toBe(false);
|
|
61
|
+
expect(group.parentOf).toBeUndefined();
|
|
62
|
+
const page = registry.get(model.itemProps([GraphNode.RootId, `${GraphNode.RootId}/a`]));
|
|
63
|
+
expect(page.label).toBe('A');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('current comes from the caller', async ({ expect }) => {
|
|
67
|
+
const { registry, model } = await setup();
|
|
68
|
+
expect(
|
|
69
|
+
registry.get(model.itemCurrent([GraphNode.RootId, `${GraphNode.RootId}/group`, `${GraphNode.RootId}/b`])),
|
|
70
|
+
).toBe(true);
|
|
71
|
+
expect(registry.get(model.itemCurrent([GraphNode.RootId, `${GraphNode.RootId}/a`]))).toBe(false);
|
|
72
|
+
});
|
|
73
|
+
});
|
package/src/hooks/index.ts
CHANGED
|
@@ -5,5 +5,6 @@
|
|
|
5
5
|
// TODO(wittjosiah): Hooks should not be exported from the plugin package at all.
|
|
6
6
|
// Either refactor callers to not need them or factor them out to a shared package.
|
|
7
7
|
|
|
8
|
-
export * from './useActionRunner';
|
|
9
|
-
export * from './
|
|
8
|
+
export * from './useActionRunner.ts';
|
|
9
|
+
export * from './useGraphTreeModel.ts';
|
|
10
|
+
export * from './useNode.ts';
|
|
@@ -4,18 +4,19 @@
|
|
|
4
4
|
|
|
5
5
|
import { useCallback } from 'react';
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
import * as Hooks from '@dxos/app-framework/Hooks';
|
|
8
|
+
import * as PluginManagerProvider from '@dxos/app-framework/PluginManagerProvider';
|
|
8
9
|
import type * as AppGraphNode from '@dxos/app-graph/AppGraphNode';
|
|
9
10
|
|
|
10
|
-
import { runAction } from '../action';
|
|
11
|
+
import { runAction } from '../action.ts';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Hook that returns a function to run action Effects.
|
|
14
15
|
* Provides Operation.Service, PluginContextService, and captured plugin context.
|
|
15
16
|
*/
|
|
16
17
|
export const useActionRunner = () => {
|
|
17
|
-
const invoker = useOperationInvoker();
|
|
18
|
-
const pluginManager = usePluginManager();
|
|
18
|
+
const invoker = Hooks.useOperationInvoker();
|
|
19
|
+
const pluginManager = PluginManagerProvider.usePluginManager();
|
|
19
20
|
|
|
20
21
|
return useCallback(
|
|
21
22
|
(action: AppGraphNode.Action, params: AppGraphNode.InvokeProps = {}) =>
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Option from 'effect/Option';
|
|
6
|
+
import * as Atom from 'effect/reactivity/Atom';
|
|
7
|
+
import { useMemo } from 'react';
|
|
8
|
+
|
|
9
|
+
import type * as AppGraph from '@dxos/app-graph/AppGraph';
|
|
10
|
+
import * as AppGraphNode from '@dxos/app-graph/AppGraphNode';
|
|
11
|
+
import * as Hooks from '@dxos/app-toolkit/Hooks';
|
|
12
|
+
import { Path, type TreeModel } from '@dxos/react-ui-list';
|
|
13
|
+
import { mx } from '@dxos/ui-theme';
|
|
14
|
+
|
|
15
|
+
export type GraphTreeState = {
|
|
16
|
+
/** Open state keyed by the joined path (`Path.create`). */
|
|
17
|
+
itemOpen: (path: string[]) => Atom.Atom<boolean>;
|
|
18
|
+
/** Selected state keyed by the joined path. */
|
|
19
|
+
itemCurrent: (path: string[]) => Atom.Atom<boolean>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** Rows the tree lists: not hidden, and either a plain node or an action the graph marks as an item. */
|
|
23
|
+
const isItem = (node: AppGraphNode.Node): boolean =>
|
|
24
|
+
!AppGraphNode.hasDisposition(node, 'hidden') &&
|
|
25
|
+
(!AppGraphNode.isAction(node) || AppGraphNode.hasDisposition(node, 'item'));
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* A `TreeModel` over the app graph: topology and row props come from the graph, open/current state
|
|
29
|
+
* from the caller, so one mapping serves the navtree (persisted state) and any other tree host.
|
|
30
|
+
*/
|
|
31
|
+
export const createGraphTreeModel = (
|
|
32
|
+
graph: AppGraph.ReadableGraph,
|
|
33
|
+
rootId: string,
|
|
34
|
+
{ itemOpen, itemCurrent }: GraphTreeState,
|
|
35
|
+
): TreeModel<AppGraphNode.Node> => {
|
|
36
|
+
const isVisibleChild = (node: AppGraphNode.Node) => !AppGraphNode.hasDisposition(node, 'hidden');
|
|
37
|
+
|
|
38
|
+
const itemPropsFamily = Atom.family((pathKey: string) => {
|
|
39
|
+
const path = Path.parts(pathKey);
|
|
40
|
+
const id = Path.last(pathKey);
|
|
41
|
+
return Atom.make((get) => {
|
|
42
|
+
const node = Option.getOrUndefined(get(graph.node(id)));
|
|
43
|
+
if (!node) {
|
|
44
|
+
return { id, label: id };
|
|
45
|
+
}
|
|
46
|
+
const safeChildren = get(graph.connections(node.id, 'child')).filter((child) => !path.includes(child.id));
|
|
47
|
+
const visibleChildren = safeChildren.filter(isVisibleChild);
|
|
48
|
+
const parentOf =
|
|
49
|
+
visibleChildren.length > 0
|
|
50
|
+
? visibleChildren.map((child) => child.id)
|
|
51
|
+
: node.properties.role === 'branch'
|
|
52
|
+
? []
|
|
53
|
+
: undefined;
|
|
54
|
+
const parentId = path.length >= 2 ? path[path.length - 2] : undefined;
|
|
55
|
+
const parentNode = parentId ? Option.getOrUndefined(get(graph.node(parentId))) : undefined;
|
|
56
|
+
const droppable =
|
|
57
|
+
node.properties.droppable === false || parentNode?.properties.childrenDroppable === false ? false : undefined;
|
|
58
|
+
const disposition = node.properties.disposition as string | undefined;
|
|
59
|
+
const isGroup = disposition === 'group';
|
|
60
|
+
return {
|
|
61
|
+
id: node.id,
|
|
62
|
+
parentOf: isGroup ? undefined : parentOf,
|
|
63
|
+
disposition,
|
|
64
|
+
disabled: isGroup || node.properties.disabled,
|
|
65
|
+
draggable: isGroup ? false : node.properties.draggable,
|
|
66
|
+
droppable: isGroup ? false : droppable,
|
|
67
|
+
label: node.properties.label ?? node.id,
|
|
68
|
+
className: mx(node.properties.className, node.properties.modified && 'italic'),
|
|
69
|
+
headingClassName: node.properties.headingClassName,
|
|
70
|
+
icon: node.properties.icon,
|
|
71
|
+
iconHue: node.properties.iconHue,
|
|
72
|
+
testId: node.properties.testId,
|
|
73
|
+
count: node.properties.count,
|
|
74
|
+
modifiedCount: node.properties.modifiedCount,
|
|
75
|
+
};
|
|
76
|
+
}).pipe(Atom.keepAlive);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const childIdsFamily = Atom.family((id: string) =>
|
|
80
|
+
Atom.make((get) =>
|
|
81
|
+
get(graph.connections(id, 'child'))
|
|
82
|
+
.filter(isVisibleChild)
|
|
83
|
+
.map((child) => child.id),
|
|
84
|
+
).pipe(Atom.keepAlive),
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const itemFamily = Atom.family((id: string) =>
|
|
88
|
+
Atom.make((get) => {
|
|
89
|
+
const node = Option.getOrUndefined(get(graph.node(id)));
|
|
90
|
+
return node && isItem(node) ? node : undefined;
|
|
91
|
+
}).pipe(Atom.keepAlive),
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const itemOpenFamily = Atom.family((pathKey: string) => itemOpen(Path.parts(pathKey)));
|
|
95
|
+
const itemCurrentFamily = Atom.family((pathKey: string) => itemCurrent(Path.parts(pathKey)));
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
item: (id) => itemFamily(id),
|
|
99
|
+
itemProps: (path) => itemPropsFamily(Path.create(...path)),
|
|
100
|
+
itemOpen: (path) => itemOpenFamily(Path.create(...path)),
|
|
101
|
+
itemCurrent: (path) => itemCurrentFamily(Path.create(...path)),
|
|
102
|
+
childIds: (parentId) => childIdsFamily(parentId ?? rootId),
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/** {@link createGraphTreeModel} over the app graph capability, memoised on its inputs. */
|
|
107
|
+
export const useGraphTreeModel = (rootId: string, options: GraphTreeState): TreeModel<AppGraphNode.Node> => {
|
|
108
|
+
const { graph } = Hooks.useAppGraph();
|
|
109
|
+
const { itemOpen, itemCurrent } = options;
|
|
110
|
+
return useMemo(
|
|
111
|
+
() => createGraphTreeModel(graph, rootId, { itemOpen, itemCurrent }),
|
|
112
|
+
[graph, rootId, itemOpen, itemCurrent],
|
|
113
|
+
);
|
|
114
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
export * as
|
|
6
|
-
export * from './
|
|
7
|
-
export * from '
|
|
5
|
+
export * as GraphAction from './GraphAction.ts';
|
|
6
|
+
export * as GraphPlugin from './GraphPlugin.ts';
|
|
7
|
+
export * as Hooks from './Hooks.ts';
|
package/src/meta.ts
CHANGED
package/src/plugin.test.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { describe, test } from 'vitest';
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
import * as ProcessManagerPlugin from '@dxos/app-framework/ProcessManagerPlugin';
|
|
8
8
|
import { createTestApp } from '@dxos/app-framework/testing';
|
|
9
9
|
|
|
10
10
|
import { meta } from '#meta';
|
|
@@ -16,7 +16,7 @@ describe('GraphPlugin', () => {
|
|
|
16
16
|
test('modules activate on the expected events', async ({ expect }) => {
|
|
17
17
|
// Use createTestApp directly — createComposerTestApp already includes GraphPlugin.
|
|
18
18
|
await using harness = await createTestApp({
|
|
19
|
-
plugins: [GraphPlugin(), ProcessManagerPlugin()],
|
|
19
|
+
plugins: [GraphPlugin(), ProcessManagerPlugin.make()],
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
// Graph activates on Startup; fires SetupAppGraph + SetupMetadata before, AppGraphReady after.
|
package/src/plugin.ts
CHANGED
package/src/testing.ts
CHANGED