@dxos/plugin-debug 0.7.1 → 0.7.2-main.f1adc9f

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 (80) hide show
  1. package/dist/lib/browser/{DebugGlobal-ZTCBF3XR.mjs → DebugApp-HCHR6GKO.mjs} +18 -16
  2. package/dist/lib/browser/DebugApp-HCHR6GKO.mjs.map +7 -0
  3. package/dist/lib/browser/DebugSpace-HK2GQYIB.mjs +276 -0
  4. package/dist/lib/browser/DebugSpace-HK2GQYIB.mjs.map +7 -0
  5. package/dist/lib/browser/SpaceGenerator-TIBUROQA.mjs +155 -0
  6. package/dist/lib/browser/SpaceGenerator-TIBUROQA.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-GSJS3HEM.mjs +15 -0
  8. package/dist/lib/browser/chunk-GSJS3HEM.mjs.map +7 -0
  9. package/dist/lib/browser/index.mjs +39 -41
  10. package/dist/lib/browser/index.mjs.map +3 -3
  11. package/dist/lib/browser/meta.json +1 -1
  12. package/dist/types/src/DebugPlugin.d.ts.map +1 -1
  13. package/dist/types/src/components/Container.d.ts +5 -0
  14. package/dist/types/src/components/Container.d.ts.map +1 -0
  15. package/dist/types/src/components/{DebugGlobal.d.ts → DebugApp/DebugApp.d.ts} +2 -3
  16. package/dist/types/src/components/DebugApp/DebugApp.d.ts.map +1 -0
  17. package/dist/types/src/components/DebugApp/Tree.d.ts.map +1 -0
  18. package/dist/types/src/components/DebugApp/index.d.ts +3 -0
  19. package/dist/types/src/components/DebugApp/index.d.ts.map +1 -0
  20. package/dist/types/src/components/DebugObjectPanel.d.ts +2 -2
  21. package/dist/types/src/components/DebugObjectPanel.d.ts.map +1 -1
  22. package/dist/types/src/components/{DebugSpace.d.ts → DebugSpace/DebugSpace.d.ts} +1 -2
  23. package/dist/types/src/components/DebugSpace/DebugSpace.d.ts.map +1 -0
  24. package/dist/types/src/components/{DebugSpace.stories.d.ts → DebugSpace/DebugSpace.stories.d.ts} +1 -1
  25. package/dist/types/src/components/DebugSpace/DebugSpace.stories.d.ts.map +1 -0
  26. package/dist/types/src/components/{ObjectCreator.d.ts → DebugSpace/ObjectCreator.d.ts} +4 -6
  27. package/dist/types/src/components/DebugSpace/ObjectCreator.d.ts.map +1 -0
  28. package/dist/types/src/components/{ObjectCreator.stories.d.ts → DebugSpace/ObjectCreator.stories.d.ts} +1 -1
  29. package/dist/types/src/components/DebugSpace/ObjectCreator.stories.d.ts.map +1 -0
  30. package/dist/types/src/components/DebugSpace/index.d.ts +3 -0
  31. package/dist/types/src/components/DebugSpace/index.d.ts.map +1 -0
  32. package/dist/types/src/components/{SurfaceDebug.d.ts → DebugSurface.d.ts} +2 -2
  33. package/dist/types/src/components/{SurfaceDebug.d.ts.map → DebugSurface.d.ts.map} +1 -1
  34. package/dist/types/src/components/SpaceGenerator/SpaceGenerator.d.ts +9 -0
  35. package/dist/types/src/components/SpaceGenerator/SpaceGenerator.d.ts.map +1 -0
  36. package/dist/types/src/components/SpaceGenerator/SpaceGenerator.stories.d.ts +6 -0
  37. package/dist/types/src/components/SpaceGenerator/SpaceGenerator.stories.d.ts.map +1 -0
  38. package/dist/types/src/components/SpaceGenerator/index.d.ts +3 -0
  39. package/dist/types/src/components/SpaceGenerator/index.d.ts.map +1 -0
  40. package/dist/types/src/components/index.d.ts +2 -1
  41. package/dist/types/src/components/index.d.ts.map +1 -1
  42. package/package.json +41 -38
  43. package/src/DebugPlugin.tsx +61 -48
  44. package/src/components/Container.tsx +15 -0
  45. package/src/components/{DebugGlobal.tsx → DebugApp/DebugApp.tsx} +9 -11
  46. package/src/components/{Tree.tsx → DebugApp/Tree.tsx} +1 -1
  47. package/src/components/DebugApp/index.ts +7 -0
  48. package/src/components/DebugObjectPanel.tsx +4 -3
  49. package/src/components/{DebugSpace.stories.tsx → DebugSpace/DebugSpace.stories.tsx} +8 -6
  50. package/src/components/{DebugSpace.tsx → DebugSpace/DebugSpace.tsx} +70 -82
  51. package/src/components/{ObjectCreator.stories.tsx → DebugSpace/ObjectCreator.stories.tsx} +14 -11
  52. package/src/components/DebugSpace/ObjectCreator.tsx +100 -0
  53. package/src/components/DebugSpace/index.ts +7 -0
  54. package/src/components/{SurfaceDebug.tsx → DebugSurface.tsx} +1 -1
  55. package/src/components/SpaceGenerator/SpaceGenerator.stories.tsx +37 -0
  56. package/src/components/SpaceGenerator/SpaceGenerator.tsx +169 -0
  57. package/src/components/SpaceGenerator/index.ts +7 -0
  58. package/src/components/index.ts +2 -1
  59. package/dist/lib/browser/DebugGlobal-ZTCBF3XR.mjs.map +0 -7
  60. package/dist/lib/browser/DebugSpace-6TGT3H4I.mjs +0 -411
  61. package/dist/lib/browser/DebugSpace-6TGT3H4I.mjs.map +0 -7
  62. package/dist/lib/browser/chunk-H3BJHVRD.mjs +0 -24
  63. package/dist/lib/browser/chunk-H3BJHVRD.mjs.map +0 -7
  64. package/dist/types/src/components/DebugGlobal.d.ts.map +0 -1
  65. package/dist/types/src/components/DebugPanel.d.ts +0 -5
  66. package/dist/types/src/components/DebugPanel.d.ts.map +0 -1
  67. package/dist/types/src/components/DebugSpace.d.ts.map +0 -1
  68. package/dist/types/src/components/DebugSpace.stories.d.ts.map +0 -1
  69. package/dist/types/src/components/ObjectCreator.d.ts.map +0 -1
  70. package/dist/types/src/components/ObjectCreator.stories.d.ts.map +0 -1
  71. package/dist/types/src/components/Tree.d.ts.map +0 -1
  72. package/dist/types/src/scaffolding/generator.d.ts +0 -11
  73. package/dist/types/src/scaffolding/generator.d.ts.map +0 -1
  74. package/dist/types/src/scaffolding/index.d.ts +0 -2
  75. package/dist/types/src/scaffolding/index.d.ts.map +0 -1
  76. package/src/components/DebugPanel.tsx +0 -29
  77. package/src/components/ObjectCreator.tsx +0 -99
  78. package/src/scaffolding/generator.ts +0 -146
  79. package/src/scaffolding/index.ts +0 -5
  80. /package/dist/types/src/components/{Tree.d.ts → DebugApp/Tree.d.ts} +0 -0
@@ -17,10 +17,19 @@ import { Devtools } from '@dxos/devtools';
17
17
  import { invariant } from '@dxos/invariant';
18
18
  import { type ClientPluginProvides, parseClientPlugin } from '@dxos/plugin-client';
19
19
  import { createExtension, Graph, type Node, toSignal } from '@dxos/plugin-graph';
20
- import { SpaceAction } from '@dxos/plugin-space';
20
+ import { memoizeQuery, SpaceAction } from '@dxos/plugin-space';
21
21
  import { CollectionType } from '@dxos/plugin-space/types';
22
22
  import { type Client } from '@dxos/react-client';
23
- import { create, getTypename, isEchoObject, isSpace, parseId, type Space, SpaceState } from '@dxos/react-client/echo';
23
+ import {
24
+ create,
25
+ getTypename,
26
+ isEchoObject,
27
+ isSpace,
28
+ parseId,
29
+ type ReactiveObject,
30
+ type Space,
31
+ SpaceState,
32
+ } from '@dxos/react-client/echo';
24
33
  import { Main } from '@dxos/react-ui';
25
34
  import {
26
35
  baseSurface,
@@ -29,7 +38,15 @@ import {
29
38
  topbarBlockPaddingStart,
30
39
  } from '@dxos/react-ui-theme';
31
40
 
32
- import { DebugGlobal, DebugObjectPanel, DebugSettings, DebugSpace, DebugStatus, Wireframe } from './components';
41
+ import {
42
+ DebugApp,
43
+ DebugObjectPanel,
44
+ DebugSettings,
45
+ DebugSpace,
46
+ DebugStatus,
47
+ SpaceGenerator,
48
+ Wireframe,
49
+ } from './components';
33
50
  import meta, { DEBUG_PLUGIN } from './meta';
34
51
  import translations from './translations';
35
52
  import {
@@ -157,7 +174,7 @@ export const DebugPlugin = definePlugin<DebugPluginProvides>((context) => {
157
174
  {
158
175
  id: `${space.id}-debug`, // TODO(burdon): Change to slashes consistently.
159
176
  type: 'dxos.org/plugin/debug/space',
160
- data: { space },
177
+ data: { space, type: 'dxos.org/plugin/debug/space' },
161
178
  properties: {
162
179
  label: ['debug label', { ns: DEBUG_PLUGIN }],
163
180
  icon: 'ph--bug--regular',
@@ -181,7 +198,13 @@ export const DebugPlugin = definePlugin<DebugPluginProvides>((context) => {
181
198
 
182
199
  const [subjectId] = id.split('~');
183
200
  const { spaceId, objectId } = parseId(subjectId);
184
- const space = client.spaces.get().find((space) => space.id === spaceId);
201
+ const spaces = toSignal(
202
+ (onChange) => client.spaces.subscribe(() => onChange()).unsubscribe,
203
+ () => client.spaces.get(),
204
+ );
205
+ const space = spaces?.find(
206
+ (space) => space.state.get() === SpaceState.SPACE_READY && space.id === spaceId,
207
+ );
185
208
  if (!objectId) {
186
209
  // TODO(burdon): Ref SPACE_PLUGIN ns.
187
210
  const label = space
@@ -204,18 +227,7 @@ export const DebugPlugin = definePlugin<DebugPluginProvides>((context) => {
204
227
  };
205
228
  }
206
229
 
207
- const object = toSignal(
208
- (onChange) => {
209
- const timeout = setTimeout(async () => {
210
- await space?.db.loadObjectById(objectId);
211
- onChange();
212
- });
213
-
214
- return () => clearTimeout(timeout);
215
- },
216
- () => space?.db.getObjectById(objectId),
217
- subjectId,
218
- );
230
+ const [object] = memoizeQuery(space, { id: objectId });
219
231
  if (!object || !subjectId) {
220
232
  return;
221
233
  }
@@ -277,41 +289,46 @@ export const DebugPlugin = definePlugin<DebugPluginProvides>((context) => {
277
289
  }
278
290
 
279
291
  const primary = data.active ?? data.object;
280
- let component: ReactNode;
292
+ let component: ReactNode = null;
281
293
  if (role === 'main' || role === 'article') {
282
294
  if (primary === 'devtools' && settings.devtools) {
283
295
  component = <Devtools />;
284
296
  } else if (!primary || typeof primary !== 'object' || !settings.debug) {
285
297
  component = null;
286
- } else if ('space' in primary && isSpace(primary.space)) {
287
- component = (
288
- <DebugSpace
289
- space={primary.space}
290
- onAddObjects={(objects) => {
291
- if (!isSpace(primary.space)) {
292
- return;
293
- }
298
+ } else if (
299
+ 'type' in primary &&
300
+ primary.type === 'dxos.org/plugin/debug/space' &&
301
+ 'space' in primary &&
302
+ isSpace(primary.space)
303
+ ) {
304
+ const handleAddObject = (objects: ReactiveObject<any>[]) => {
305
+ if (!isSpace(primary.space)) {
306
+ return;
307
+ }
294
308
 
295
- const collection =
296
- primary.space.state.get() === SpaceState.SPACE_READY &&
297
- primary.space.properties[CollectionType.typename];
298
- if (!(collection instanceof CollectionType)) {
299
- return;
300
- }
309
+ const collection =
310
+ primary.space.state.get() === SpaceState.SPACE_READY &&
311
+ primary.space.properties[CollectionType.typename];
312
+ if (!(collection instanceof CollectionType)) {
313
+ return;
314
+ }
315
+
316
+ void context.resolvePlugin(parseIntentPlugin).provides.intent.dispatch(
317
+ objects.map((object) => ({
318
+ action: SpaceAction.ADD_OBJECT,
319
+ data: { target: collection, object },
320
+ })),
321
+ );
322
+ };
301
323
 
302
- void context.resolvePlugin(parseIntentPlugin).provides.intent.dispatch(
303
- objects.map((object) => ({
304
- action: SpaceAction.ADD_OBJECT,
305
- data: { target: collection, object },
306
- })),
307
- );
308
- }}
309
- />
324
+ const deprecated = false;
325
+ component = deprecated ? (
326
+ <DebugSpace space={primary.space} onAddObjects={handleAddObject} />
327
+ ) : (
328
+ <SpaceGenerator space={primary.space} onAddObjects={handleAddObject} />
310
329
  );
311
330
  } else if ('graph' in primary && primary.graph instanceof Graph) {
312
- component = <DebugGlobal graph={primary.graph} />;
313
- } else {
314
- component = null;
331
+ component = <DebugApp graph={primary.graph} />;
315
332
  }
316
333
  }
317
334
 
@@ -337,11 +354,7 @@ export const DebugPlugin = definePlugin<DebugPluginProvides>((context) => {
337
354
 
338
355
  switch (role) {
339
356
  case 'article':
340
- return (
341
- <div role='none' className='row-span-2 rounded-t-md overflow-x-auto'>
342
- {component}
343
- </div>
344
- );
357
+ return <>{component}</>;
345
358
  case 'main':
346
359
  return (
347
360
  <Main.Content
@@ -0,0 +1,15 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import React, { type PropsWithChildren, type ReactNode } from 'react';
6
+
7
+ // TODO(burdon): Convert to grid.
8
+ export const Container = ({ toolbar, children }: PropsWithChildren<{ toolbar: ReactNode }>) => {
9
+ return (
10
+ <div role='none' className='flex flex-col grow overflow-hidden divide-y divide-separator'>
11
+ {toolbar}
12
+ <div className='flex flex-col grow overflow-auto'>{children}</div>
13
+ </div>
14
+ );
15
+ };
@@ -7,13 +7,13 @@ import React, { type FC, useEffect, useState } from 'react';
7
7
 
8
8
  import { type Graph } from '@dxos/plugin-graph';
9
9
  import { useClient, useConfig } from '@dxos/react-client';
10
- import { Button, ToggleGroup, ToggleGroupItem } from '@dxos/react-ui';
10
+ import { Button, ToggleGroup, ToggleGroupItem, Toolbar } from '@dxos/react-ui';
11
11
  import { getSize, mx } from '@dxos/react-ui-theme';
12
12
 
13
- import { DebugPanel } from './DebugPanel';
14
13
  import { Json, Tree } from './Tree';
14
+ import { Container } from '../Container';
15
15
 
16
- const DebugGlobal: FC<{ graph: Graph }> = ({ graph }) => {
16
+ export const DebugApp: FC<{ graph: Graph }> = ({ graph }) => {
17
17
  const [view, setView] = useState<'config' | 'diagnostics' | 'graph'>('graph');
18
18
  const [data, setData] = useState<any>({});
19
19
  const client = useClient();
@@ -44,9 +44,9 @@ const DebugGlobal: FC<{ graph: Graph }> = ({ graph }) => {
44
44
  };
45
45
 
46
46
  return (
47
- <DebugPanel
48
- menu={
49
- <>
47
+ <Container
48
+ toolbar={
49
+ <Toolbar.Root classNames='p-1'>
50
50
  <ToggleGroup type='single' value={view}>
51
51
  <ToggleGroupItem value={'graph'} onClick={() => setView('graph')} title={'Plugin graph'}>
52
52
  <GraphIcon className={getSize(5)} />
@@ -59,21 +59,19 @@ const DebugGlobal: FC<{ graph: Graph }> = ({ graph }) => {
59
59
  </ToggleGroupItem>
60
60
  </ToggleGroup>
61
61
 
62
- <div className='grow' />
62
+ <Toolbar.Expander />
63
63
  <Button onClick={(event) => handleResetClient(event.shiftKey)} title='Reset client'>
64
64
  <Warning className={mx(getSize(5), 'text-red-700')} />
65
65
  </Button>
66
66
  <Button onClick={handleOpenDevtools} title='Open Devtools'>
67
67
  <Toolbox weight='duotone' className={mx(getSize(5), 'text-700')} />
68
68
  </Button>
69
- </>
69
+ </Toolbar.Root>
70
70
  }
71
71
  >
72
72
  {view === 'graph' && <Tree data={graph.toJSON()} />}
73
73
  {view === 'config' && <Json data={data.diagnostics?.config} />}
74
74
  {view === 'diagnostics' && <Json data={data} />}
75
- </DebugPanel>
75
+ </Container>
76
76
  );
77
77
  };
78
-
79
- export default DebugGlobal;
@@ -34,7 +34,7 @@ export const Json: FC<{ data?: object }> = ({ data }) => {
34
34
 
35
35
  export const Tree: FC<{ data?: object }> = ({ data }) => {
36
36
  return (
37
- <div className='flex overflow-auto ml-2 border-l-2 border-blue-500'>
37
+ <div className='p-2'>
38
38
  <Node data={data} root />
39
39
  </div>
40
40
  );
@@ -0,0 +1,7 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { DebugApp } from './DebugApp';
6
+
7
+ export default DebugApp;
@@ -4,16 +4,17 @@
4
4
 
5
5
  import React from 'react';
6
6
 
7
- import { type EchoReactiveObject } from '@dxos/client/echo';
7
+ import { type ReactiveEchoObject } from '@dxos/client/echo';
8
8
  import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
9
9
 
10
10
  export type DebugObjectPanelProps = {
11
- object: EchoReactiveObject<any>;
11
+ object: ReactiveEchoObject<any>;
12
12
  };
13
13
 
14
+ // TODO(burdon): Get schema and traverse references.
14
15
  export const DebugObjectPanel = ({ object }: DebugObjectPanelProps) => {
15
16
  return (
16
- <div role='form' className='flex flex-col'>
17
+ <div className='flex flex-col'>
17
18
  <SyntaxHighlighter classNames='flex text-xs' language='json'>
18
19
  {JSON.stringify(object, null, 2)}
19
20
  </SyntaxHighlighter>
@@ -9,11 +9,12 @@ import React, { useEffect } from 'react';
9
9
 
10
10
  import { createSpaceObjectGenerator } from '@dxos/echo-generator';
11
11
  import { useSpaces } from '@dxos/react-client/echo';
12
- import { ClientRepeater } from '@dxos/react-client/testing';
12
+ import { withClientProvider } from '@dxos/react-client/testing';
13
+ import { render, withLayout, withTheme } from '@dxos/storybook-utils';
13
14
 
14
- import DebugSpace from './DebugSpace';
15
+ import { DebugSpace } from './DebugSpace';
15
16
 
16
- const Story = () => {
17
+ const DefaultStory = () => {
17
18
  const [space] = useSpaces();
18
19
  useEffect(() => {
19
20
  if (space) {
@@ -29,15 +30,16 @@ const Story = () => {
29
30
  return <DebugSpace space={space} />;
30
31
  };
31
32
 
32
- export const Default = {};
33
-
34
33
  const meta: Meta = {
35
34
  title: 'plugins/plugin-debug/DebugSpace',
36
35
  component: DebugSpace,
37
- render: () => <ClientRepeater component={Story} createSpace />,
36
+ render: render(DefaultStory),
37
+ decorators: [withClientProvider({ createSpace: true }), withLayout({ tooltips: true }), withTheme],
38
38
  parameters: {
39
39
  layout: 'fullscreen',
40
40
  },
41
41
  };
42
42
 
43
43
  export default meta;
44
+
45
+ export const Default = {};
@@ -2,19 +2,9 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import {
6
- ArrowClockwise,
7
- DownloadSimple,
8
- Flag,
9
- FlagPennant,
10
- HandPalm,
11
- Play,
12
- PlusMinus,
13
- Timer,
14
- UserCirclePlus,
15
- } from '@phosphor-icons/react';
16
5
  import React, { type FC, useContext, useMemo, useState } from 'react';
17
6
 
7
+ import { createSpaceObjectGenerator } from '@dxos/echo-generator';
18
8
  import { type ReactiveObject } from '@dxos/echo-schema';
19
9
  import { DocumentType } from '@dxos/plugin-markdown/types';
20
10
  import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
@@ -23,26 +13,34 @@ import { useClient } from '@dxos/react-client';
23
13
  import { Filter, type Space, useSpaceInvitation } from '@dxos/react-client/echo';
24
14
  import { InvitationEncoder } from '@dxos/react-client/invitations';
25
15
  import { useAsyncEffect } from '@dxos/react-hooks';
26
- import { Button, Input, useFileDownload } from '@dxos/react-ui';
27
- import { getSize, mx } from '@dxos/react-ui-theme';
16
+ import { Icon, IconButton, Input, type IconProps, type TextInputProps, Toolbar, useFileDownload } from '@dxos/react-ui';
28
17
  import { safeParseInt } from '@dxos/util';
29
18
 
30
- import { DebugPanel } from './DebugPanel';
31
19
  import { ObjectCreator } from './ObjectCreator';
32
- import { createSpaceObjectGenerator } from '../scaffolding';
33
- import { DebugContext } from '../types';
20
+ import { DebugContext } from '../../types';
21
+ import { Container } from '../Container';
34
22
 
35
23
  const DEFAULT_COUNT = 100;
36
24
  const DEFAULT_PERIOD = 500;
37
25
  const DEFAULT_JITTER = 50;
38
26
 
39
- // TODO(burdon): Factor out.
40
27
  const useRefresh = (): [any, () => void] => {
41
28
  const [update, setUpdate] = useState({});
42
29
  return [update, () => setUpdate({})];
43
30
  };
44
31
 
45
- const DebugSpace: FC<{
32
+ const CustomInput = ({ icon, ...props }: Pick<IconProps, 'icon'> & TextInputProps) => {
33
+ return (
34
+ <div role='none' className='relative'>
35
+ <Input.Root>
36
+ <Input.TextInput classNames='w-[100px] text-right pie-[22px]' size={5} {...props} />
37
+ </Input.Root>
38
+ <Icon icon={icon} size={3} classNames='absolute inline-end-1 block-start-1 mt-[6px]' />
39
+ </div>
40
+ );
41
+ };
42
+
43
+ export const DebugSpace: FC<{
46
44
  space: Space;
47
45
  onAddObjects?: (objects: ReactiveObject<any>[]) => void;
48
46
  }> = ({ space, onAddObjects }) => {
@@ -66,7 +64,7 @@ const DebugSpace: FC<{
66
64
  );
67
65
 
68
66
  const download = useFileDownload();
69
- const handleCopy = async () => {
67
+ const handleDownload = async () => {
70
68
  download(
71
69
  new Blob([JSON.stringify(data, undefined, 2)], { type: 'text/plain' }),
72
70
  `${new Date().toISOString().replace(/\W/g, '-')}.json`,
@@ -125,71 +123,61 @@ const DebugSpace: FC<{
125
123
  };
126
124
 
127
125
  return (
128
- <DebugPanel
129
- menu={
130
- <>
131
- <div className='relative' title='mutation count'>
132
- <Input.Root>
133
- <Input.TextInput
134
- autoComplete='off'
135
- size={5}
136
- classNames='w-[100px] text-right pie-[22px]'
137
- placeholder='Count'
138
- value={mutationCount}
139
- onChange={({ target: { value } }) => setMutationCount(value)}
140
- />
141
- </Input.Root>
142
- <Flag className={mx('absolute inline-end-1 block-start-1 mt-[6px]', getSize(3))} />
143
- </div>
144
- <div className='relative' title='mutation period'>
145
- <Input.Root>
146
- <Input.TextInput
147
- autoComplete='off'
148
- size={5}
149
- classNames='w-[100px] text-right pie-[22px]'
150
- placeholder='Interval'
151
- value={mutationInterval}
152
- onChange={({ target: { value } }) => setMutationInterval(value)}
153
- />
154
- </Input.Root>
155
- <Timer className={mx('absolute inline-end-1 block-start-1 mt-[6px]', getSize(3))} />
156
- </div>
157
- <div className='relative' title='mutation jitter'>
158
- <Input.Root>
159
- <Input.TextInput
160
- autoComplete='off'
161
- size={5}
162
- classNames='w-[100px] text-right pie-[22px]'
163
- placeholder='Jitter'
164
- value={mutationJitter}
165
- onChange={({ target: { value } }) => setMutationJitter(value)}
166
- />
167
- </Input.Root>
168
- <PlusMinus className={mx('absolute inline-end-1 block-start-1 mt-[6px]', getSize(3))} />
169
- </div>
170
- <Button onClick={handleToggleRunning}>
171
- {running ? <HandPalm className={getSize(5)} /> : <Play className={getSize(5)} />}
172
- </Button>
173
- <Button onClick={handleUpdate}>
174
- <ArrowClockwise className={getSize(5)} />
175
- </Button>
176
- <Button onClick={handleCopy}>
177
- <DownloadSimple className={getSize(5)} />
178
- </Button>
179
-
180
- <div className='grow' />
181
- <Button onClick={handleCreateEpoch} title='Create epoch'>
182
- <FlagPennant className={mx(getSize(5))} />
183
- </Button>
184
- <Button onClick={handleCreateInvitation} title='Create Space invitation'>
185
- <UserCirclePlus className={mx(getSize(5), 'text-blue-500')} />
186
- </Button>
187
- </>
126
+ <Container
127
+ toolbar={
128
+ <Toolbar.Root classNames='p-1'>
129
+ <CustomInput
130
+ icon='ph--flag--regular'
131
+ autoComplete='off'
132
+ placeholder='Count'
133
+ value={mutationCount}
134
+ onChange={({ target: { value } }) => setMutationCount(value)}
135
+ />
136
+ <CustomInput
137
+ icon='ph--timer--regular'
138
+ autoComplete='off'
139
+ placeholder='Interval'
140
+ value={mutationInterval}
141
+ onChange={({ target: { value } }) => setMutationInterval(value)}
142
+ />
143
+ <CustomInput
144
+ icon='ph--plus-minus--regular'
145
+ autoComplete='off'
146
+ placeholder='Jitter'
147
+ value={mutationJitter}
148
+ onChange={({ target: { value } }) => setMutationJitter(value)}
149
+ />
150
+
151
+ <IconButton
152
+ icon={running ? 'ph--pause-circle--regular' : 'ph--play-circle--regular'}
153
+ iconOnly
154
+ label='Start/stop'
155
+ size={5}
156
+ onClick={handleToggleRunning}
157
+ />
158
+ <IconButton icon='ph--arrow-clockwise--regular' iconOnly label='Refresh' size={5} onClick={handleUpdate} />
159
+ <IconButton icon='ph--download-simple--regular' iconOnly label='Download' size={5} onClick={handleDownload} />
160
+
161
+ <Toolbar.Expander />
162
+ <IconButton
163
+ icon='ph--flag-pennant--regular'
164
+ iconOnly
165
+ label='Create epoch'
166
+ size={5}
167
+ onClick={handleCreateEpoch}
168
+ />
169
+ <IconButton
170
+ icon='ph--user-circle-plus--regular'
171
+ iconOnly
172
+ iconClassNames='text-blue-500'
173
+ label='Create Invitation'
174
+ size={5}
175
+ onClick={handleCreateInvitation}
176
+ />
177
+ </Toolbar.Root>
188
178
  }
189
179
  >
190
180
  <ObjectCreator space={space} onAddObjects={onAddObjects} />
191
- </DebugPanel>
181
+ </Container>
192
182
  );
193
183
  };
194
-
195
- export default DebugSpace;
@@ -8,12 +8,14 @@ import { type Meta } from '@storybook/react';
8
8
  import React, { useEffect } from 'react';
9
9
 
10
10
  import { createSpaceObjectGenerator } from '@dxos/echo-generator';
11
- import { type ReactiveObject, useSpaces } from '@dxos/react-client/echo';
12
- import { ClientRepeater } from '@dxos/react-client/testing';
11
+ import { log } from '@dxos/log';
12
+ import { useSpaces } from '@dxos/react-client/echo';
13
+ import { withClientProvider } from '@dxos/react-client/testing';
14
+ import { render, withLayout, withTheme } from '@dxos/storybook-utils';
13
15
 
14
16
  import { ObjectCreator, type ObjectCreatorProps } from './ObjectCreator';
15
17
 
16
- const Story = () => {
18
+ const DefaultStory = () => {
17
19
  const [space] = useSpaces();
18
20
  useEffect(() => {
19
21
  if (space) {
@@ -22,26 +24,27 @@ const Story = () => {
22
24
  }
23
25
  }, [space]);
24
26
 
27
+ const handleCreate: ObjectCreatorProps['onAddObjects'] = (objects) => {
28
+ log.info('created', { objects });
29
+ };
30
+
25
31
  if (!space) {
26
32
  return null;
27
33
  }
28
34
 
29
- const handleCreate: ObjectCreatorProps['onAddObjects'] = (objects: ReactiveObject<any>[]) => {
30
- console.log('Created:', objects);
31
- };
32
-
33
35
  return <ObjectCreator space={space} onAddObjects={handleCreate} />;
34
36
  };
35
37
 
36
- export const Default = {};
37
-
38
38
  const meta: Meta = {
39
- title: 'plugins/plugin-debug/SchemaList',
39
+ title: 'plugins/plugin-debug/ObjectCreator',
40
40
  component: ObjectCreator,
41
- render: () => <ClientRepeater component={Story} createSpace />,
41
+ render: render(DefaultStory),
42
+ decorators: [withClientProvider({ createSpace: true }), withLayout({ tooltips: true }), withTheme],
42
43
  parameters: {
43
44
  layout: 'fullscreen',
44
45
  },
45
46
  };
46
47
 
47
48
  export default meta;
49
+
50
+ export const Default = {};
@@ -0,0 +1,100 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import React, { useMemo, useState } from 'react';
6
+
7
+ import { type MutationsProviderParams, TestSchemaType, createSpaceObjectGenerator } from '@dxos/echo-generator';
8
+ import { type ReactiveEchoObject, type ReactiveObject, type Space } from '@dxos/react-client/echo';
9
+ import { IconButton, Toolbar } from '@dxos/react-ui';
10
+ import { createColumnBuilder, type TableColumnDef, Table } from '@dxos/react-ui-table/deprecated';
11
+
12
+ const BATCH_SIZE = 10;
13
+
14
+ export type CreateObjectsParams = {
15
+ schema: string;
16
+ enabled: boolean;
17
+ objects: number;
18
+ mutations: Pick<MutationsProviderParams, 'count' | 'mutationSize' | 'maxContentLength'>;
19
+ };
20
+
21
+ export type ObjectCreatorProps = {
22
+ space: Space;
23
+ onAddObjects?: (objects: ReactiveObject<any>[]) => void;
24
+ };
25
+
26
+ export const ObjectCreator = ({ space, onAddObjects }: ObjectCreatorProps) => {
27
+ const generator = useMemo(() => createSpaceObjectGenerator(space), [space]);
28
+
29
+ const [objects, setObjects] = useState<CreateObjectsParams[]>(
30
+ Object.values(TestSchemaType).map((schema) => ({
31
+ schema,
32
+ enabled: true,
33
+ objects: 10,
34
+ mutations: {
35
+ count: 10,
36
+ mutationSize: 10,
37
+ maxContentLength: 1000,
38
+ },
39
+ })),
40
+ );
41
+
42
+ const handleCreate = async () => {
43
+ for (const params of objects) {
44
+ if (!params.enabled) {
45
+ continue;
46
+ }
47
+
48
+ let objectsCreated = 0;
49
+ while (objectsCreated < params.objects) {
50
+ const objects = (await generator.createObjects({
51
+ [params.schema]: Math.min(BATCH_SIZE, params.objects - objectsCreated),
52
+ })) as ReactiveEchoObject<any>[];
53
+
54
+ await generator.mutateObjects(objects, params.mutations);
55
+ objectsCreated += objects.length;
56
+ onAddObjects?.(objects);
57
+ }
58
+ }
59
+
60
+ await space.db.flush();
61
+ };
62
+
63
+ const handleUpdate = (row: CreateObjectsParams, key: string, value: any) => {
64
+ const newObjects = [...objects];
65
+ Object.assign(newObjects.find((object) => object.schema === row.schema)!, { [key]: value });
66
+ setObjects(newObjects);
67
+ };
68
+
69
+ const { helper, builder } = createColumnBuilder<CreateObjectsParams>();
70
+ const columns: TableColumnDef<CreateObjectsParams>[] = [
71
+ helper.accessor('enabled', builder.switch({ label: 'Live', onUpdate: handleUpdate })),
72
+ helper.accessor('schema', builder.string({ label: 'Schema', classNames: 'font-mono' })),
73
+ helper.accessor('objects', builder.number({ label: 'Objects', onUpdate: handleUpdate })),
74
+ helper.accessor((obj) => obj.mutations.count, {
75
+ id: 'mutations',
76
+ ...builder.number({ label: 'Mutations', onUpdate: handleUpdate }),
77
+ }),
78
+ helper.accessor((obj) => obj.mutations.mutationSize, {
79
+ id: 'mutationSize',
80
+ ...builder.number({ label: 'Mut. Size', onUpdate: handleUpdate }),
81
+ }),
82
+ helper.accessor((obj) => obj.mutations.maxContentLength, {
83
+ id: 'mutationMaxContentLength',
84
+ ...builder.number({ label: 'Length', onUpdate: handleUpdate }),
85
+ }),
86
+ ];
87
+
88
+ return (
89
+ <>
90
+ <Table.Root>
91
+ <Table.Viewport>
92
+ <Table.Main<CreateObjectsParams> columns={columns} data={objects} />
93
+ </Table.Viewport>
94
+ </Table.Root>
95
+ <Toolbar.Root classNames='p-1'>
96
+ <IconButton icon='ph--plus--regular' label='Create' onClick={handleCreate} />
97
+ </Toolbar.Root>
98
+ </>
99
+ );
100
+ };