@dxos/plugin-debug 0.7.2 → 0.7.3-main.2dd075e
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/browser/{DebugSpace-ZLGGNDAR.mjs → DebugSpace-DHKEAMIC.mjs} +3 -3
- package/dist/lib/browser/{DebugSpace-ZLGGNDAR.mjs.map → DebugSpace-DHKEAMIC.mjs.map} +2 -2
- package/dist/lib/browser/SpaceGenerator-P7ZPQ4N7.mjs +429 -0
- package/dist/lib/browser/SpaceGenerator-P7ZPQ4N7.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +35 -34
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/DebugPlugin.d.ts.map +1 -1
- package/dist/types/src/components/DebugObjectPanel.d.ts.map +1 -1
- package/dist/types/src/components/DebugSpace/DebugSpace.d.ts +1 -1
- package/dist/types/src/components/{SurfaceDebug.d.ts → DebugSurface.d.ts} +2 -2
- package/dist/types/src/components/{SurfaceDebug.d.ts.map → DebugSurface.d.ts.map} +1 -1
- package/dist/types/src/components/SpaceGenerator/ObjectGenerator.d.ts +7 -0
- package/dist/types/src/components/SpaceGenerator/ObjectGenerator.d.ts.map +1 -0
- package/dist/types/src/components/SpaceGenerator/SchemaTable.d.ts +9 -0
- package/dist/types/src/components/SpaceGenerator/SchemaTable.d.ts.map +1 -0
- package/dist/types/src/components/SpaceGenerator/SpaceGenerator.d.ts +9 -0
- package/dist/types/src/components/SpaceGenerator/SpaceGenerator.d.ts.map +1 -0
- package/dist/types/src/components/SpaceGenerator/SpaceGenerator.stories.d.ts +6 -0
- package/dist/types/src/components/SpaceGenerator/SpaceGenerator.stories.d.ts.map +1 -0
- package/dist/types/src/components/SpaceGenerator/draw-util.d.ts +8 -0
- package/dist/types/src/components/SpaceGenerator/draw-util.d.ts.map +1 -0
- package/dist/types/src/components/SpaceGenerator/index.d.ts +3 -0
- package/dist/types/src/components/SpaceGenerator/index.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +2 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/package.json +45 -38
- package/src/DebugPlugin.tsx +58 -39
- package/src/components/DebugObjectPanel.tsx +2 -1
- package/src/components/DebugSpace/DebugSpace.tsx +2 -2
- package/src/components/DebugSpace/ObjectCreator.tsx +1 -1
- package/src/components/{SurfaceDebug.tsx → DebugSurface.tsx} +1 -1
- package/src/components/SpaceGenerator/ObjectGenerator.tsx +178 -0
- package/src/components/SpaceGenerator/SchemaTable.tsx +38 -0
- package/src/components/SpaceGenerator/SpaceGenerator.stories.tsx +37 -0
- package/src/components/SpaceGenerator/SpaceGenerator.tsx +113 -0
- package/src/components/SpaceGenerator/draw-util.ts +124 -0
- package/src/components/SpaceGenerator/index.ts +7 -0
- package/src/components/index.ts +1 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { IconButton } from '@dxos/react-ui';
|
|
8
|
+
|
|
9
|
+
export type SchemaTableProps = {
|
|
10
|
+
types: any[];
|
|
11
|
+
objects?: Record<string, number | undefined>;
|
|
12
|
+
label: string;
|
|
13
|
+
onClick: (typename: string) => void;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const SchemaTable = ({ types, objects = {}, label, onClick }: SchemaTableProps) => {
|
|
17
|
+
return (
|
|
18
|
+
<div className='grid grid-cols-[1fr_80px_40px] gap-1 overflow-hidden'>
|
|
19
|
+
<div className='grid grid-cols-subgrid col-span-3'>
|
|
20
|
+
<div className='px-2 text-sm text-primary-500'>{label}</div>
|
|
21
|
+
<div className='px-2 text-xs text-subdued text-right'>count</div>
|
|
22
|
+
</div>
|
|
23
|
+
{types.map((type) => (
|
|
24
|
+
<div key={type.typename} className='grid grid-cols-subgrid col-span-3 items-center'>
|
|
25
|
+
<div className='px-2 text-sm font-mono text-green-500'>{type.typename}</div>
|
|
26
|
+
<div className='px-2 text-right font-mono'>{objects[type.typename] ?? 0}</div>
|
|
27
|
+
<IconButton
|
|
28
|
+
variant='ghost'
|
|
29
|
+
icon='ph--plus--regular'
|
|
30
|
+
iconOnly
|
|
31
|
+
label='Create data'
|
|
32
|
+
onClick={() => onClick(type.typename)}
|
|
33
|
+
/>
|
|
34
|
+
</div>
|
|
35
|
+
))}
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import '@dxos-theme';
|
|
6
|
+
|
|
7
|
+
import { type Meta } from '@storybook/react';
|
|
8
|
+
import React from 'react';
|
|
9
|
+
|
|
10
|
+
import { useSpaces } from '@dxos/react-client/echo';
|
|
11
|
+
import { withClientProvider } from '@dxos/react-client/testing';
|
|
12
|
+
import { render, withLayout, withTheme } from '@dxos/storybook-utils';
|
|
13
|
+
|
|
14
|
+
import { SpaceGenerator } from './SpaceGenerator';
|
|
15
|
+
|
|
16
|
+
const DefaultStory = () => {
|
|
17
|
+
const [space] = useSpaces();
|
|
18
|
+
if (!space) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return <SpaceGenerator space={space} />;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const meta: Meta = {
|
|
26
|
+
title: 'plugins/plugin-debug/SpaceGenerator',
|
|
27
|
+
component: SpaceGenerator,
|
|
28
|
+
render: render(DefaultStory),
|
|
29
|
+
decorators: [withClientProvider({ createSpace: true }), withLayout({ tooltips: true }), withTheme],
|
|
30
|
+
parameters: {
|
|
31
|
+
layout: 'fullscreen',
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default meta;
|
|
36
|
+
|
|
37
|
+
export const Default = {};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useCallback, useMemo, useState } from 'react';
|
|
6
|
+
|
|
7
|
+
import { type ReactiveObject } from '@dxos/live-object';
|
|
8
|
+
import { DocumentType } from '@dxos/plugin-markdown/types';
|
|
9
|
+
import { SheetType } from '@dxos/plugin-sheet/types';
|
|
10
|
+
import { DiagramType } from '@dxos/plugin-sketch/types';
|
|
11
|
+
import { useClient } from '@dxos/react-client';
|
|
12
|
+
import { getTypename, type Space } from '@dxos/react-client/echo';
|
|
13
|
+
import { IconButton, Input, Toolbar, useAsyncEffect } from '@dxos/react-ui';
|
|
14
|
+
import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
15
|
+
import { Testing } from '@dxos/schema/testing';
|
|
16
|
+
import { jsonKeyReplacer, sortKeys } from '@dxos/util';
|
|
17
|
+
|
|
18
|
+
import { type ObjectGenerator, createGenerator, staticGenerators } from './ObjectGenerator';
|
|
19
|
+
import { SchemaTable } from './SchemaTable';
|
|
20
|
+
|
|
21
|
+
export type SpaceGeneratorProps = {
|
|
22
|
+
space: Space;
|
|
23
|
+
onCreateObjects?: (objects: ReactiveObject<any>[]) => void;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const SpaceGenerator = ({ space, onCreateObjects }: SpaceGeneratorProps) => {
|
|
27
|
+
const client = useClient();
|
|
28
|
+
const staticTypes = [DocumentType, DiagramType, SheetType]; // TODO(burdon): Make extensible.
|
|
29
|
+
const mutableTypes = [Testing.OrgType, Testing.ProjectType, Testing.ContactType];
|
|
30
|
+
const [count, setCount] = useState(1);
|
|
31
|
+
const [info, setInfo] = useState<any>({});
|
|
32
|
+
|
|
33
|
+
// Create type generators.
|
|
34
|
+
const typeMap = useMemo(() => {
|
|
35
|
+
client.addTypes(staticTypes);
|
|
36
|
+
const mutableGenerators = new Map<string, ObjectGenerator<any>>(
|
|
37
|
+
mutableTypes.map((type) => [type.typename, createGenerator(type)]),
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
return new Map([...staticGenerators, ...mutableGenerators]);
|
|
41
|
+
}, [client, mutableTypes]);
|
|
42
|
+
|
|
43
|
+
// Query space to get info.
|
|
44
|
+
const updateInfo = async () => {
|
|
45
|
+
// Create schema map.
|
|
46
|
+
const mutableSchema = await space.db.schemaRegistry.query();
|
|
47
|
+
const staticSchema = space.db.graph.schemaRegistry.schemas;
|
|
48
|
+
|
|
49
|
+
// Create object map.
|
|
50
|
+
const { objects } = await space.db.query().run();
|
|
51
|
+
const objectMap = sortKeys(
|
|
52
|
+
objects.reduce<Record<string, number>>((map, obj) => {
|
|
53
|
+
const type = getTypename(obj);
|
|
54
|
+
if (type) {
|
|
55
|
+
const count = map[type] ?? 0;
|
|
56
|
+
map[type] = count + 1;
|
|
57
|
+
}
|
|
58
|
+
return map;
|
|
59
|
+
}, {}),
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
setInfo({
|
|
63
|
+
schema: {
|
|
64
|
+
static: staticSchema.length,
|
|
65
|
+
mutable: mutableSchema.length,
|
|
66
|
+
},
|
|
67
|
+
objects: objectMap,
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
useAsyncEffect(updateInfo, [space]);
|
|
72
|
+
|
|
73
|
+
const handleCreateData = useCallback(
|
|
74
|
+
async (typename: string) => {
|
|
75
|
+
const constructor = typeMap.get(typename);
|
|
76
|
+
if (constructor) {
|
|
77
|
+
// TODO(burdon): Input to specify number of objects.
|
|
78
|
+
await constructor(space, count, onCreateObjects);
|
|
79
|
+
await updateInfo();
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
[typeMap, count],
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<div role='none' className='flex flex-col divide-y divide-separator'>
|
|
87
|
+
<Toolbar.Root classNames='p-1'>
|
|
88
|
+
<IconButton icon='ph--arrow-clockwise--regular' iconOnly label='Refresh' onClick={updateInfo} />
|
|
89
|
+
<Toolbar.Expander />
|
|
90
|
+
<div className='flex'>
|
|
91
|
+
<Input.Root>
|
|
92
|
+
<Input.TextInput
|
|
93
|
+
type='number'
|
|
94
|
+
min={1}
|
|
95
|
+
max={100}
|
|
96
|
+
placeholder={'Count'}
|
|
97
|
+
classNames='w-[80px]'
|
|
98
|
+
value={count}
|
|
99
|
+
onChange={(ev) => setCount(parseInt(ev.target.value))}
|
|
100
|
+
/>
|
|
101
|
+
</Input.Root>
|
|
102
|
+
</div>
|
|
103
|
+
</Toolbar.Root>
|
|
104
|
+
|
|
105
|
+
<SchemaTable types={staticTypes} objects={info.objects} label='Static Types' onClick={handleCreateData} />
|
|
106
|
+
<SchemaTable types={mutableTypes} objects={info.objects} label='Mutable Types' onClick={handleCreateData} />
|
|
107
|
+
|
|
108
|
+
<SyntaxHighlighter classNames='flex text-xs' language='json'>
|
|
109
|
+
{JSON.stringify({ space, ...info }, jsonKeyReplacer({ truncate: true }), 2)}
|
|
110
|
+
</SyntaxHighlighter>
|
|
111
|
+
</div>
|
|
112
|
+
);
|
|
113
|
+
};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// TODO(burdon): workerize-loader dep.
|
|
6
|
+
import { Graph, type Node, type PlainObject } from '@antv/graphlib';
|
|
7
|
+
import { D3ForceLayout } from '@antv/layout';
|
|
8
|
+
import { createBindingId, createShapeId, type Editor, type SerializedStore, type TLRecord } from '@tldraw/tldraw';
|
|
9
|
+
|
|
10
|
+
import { invariant } from '@dxos/invariant';
|
|
11
|
+
import { faker } from '@dxos/random';
|
|
12
|
+
import { range } from '@dxos/util';
|
|
13
|
+
|
|
14
|
+
// TODO(burdon): Graph layout:
|
|
15
|
+
// - https://www.npmjs.com/package/@antv/layout (uses d3)
|
|
16
|
+
// - https://observablehq.com/d/2db6b0cc5e97d8d6
|
|
17
|
+
// - https://github.com/antvis/graphlib
|
|
18
|
+
// - https://www.npmjs.com/package/@dagrejs/dagre
|
|
19
|
+
// - https://github.com/dagrejs/dagre/wiki
|
|
20
|
+
// - https://www.npmjs.com/package/elkjs
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* https://github.com/antvis/graphlib/blob/master/docs/classes/Graph.md
|
|
24
|
+
*/
|
|
25
|
+
// TODO(burdon): Factor out.
|
|
26
|
+
// TODO(burdon): Map ECHO to Graph.
|
|
27
|
+
export const generateGraph = (): Graph<PlainObject, PlainObject> => {
|
|
28
|
+
const nodes = range(faker.number.int({ min: 8, max: 32 })).map(() => ({
|
|
29
|
+
id: faker.string.uuid(),
|
|
30
|
+
data: {
|
|
31
|
+
label: faker.lorem
|
|
32
|
+
.words(2)
|
|
33
|
+
.split(' ')
|
|
34
|
+
.map((word) => word.charAt(0).toUpperCase())
|
|
35
|
+
.join('-'),
|
|
36
|
+
},
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
const edges = range(faker.number.int({ min: nodes.length, max: nodes.length * 2 })).map(() => {
|
|
40
|
+
invariant(nodes.length >= 2);
|
|
41
|
+
let source: Node<PlainObject>;
|
|
42
|
+
let target: Node<PlainObject>;
|
|
43
|
+
do {
|
|
44
|
+
source = faker.helpers.arrayElement(nodes);
|
|
45
|
+
target = faker.helpers.arrayElement(nodes);
|
|
46
|
+
} while (source.id === target.id);
|
|
47
|
+
|
|
48
|
+
return { id: faker.string.uuid(), source: source.id, target: target.id, data: {} };
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return new Graph<PlainObject, PlainObject>({ nodes, edges });
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const drawGraph = async (
|
|
55
|
+
editor: Editor,
|
|
56
|
+
graph: Graph<PlainObject, PlainObject>,
|
|
57
|
+
): Promise<SerializedStore<TLRecord>> => {
|
|
58
|
+
const grid = 40;
|
|
59
|
+
const nodeSize = 80;
|
|
60
|
+
|
|
61
|
+
const snap = (n: number) => Math.round(n / grid) * grid;
|
|
62
|
+
|
|
63
|
+
const layout = new D3ForceLayout({
|
|
64
|
+
center: [0, 0],
|
|
65
|
+
preventOverlap: true,
|
|
66
|
+
collideStrength: 0.5,
|
|
67
|
+
linkDistance: grid * 2,
|
|
68
|
+
nodeSize,
|
|
69
|
+
nodeSpacing: nodeSize,
|
|
70
|
+
});
|
|
71
|
+
const { nodes, edges } = await layout.execute(graph);
|
|
72
|
+
|
|
73
|
+
for (const node of nodes) {
|
|
74
|
+
const id = createShapeId(node.id as string);
|
|
75
|
+
editor.createShape({
|
|
76
|
+
id,
|
|
77
|
+
type: 'geo',
|
|
78
|
+
x: snap(node.data.x),
|
|
79
|
+
y: snap(node.data.y),
|
|
80
|
+
props: {
|
|
81
|
+
w: nodeSize,
|
|
82
|
+
h: nodeSize,
|
|
83
|
+
text: node.data.label,
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
for (const edge of edges) {
|
|
89
|
+
const arrowId = createShapeId(edge.id as string);
|
|
90
|
+
editor.createShape({ id: arrowId, type: 'arrow' });
|
|
91
|
+
|
|
92
|
+
editor.createBinding({
|
|
93
|
+
id: createBindingId(),
|
|
94
|
+
type: 'arrow',
|
|
95
|
+
fromId: arrowId,
|
|
96
|
+
toId: createShapeId(edge.source as string),
|
|
97
|
+
props: {
|
|
98
|
+
terminal: 'start',
|
|
99
|
+
isExact: false,
|
|
100
|
+
isPrecise: false,
|
|
101
|
+
normalizedAnchor: { x: 0.5, y: 0.5 },
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
editor.createBinding({
|
|
106
|
+
id: createBindingId(),
|
|
107
|
+
type: 'arrow',
|
|
108
|
+
fromId: arrowId,
|
|
109
|
+
toId: createShapeId(edge.target as string),
|
|
110
|
+
props: {
|
|
111
|
+
terminal: 'end',
|
|
112
|
+
isExact: false,
|
|
113
|
+
isPrecise: false,
|
|
114
|
+
normalizedAnchor: { x: 0.5, y: 0.5 },
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const data = editor.store.getStoreSnapshot();
|
|
120
|
+
// TODO(burdon): Strip readonly properties (e.g., `meta`). Factor out.
|
|
121
|
+
const content: SerializedStore<TLRecord> = JSON.parse(JSON.stringify(data.store));
|
|
122
|
+
|
|
123
|
+
return content;
|
|
124
|
+
};
|
package/src/components/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { lazy } from 'react';
|
|
|
6
6
|
|
|
7
7
|
export const DebugApp = lazy(() => import('./DebugApp'));
|
|
8
8
|
export const DebugSpace = lazy(() => import('./DebugSpace'));
|
|
9
|
+
export const SpaceGenerator = lazy(() => import('./SpaceGenerator'));
|
|
9
10
|
|
|
10
11
|
export * from './DebugObjectPanel';
|
|
11
12
|
export * from './DebugSettings';
|