@dxos/plugin-debug 0.11.1 → 0.12.0-next-bbabc02363f18d7643ea4160e3a7a7809870fd0d

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 (130) hide show
  1. package/dist/lib/Debug.mjs +22 -0
  2. package/dist/lib/Debug.mjs.map +1 -0
  3. package/dist/lib/DebugEvents.mjs +15 -0
  4. package/dist/lib/DebugEvents.mjs.map +1 -0
  5. package/dist/lib/DebugNodes.mjs +19 -0
  6. package/dist/lib/DebugNodes.mjs.map +1 -0
  7. package/dist/lib/DebugPlugin.mjs +4 -16
  8. package/dist/lib/DebugPlugin.mjs.map +1 -1
  9. package/dist/lib/DebugPlugin.node.mjs +2 -3
  10. package/dist/lib/DebugPlugin.node.mjs.map +1 -1
  11. package/dist/lib/DebugPlugin.workerd.mjs +1 -1
  12. package/dist/lib/DebugPlugin.workerd.mjs.map +1 -1
  13. package/dist/lib/DebugSurface.mjs +15 -0
  14. package/dist/lib/DebugSurface.mjs.map +1 -0
  15. package/dist/lib/Settings.mjs +12 -0
  16. package/dist/lib/Settings.mjs.map +1 -0
  17. package/dist/lib/capabilities.mjs +38 -6
  18. package/dist/lib/capabilities.mjs.map +1 -1
  19. package/dist/lib/chunk-DebugObjectPanel.mjs +1 -1
  20. package/dist/lib/chunk-DebugSettings.mjs +95 -15
  21. package/dist/lib/chunk-DebugSettings.mjs.map +1 -1
  22. package/dist/lib/chunk-DebugSpaceObjectsPanel.mjs +1 -1
  23. package/dist/lib/chunk-SpaceGenerator.mjs +52 -10
  24. package/dist/lib/chunk-SpaceGenerator.mjs.map +1 -1
  25. package/dist/lib/chunk-StatsPanel.mjs +2 -2
  26. package/dist/lib/chunk-StatsPanel.mjs.map +1 -1
  27. package/dist/lib/chunk-Wireframe.mjs +1 -1
  28. package/dist/lib/chunk-app-graph-builder.mjs +13 -10
  29. package/dist/lib/chunk-app-graph-builder.mjs.map +1 -1
  30. package/dist/lib/chunk-log-recording.mjs +3 -2
  31. package/dist/lib/chunk-log-recording.mjs.map +1 -1
  32. package/dist/lib/chunk-meta.mjs +1 -1
  33. package/dist/lib/chunk-meta.mjs.map +1 -1
  34. package/dist/lib/chunk-react-surface.mjs +20 -14
  35. package/dist/lib/chunk-react-surface.mjs.map +1 -1
  36. package/dist/lib/chunk-rolldown-runtime.mjs +13 -0
  37. package/dist/lib/chunk-settings.mjs +7 -6
  38. package/dist/lib/chunk-settings.mjs.map +1 -1
  39. package/dist/lib/chunk-stats-panel.mjs +5 -4
  40. package/dist/lib/chunk-stats-panel.mjs.map +1 -1
  41. package/dist/lib/components.mjs +41 -35
  42. package/dist/lib/components.mjs.map +1 -1
  43. package/dist/lib/index.mjs +6 -2
  44. package/dist/lib/plugin.mjs +1 -1
  45. package/dist/lib/plugin.mjs.map +1 -1
  46. package/dist/lib/translations.mjs +10 -1
  47. package/dist/lib/translations.mjs.map +1 -1
  48. package/dist/types/src/DebugPlugin.d.ts +3 -3
  49. package/dist/types/src/DebugPlugin.d.ts.map +1 -1
  50. package/dist/types/src/DebugPlugin.node.d.ts +3 -3
  51. package/dist/types/src/DebugPlugin.node.d.ts.map +1 -1
  52. package/dist/types/src/DebugPlugin.workerd.d.ts +3 -3
  53. package/dist/types/src/DebugPlugin.workerd.d.ts.map +1 -1
  54. package/dist/types/src/capabilities/DebugSurfaces.d.ts +1 -1
  55. package/dist/types/src/capabilities/DebugSurfaces.d.ts.map +1 -1
  56. package/dist/types/src/capabilities/app-graph-builder.d.ts +2 -2
  57. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  58. package/dist/types/src/capabilities/index.d.ts +7 -10
  59. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  60. package/dist/types/src/capabilities/log-recording.d.ts +1 -2
  61. package/dist/types/src/capabilities/log-recording.d.ts.map +1 -1
  62. package/dist/types/src/capabilities/react-surface.d.ts +2 -2
  63. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  64. package/dist/types/src/capabilities/settings.d.ts +2 -2
  65. package/dist/types/src/capabilities/settings.d.ts.map +1 -1
  66. package/dist/types/src/capabilities/stats-panel.d.ts +2 -2
  67. package/dist/types/src/capabilities/stats-panel.d.ts.map +1 -1
  68. package/dist/types/src/components/SchemaTable/SchemaTable.d.ts.map +1 -1
  69. package/dist/types/src/components/SpaceGenerator/presets.d.ts.map +1 -1
  70. package/dist/types/src/containers/DebugPortSettings/DebugPortSettings.d.ts +13 -0
  71. package/dist/types/src/containers/DebugPortSettings/DebugPortSettings.d.ts.map +1 -0
  72. package/dist/types/src/containers/DebugPortSettings/DebugPortSettings.stories.d.ts +93 -0
  73. package/dist/types/src/containers/DebugPortSettings/DebugPortSettings.stories.d.ts.map +1 -0
  74. package/dist/types/src/containers/DebugPortSettings/index.d.ts +2 -0
  75. package/dist/types/src/containers/DebugPortSettings/index.d.ts.map +1 -0
  76. package/dist/types/src/containers/DebugSettings/DebugSettings.d.ts +2 -2
  77. package/dist/types/src/containers/DebugSettings/DebugSettings.d.ts.map +1 -1
  78. package/dist/types/src/containers/DebugSettings/DebugSettings.stories.d.ts +10 -1
  79. package/dist/types/src/containers/DebugSettings/DebugSettings.stories.d.ts.map +1 -1
  80. package/dist/types/src/containers/SpaceGenerator/SpaceGenerator.d.ts.map +1 -1
  81. package/dist/types/src/containers/SpaceGenerator/SpaceGenerator.stories.d.ts +87 -0
  82. package/dist/types/src/containers/SpaceGenerator/SpaceGenerator.stories.d.ts.map +1 -0
  83. package/dist/types/src/index.d.ts +5 -1
  84. package/dist/types/src/index.d.ts.map +1 -1
  85. package/dist/types/src/plugin.d.ts +3 -3
  86. package/dist/types/src/plugin.d.ts.map +1 -1
  87. package/dist/types/src/translations.d.ts +9 -0
  88. package/dist/types/src/translations.d.ts.map +1 -1
  89. package/dist/types/src/types/{debug.d.ts → Debug.d.ts} +3 -3
  90. package/dist/types/src/types/Debug.d.ts.map +1 -0
  91. package/dist/types/src/types/DebugEvents.d.ts +8 -0
  92. package/dist/types/src/types/DebugEvents.d.ts.map +1 -0
  93. package/dist/types/tsconfig.tsbuildinfo +1 -1
  94. package/package.json +93 -54
  95. package/src/DebugPlugin.node.ts +6 -6
  96. package/src/DebugPlugin.tsx +18 -25
  97. package/src/DebugPlugin.workerd.ts +4 -3
  98. package/src/capabilities/DebugSurfaces.tsx +9 -4
  99. package/src/capabilities/app-graph-builder.ts +8 -4
  100. package/src/capabilities/index.ts +40 -6
  101. package/src/capabilities/log-recording.ts +3 -2
  102. package/src/capabilities/react-surface.ts +10 -8
  103. package/src/capabilities/settings.ts +7 -5
  104. package/src/capabilities/stats-panel.ts +5 -4
  105. package/src/components/SchemaTable/SchemaTable.tsx +19 -13
  106. package/src/components/SpaceGenerator/ObjectGenerator.tsx +5 -5
  107. package/src/components/SpaceGenerator/presets.ts +6 -4
  108. package/src/containers/DebugPortSettings/DebugPortSettings.stories.tsx +144 -0
  109. package/src/containers/DebugPortSettings/DebugPortSettings.tsx +112 -0
  110. package/src/containers/DebugPortSettings/index.ts +5 -0
  111. package/src/containers/DebugSettings/DebugSettings.tsx +11 -6
  112. package/src/containers/SpaceGenerator/SpaceGenerator.stories.tsx +93 -0
  113. package/src/containers/SpaceGenerator/SpaceGenerator.tsx +44 -6
  114. package/src/containers/StatsPanel/StatsPanel.tsx +2 -2
  115. package/src/index.ts +5 -1
  116. package/src/meta.ts +1 -1
  117. package/src/plugin.ts +2 -2
  118. package/src/translations.ts +13 -0
  119. package/src/types/{debug.ts → Debug.ts} +4 -2
  120. package/src/types/DebugEvents.ts +16 -0
  121. package/dist/lib/chunk-types.mjs +0 -60
  122. package/dist/lib/chunk-types.mjs.map +0 -1
  123. package/dist/lib/types.mjs +0 -2
  124. package/dist/types/src/components/SpaceGenerator/draw-util.d.ts +0 -8
  125. package/dist/types/src/components/SpaceGenerator/draw-util.d.ts.map +0 -1
  126. package/dist/types/src/types/debug.d.ts.map +0 -1
  127. package/dist/types/src/types/index.d.ts +0 -5
  128. package/dist/types/src/types/index.d.ts.map +0 -1
  129. package/src/components/SpaceGenerator/draw-util.ts +0 -191
  130. package/src/types/index.ts +0 -8
@@ -0,0 +1,144 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import * as Schema from 'effect/Schema';
7
+ import React, { useEffect, useMemo, useRef } from 'react';
8
+
9
+ import { type CallMetadata, log } from '@dxos/log';
10
+ import {
11
+ type DebugPortController,
12
+ type DebugPortStartOptions,
13
+ type DebugPortStatus,
14
+ } from '@dxos/react-client/devtools';
15
+ import { ViewStateProvider } from '@dxos/react-ui-attention';
16
+ import { Form } from '@dxos/react-ui-form';
17
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
18
+
19
+ import { translations } from '#translations';
20
+
21
+ import { DebugPortSettings } from './DebugPortSettings';
22
+
23
+ const DEBUG_PORT_ORIGIN = 'http://127.0.0.1:9321';
24
+
25
+ /** Matches the path `DebugPortSettings` filters the log panel on. */
26
+ const CONTROLLER_META: CallMetadata = {
27
+ F: 'packages/sdk/client/src/devtools/debug-port-controller.ts',
28
+ L: 1,
29
+ S: undefined,
30
+ };
31
+
32
+ /**
33
+ * Stand-in for the page-wide controller: the real one long-polls a loopback server and evaluates
34
+ * whatever it returns, which a story must never do.
35
+ */
36
+ const createFakeController = (initial: Partial<DebugPortStatus> = {}): DebugPortController => {
37
+ const listeners = new Set<() => void>();
38
+ let status: DebugPortStatus = { running: false, ...initial };
39
+
40
+ const update = (patch: Partial<DebugPortStatus>) => {
41
+ status = { ...status, ...patch };
42
+ listeners.forEach((listener) => listener());
43
+ };
44
+
45
+ // The panel reads its rows from the process-wide log buffer, so the fake emits through the same
46
+ // logger the real controller uses rather than carrying a log of its own. The metadata spoofs the
47
+ // controller's file because the panel filters on it — logging from here would be filtered out.
48
+ const emit = (line: string, session?: string) => log.info(line, { session }, CONTROLLER_META);
49
+
50
+ return {
51
+ getStatus: () => status,
52
+ subscribe: (listener) => {
53
+ listeners.add(listener);
54
+ return () => listeners.delete(listener);
55
+ },
56
+ start: (_options?: DebugPortStartOptions) => {
57
+ const session = crypto.randomUUID();
58
+ update({ running: true, session, origin: DEBUG_PORT_ORIGIN });
59
+ emit(`Session: ${session}`, session);
60
+ emit(`Connecting to ${DEBUG_PORT_ORIGIN}…`, session);
61
+ emit('Waiting for debug server…', session);
62
+ return session;
63
+ },
64
+ // Nothing is persisted in a story, so there is never a session to carry across a reload.
65
+ resume: () => undefined,
66
+ stop: () => {
67
+ emit('Debug port stopped.', status.session);
68
+ update({ running: false });
69
+ },
70
+ };
71
+ };
72
+
73
+ /** `Form.Row` reads the form context for its variant; the section itself binds no fields. */
74
+ const NoFields = Schema.Struct({});
75
+
76
+ type StoryProps = {
77
+ initial?: Partial<DebugPortStatus>;
78
+ /** Emitted into the shared log buffer on mount, standing in for a session already under way. */
79
+ lines?: string[];
80
+ };
81
+
82
+ const DefaultStory = ({ initial, lines = [] }: StoryProps) => {
83
+ const controller = useMemo(() => createFakeController(initial), [initial]);
84
+ // Seed the shared buffer so a story that opens already-running has rows to show. The buffer
85
+ // outlives the component, so a remount (StrictMode, story switch) must not append a second copy.
86
+ const seeded = useRef(false);
87
+ useEffect(() => {
88
+ if (seeded.current) {
89
+ return;
90
+ }
91
+ seeded.current = true;
92
+ lines.forEach((line) => log.info(line, { session: initial?.session }, CONTROLLER_META));
93
+ }, [lines, initial?.session]);
94
+
95
+ return (
96
+ // `Logger.Root` keeps its per-file level overrides in view state.
97
+ <ViewStateProvider>
98
+ <Form.Root schema={NoFields} values={{}} variant='settings'>
99
+ <Form.Viewport scroll>
100
+ <Form.Content>
101
+ <DebugPortSettings controller={controller} />
102
+ </Form.Content>
103
+ </Form.Viewport>
104
+ </Form.Root>
105
+ </ViewStateProvider>
106
+ );
107
+ };
108
+
109
+ const meta = {
110
+ title: 'plugins/plugin-debug/containers/DebugPortSettings',
111
+ component: DefaultStory,
112
+ render: DefaultStory,
113
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
114
+ tags: ['settings'],
115
+ parameters: {
116
+ layout: 'fullscreen',
117
+ translations,
118
+ },
119
+ } satisfies Meta<typeof DefaultStory>;
120
+
121
+ export default meta;
122
+
123
+ type Story = StoryObj<typeof meta>;
124
+
125
+ export const Stopped: Story = {
126
+ args: {},
127
+ };
128
+
129
+ export const Running: Story = {
130
+ args: {
131
+ initial: {
132
+ running: true,
133
+ session: '951c7576-b636-47ff-acc6-a1c4fdf65fb6',
134
+ origin: DEBUG_PORT_ORIGIN,
135
+ },
136
+ lines: [
137
+ 'Session: 951c7576-b636-47ff-acc6-a1c4fdf65fb6',
138
+ `Connecting to ${DEBUG_PORT_ORIGIN}…`,
139
+ 'Command #1:',
140
+ 'return dxos.client.spaces.get().length',
141
+ 'Command #1 ok',
142
+ ],
143
+ },
144
+ };
@@ -0,0 +1,112 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import React, { useCallback, useSyncExternalStore } from 'react';
6
+
7
+ import { type DebugPortController, getDebugPortController } from '@dxos/react-client/devtools';
8
+ import { IconButton, Input, useTranslation } from '@dxos/react-ui';
9
+ import { Logger, type LogRow } from '@dxos/react-ui-debug';
10
+ import { Form } from '@dxos/react-ui-form';
11
+
12
+ import { meta } from '#meta';
13
+
14
+ /**
15
+ * Scopes the panel to the controller's own entries.
16
+ *
17
+ * A display filter, not `initialFilter`: that one narrows what the process-wide buffer captures,
18
+ * which would starve the main log companion reading the same buffer.
19
+ */
20
+ const isDebugPortRow = (row: LogRow): boolean => (row.entry.meta?.F ?? '').includes(CONTROLLER_FILE);
21
+
22
+ const CONTROLLER_FILE = 'devtools/debug-port-controller';
23
+
24
+ export type DebugPortSettingsProps = {
25
+ /** Injectable for stories/tests; defaults to the page-wide controller. */
26
+ controller?: DebugPortController;
27
+ /** The switch starts arbitrary eval, so it follows the form's readonly state like every other control. */
28
+ disabled?: boolean;
29
+ };
30
+
31
+ /**
32
+ * Start/stop the agent debug port and surface the session id an agent needs.
33
+ *
34
+ * The port evaluates agent-supplied code against the live client, so it is off until this switch is
35
+ * flipped, the session id is regenerated on every activation, and nothing survives a reload.
36
+ */
37
+ export const DebugPortSettings = ({ controller = getDebugPortController(), disabled }: DebugPortSettingsProps) => {
38
+ const { t } = useTranslation(meta.profile.key);
39
+ const subscribe = useCallback((listener: () => void) => controller.subscribe(listener), [controller]);
40
+ const getStatus = useCallback(() => controller.getStatus(), [controller]);
41
+ const status = useSyncExternalStore(subscribe, getStatus);
42
+
43
+ // `persist` keeps the session across reloads of this tab: an OAuth redirect or a HMR reload would
44
+ // otherwise strand the agent mid-investigation with a dead session id.
45
+ const handleToggle = useCallback(
46
+ (checked: boolean) => (checked ? controller.start({ persist: true }) : controller.stop()),
47
+ [controller],
48
+ );
49
+
50
+ const handleCopy = useCallback(() => {
51
+ if (status.session) {
52
+ void navigator.clipboard.writeText(status.session);
53
+ }
54
+ }, [status.session]);
55
+
56
+ return (
57
+ <Form.Section
58
+ title={t('settings.debug-port.section.label')}
59
+ description={t('settings.debug-port.section.description')}
60
+ >
61
+ <Form.Row label={t('settings.debug-port.label')} description={t('settings.debug-port.description')}>
62
+ <div className='flex items-center gap-3'>
63
+ {status.running && (
64
+ <span className='text-sm text-description'>
65
+ {t('settings.debug-port.running.label')} <span className='font-mono'>{status.origin}</span>
66
+ </span>
67
+ )}
68
+ <Input.Root>
69
+ <Input.Switch checked={status.running} disabled={disabled} onCheckedChange={handleToggle} />
70
+ </Input.Root>
71
+ </div>
72
+ </Form.Row>
73
+
74
+ {status.running && (
75
+ <>
76
+ <Form.Row
77
+ standalone
78
+ label={t('settings.debug-port.session.label')}
79
+ description={t('settings.debug-port.session.description')}
80
+ >
81
+ <div className='flex items-center gap-2'>
82
+ <span className='grow truncate font-mono text-sm'>{status.session}</span>
83
+ <IconButton
84
+ icon='ph--copy--regular'
85
+ iconOnly
86
+ label={t('settings.debug-port.copy-session.label')}
87
+ onClick={handleCopy}
88
+ />
89
+ </div>
90
+ </Form.Row>
91
+
92
+ {/* The settings variant puts the control in a right-hand column; log rows need the full
93
+ width, so this row collapses to a single column. */}
94
+ <Form.Row
95
+ standalone
96
+ label={t('settings.debug-port.log.label')}
97
+ classNames='md:grid-cols-1 md:[grid-template-areas:"header""description""control""validation"]'
98
+ >
99
+ {/* Only the rows: a settings card has no room for the panel's toolbar, levels or filter. */}
100
+ <Logger.Root rowFilter={isDebugPortRow}>
101
+ <Logger.Content classNames='max-h-[16lh]'>
102
+ <Logger.List />
103
+ </Logger.Content>
104
+ </Logger.Root>
105
+ </Form.Row>
106
+ </>
107
+ )}
108
+ </Form.Section>
109
+ );
110
+ };
111
+
112
+ DebugPortSettings.displayName = 'DebugPortSettings';
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ export * from './DebugPortSettings';
@@ -4,7 +4,8 @@
4
4
 
5
5
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
6
6
 
7
- import { AppCapabilities, AppSpace } from '@dxos/app-toolkit';
7
+ import * as AppCapabilities from '@dxos/app-toolkit/AppCapabilities';
8
+ import * as AppSpace from '@dxos/app-toolkit/AppSpace';
8
9
  import { type AppSurface } from '@dxos/app-toolkit/ui';
9
10
  import { type ConfigProto, SaveConfig, Storage, defs } from '@dxos/config';
10
11
  import { log } from '@dxos/log';
@@ -16,7 +17,9 @@ import { TRACE_ALL_KEY } from '@dxos/tracing';
16
17
  import { setDeep } from '@dxos/util';
17
18
 
18
19
  import { meta } from '#meta';
19
- import { Settings } from '#types';
20
+
21
+ import * as Settings from '../../types/Settings';
22
+ import { DebugPortSettings } from '../DebugPortSettings';
20
23
 
21
24
  type Toast = {
22
25
  title: string;
@@ -65,12 +68,12 @@ export const DebugSettings = ({ settings, onSettingsChange, logStore, onUpload }
65
68
  download(file, fileName);
66
69
 
67
70
  if (onUpload) {
68
- const personalSpace = AppSpace.getPersonalSpace(client);
69
- if (!personalSpace) {
70
- log.error('no personal space available for upload');
71
+ const defaultSpace = AppSpace.getDefaultSpace(client);
72
+ if (!defaultSpace) {
73
+ log.error('no default space available for upload');
71
74
  return;
72
75
  }
73
- const info = await onUpload(personalSpace.db, new File([file], fileName));
76
+ const info = await onUpload(defaultSpace.db, new File([file], fileName));
74
77
  if (!info) {
75
78
  log.error('diagnostics failed to upload to IPFS');
76
79
  return;
@@ -244,6 +247,8 @@ export const DebugSettings = ({ settings, onSettingsChange, logStore, onUpload }
244
247
  </Select.Root>
245
248
  </Form.Row>
246
249
  </Form.Section>
250
+
251
+ <DebugPortSettings disabled={!onSettingsChange} />
247
252
  </Form.Content>
248
253
  </Form.Viewport>
249
254
  </Form.Root>
@@ -0,0 +1,93 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import * as Effect from 'effect/Effect';
7
+ import React from 'react';
8
+
9
+ import { withPluginManager } from '@dxos/app-framework/testing';
10
+ import { Filter, Obj, Query } from '@dxos/echo';
11
+ import { useQuery } from '@dxos/echo-react';
12
+ import { ClientPlugin, initializeIdentity } from '@dxos/plugin-client/testing';
13
+ import { SpacePlugin } from '@dxos/plugin-space/testing';
14
+ import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
15
+ import { useSpaces } from '@dxos/react-client/echo';
16
+ import { ScrollArea } from '@dxos/react-ui';
17
+ import { Listbox } from '@dxos/react-ui-list';
18
+ import { withLayout } from '@dxos/react-ui/testing';
19
+
20
+ import { translations } from '#translations';
21
+
22
+ import { SpaceGenerator } from './SpaceGenerator';
23
+
24
+ /** Live list of every object in the space — updates as the generator creates objects. */
25
+ const ObjectList = ({ space }: { space: Parameters<typeof SpaceGenerator>[0]['space'] }) => {
26
+ const objects = useQuery(space.db, Query.select(Filter.everything()));
27
+
28
+ return (
29
+ <ScrollArea.Root thin orientation='vertical'>
30
+ <ScrollArea.Viewport>
31
+ <Listbox.Root>
32
+ <Listbox.Content>
33
+ {objects.map((object) => (
34
+ <Listbox.Item key={object.id} id={object.id} classNames='px-2 gap-2'>
35
+ <div className='flex flex-col grow truncate'>
36
+ <span className='truncate'>{Obj.getLabel(object) ?? object.id}</span>
37
+ <span className='text-description text-sm truncate'>{Obj.getTypename(object)}</span>
38
+ </div>
39
+ </Listbox.Item>
40
+ ))}
41
+ </Listbox.Content>
42
+ </Listbox.Root>
43
+ </ScrollArea.Viewport>
44
+ </ScrollArea.Root>
45
+ );
46
+ };
47
+
48
+ const DefaultStory = () => {
49
+ const [space] = useSpaces();
50
+ if (!space) {
51
+ return <></>;
52
+ }
53
+
54
+ return (
55
+ <div className='dx-container grid grid-cols-2 divide-x divide-separator'>
56
+ <SpaceGenerator space={space} />
57
+ <ObjectList space={space} />
58
+ </div>
59
+ );
60
+ };
61
+
62
+ const meta = {
63
+ title: 'plugins/plugin-debug/containers/SpaceGenerator',
64
+ render: DefaultStory,
65
+ decorators: [
66
+ withLayout({ layout: 'fullscreen' }),
67
+ withPluginManager(() => ({
68
+ plugins: [
69
+ ...corePlugins(),
70
+ StorybookPlugin({}),
71
+ ClientPlugin({
72
+ onClientInitialized: ({ client }) =>
73
+ Effect.gen(function* () {
74
+ yield* initializeIdentity(client);
75
+ }),
76
+ }),
77
+ // Contributes the operation handlers the generators invoke (e.g. `SpaceOperation.AddType`).
78
+ SpacePlugin({}),
79
+ ],
80
+ })),
81
+ ],
82
+ parameters: {
83
+ layout: 'fullscreen',
84
+ controls: { disable: true },
85
+ translations,
86
+ },
87
+ } satisfies Meta<typeof DefaultStory>;
88
+
89
+ export default meta;
90
+
91
+ type Story = StoryObj<typeof meta>;
92
+
93
+ export const Default: Story = {};
@@ -5,16 +5,27 @@
5
5
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
6
6
 
7
7
  import { useOperationInvoker, useOptionalCapability } from '@dxos/app-framework/ui';
8
- import { AppCapabilities } from '@dxos/app-toolkit';
8
+ import * as AppCapabilities from '@dxos/app-toolkit/AppCapabilities';
9
9
  import { useProgress } from '@dxos/app-toolkit/ui';
10
10
  import { ComputeGraph } from '@dxos/conductor';
11
11
  import { Filter, Obj, Type } from '@dxos/echo';
12
- import { Drawing } from '@dxos/plugin-illustrator';
13
- import { Markdown } from '@dxos/plugin-markdown';
14
- import { Sheet } from '@dxos/plugin-sheet';
12
+ import { log } from '@dxos/log';
13
+ import * as Drawing from '@dxos/plugin-illustrator/Drawing';
14
+ import * as Markdown from '@dxos/plugin-markdown/Markdown';
15
+ import * as Sheet from '@dxos/plugin-sheet/Sheet';
16
+ import { SpaceOperation } from '@dxos/plugin-space';
15
17
  import { useClient } from '@dxos/react-client';
16
18
  import { type Space } from '@dxos/react-client/echo';
17
- import { IconButton, Input, Panel, ScrollArea, ThemedClassName, Toolbar, useAsyncEffect } from '@dxos/react-ui';
19
+ import {
20
+ DropdownMenu,
21
+ IconButton,
22
+ Input,
23
+ Panel,
24
+ ScrollArea,
25
+ ThemedClassName,
26
+ Toolbar,
27
+ useAsyncEffect,
28
+ } from '@dxos/react-ui';
18
29
  import { composable, composableProps } from '@dxos/react-ui';
19
30
  import { Organization, Person, Task } from '@dxos/types';
20
31
  import { mx } from '@dxos/ui-theme';
@@ -84,6 +95,18 @@ export const SpaceGenerator = composable<HTMLDivElement, SpaceGeneratorProps>(
84
95
 
85
96
  useAsyncEffect(updateInfo, [updateInfo]);
86
97
 
98
+ const handleReset = useCallback(async () => {
99
+ try {
100
+ const { error } = await invokePromise(SpaceOperation.RemoveAllObjects, undefined, { spaceId: space.id });
101
+ if (error) {
102
+ log.catch(error);
103
+ }
104
+ await updateInfo();
105
+ } catch (error) {
106
+ log.catch(error);
107
+ }
108
+ }, [space, invokePromise, updateInfo]);
109
+
87
110
  const handleCreateData = useCallback(
88
111
  async (typename: string) => {
89
112
  const constructor = typeMap.get(typename);
@@ -93,7 +116,7 @@ export const SpaceGenerator = composable<HTMLDivElement, SpaceGeneratorProps>(
93
116
  await updateInfo();
94
117
  }
95
118
  },
96
- [typeMap, count, space, onCreateObjects, updateInfo],
119
+ [space, typeMap, count, updateInfo, onCreateObjects],
97
120
  );
98
121
 
99
122
  return (
@@ -101,6 +124,21 @@ export const SpaceGenerator = composable<HTMLDivElement, SpaceGeneratorProps>(
101
124
  <Panel.Toolbar>
102
125
  <Toolbar.Root classNames='dx-document'>
103
126
  <IconButton icon='ph--arrow-clockwise--regular' iconOnly label='Refresh' onClick={updateInfo} />
127
+ <DropdownMenu.Root>
128
+ <DropdownMenu.Trigger asChild>
129
+ <IconButton icon='ph--trash--regular' iconOnly label='Reset space' data-testid='spaceGenerator.reset' />
130
+ </DropdownMenu.Trigger>
131
+ <DropdownMenu.Portal>
132
+ <DropdownMenu.Content side='bottom'>
133
+ <DropdownMenu.Viewport>
134
+ <DropdownMenu.Item data-testid='spaceGenerator.confirmReset' onClick={handleReset}>
135
+ Confirm to remove all objects from the space.
136
+ </DropdownMenu.Item>
137
+ </DropdownMenu.Viewport>
138
+ <DropdownMenu.Arrow />
139
+ </DropdownMenu.Content>
140
+ </DropdownMenu.Portal>
141
+ </DropdownMenu.Root>
104
142
  <Toolbar.Separator />
105
143
  <Input.Root>
106
144
  <Input.TextInput
@@ -6,7 +6,7 @@ import { Atom, useAtomValue } from '@effect-atom/atom-react';
6
6
  import React, { Fragment } from 'react';
7
7
 
8
8
  import { useOptionalCapability } from '@dxos/app-framework/ui';
9
- import { AppCapabilities } from '@dxos/app-toolkit';
9
+ import * as AppCapabilities from '@dxos/app-toolkit/AppCapabilities';
10
10
  import { ScrollArea } from '@dxos/react-ui';
11
11
 
12
12
  // Fallback so the atom hook is called unconditionally when no store is contributed (host plugin not
@@ -57,7 +57,7 @@ export const StatsPanel = () => {
57
57
  <dl className='grid grid-cols-[auto_minmax(0,1fr)] w-full'>
58
58
  {flatten(value).map(([key, cell]) => (
59
59
  <Fragment key={key}>
60
- <dt className='border-be border-separator pbe-1 pbs-1 pie-2 text-description self-start'>{key}</dt>
60
+ <dt className='border-be border-separator pbe-1 pbs-1 pe-2 text-description self-start'>{key}</dt>
61
61
  <dd className='border-be border-separator pbe-1 pbs-1 truncate font-mono text-end'>
62
62
  {formatValue(cell)}
63
63
  </dd>
package/src/index.ts CHANGED
@@ -2,5 +2,9 @@
2
2
  // Copyright 2026 DXOS.org
3
3
  //
4
4
 
5
+ export * as Debug from './types/Debug';
6
+ export * as DebugEvents from './types/DebugEvents';
7
+ export * as DebugNodes from './types/DebugNodes';
8
+ export * as DebugSurface from './types/DebugSurface';
9
+ export * as Settings from './types/Settings';
5
10
  export * from './meta';
6
- export * from './types';
package/src/meta.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  // Copyright 2026 DXOS.org
3
3
  //
4
4
 
5
- import { Plugin } from '@dxos/app-framework';
5
+ import * as Plugin from '@dxos/app-framework/Plugin';
6
6
 
7
7
  import config from '../dx.config';
8
8
 
package/src/plugin.ts CHANGED
@@ -2,9 +2,9 @@
2
2
  // Copyright 2026 DXOS.org
3
3
  //
4
4
 
5
- import { Plugin } from '@dxos/app-framework';
5
+ import * as Plugin from '@dxos/app-framework/Plugin';
6
6
 
7
7
  import { meta } from './meta';
8
8
 
9
9
  export const DebugPlugin = Plugin.lazy(meta, () => import('#plugin'));
10
- export type { DebugPluginOptions } from '#types';
10
+ export type { DebugPluginOptions } from './types/Debug';
@@ -49,6 +49,19 @@ export const translations = [
49
49
  'Warning: Swapping the storage adapter will make your data unavailable.',
50
50
 
51
51
  'settings.data-store.label': 'Data Store',
52
+
53
+ 'settings.debug-port.section.label': 'Agent debug port',
54
+ 'settings.debug-port.section.description':
55
+ 'Lets a local agent evaluate code against this page via composer-recovery.js. Off by default; stops on reload.',
56
+ 'settings.debug-port.label': 'Open debug port.',
57
+ 'settings.debug-port.description':
58
+ 'Runs arbitrary code from a loopback server with full access to your data. Only enable while you are working with an agent you trust.',
59
+ 'settings.debug-port.running.label': 'Listening on',
60
+ 'settings.debug-port.session.label': 'Session id',
61
+ 'settings.debug-port.session.description':
62
+ 'Pass to composer-recovery.js --session. A new id is issued on every restart.',
63
+ 'settings.debug-port.copy-session.label': 'Copy session id.',
64
+ 'settings.debug-port.log.label': 'Debug port log',
52
65
  },
53
66
  },
54
67
  },
@@ -5,7 +5,7 @@
5
5
  import { type Atom } from '@effect-atom/atom';
6
6
  import { type Context, createContext } from 'react';
7
7
 
8
- import { Capability } from '@dxos/app-framework';
8
+ import * as Capability from '@dxos/app-framework/Capability';
9
9
  import type { TimerCallback, TimerOptions } from '@dxos/async';
10
10
  import { type IdbLogStore } from '@dxos/log-store-idb';
11
11
 
@@ -33,5 +33,7 @@ export const DebugContext: Context<DebugContextType> = createContext<DebugContex
33
33
  });
34
34
 
35
35
  export namespace DebugCapabilities {
36
- export const Settings = Capability.make<Atom.Writable<Settings.Settings>>(`${meta.profile.key}.capability.settings`);
36
+ export const Settings = Capability.makeSingleton<Atom.Writable<Settings.Settings>>()(
37
+ `${meta.profile.key}.capability.settings`,
38
+ );
37
39
  }
@@ -0,0 +1,16 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ // @import-as-namespace
6
+
7
+ import * as ActivationEvent from '@dxos/app-framework/ActivationEvent';
8
+
9
+ import { meta } from '#meta';
10
+
11
+ /**
12
+ * The feature's start event: the plugin's start-gated modules (and cross-plugin
13
+ * contributions consumed by this feature) activate here. Fired on demand and by the
14
+ * host's idle trickle.
15
+ */
16
+ export const Start = ActivationEvent.pluginStart(meta.profile.key);
@@ -1,60 +0,0 @@
1
- import { Capability, Role } from "@dxos/app-framework";
2
- import { createContext } from "react";
3
- import { meta } from "#meta";
4
- import * as Schema from "effect/Schema";
5
- //#region \0rolldown/runtime.js
6
- var __defProp = Object.defineProperty;
7
- var __exportAll = (all, no_symbols) => {
8
- let target = {};
9
- for (var name in all) __defProp(target, name, {
10
- get: all[name],
11
- enumerable: true
12
- });
13
- if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
14
- return target;
15
- };
16
- //#endregion
17
- //#region src/types/debug.ts
18
- var DebugContext = createContext({
19
- running: false,
20
- start: () => {},
21
- stop: () => {}
22
- });
23
- var DebugCapabilities;
24
- (function(_DebugCapabilities) {
25
- _DebugCapabilities.Settings = Capability.make(`${meta.profile.key}.capability.settings`);
26
- })(DebugCapabilities || (DebugCapabilities = {}));
27
- //#endregion
28
- //#region src/types/DebugNodes.ts
29
- var DebugNodes_exports = /* @__PURE__ */ __exportAll({
30
- SpaceType: () => SpaceType,
31
- id: () => id,
32
- nodeId: () => nodeId
33
- });
34
- /** Extracts the last dot-separated segment of a namespaced ID string for use as a node segment id. */
35
- var nodeId = (fullId) => fullId.split(".").at(-1) ?? "";
36
- var debugId = meta.profile.key;
37
- /** Top-level Debug node (sibling of DevTools under the SYSTEM navtree group). */
38
- var id = `${debugId}.debug`;
39
- /** Discriminator for the space-generator article surface (`node.data.type`). */
40
- var SpaceType = `${debugId}.space`;
41
- //#endregion
42
- //#region src/types/DebugSurface.ts
43
- var DebugSurface_exports = /* @__PURE__ */ __exportAll({ Stats: () => Stats });
44
- /**
45
- * Role token for the transient-stats panel — renders the {@link AppCapabilities.StatsPanel} store
46
- * (arbitrary reactive telemetry keyed by owner id). A layout targets this role (role-only dispatch);
47
- * the surface reads the store directly, so it needs no subject.
48
- */
49
- var Stats = Role.make(`${meta.profile.key}.surface.stats`);
50
- //#endregion
51
- //#region src/types/Settings.ts
52
- var Settings_exports = /* @__PURE__ */ __exportAll({ Settings: () => Settings });
53
- var Settings = Schema.mutable(Schema.Struct({
54
- wireframe: Schema.optional(Schema.Boolean),
55
- traceAll: Schema.optional(Schema.Boolean)
56
- }));
57
- //#endregion
58
- export { DebugContext as a, DebugCapabilities as i, DebugSurface_exports as n, DebugNodes_exports as r, Settings_exports as t };
59
-
60
- //# sourceMappingURL=chunk-types.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"chunk-types.mjs","names":[],"sources":["../../src/types/debug.ts","../../src/types/DebugNodes.ts","../../src/types/DebugSurface.ts","../../src/types/Settings.ts"],"sourcesContent":["//\n// Copyright 2023 DXOS.org\n//\n\nimport { type Atom } from '@effect-atom/atom';\nimport { type Context, createContext } from 'react';\n\nimport { Capability } from '@dxos/app-framework';\nimport type { TimerCallback, TimerOptions } from '@dxos/async';\nimport { type IdbLogStore } from '@dxos/log-store-idb';\n\nimport { meta } from '#meta';\n\nimport * as Settings from './Settings';\n\nexport type DebugPluginOptions = {\n /** Shared persistent log store for capturing and downloading logs. */\n logStore?: IdbLogStore;\n /** Persist the stats-panel store to localStorage so it survives a reload. Defaults to `true`. */\n persistStats?: boolean;\n};\n\nexport type DebugContextType = {\n running: boolean;\n start: (cb: TimerCallback, options: TimerOptions) => void;\n stop: () => void;\n};\n\nexport const DebugContext: Context<DebugContextType> = createContext<DebugContextType>({\n running: false,\n start: () => {},\n stop: () => {},\n});\n\nexport namespace DebugCapabilities {\n export const Settings = Capability.make<Atom.Writable<Settings.Settings>>(`${meta.profile.key}.capability.settings`);\n}\n","//\n// Copyright 2026 DXOS.org\n//\n\n// @import-as-namespace\n\nimport { meta } from '#meta';\n\n/** Extracts the last dot-separated segment of a namespaced ID string for use as a node segment id. */\nexport const nodeId = (fullId: string): string => fullId.split('.').at(-1) ?? '';\n\nconst debugId = meta.profile.key;\n\n/** Top-level Debug node (sibling of DevTools under the SYSTEM navtree group). */\nexport const id = `${debugId}.debug`;\n\n/** Discriminator for the space-generator article surface (`node.data.type`). */\nexport const SpaceType = `${debugId}.space`;\n","//\n// Copyright 2026 DXOS.org\n//\n\n// @import-as-namespace\n\nimport { Role } from '@dxos/app-framework';\n\nimport { meta } from '#meta';\n\n/**\n * Role token for the transient-stats panel — renders the {@link AppCapabilities.StatsPanel} store\n * (arbitrary reactive telemetry keyed by owner id). A layout targets this role (role-only dispatch);\n * the surface reads the store directly, so it needs no subject.\n */\nexport const Stats = Role.make<Record<string, unknown>>(`${meta.profile.key}.surface.stats`);\n","//\n// Copyright 2023 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as Schema from 'effect/Schema';\n\nexport const Settings = Schema.mutable(\n Schema.Struct({\n wireframe: Schema.optional(Schema.Boolean),\n traceAll: Schema.optional(Schema.Boolean),\n }),\n);\n\nexport interface Settings extends Schema.Schema.Type<typeof Settings> {}\n"],"mappings":";;;;;;;;;;;;;;;;;AA4BA,IAAa,eAA0C,cAAgC;CACrF,SAAS;CACT,aAAa,CAAC;CACd,YAAY,CAAC;AACf,CAAC;AAEM,IAAA;;CACQ,mBAAA,WAAW,WAAW,KAAuC,GAAG,KAAK,QAAQ,IAAI,qBAAqB;GACrH,sBAAA,oBAAA,CAAA,EAAA;;;;;;;;;AC3BA,IAAa,UAAU,WAA2B,OAAO,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK;AAE9E,IAAM,UAAU,KAAK,QAAQ;;AAG7B,IAAa,KAAK,GAAG,QAAQ;;AAG7B,IAAa,YAAY,GAAG,QAAQ;;;;;;;;;ACFpC,IAAa,QAAQ,KAAK,KAA8B,GAAG,KAAK,QAAQ,IAAI,eAAe;;;;ACP3F,IAAa,WAAW,OAAO,QAC7B,OAAO,OAAO;CACZ,WAAW,OAAO,SAAS,OAAO,OAAO;CACzC,UAAU,OAAO,SAAS,OAAO,OAAO;AAC1C,CAAC,CACH"}
@@ -1,2 +0,0 @@
1
- import { a as DebugContext, i as DebugCapabilities, n as DebugSurface_exports, r as DebugNodes_exports, t as Settings_exports } from "./chunk-types.mjs";
2
- export { DebugCapabilities, DebugContext, DebugNodes_exports as DebugNodes, DebugSurface_exports as DebugSurface, Settings_exports as Settings };