@dxos/plugin-debug 0.8.4-main.3fbcb4aa9b → 0.8.4-main.43cb759274

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 (108) hide show
  1. package/LICENSE +102 -5
  2. package/PLUGIN.mdl +373 -0
  3. package/README.md +1 -1
  4. package/dist/lib/neutral/{DebugObjectPanel-UJ63CV25.mjs → DebugObjectPanel-OS65NC4M.mjs} +1 -1
  5. package/dist/lib/neutral/{DebugObjectPanel-UJ63CV25.mjs.map → DebugObjectPanel-OS65NC4M.mjs.map} +1 -1
  6. package/dist/lib/neutral/DebugPlugin.mjs +12 -0
  7. package/dist/lib/neutral/DebugPlugin.mjs.map +4 -4
  8. package/dist/lib/neutral/DebugPlugin.workerd.mjs +12 -0
  9. package/dist/lib/neutral/DebugPlugin.workerd.mjs.map +7 -0
  10. package/dist/lib/neutral/{DebugSettings-Z3XADRNK.mjs → DebugSettings-RKYNQIKH.mjs} +5 -6
  11. package/dist/lib/neutral/DebugSettings-RKYNQIKH.mjs.map +7 -0
  12. package/dist/lib/neutral/{DebugSpaceObjectsPanel-NISULU6U.mjs → DebugSpaceObjectsPanel-BHOCFWDJ.mjs} +1 -1
  13. package/dist/lib/neutral/{DebugSpaceObjectsPanel-NISULU6U.mjs.map → DebugSpaceObjectsPanel-BHOCFWDJ.mjs.map} +1 -1
  14. package/dist/lib/neutral/{DebugStatus-YB3KFP7G.mjs → DebugStatus-H2BAHN26.mjs} +2 -1
  15. package/dist/lib/neutral/DebugStatus-H2BAHN26.mjs.map +7 -0
  16. package/dist/lib/neutral/GithubPanel-QJKDJRVQ.mjs +177 -0
  17. package/dist/lib/neutral/GithubPanel-QJKDJRVQ.mjs.map +7 -0
  18. package/dist/lib/neutral/RegistryPanel-64GIYJHN.mjs +161 -0
  19. package/dist/lib/neutral/RegistryPanel-64GIYJHN.mjs.map +7 -0
  20. package/dist/lib/neutral/{SpaceGenerator-UBYB4NPD.mjs → SpaceGenerator-Y35G6DRV.mjs} +9 -6
  21. package/dist/lib/neutral/SpaceGenerator-Y35G6DRV.mjs.map +7 -0
  22. package/dist/lib/neutral/{Wireframe-7SNRUKET.mjs → Wireframe-WD7S2AUU.mjs} +2 -2
  23. package/dist/lib/neutral/Wireframe-WD7S2AUU.mjs.map +7 -0
  24. package/dist/lib/neutral/{app-graph-builder-YPYHL2K5.mjs → app-graph-builder-XYLTBYKN.mjs} +46 -11
  25. package/dist/lib/neutral/app-graph-builder-XYLTBYKN.mjs.map +7 -0
  26. package/dist/lib/neutral/capabilities/index.mjs +2 -2
  27. package/dist/lib/neutral/chunk-HOV6MV5B.mjs +43 -0
  28. package/dist/lib/neutral/chunk-HOV6MV5B.mjs.map +7 -0
  29. package/dist/lib/neutral/{chunk-NFIOTQRZ.mjs → chunk-UAAGABXZ.mjs} +2 -1
  30. package/dist/lib/neutral/{chunk-NFIOTQRZ.mjs.map → chunk-UAAGABXZ.mjs.map} +3 -3
  31. package/dist/lib/neutral/components/index.mjs +39 -48
  32. package/dist/lib/neutral/components/index.mjs.map +3 -3
  33. package/dist/lib/neutral/containers/index.mjs +9 -5
  34. package/dist/lib/neutral/containers/index.mjs.map +3 -3
  35. package/dist/lib/neutral/index.mjs +2 -2
  36. package/dist/lib/neutral/meta.json +1 -1
  37. package/dist/lib/neutral/meta.mjs +1 -1
  38. package/dist/lib/neutral/plugin.mjs +1 -1
  39. package/dist/lib/neutral/{react-surface-NH2H63KW.mjs → react-surface-3MDMZGCA.mjs} +23 -18
  40. package/dist/lib/neutral/react-surface-3MDMZGCA.mjs.map +7 -0
  41. package/dist/lib/neutral/translations.mjs +10 -2
  42. package/dist/lib/neutral/translations.mjs.map +2 -2
  43. package/dist/lib/neutral/types/index.mjs +1 -1
  44. package/dist/types/src/DebugPlugin.d.ts.map +1 -1
  45. package/dist/types/src/DebugPlugin.workerd.d.ts +5 -0
  46. package/dist/types/src/DebugPlugin.workerd.d.ts.map +1 -0
  47. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  48. package/dist/types/src/components/DebugSettings/DebugSettings.d.ts.map +1 -1
  49. package/dist/types/src/components/DebugSettings/DebugSettings.stories.d.ts +8 -1
  50. package/dist/types/src/components/DebugSettings/DebugSettings.stories.d.ts.map +1 -1
  51. package/dist/types/src/components/SpaceGenerator/ObjectGenerator.d.ts +2 -3
  52. package/dist/types/src/components/SpaceGenerator/ObjectGenerator.d.ts.map +1 -1
  53. package/dist/types/src/components/SpaceGenerator/presets.d.ts.map +1 -1
  54. package/dist/types/src/containers/DebugStatus/DebugStatus.d.ts.map +1 -1
  55. package/dist/types/src/containers/GithubPanel/GithubComponent.d.ts +15 -0
  56. package/dist/types/src/containers/GithubPanel/GithubComponent.d.ts.map +1 -0
  57. package/dist/types/src/containers/GithubPanel/GithubComponent.stories.d.ts +106 -0
  58. package/dist/types/src/containers/GithubPanel/GithubComponent.stories.d.ts.map +1 -0
  59. package/dist/types/src/containers/GithubPanel/GithubPanel.d.ts +3 -0
  60. package/dist/types/src/containers/GithubPanel/GithubPanel.d.ts.map +1 -0
  61. package/dist/types/src/containers/GithubPanel/index.d.ts +2 -0
  62. package/dist/types/src/containers/GithubPanel/index.d.ts.map +1 -0
  63. package/dist/types/src/containers/RegistryPanel/RegistryPanel.d.ts +3 -0
  64. package/dist/types/src/containers/RegistryPanel/RegistryPanel.d.ts.map +1 -0
  65. package/dist/types/src/containers/RegistryPanel/index.d.ts +2 -0
  66. package/dist/types/src/containers/RegistryPanel/index.d.ts.map +1 -0
  67. package/dist/types/src/containers/SpaceGenerator/SpaceGenerator.d.ts.map +1 -1
  68. package/dist/types/src/containers/index.d.ts +2 -0
  69. package/dist/types/src/containers/index.d.ts.map +1 -1
  70. package/dist/types/src/meta.d.ts +1 -1
  71. package/dist/types/src/meta.d.ts.map +1 -1
  72. package/dist/types/src/translations.d.ts +8 -1
  73. package/dist/types/src/translations.d.ts.map +1 -1
  74. package/dist/types/src/types/index.d.ts +1 -0
  75. package/dist/types/src/types/index.d.ts.map +1 -1
  76. package/dist/types/tsconfig.tsbuildinfo +1 -1
  77. package/package.json +60 -70
  78. package/src/DebugPlugin.tsx +6 -0
  79. package/src/DebugPlugin.workerd.ts +12 -0
  80. package/src/capabilities/app-graph-builder.ts +38 -10
  81. package/src/capabilities/react-surface.tsx +22 -15
  82. package/src/components/DebugSettings/DebugSettings.tsx +5 -8
  83. package/src/components/SpaceGenerator/ObjectGenerator.tsx +16 -14
  84. package/src/components/SpaceGenerator/presets.ts +13 -14
  85. package/src/containers/DebugObjectPanel/DebugObjectPanel.tsx +2 -2
  86. package/src/containers/DebugSpaceObjectsPanel/DebugSpaceObjectsPanel.tsx +2 -2
  87. package/src/containers/DebugStatus/DebugStatus.tsx +1 -0
  88. package/src/containers/GithubPanel/GithubComponent.stories.tsx +38 -0
  89. package/src/containers/GithubPanel/GithubComponent.tsx +192 -0
  90. package/src/containers/GithubPanel/GithubPanel.tsx +17 -0
  91. package/src/containers/GithubPanel/index.ts +5 -0
  92. package/src/containers/RegistryPanel/RegistryPanel.tsx +150 -0
  93. package/src/containers/RegistryPanel/index.ts +5 -0
  94. package/src/containers/SpaceGenerator/SpaceGenerator.tsx +6 -5
  95. package/src/containers/Wireframe/Wireframe.tsx +1 -1
  96. package/src/containers/index.ts +2 -0
  97. package/src/meta.ts +26 -6
  98. package/src/translations.ts +9 -1
  99. package/src/types/index.ts +1 -0
  100. package/src/vite-env.d.ts +10 -0
  101. package/dist/lib/neutral/DebugSettings-Z3XADRNK.mjs.map +0 -7
  102. package/dist/lib/neutral/DebugStatus-YB3KFP7G.mjs.map +0 -7
  103. package/dist/lib/neutral/SpaceGenerator-UBYB4NPD.mjs.map +0 -7
  104. package/dist/lib/neutral/Wireframe-7SNRUKET.mjs.map +0 -7
  105. package/dist/lib/neutral/app-graph-builder-YPYHL2K5.mjs.map +0 -7
  106. package/dist/lib/neutral/chunk-3OGPOE7H.mjs +0 -20
  107. package/dist/lib/neutral/chunk-3OGPOE7H.mjs.map +0 -7
  108. package/dist/lib/neutral/react-surface-NH2H63KW.mjs.map +0 -7
@@ -7,8 +7,9 @@ import * as Schema from 'effect/Schema';
7
7
  import { AgentPrompt, WebSearchBlueprint } from '@dxos/assistant-toolkit';
8
8
  import { Routine, Trigger, Operation } from '@dxos/compute';
9
9
  import { type ComputeGraphModel, NODE_INPUT } from '@dxos/conductor';
10
- import { DXN, Feed, Filter, JsonSchema, Key, Obj, Query, type QueryAST, Ref, Tag } from '@dxos/echo';
10
+ import { Feed, Filter, JsonSchema, Key, Obj, Query, type QueryAST, Ref, Scope, Tag } from '@dxos/echo';
11
11
  import { invariant } from '@dxos/invariant';
12
+ import { EID } from '@dxos/keys';
12
13
  import { InboxOperation } from '@dxos/plugin-inbox';
13
14
  import { Mailbox } from '@dxos/plugin-inbox';
14
15
  import { Markdown } from '@dxos/plugin-markdown';
@@ -71,9 +72,9 @@ export const generator = () => ({
71
72
  );
72
73
 
73
74
  const tag = space.db.add(Tag.make({ label: 'Investor' }));
74
- const tagDxn = Obj.getDXN(tag).toString();
75
+ const tagRef = Ref.make(tag);
75
76
  Obj.update(doc, (doc) => {
76
- Obj.getMeta(doc).tags = [tagDxn];
77
+ Obj.getMeta(doc).tags = [tagRef];
77
78
  });
78
79
 
79
80
  // space.db.add(
@@ -85,7 +86,7 @@ export const generator = () => ({
85
86
  // );
86
87
 
87
88
  space.db.add(
88
- Obj.make(Person.Person, { [Obj.Meta]: { tags: [tagDxn] }, fullName: 'Rich', organization: Ref.make(org) }),
89
+ Obj.make(Person.Person, { [Obj.Meta]: { tags: [tagRef] }, fullName: 'Rich', organization: Ref.make(org) }),
89
90
  );
90
91
  space.db.add(
91
92
  Obj.make(Person.Person, {
@@ -120,15 +121,15 @@ export const generator = () => ({
120
121
  invariant(mailbox, 'Mailbox not found');
121
122
  const mailboxFeed = await mailbox.feed?.tryLoad();
122
123
  invariant(mailboxFeed, 'Mailbox missing feed reference');
123
- const queueDxn = Feed.getQueueDxn(mailboxFeed)?.toString();
124
+ const queueDxn = Feed.getQueueUri(mailboxFeed);
124
125
  invariant(queueDxn, 'Mailbox feed missing queue DXN key');
125
126
  const tag = await space.db.query(Filter.type(Tag.Tag, { label: 'Investor' })).first();
126
- const tagDxn = Obj.getDXN(tag).toString();
127
+ const tagUri = Obj.getURI(tag);
127
128
 
128
129
  const objects = range(n, () => {
129
- const contactsQuery = Query.select(Filter.type(Person.Person)).select(Filter.tag(tagDxn));
130
- const organizationsQuery = Query.select(Filter.type(Organization.Organization)).select(Filter.tag(tagDxn));
131
- const notesQuery = Query.select(Filter.type(Markdown.Document)).select(Filter.tag(tagDxn));
130
+ const contactsQuery = Query.select(Filter.type(Person.Person)).select(Filter.tag(tagUri));
131
+ const organizationsQuery = Query.select(Filter.type(Organization.Organization)).select(Filter.tag(tagUri));
132
+ const notesQuery = Query.select(Filter.type(Markdown.Document)).select(Filter.tag(tagUri));
132
133
 
133
134
  space.db.add(
134
135
  Trigger.make({
@@ -178,9 +179,7 @@ export const generator = () => ({
178
179
  Filter.type(Message.Message, {
179
180
  properties: { labels: Filter.contains('investor') },
180
181
  }),
181
- ).from({
182
- queues: [queueDxn],
183
- }),
182
+ ).from(Scope.feed(Obj.getURI(mailboxFeed))),
184
183
  jsonSchema: JsonSchema.toJsonSchema(Message.Message),
185
184
  });
186
185
  const contactsView = ViewModel.make({
@@ -587,7 +586,7 @@ export const generator = () => ({
587
586
  );
588
587
  const queueId = canvasModel.createNode(
589
588
  createConstant({
590
- value: new DXN(DXN.kind.QUEUE, ['data', space.id, Key.ObjectId.random()]).toString(),
589
+ value: EID.make({ spaceId: space.id, entityId: Key.EntityId.random() }),
591
590
  ...position({ x: -10, y: 5 }),
592
591
  }),
593
592
  );
@@ -774,7 +773,7 @@ const setupQueue = (
774
773
  ) => {
775
774
  const queueId = canvasModel.createNode(
776
775
  createConstant({
777
- value: new DXN(DXN.kind.QUEUE, ['data', space.id, Key.ObjectId.random()]).toString(),
776
+ value: EID.make({ spaceId: space.id, entityId: Key.EntityId.random() }),
778
777
  ...(args?.idPosition ? rawPosition(args.idPosition) : position({ x: -18, y: 5, width: 8, height: 6 })),
779
778
  }),
780
779
  );
@@ -7,7 +7,7 @@ import React, { useMemo, useState } from 'react';
7
7
  import { AppSurface } from '@dxos/app-toolkit/ui';
8
8
  import { ObjectsTree } from '@dxos/devtools';
9
9
  import { Filter, Json, Obj, Query } from '@dxos/echo';
10
- import type { ObjectId } from '@dxos/keys';
10
+ import type { EntityId } from '@dxos/keys';
11
11
  import { useQuery } from '@dxos/react-client/echo';
12
12
  import { Clipboard, Input, Panel, ScrollArea, Toolbar } from '@dxos/react-ui';
13
13
  import { Syntax } from '@dxos/react-ui-syntax-highlighter';
@@ -20,7 +20,7 @@ export type DebugObjectPanelProps = Pick<
20
20
 
21
21
  export const DebugObjectPanel = ({ role, companionTo }: DebugObjectPanelProps) => {
22
22
  const db = Obj.getDatabase(companionTo);
23
- const [selectedId, setSelectedId] = useState<ObjectId | null>(null);
23
+ const [selectedId, setSelectedId] = useState<EntityId | null>(null);
24
24
  const [depth, setDepth] = useState(0);
25
25
  const [selectedObject] = useQuery(
26
26
  db,
@@ -7,7 +7,7 @@ import React, { useState } from 'react';
7
7
  import { type AppSurface } from '@dxos/app-toolkit/ui';
8
8
  import { ObjectsTree } from '@dxos/devtools';
9
9
  import { Filter, Query } from '@dxos/echo';
10
- import { type ObjectId } from '@dxos/keys';
10
+ import { type EntityId } from '@dxos/keys';
11
11
  import { useQuery } from '@dxos/react-client/echo';
12
12
  import { Clipboard, Grid, Input, Panel, ScrollArea, Toolbar } from '@dxos/react-ui';
13
13
  import { JsonHighlighter } from '@dxos/react-ui-syntax-highlighter';
@@ -15,7 +15,7 @@ import { JsonHighlighter } from '@dxos/react-ui-syntax-highlighter';
15
15
  export type DebugSpaceObjectsPanelProps = AppSurface.SpaceArticleProps;
16
16
 
17
17
  export const DebugSpaceObjectsPanel = ({ space }: DebugSpaceObjectsPanelProps) => {
18
- const [selectedId, setSelectedId] = useState<ObjectId | null>(null);
18
+ const [selectedId, setSelectedId] = useState<EntityId | null>(null);
19
19
  const [selectedObject] = useQuery(
20
20
  space.db,
21
21
  selectedId ? Query.select(Filter.id(selectedId)) : Query.select(Filter.nothing()),
@@ -90,6 +90,7 @@ const ErrorIndicator = () => {
90
90
  return (
91
91
  <StatusBar.Item>
92
92
  <IconButton
93
+ variant='ghost'
93
94
  icon='ph--warning-circle--duotone'
94
95
  iconOnly
95
96
  label={errorRef.current.message}
@@ -0,0 +1,38 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import React from 'react';
7
+
8
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
9
+
10
+ import { translations } from '#translations';
11
+
12
+ import { GithubComponent } from './GithubComponent';
13
+
14
+ const DefaultStory = () => (
15
+ <GithubComponent.Root>
16
+ <div className='grid grid-rows-[auto_minmax(0,1fr)_auto] overflow-hidden h-full is-full'>
17
+ <GithubComponent.Header />
18
+ <GithubComponent.Content />
19
+ <GithubComponent.StatusBar />
20
+ </div>
21
+ </GithubComponent.Root>
22
+ );
23
+
24
+ const meta = {
25
+ title: 'plugins/plugin-debug/containers/GithubComponent',
26
+ component: DefaultStory,
27
+ decorators: [withTheme(), withLayout({ layout: 'column', classNames: 'w-(--dx-r1-size)' })],
28
+ parameters: {
29
+ layout: 'fullscreen',
30
+ translations,
31
+ },
32
+ } satisfies Meta<typeof DefaultStory>;
33
+
34
+ export default meta;
35
+
36
+ type Story = StoryObj<typeof meta>;
37
+
38
+ export const Default: Story = {};
@@ -0,0 +1,192 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import React, { type ReactNode, createContext, useContext, useEffect, useState } from 'react';
6
+
7
+ import { IconButton, ScrollArea, useTranslation } from '@dxos/react-ui';
8
+
9
+ import { meta } from '#meta';
10
+
11
+ const DEFAULT_REPO = 'dxos/dxos';
12
+ const DEFAULT_LIMIT = 20;
13
+
14
+ type GithubUser = {
15
+ login: string;
16
+ avatar_url: string;
17
+ html_url: string;
18
+ };
19
+
20
+ type GithubPullRequest = {
21
+ number: number;
22
+ title: string;
23
+ html_url: string;
24
+ merged_at: string | null;
25
+ user: GithubUser;
26
+ };
27
+
28
+ type ComponentContextValue = {
29
+ repo: string;
30
+ pulls: GithubPullRequest[];
31
+ unavailable: boolean;
32
+ };
33
+
34
+ const ComponentContext = createContext<ComponentContextValue | null>(null);
35
+
36
+ const useComponentContext = () => {
37
+ const ctx = useContext(ComponentContext);
38
+ if (!ctx) {
39
+ throw new Error('GithubComponent.* parts must be rendered inside GithubComponent.Root.');
40
+ }
41
+ return ctx;
42
+ };
43
+
44
+ export type GithubComponentRootProps = {
45
+ /** `<owner>/<name>`. Defaults to `dxos/dxos`. */
46
+ repo?: string;
47
+ /** Maximum number of merged PRs to fetch. */
48
+ limit?: number;
49
+ children?: ReactNode;
50
+ };
51
+
52
+ const Root = ({ repo = DEFAULT_REPO, limit = DEFAULT_LIMIT, children }: GithubComponentRootProps) => {
53
+ const [pulls, setPulls] = useState<GithubPullRequest[]>([]);
54
+ const [unavailable, setUnavailable] = useState(false);
55
+
56
+ useEffect(() => {
57
+ const controller = new AbortController();
58
+ setPulls([]);
59
+ setUnavailable(false);
60
+ void (async () => {
61
+ try {
62
+ const url = new URL(`https://api.github.com/repos/${repo}/pulls`);
63
+ url.searchParams.set('state', 'closed');
64
+ url.searchParams.set('sort', 'updated');
65
+ url.searchParams.set('direction', 'desc');
66
+ url.searchParams.set('per_page', String(limit * 2));
67
+ const response = await fetch(url.toString(), {
68
+ signal: controller.signal,
69
+ headers: { Accept: 'application/vnd.github+json' },
70
+ });
71
+ if (!response.ok) {
72
+ setUnavailable(true);
73
+ return;
74
+ }
75
+ const items = (await response.json()) as GithubPullRequest[];
76
+ setPulls(items.filter((pull) => pull.merged_at !== null).slice(0, limit));
77
+ setUnavailable(false);
78
+ } catch {
79
+ if (controller.signal.aborted) {
80
+ return;
81
+ }
82
+ setPulls([]);
83
+ setUnavailable(true);
84
+ }
85
+ })();
86
+ return () => controller.abort();
87
+ }, [repo, limit]);
88
+
89
+ return <ComponentContext.Provider value={{ repo, pulls, unavailable }}>{children}</ComponentContext.Provider>;
90
+ };
91
+
92
+ const Header = () => {
93
+ const { t } = useTranslation(meta.id);
94
+ const { repo, pulls, unavailable } = useComponentContext();
95
+ return (
96
+ <header className='flex items-center justify-between gap-1 px-4 py-3 bg-modal-surface border-b border-subdued-separator'>
97
+ <a
98
+ href={`https://github.com/${repo}`}
99
+ target='_blank'
100
+ rel='noopener noreferrer'
101
+ className='text-sm font-medium truncate'
102
+ >
103
+ @{repo}
104
+ </a>
105
+ <div className='text-xs text-description'>
106
+ {unavailable
107
+ ? t('github-unavailable.message')
108
+ : pulls.length > 0
109
+ ? t('recent-prs.label', { count: pulls.length })
110
+ : t('github-loading.message')}
111
+ </div>
112
+ </header>
113
+ );
114
+ };
115
+
116
+ const RELATIVE_UNITS: Array<[Intl.RelativeTimeFormatUnit, number]> = [
117
+ ['year', 60 * 60 * 24 * 365],
118
+ ['month', 60 * 60 * 24 * 30],
119
+ ['week', 60 * 60 * 24 * 7],
120
+ ['day', 60 * 60 * 24],
121
+ ['hour', 60 * 60],
122
+ ['minute', 60],
123
+ ];
124
+
125
+ const formatRelative = (iso: string): string => {
126
+ const formatter = new Intl.RelativeTimeFormat(undefined, { numeric: 'auto' });
127
+ const diffSeconds = (Date.parse(iso) - Date.now()) / 1000;
128
+ const absDiff = Math.abs(diffSeconds);
129
+ for (const [unit, secondsInUnit] of RELATIVE_UNITS) {
130
+ if (absDiff >= secondsInUnit) {
131
+ return formatter.format(Math.round(diffSeconds / secondsInUnit), unit);
132
+ }
133
+ }
134
+ return formatter.format(Math.round(diffSeconds), 'second');
135
+ };
136
+
137
+ const PullRow = ({ pull }: { pull: GithubPullRequest }) => (
138
+ <li>
139
+ <a
140
+ href={pull.html_url}
141
+ target='_blank'
142
+ rel='noopener noreferrer'
143
+ className='flex items-start gap-2 px-2 py-1 rounded-sm hover:bg-hover-surface'
144
+ >
145
+ <img src={pull.user.avatar_url} alt='' className='w-6 h-6 rounded-full shrink-0 mt-0.5' />
146
+ <div className='flex flex-col min-w-0 flex-1'>
147
+ <span className='text-sm truncate'>{pull.title}</span>
148
+ <span className='text-xs text-description truncate'>
149
+ #{pull.number} · {pull.user.login} · {pull.merged_at ? formatRelative(pull.merged_at) : ''}
150
+ </span>
151
+ </div>
152
+ </a>
153
+ </li>
154
+ );
155
+
156
+ const Content = () => {
157
+ const { pulls } = useComponentContext();
158
+ return (
159
+ <ScrollArea.Root orientation='vertical'>
160
+ <ScrollArea.Viewport>
161
+ <ul className='flex flex-col p-1'>
162
+ {pulls.map((pull) => (
163
+ <PullRow key={pull.number} pull={pull} />
164
+ ))}
165
+ </ul>
166
+ </ScrollArea.Viewport>
167
+ </ScrollArea.Root>
168
+ );
169
+ };
170
+
171
+ const StatusBar = () => {
172
+ const { t } = useTranslation(meta.id);
173
+ const { repo } = useComponentContext();
174
+ return (
175
+ <IconButton
176
+ icon='ph--github-logo--regular'
177
+ label={t('view-on-github.button')}
178
+ variant='primary'
179
+ classNames='w-full'
180
+ onClick={() => {
181
+ window.open(`https://github.com/${repo}`, '_blank', 'noopener,noreferrer');
182
+ }}
183
+ />
184
+ );
185
+ };
186
+
187
+ export const GithubComponent = {
188
+ Root,
189
+ Header,
190
+ Content,
191
+ StatusBar,
192
+ };
@@ -0,0 +1,17 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import React from 'react';
6
+
7
+ import { GithubComponent } from './GithubComponent';
8
+
9
+ export const GithubPanel = () => (
10
+ <GithubComponent.Root>
11
+ <div className='h-full grid grid-rows-[auto_minmax(0,1fr)_auto] overflow-hidden h-full is-full'>
12
+ <GithubComponent.Header />
13
+ <GithubComponent.Content />
14
+ <GithubComponent.StatusBar />
15
+ </div>
16
+ </GithubComponent.Root>
17
+ );
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ export { GithubPanel as default } from './GithubPanel';
@@ -0,0 +1,150 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
6
+
7
+ import { Operation } from '@dxos/compute';
8
+ import { JsonView, PanelContainer, Placeholder, Searchbar } from '@dxos/devtools';
9
+ import { Entity, Format, Obj, Type } from '@dxos/echo';
10
+ import { useClient } from '@dxos/react-client';
11
+ import { Toolbar } from '@dxos/react-ui';
12
+ import { DynamicTable, type TableFeatures } from '@dxos/react-ui-table';
13
+ import { mx } from '@dxos/ui-theme';
14
+
15
+ type RegistryRow = {
16
+ id: string;
17
+ kind: string;
18
+ label: string;
19
+ _entity: Entity.Unknown;
20
+ };
21
+
22
+ const textFilter = (text?: string) => {
23
+ if (!text) {
24
+ return () => true;
25
+ }
26
+
27
+ const matcher = new RegExp(text, 'i');
28
+ return (entity: Entity.Unknown) => {
29
+ const typename = Entity.getTypename(entity) ?? '';
30
+ const metaKey = Obj.isObject(entity) ? (Obj.getMeta(entity).key ?? '') : '';
31
+ const uri = Type.isType(entity) ? (Type.getURI(entity)?.toString() ?? '') : '';
32
+ const operationKey =
33
+ Obj.isObject(entity) && Obj.instanceOf(Operation.PersistentOperation, entity)
34
+ ? (Operation.getKey(entity) ?? '')
35
+ : '';
36
+ const name = Obj.isObject(entity) && Obj.instanceOf(Operation.PersistentOperation, entity) ? entity.name : '';
37
+ return [typename, metaKey, uri, operationKey, name, getEntityId(entity)].some((value) => value.match(matcher));
38
+ };
39
+ };
40
+
41
+ const getEntityId = (entity: Entity.Unknown): string => {
42
+ if (entity.id) {
43
+ return entity.id;
44
+ }
45
+ if (Type.isType(entity)) {
46
+ return Type.getURI(entity)?.toString() ?? Type.getTypename(entity) ?? 'unknown-type';
47
+ }
48
+ return Entity.getTypename(entity) ?? 'unknown';
49
+ };
50
+
51
+ const getKind = (entity: Entity.Unknown): string => {
52
+ if (Type.isType(entity)) {
53
+ return 'type';
54
+ }
55
+ if (Obj.isObject(entity) && Obj.instanceOf(Operation.PersistentOperation, entity)) {
56
+ return 'operation';
57
+ }
58
+ return 'other';
59
+ };
60
+
61
+ const getLabel = (entity: Entity.Unknown): string => {
62
+ if (Obj.isObject(entity) && Obj.instanceOf(Operation.PersistentOperation, entity)) {
63
+ return entity.name || Operation.getKey(entity) || getEntityId(entity);
64
+ }
65
+ if (Type.isType(entity)) {
66
+ return Type.getTypename(entity) ?? getEntityId(entity);
67
+ }
68
+ return getEntityId(entity);
69
+ };
70
+
71
+ const toDetailJson = (entity: Entity.Unknown): object => {
72
+ if (Type.isType(entity)) {
73
+ return {
74
+ id: getEntityId(entity),
75
+ typename: Type.getTypename(entity),
76
+ uri: Type.getURI(entity)?.toString(),
77
+ version: Type.getVersion(entity),
78
+ jsonSchema: entity.jsonSchema,
79
+ };
80
+ }
81
+ if (Obj.isObject(entity)) {
82
+ return Obj.toJSON(entity);
83
+ }
84
+ return { id: getEntityId(entity), typename: Entity.getTypename(entity) };
85
+ };
86
+
87
+ export const RegistryPanel = () => {
88
+ const client = useClient();
89
+ const [entities, setEntities] = useState<Entity.Unknown[]>([]);
90
+ const [filter, setFilter] = useState('');
91
+ const [selected, setSelected] = useState<Entity.Unknown>();
92
+
93
+ useEffect(() => {
94
+ const registry = client.graph.registry;
95
+ const refresh = () => setEntities([...registry.list()]);
96
+ refresh();
97
+ return registry.changed.on(refresh);
98
+ }, [client]);
99
+
100
+ const properties = useMemo(
101
+ () => [
102
+ { name: 'kind', format: Format.TypeFormat.String, size: 100 },
103
+ { name: 'label', format: Format.TypeFormat.String },
104
+ { name: 'id', format: Format.TypeFormat.String, size: 280 },
105
+ ],
106
+ [],
107
+ );
108
+
109
+ const rows = useMemo((): RegistryRow[] => {
110
+ return entities
111
+ .filter(textFilter(filter))
112
+ .map((entity) => ({
113
+ id: getEntityId(entity),
114
+ kind: getKind(entity),
115
+ label: getLabel(entity),
116
+ _entity: entity,
117
+ }))
118
+ .toSorted((left, right) => left.label.localeCompare(right.label));
119
+ }, [entities, filter]);
120
+
121
+ const handleRowClicked = useCallback((row: RegistryRow | undefined) => {
122
+ if (!row?._entity) {
123
+ return;
124
+ }
125
+ setSelected(row._entity);
126
+ }, []);
127
+
128
+ const detailJson = useMemo(() => (selected ? toDetailJson(selected) : undefined), [selected]);
129
+
130
+ const features: Partial<TableFeatures> = useMemo(() => ({ selection: { enabled: true, mode: 'single' } }), []);
131
+
132
+ return (
133
+ <PanelContainer
134
+ toolbar={
135
+ <Toolbar.Root>
136
+ <Searchbar placeholder='Filter...' onChange={setFilter} />
137
+ </Toolbar.Root>
138
+ }
139
+ >
140
+ <div className={mx('h-full grid grid-cols-[2fr_1fr] overflow-hidden')}>
141
+ <div className={mx('flex flex-col min-h-0 overflow-hidden')}>
142
+ <DynamicTable properties={properties} rows={rows} features={features} onRowClick={handleRowClicked} />
143
+ </div>
144
+ <div className={mx('min-h-0 h-full overflow-auto border-s border-separator text-sm')}>
145
+ {detailJson ? <JsonView data={detailJson} /> : <Placeholder label='Details' />}
146
+ </div>
147
+ </div>
148
+ </PanelContainer>
149
+ );
150
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ export { RegistryPanel as default, RegistryPanel } from './RegistryPanel';
@@ -6,15 +6,15 @@ import React, { useCallback, useMemo, useState } from 'react';
6
6
 
7
7
  import { useOperationInvoker } from '@dxos/app-framework/ui';
8
8
  import { ComputeGraph } from '@dxos/conductor';
9
- import { Filter, Obj, type Type } from '@dxos/echo';
9
+ import { Filter, Obj, Type } from '@dxos/echo';
10
10
  import { Markdown } from '@dxos/plugin-markdown';
11
11
  import { Sheet } from '@dxos/plugin-sheet';
12
12
  import { Sketch } from '@dxos/plugin-sketch';
13
13
  import { useClient } from '@dxos/react-client';
14
14
  import { type Space } from '@dxos/react-client/echo';
15
15
  import { IconButton, Input, Panel, ScrollArea, Toolbar, useAsyncEffect } from '@dxos/react-ui';
16
+ import { composable, composableProps } from '@dxos/react-ui';
16
17
  import { Organization, Person, Task } from '@dxos/types';
17
- import { composable, composableProps } from '@dxos/ui-theme';
18
18
  import { sortKeys } from '@dxos/util';
19
19
 
20
20
  import { type ObjectGenerator, SchemaTable, createGenerator, generator, staticGenerators } from '#components';
@@ -44,7 +44,7 @@ export const SpaceGenerator = composable<HTMLDivElement, SpaceGeneratorProps>(
44
44
  // Create type generators.
45
45
  const typeMap = useMemo(() => {
46
46
  const recordGenerators = new Map<string, ObjectGenerator<any>>(
47
- recordTypes.map((type) => [type.typename, createGenerator(client, invokePromise, type)]),
47
+ recordTypes.map((type) => [Type.getTypename(type), createGenerator(client, invokePromise, type)]),
48
48
  );
49
49
 
50
50
  return new Map([...staticGenerators, ...presets.items, ...recordGenerators]);
@@ -52,8 +52,9 @@ export const SpaceGenerator = composable<HTMLDivElement, SpaceGeneratorProps>(
52
52
 
53
53
  // Query space to get info.
54
54
  const updateInfo = useCallback(async () => {
55
- const echoSchema = await space.db.schemaRegistry.query().run();
56
- const staticSchema = await space.db.graph.schemaRegistry.query().run();
55
+ const allSchema = [...space.db.graph.registry.list().filter(Type.isType)];
56
+ const echoSchema = allSchema.filter((t) => Type.isTypeKind(t));
57
+ const staticSchema = allSchema.filter((t) => !Type.isTypeKind(t));
57
58
 
58
59
  const objects = await space.db.query(Filter.everything()).run();
59
60
  const objectMap = sortKeys(
@@ -19,7 +19,7 @@ export type WireframeProps = ThemedClassName<{
19
19
 
20
20
  // TODO(burdon): Make focusable and attendable with input.
21
21
  export const Wireframe = ({ classNames, label, object }: WireframeProps) => {
22
- const attentionAttrs = useAttentionAttributes(Obj.getDXN(object).toString());
22
+ const attentionAttrs = useAttentionAttributes(Obj.getURI(object));
23
23
  const { width, height, ref } = useResizeDetector();
24
24
 
25
25
  return (
@@ -11,3 +11,5 @@ export const DevtoolsOverviewContainer: ComponentType<any> = lazy(() => import('
11
11
  export const SpaceGenerator: ComponentType<any> = lazy(() => import('./SpaceGenerator'));
12
12
  export const Wireframe: ComponentType<any> = lazy(() => import('./Wireframe'));
13
13
  export const DebugSpaceObjectsPanel: ComponentType<any> = lazy(() => import('./DebugSpaceObjectsPanel'));
14
+ export const GithubPanel: ComponentType<any> = lazy(() => import('./GithubPanel'));
15
+ export const RegistryPanel: ComponentType<any> = lazy(() => import('./RegistryPanel'));
package/src/meta.ts CHANGED
@@ -2,17 +2,37 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { type Plugin } from '@dxos/app-framework';
5
+ import { Plugin } from '@dxos/app-framework';
6
+ import { DXN } from '@dxos/keys';
6
7
  import { trim } from '@dxos/util';
7
8
 
8
- export const meta: Plugin.Meta = {
9
- id: 'org.dxos.plugin.debug',
9
+ export const meta = Plugin.makeMeta({
10
+ key: DXN.make('org.dxos.plugin.debug'),
10
11
  name: 'Debug',
12
+ author: 'DXOS',
13
+ spec: 'PLUGIN.mdl',
11
14
  description: trim`
12
- Comprehensive developer toolkit for troubleshooting applications, generating test data, and exploring automation capabilities.
13
- Inspect objects, monitor events, and debug plugin behavior in real-time.
15
+ DebugPlugin is the developer toolkit for DXOS Composer. It adds a structured Devtools node
16
+ to the navigation graph — grouped into Client, HALO, ECHO, Mesh, and EDGE sub-sections —
17
+ exposing panel views for config, storage, logs, diagnostics, identity, devices, feeds,
18
+ objects, schemas, automerge internals, network topology, EDGE workflows, and invocation
19
+ traces, all driven by the @dxos/devtools component library.
20
+
21
+ The plugin contributes a Debug companion tab to every ECHO object in the deck so developers
22
+ can inspect raw field values and DXNs inline, and a Space Objects companion panel that lists
23
+ all objects in the active space with live reactive updates.
24
+
25
+ Test-data generation is available via a SpaceGenerator article surface: developers can
26
+ create configurable batches of synthetic ECHO objects into any space collection, with a
27
+ status indicator showing when generation is running.
28
+
29
+ Additional utilities include a wireframe overlay mode that draws labelled borders around
30
+ every article and section surface, a log-capture and download facility backed by an
31
+ IdbLogStore, a ToolsExplorer connected to the DXOS MCP introspect service, and a
32
+ globalThis helper for manual storage-version testing.
14
33
  `,
15
34
  icon: 'ph--bug--regular',
16
35
  source: 'https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-debug',
17
36
  screenshots: ['https://dxos.network/plugin-details-debug-dark.png'],
18
- };
37
+ tags: ['labs'],
38
+ });