@dxos/plugin-space 0.7.5-main.9d26e3a → 0.7.5-main.e9bb01b

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 (211) hide show
  1. package/dist/lib/browser/app-graph-builder-J4NX44Y4.mjs +367 -0
  2. package/dist/lib/browser/app-graph-builder-J4NX44Y4.mjs.map +7 -0
  3. package/dist/lib/browser/app-graph-serializer-QF7VVRCV.mjs +80 -0
  4. package/dist/lib/browser/app-graph-serializer-QF7VVRCV.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-6UIFMVVI.mjs +528 -0
  6. package/dist/lib/browser/chunk-6UIFMVVI.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-PQXZCNAU.mjs +13 -0
  8. package/dist/lib/browser/chunk-PQXZCNAU.mjs.map +7 -0
  9. package/dist/lib/browser/chunk-RLZQJD47.mjs +22 -0
  10. package/dist/lib/browser/chunk-RLZQJD47.mjs.map +7 -0
  11. package/dist/lib/browser/chunk-SDK7RZI3.mjs +133 -0
  12. package/dist/lib/browser/chunk-SDK7RZI3.mjs.map +7 -0
  13. package/dist/lib/browser/{chunk-54VE4GTA.mjs → chunk-XPZ6IIXF.mjs} +11 -15
  14. package/dist/lib/browser/chunk-XPZ6IIXF.mjs.map +7 -0
  15. package/dist/lib/browser/chunk-XZ2NH3CP.mjs +1713 -0
  16. package/dist/lib/browser/chunk-XZ2NH3CP.mjs.map +7 -0
  17. package/dist/lib/browser/identity-created-25TK5XNO.mjs +28 -0
  18. package/dist/lib/browser/identity-created-25TK5XNO.mjs.map +7 -0
  19. package/dist/lib/browser/index.mjs +186 -3481
  20. package/dist/lib/browser/index.mjs.map +4 -4
  21. package/dist/lib/browser/intent-resolver-IEDVV74T.mjs +523 -0
  22. package/dist/lib/browser/intent-resolver-IEDVV74T.mjs.map +7 -0
  23. package/dist/lib/browser/meta.json +1 -1
  24. package/dist/lib/browser/react-root-6VRPRLQR.mjs +28 -0
  25. package/dist/lib/browser/react-root-6VRPRLQR.mjs.map +7 -0
  26. package/dist/lib/browser/react-surface-DMNDXLZ7.mjs +231 -0
  27. package/dist/lib/browser/react-surface-DMNDXLZ7.mjs.map +7 -0
  28. package/dist/lib/browser/settings-K4JPYYEM.mjs +24 -0
  29. package/dist/lib/browser/settings-K4JPYYEM.mjs.map +7 -0
  30. package/dist/lib/browser/spaces-ready-2MUWWVVO.mjs +199 -0
  31. package/dist/lib/browser/spaces-ready-2MUWWVVO.mjs.map +7 -0
  32. package/dist/lib/browser/state-6DCY5YJP.mjs +47 -0
  33. package/dist/lib/browser/state-6DCY5YJP.mjs.map +7 -0
  34. package/dist/lib/browser/types/index.mjs +6 -6
  35. package/dist/lib/node/app-graph-builder-ZOPG4MOS.cjs +370 -0
  36. package/dist/lib/node/app-graph-builder-ZOPG4MOS.cjs.map +7 -0
  37. package/dist/lib/node/app-graph-serializer-QE2G4MWB.cjs +88 -0
  38. package/dist/lib/node/app-graph-serializer-QE2G4MWB.cjs.map +7 -0
  39. package/dist/lib/node/chunk-3QWZCN6L.cjs +556 -0
  40. package/dist/lib/node/chunk-3QWZCN6L.cjs.map +7 -0
  41. package/dist/lib/node/{chunk-YF2AQ7KP.cjs → chunk-6MEOP3DH.cjs} +18 -22
  42. package/dist/lib/node/chunk-6MEOP3DH.cjs.map +7 -0
  43. package/dist/lib/node/chunk-D6BFKOXY.cjs +150 -0
  44. package/dist/lib/node/chunk-D6BFKOXY.cjs.map +7 -0
  45. package/dist/lib/node/chunk-SZEIQGQU.cjs +1710 -0
  46. package/dist/lib/node/chunk-SZEIQGQU.cjs.map +7 -0
  47. package/dist/lib/node/chunk-UX3U4RU2.cjs +42 -0
  48. package/dist/lib/node/chunk-UX3U4RU2.cjs.map +7 -0
  49. package/dist/lib/node/{chunk-46S3JOES.cjs → chunk-WZR6OAN3.cjs} +9 -12
  50. package/dist/lib/node/chunk-WZR6OAN3.cjs.map +7 -0
  51. package/dist/lib/node/identity-created-7G5U7R36.cjs +44 -0
  52. package/dist/lib/node/identity-created-7G5U7R36.cjs.map +7 -0
  53. package/dist/lib/node/index.cjs +171 -3469
  54. package/dist/lib/node/index.cjs.map +4 -4
  55. package/dist/lib/node/intent-resolver-N2W2L6OL.cjs +522 -0
  56. package/dist/lib/node/intent-resolver-N2W2L6OL.cjs.map +7 -0
  57. package/dist/lib/node/meta.json +1 -1
  58. package/dist/lib/node/react-root-YD35W3VU.cjs +50 -0
  59. package/dist/lib/node/react-root-YD35W3VU.cjs.map +7 -0
  60. package/dist/lib/node/react-surface-QPLXK7DT.cjs +229 -0
  61. package/dist/lib/node/react-surface-QPLXK7DT.cjs.map +7 -0
  62. package/dist/lib/node/{meta.cjs → settings-TEELGWS4.cjs} +19 -13
  63. package/dist/lib/node/settings-TEELGWS4.cjs.map +7 -0
  64. package/dist/lib/node/spaces-ready-PZZQWS6T.cjs +210 -0
  65. package/dist/lib/node/spaces-ready-PZZQWS6T.cjs.map +7 -0
  66. package/dist/lib/node/state-WPZC4JXB.cjs +61 -0
  67. package/dist/lib/node/state-WPZC4JXB.cjs.map +7 -0
  68. package/dist/lib/node/types/index.cjs +18 -18
  69. package/dist/lib/node/types/index.cjs.map +1 -1
  70. package/dist/lib/node-esm/app-graph-builder-TKGLOK22.mjs +368 -0
  71. package/dist/lib/node-esm/app-graph-builder-TKGLOK22.mjs.map +7 -0
  72. package/dist/lib/node-esm/app-graph-serializer-HLX2JRNF.mjs +81 -0
  73. package/dist/lib/node-esm/app-graph-serializer-HLX2JRNF.mjs.map +7 -0
  74. package/dist/lib/node-esm/chunk-4UX5WGKJ.mjs +134 -0
  75. package/dist/lib/node-esm/chunk-4UX5WGKJ.mjs.map +7 -0
  76. package/dist/lib/node-esm/{chunk-CDZETPO7.mjs → chunk-AAQRELDK.mjs} +11 -15
  77. package/dist/lib/node-esm/chunk-AAQRELDK.mjs.map +7 -0
  78. package/dist/lib/node-esm/{chunk-2MNFEB23.mjs → chunk-ICCM4YRJ.mjs} +5 -7
  79. package/dist/lib/node-esm/chunk-ICCM4YRJ.mjs.map +7 -0
  80. package/dist/lib/node-esm/chunk-LA45TPAN.mjs +529 -0
  81. package/dist/lib/node-esm/chunk-LA45TPAN.mjs.map +7 -0
  82. package/dist/lib/node-esm/chunk-LGL4A5B5.mjs +23 -0
  83. package/dist/lib/node-esm/chunk-LGL4A5B5.mjs.map +7 -0
  84. package/dist/lib/node-esm/chunk-N6EAOMIS.mjs +1714 -0
  85. package/dist/lib/node-esm/chunk-N6EAOMIS.mjs.map +7 -0
  86. package/dist/lib/node-esm/identity-created-7THGZ7EW.mjs +29 -0
  87. package/dist/lib/node-esm/identity-created-7THGZ7EW.mjs.map +7 -0
  88. package/dist/lib/node-esm/index.mjs +186 -3481
  89. package/dist/lib/node-esm/index.mjs.map +4 -4
  90. package/dist/lib/node-esm/intent-resolver-L2TKJU4I.mjs +524 -0
  91. package/dist/lib/node-esm/intent-resolver-L2TKJU4I.mjs.map +7 -0
  92. package/dist/lib/node-esm/meta.json +1 -1
  93. package/dist/lib/node-esm/react-root-Q5MFDXZE.mjs +29 -0
  94. package/dist/lib/node-esm/react-root-Q5MFDXZE.mjs.map +7 -0
  95. package/dist/lib/node-esm/react-surface-RQQZ5BOG.mjs +232 -0
  96. package/dist/lib/node-esm/react-surface-RQQZ5BOG.mjs.map +7 -0
  97. package/dist/lib/node-esm/settings-KXYUZBLN.mjs +25 -0
  98. package/dist/lib/node-esm/settings-KXYUZBLN.mjs.map +7 -0
  99. package/dist/lib/node-esm/spaces-ready-MWNP3WBX.mjs +200 -0
  100. package/dist/lib/node-esm/spaces-ready-MWNP3WBX.mjs.map +7 -0
  101. package/dist/lib/node-esm/state-5GH2D5U4.mjs +48 -0
  102. package/dist/lib/node-esm/state-5GH2D5U4.mjs.map +7 -0
  103. package/dist/lib/node-esm/types/index.mjs +6 -6
  104. package/dist/types/src/SpacePlugin.d.ts +1 -24
  105. package/dist/types/src/SpacePlugin.d.ts.map +1 -1
  106. package/dist/types/src/capabilities/app-graph-builder.d.ts +181 -0
  107. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -0
  108. package/dist/types/src/capabilities/app-graph-serializer.d.ts +4 -0
  109. package/dist/types/src/capabilities/app-graph-serializer.d.ts.map +1 -0
  110. package/dist/types/src/capabilities/capabilities.d.ts +21 -0
  111. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -0
  112. package/dist/types/src/capabilities/identity-created.d.ts +4 -0
  113. package/dist/types/src/capabilities/identity-created.d.ts.map +1 -0
  114. package/dist/types/src/capabilities/index.d.ts +195 -0
  115. package/dist/types/src/capabilities/index.d.ts.map +1 -0
  116. package/dist/types/src/capabilities/intent-resolver.d.ts +8 -0
  117. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -0
  118. package/dist/types/src/capabilities/react-root.d.ts +7 -0
  119. package/dist/types/src/capabilities/react-root.d.ts.map +1 -0
  120. package/dist/types/src/capabilities/react-surface.d.ts +7 -0
  121. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
  122. package/dist/types/src/capabilities/settings.d.ts +4 -0
  123. package/dist/types/src/capabilities/settings.d.ts.map +1 -0
  124. package/dist/types/src/capabilities/spaces-ready.d.ts +4 -0
  125. package/dist/types/src/capabilities/spaces-ready.d.ts.map +1 -0
  126. package/dist/types/src/capabilities/state.d.ts +5 -0
  127. package/dist/types/src/capabilities/state.d.ts.map +1 -0
  128. package/dist/types/src/components/AdvancedObjectSettings/AdvancedObjectSettings.d.ts +7 -0
  129. package/dist/types/src/components/AdvancedObjectSettings/AdvancedObjectSettings.d.ts.map +1 -0
  130. package/dist/types/src/components/AdvancedObjectSettings/ForeignKeys.d.ts +8 -0
  131. package/dist/types/src/components/AdvancedObjectSettings/ForeignKeys.d.ts.map +1 -0
  132. package/dist/types/src/components/AdvancedObjectSettings/index.d.ts +2 -0
  133. package/dist/types/src/components/AdvancedObjectSettings/index.d.ts.map +1 -0
  134. package/dist/types/src/components/AwaitingObject.d.ts.map +1 -1
  135. package/dist/types/src/components/BaseObjectSettings.d.ts +7 -0
  136. package/dist/types/src/components/BaseObjectSettings.d.ts.map +1 -0
  137. package/dist/types/src/components/CreateDialog/CreateObjectDialog.d.ts +1 -2
  138. package/dist/types/src/components/CreateDialog/CreateObjectDialog.d.ts.map +1 -1
  139. package/dist/types/src/components/CreateDialog/CreateObjectPanel.d.ts +6 -6
  140. package/dist/types/src/components/CreateDialog/CreateObjectPanel.d.ts.map +1 -1
  141. package/dist/types/src/components/CreateDialog/CreateSpaceDialog.d.ts.map +1 -1
  142. package/dist/types/src/components/JoinDialog.d.ts.map +1 -1
  143. package/dist/types/src/components/SpacePluginSettings.d.ts.map +1 -1
  144. package/dist/types/src/components/SpacePresence.d.ts.map +1 -1
  145. package/dist/types/src/components/SpaceSettings/SpaceSettingsDialog.d.ts.map +1 -1
  146. package/dist/types/src/components/SpaceSettings/SpaceSettingsPanel.d.ts.map +1 -1
  147. package/dist/types/src/components/SyncStatus/SyncStatus.d.ts.map +1 -1
  148. package/dist/types/src/components/index.d.ts +2 -1
  149. package/dist/types/src/components/index.d.ts.map +1 -1
  150. package/dist/types/src/events.d.ts +5 -0
  151. package/dist/types/src/events.d.ts.map +1 -0
  152. package/dist/types/src/index.d.ts +3 -5
  153. package/dist/types/src/index.d.ts.map +1 -1
  154. package/dist/types/src/meta.d.ts +2 -4
  155. package/dist/types/src/meta.d.ts.map +1 -1
  156. package/dist/types/src/translations.d.ts +12 -0
  157. package/dist/types/src/translations.d.ts.map +1 -1
  158. package/dist/types/src/types/types.d.ts +7 -28
  159. package/dist/types/src/types/types.d.ts.map +1 -1
  160. package/dist/types/src/util.d.ts +7 -6
  161. package/dist/types/src/util.d.ts.map +1 -1
  162. package/dist/types/tsconfig.tsbuildinfo +1 -1
  163. package/package.json +38 -46
  164. package/src/SpacePlugin.tsx +114 -1412
  165. package/src/capabilities/app-graph-builder.ts +394 -0
  166. package/src/capabilities/app-graph-serializer.ts +73 -0
  167. package/src/capabilities/capabilities.ts +26 -0
  168. package/src/capabilities/identity-created.ts +26 -0
  169. package/src/capabilities/index.ts +17 -0
  170. package/src/capabilities/intent-resolver.ts +489 -0
  171. package/src/capabilities/react-root.tsx +20 -0
  172. package/src/capabilities/react-surface.tsx +226 -0
  173. package/src/capabilities/settings.ts +17 -0
  174. package/src/capabilities/spaces-ready.ts +230 -0
  175. package/src/capabilities/state.ts +45 -0
  176. package/src/components/AdvancedObjectSettings/AdvancedObjectSettings.tsx +72 -0
  177. package/src/components/AdvancedObjectSettings/ForeignKeys.tsx +51 -0
  178. package/src/components/AdvancedObjectSettings/index.ts +5 -0
  179. package/src/components/AwaitingObject.tsx +5 -11
  180. package/src/components/{DefaultObjectSettings.tsx → BaseObjectSettings.tsx} +2 -2
  181. package/src/components/CreateDialog/CreateObjectDialog.tsx +19 -19
  182. package/src/components/CreateDialog/CreateObjectPanel.tsx +136 -99
  183. package/src/components/CreateDialog/CreateSpaceDialog.tsx +10 -8
  184. package/src/components/JoinDialog.tsx +27 -19
  185. package/src/components/SpacePluginSettings.tsx +5 -10
  186. package/src/components/SpacePresence.tsx +7 -7
  187. package/src/components/SpaceSettings/SpaceSettingsDialog.tsx +16 -1
  188. package/src/components/SpaceSettings/SpaceSettingsPanel.tsx +3 -4
  189. package/src/components/SyncStatus/InlineSyncStatus.tsx +2 -2
  190. package/src/components/SyncStatus/SyncStatus.tsx +2 -1
  191. package/src/components/index.ts +2 -1
  192. package/src/events.ts +12 -0
  193. package/src/index.ts +3 -7
  194. package/src/meta.ts +2 -3
  195. package/src/translations.ts +4 -0
  196. package/src/types/types.ts +12 -51
  197. package/src/util.tsx +38 -28
  198. package/dist/lib/browser/chunk-54VE4GTA.mjs.map +0 -7
  199. package/dist/lib/browser/chunk-73BCBSLP.mjs +0 -15
  200. package/dist/lib/browser/chunk-73BCBSLP.mjs.map +0 -7
  201. package/dist/lib/browser/meta.mjs +0 -11
  202. package/dist/lib/browser/meta.mjs.map +0 -7
  203. package/dist/lib/node/chunk-46S3JOES.cjs.map +0 -7
  204. package/dist/lib/node/chunk-YF2AQ7KP.cjs.map +0 -7
  205. package/dist/lib/node/meta.cjs.map +0 -7
  206. package/dist/lib/node-esm/chunk-2MNFEB23.mjs.map +0 -7
  207. package/dist/lib/node-esm/chunk-CDZETPO7.mjs.map +0 -7
  208. package/dist/lib/node-esm/meta.mjs +0 -12
  209. package/dist/lib/node-esm/meta.mjs.map +0 -7
  210. package/dist/types/src/components/DefaultObjectSettings.d.ts +0 -7
  211. package/dist/types/src/components/DefaultObjectSettings.d.ts.map +0 -1
@@ -0,0 +1,394 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Capabilities, contributes, createIntent, type PluginsContext } from '@dxos/app-framework';
6
+ import {
7
+ Expando,
8
+ Filter,
9
+ FQ_ID_LENGTH,
10
+ getSpace,
11
+ isEchoObject,
12
+ isSpace,
13
+ OBJECT_ID_LENGTH,
14
+ parseId,
15
+ type ReactiveEchoObject,
16
+ SPACE_ID_LENGTH,
17
+ SpaceState,
18
+ type Space,
19
+ } from '@dxos/client/echo';
20
+ import { getTypename, isDeleted } from '@dxos/live-object';
21
+ import { log } from '@dxos/log';
22
+ import { ClientCapabilities } from '@dxos/plugin-client';
23
+ import { createExtension, toSignal, type Node } from '@dxos/plugin-graph';
24
+ import { nonNullable } from '@dxos/util';
25
+
26
+ import { SpaceCapabilities } from './capabilities';
27
+ import { SPACE_PLUGIN } from '../meta';
28
+ import { CollectionType, SpaceAction, type SpaceSettingsProps } from '../types';
29
+ import {
30
+ constructObjectActions,
31
+ constructSpaceActions,
32
+ constructSpaceNode,
33
+ createObjectNode,
34
+ memoizeQuery,
35
+ SHARED,
36
+ SPACES,
37
+ } from '../util';
38
+
39
+ export default (context: PluginsContext) => {
40
+ const resolve = (typename: string) =>
41
+ context.requestCapabilities(Capabilities.Metadata).find(({ id }) => id === typename)?.metadata ?? {};
42
+
43
+ const spacesNode = {
44
+ id: SPACES,
45
+ type: SPACES,
46
+ cacheable: ['label', 'role'],
47
+ properties: {
48
+ label: ['spaces label', { ns: SPACE_PLUGIN }],
49
+ icon: 'ph--planet--regular',
50
+ testId: 'spacePlugin.spaces',
51
+ role: 'branch',
52
+ disposition: 'collection',
53
+ disabled: true,
54
+ childrenPersistenceClass: 'echo',
55
+ onRearrangeChildren: async (nextOrder: Space[]) => {
56
+ const { graph } = context.requestCapability(Capabilities.AppGraph);
57
+ const client = context.requestCapability(ClientCapabilities.Client);
58
+
59
+ // NOTE: This is needed to ensure order is updated by next animation frame.
60
+ // TODO(wittjosiah): Is there a better way to do this?
61
+ // If not, graph should be passed as an argument to the extension.
62
+ graph._sortEdges(
63
+ SPACES,
64
+ 'outbound',
65
+ nextOrder.map(({ id }) => id),
66
+ );
67
+
68
+ const {
69
+ objects: [spacesOrder],
70
+ } = await client.spaces.default.db.query(Filter.schema(Expando, { key: SHARED })).run();
71
+ if (spacesOrder) {
72
+ spacesOrder.order = nextOrder.map(({ id }) => id);
73
+ } else {
74
+ log.warn('spaces order object not found');
75
+ }
76
+ },
77
+ },
78
+ };
79
+
80
+ return contributes(Capabilities.AppGraphBuilder, [
81
+ // Primary actions.
82
+ createExtension({
83
+ id: `${SPACE_PLUGIN}/primary-actions`,
84
+ position: 'hoist',
85
+ filter: (node): node is Node<null> => node.id === 'root',
86
+ actions: () => [
87
+ {
88
+ id: SpaceAction.OpenCreateSpace._tag,
89
+ data: async () => {
90
+ const { dispatchPromise: dispatch } = context.requestCapability(Capabilities.IntentDispatcher);
91
+ await dispatch(createIntent(SpaceAction.OpenCreateSpace));
92
+ },
93
+ properties: {
94
+ label: ['create space label', { ns: SPACE_PLUGIN }],
95
+ icon: 'ph--plus--regular',
96
+ testId: 'spacePlugin.createSpace',
97
+ disposition: 'item',
98
+ },
99
+ },
100
+ {
101
+ id: SpaceAction.Join._tag,
102
+ data: async () => {
103
+ const { dispatchPromise: dispatch } = context.requestCapability(Capabilities.IntentDispatcher);
104
+ await dispatch(createIntent(SpaceAction.Join));
105
+ },
106
+ properties: {
107
+ label: ['join space label', { ns: SPACE_PLUGIN }],
108
+ icon: 'ph--sign-in--regular',
109
+ testId: 'spacePlugin.joinSpace',
110
+ disposition: 'item',
111
+ },
112
+ },
113
+ ],
114
+ }),
115
+
116
+ // Create spaces group node.
117
+ createExtension({
118
+ id: `${SPACE_PLUGIN}/root`,
119
+ position: 'hoist',
120
+ filter: (node): node is Node<null> => node.id === 'root',
121
+ connector: () => [spacesNode],
122
+ resolver: ({ id }) => (id === SPACES ? spacesNode : undefined),
123
+ }),
124
+
125
+ // Create space nodes.
126
+ createExtension({
127
+ id: SPACES,
128
+ filter: (node): node is Node<null> => node.id === SPACES,
129
+ connector: () => {
130
+ const client = context.requestCapability(ClientCapabilities.Client);
131
+ const spaces = toSignal(
132
+ (onChange) => client.spaces.subscribe(() => onChange()).unsubscribe,
133
+ () => client.spaces.get(),
134
+ );
135
+
136
+ const isReady = toSignal(
137
+ (onChange) => client.spaces.isReady.subscribe(() => onChange()).unsubscribe,
138
+ () => client.spaces.isReady.get(),
139
+ );
140
+
141
+ if (!spaces || !isReady) {
142
+ return;
143
+ }
144
+
145
+ const state = context.requestCapability(SpaceCapabilities.State);
146
+ const settings = context
147
+ .requestCapabilities(Capabilities.SettingsStore)[0]
148
+ ?.getStore<SpaceSettingsProps>(SPACE_PLUGIN)?.value;
149
+
150
+ // TODO(wittjosiah): During client reset, accessing default space throws.
151
+ try {
152
+ const [spacesOrder] = memoizeQuery(client.spaces.default, Filter.schema(Expando, { key: SHARED }));
153
+ const order: string[] = spacesOrder?.order ?? [];
154
+ const orderMap = new Map(order.map((id, index) => [id, index]));
155
+ return [
156
+ ...spaces
157
+ .filter((space) => orderMap.has(space.id))
158
+ .sort((a, b) => orderMap.get(a.id)! - orderMap.get(b.id)!),
159
+ ...spaces.filter((space) => !orderMap.has(space.id)),
160
+ ]
161
+ .filter((space) => (settings?.showHidden ? true : space.state.get() !== SpaceState.SPACE_INACTIVE))
162
+ .map((space) =>
163
+ constructSpaceNode({
164
+ space,
165
+ navigable: state.navigableCollections,
166
+ personal: space === client.spaces.default,
167
+ namesCache: state.spaceNames,
168
+ resolve,
169
+ }),
170
+ );
171
+ } catch {}
172
+ },
173
+ resolver: ({ id }) => {
174
+ if (id.length !== SPACE_ID_LENGTH) {
175
+ return;
176
+ }
177
+
178
+ const client = context.requestCapability(ClientCapabilities.Client);
179
+ const spaces = toSignal(
180
+ (onChange) => client.spaces.subscribe(() => onChange()).unsubscribe,
181
+ () => client.spaces.get(),
182
+ );
183
+
184
+ const isReady = toSignal(
185
+ (onChange) => client.spaces.isReady.subscribe(() => onChange()).unsubscribe,
186
+ () => client.spaces.isReady.get(),
187
+ );
188
+
189
+ if (!spaces || !isReady) {
190
+ return;
191
+ }
192
+
193
+ const space = spaces.find((space) => space.id === id);
194
+ if (!space) {
195
+ return;
196
+ }
197
+
198
+ if (space.state.get() === SpaceState.SPACE_INACTIVE) {
199
+ return false;
200
+ } else {
201
+ const state = context.requestCapability(SpaceCapabilities.State);
202
+ return constructSpaceNode({
203
+ space,
204
+ navigable: state.navigableCollections,
205
+ personal: space === client.spaces.default,
206
+ namesCache: state.spaceNames,
207
+ resolve,
208
+ });
209
+ }
210
+ },
211
+ }),
212
+
213
+ // Create space actions.
214
+ createExtension({
215
+ id: `${SPACE_PLUGIN}/actions`,
216
+ filter: (node): node is Node<Space> => isSpace(node.data),
217
+ actions: ({ node }) => {
218
+ const space = node.data;
219
+ const { dispatchPromise: dispatch } = context.requestCapability(Capabilities.IntentDispatcher);
220
+ const client = context.requestCapability(ClientCapabilities.Client);
221
+ const state = context.requestCapability(SpaceCapabilities.State);
222
+ return constructSpaceActions({
223
+ space,
224
+ dispatch,
225
+ personal: space === client.spaces.default,
226
+ migrating: state.sdkMigrationRunning[space.id],
227
+ });
228
+ },
229
+ }),
230
+
231
+ // Create nodes for objects in the root collection of a space.
232
+ createExtension({
233
+ id: `${SPACE_PLUGIN}/root-collection`,
234
+ filter: (node): node is Node<Space> => isSpace(node.data),
235
+ connector: ({ node }) => {
236
+ const space = node.data;
237
+ const spaceState = toSignal(
238
+ (onChange) => space.state.subscribe(() => onChange()).unsubscribe,
239
+ () => space.state.get(),
240
+ space.id,
241
+ );
242
+ if (spaceState !== SpaceState.SPACE_READY) {
243
+ return;
244
+ }
245
+
246
+ const collection = space.properties[CollectionType.typename]?.target as CollectionType | undefined;
247
+ if (!collection) {
248
+ return;
249
+ }
250
+
251
+ const state = context.requestCapability(SpaceCapabilities.State);
252
+
253
+ return collection.objects
254
+ .map((object) => object.target)
255
+ .filter(nonNullable)
256
+ .map((object) => createObjectNode({ object, space, resolve, navigable: state.navigableCollections }))
257
+ .filter(nonNullable);
258
+ },
259
+ }),
260
+
261
+ // Create nodes for objects in a collection or by its fully qualified id.
262
+ createExtension({
263
+ id: `${SPACE_PLUGIN}/objects`,
264
+ filter: (node): node is Node<CollectionType> => node.data instanceof CollectionType,
265
+ connector: ({ node }) => {
266
+ const collection = node.data;
267
+ const space = getSpace(collection);
268
+ if (!space) {
269
+ return;
270
+ }
271
+
272
+ const state = context.requestCapability(SpaceCapabilities.State);
273
+
274
+ return collection.objects
275
+ .map((object) => object.target)
276
+ .filter(nonNullable)
277
+ .map((object) => createObjectNode({ object, space, resolve, navigable: state.navigableCollections }))
278
+ .filter(nonNullable);
279
+ },
280
+ resolver: ({ id }) => {
281
+ if (id.length !== FQ_ID_LENGTH) {
282
+ return;
283
+ }
284
+
285
+ const [spaceId, objectId] = id.split(':');
286
+ if (spaceId.length !== SPACE_ID_LENGTH && objectId.length !== OBJECT_ID_LENGTH) {
287
+ return;
288
+ }
289
+
290
+ const client = context.requestCapability(ClientCapabilities.Client);
291
+ const space = client.spaces.get().find((space) => space.id === spaceId);
292
+ if (!space) {
293
+ return;
294
+ }
295
+
296
+ const spaceState = toSignal(
297
+ (onChange) => space.state.subscribe(() => onChange()).unsubscribe,
298
+ () => space.state.get(),
299
+ space.id,
300
+ );
301
+ if (spaceState !== SpaceState.SPACE_READY) {
302
+ return;
303
+ }
304
+
305
+ const [object] = memoizeQuery(space, { id: objectId });
306
+ if (!object) {
307
+ return;
308
+ }
309
+
310
+ if (isDeleted(object)) {
311
+ return false;
312
+ } else {
313
+ const state = context.requestCapability(SpaceCapabilities.State);
314
+ return createObjectNode({ object, space, resolve, navigable: state.navigableCollections });
315
+ }
316
+ },
317
+ }),
318
+
319
+ // Create collection actions and action groups.
320
+ createExtension({
321
+ id: `${SPACE_PLUGIN}/object-actions`,
322
+ filter: (node): node is Node<ReactiveEchoObject<any>> => isEchoObject(node.data),
323
+ actions: ({ node }) => {
324
+ const { dispatchPromise: dispatch } = context.requestCapability(Capabilities.IntentDispatcher);
325
+ const state = context.requestCapability(SpaceCapabilities.State);
326
+ return constructObjectActions({ node, dispatch, navigable: state.navigableCollections });
327
+ },
328
+ }),
329
+
330
+ // Create nodes for object settings.
331
+ createExtension({
332
+ id: `${SPACE_PLUGIN}/settings-for-subject`,
333
+ resolver: ({ id }) => {
334
+ // TODO(Zan): Find util (or make one).
335
+ if (!id.endsWith('~settings')) {
336
+ return;
337
+ }
338
+
339
+ const type = 'orphan-settings-for-subject';
340
+ const icon = 'ph--gear--regular';
341
+
342
+ const [subjectId] = id.split('~');
343
+ const { spaceId, objectId } = parseId(subjectId);
344
+ const client = context.requestCapability(ClientCapabilities.Client);
345
+ const spaces = toSignal(
346
+ (onChange) => client.spaces.subscribe(() => onChange()).unsubscribe,
347
+ () => client.spaces.get(),
348
+ );
349
+ const space = spaces?.find((space) => space.id === spaceId && space.state.get() === SpaceState.SPACE_READY);
350
+ if (!objectId) {
351
+ const label = space
352
+ ? space.properties.name || ['unnamed space label', { ns: SPACE_PLUGIN }]
353
+ : ['unnamed object settings label', { ns: SPACE_PLUGIN }];
354
+
355
+ // TODO(wittjosiah): Support comments for arbitrary subjects.
356
+ // This is to ensure that the comments panel is not stuck on an old object.
357
+ return {
358
+ id,
359
+ type,
360
+ data: null,
361
+ properties: {
362
+ icon,
363
+ label,
364
+ showResolvedThreads: false,
365
+ object: null,
366
+ space,
367
+ },
368
+ };
369
+ }
370
+
371
+ const [object] = memoizeQuery(space, { id: objectId });
372
+ if (!object || !subjectId) {
373
+ return;
374
+ }
375
+
376
+ const meta = resolve(getTypename(object) ?? '');
377
+ const label = meta.label?.(object) ||
378
+ object.name ||
379
+ meta.placeholder || ['unnamed object settings label', { ns: SPACE_PLUGIN }];
380
+
381
+ return {
382
+ id,
383
+ type,
384
+ data: null,
385
+ properties: {
386
+ icon,
387
+ label,
388
+ object,
389
+ },
390
+ };
391
+ },
392
+ }),
393
+ ]);
394
+ };
@@ -0,0 +1,73 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Capabilities, contributes, createIntent, type PluginsContext } from '@dxos/app-framework';
6
+ import { isSpace } from '@dxos/client/echo';
7
+ import { create } from '@dxos/live-object';
8
+
9
+ import { SPACE_PLUGIN } from '../meta';
10
+ import translations from '../translations';
11
+ import { CollectionType, SpaceAction } from '../types';
12
+ import { SPACES, SPACE_TYPE } from '../util';
13
+
14
+ // https://stackoverflow.com/a/19016910
15
+ const DIRECTORY_TYPE = 'text/directory';
16
+
17
+ export default (context: PluginsContext) =>
18
+ contributes(Capabilities.AppGraphSerializer, [
19
+ {
20
+ inputType: SPACES,
21
+ outputType: DIRECTORY_TYPE,
22
+ serialize: (node) => ({
23
+ name: translations[0]['en-US'][SPACE_PLUGIN]['spaces label'],
24
+ data: translations[0]['en-US'][SPACE_PLUGIN]['spaces label'],
25
+ type: DIRECTORY_TYPE,
26
+ }),
27
+ deserialize: () => {
28
+ // No-op.
29
+ },
30
+ },
31
+ {
32
+ inputType: SPACE_TYPE,
33
+ outputType: DIRECTORY_TYPE,
34
+ serialize: (node) => ({
35
+ name: node.data.properties.name ?? translations[0]['en-US'][SPACE_PLUGIN]['unnamed space label'],
36
+ data: node.data.properties.name ?? translations[0]['en-US'][SPACE_PLUGIN]['unnamed space label'],
37
+ type: DIRECTORY_TYPE,
38
+ }),
39
+ deserialize: async (data) => {
40
+ const { dispatchPromise: dispatch } = context.requestCapability(Capabilities.IntentDispatcher);
41
+ const result = await dispatch(createIntent(SpaceAction.Create, { name: data.name, edgeReplication: true }));
42
+ return result.data?.space;
43
+ },
44
+ },
45
+ {
46
+ inputType: CollectionType.typename,
47
+ outputType: DIRECTORY_TYPE,
48
+ serialize: (node) => ({
49
+ name: node.data.name ?? translations[0]['en-US'][SPACE_PLUGIN]['unnamed collection label'],
50
+ data: node.data.name ?? translations[0]['en-US'][SPACE_PLUGIN]['unnamed collection label'],
51
+ type: DIRECTORY_TYPE,
52
+ }),
53
+ deserialize: async (data, ancestors) => {
54
+ const space = ancestors.find(isSpace);
55
+ const collection =
56
+ ancestors.findLast((ancestor) => ancestor instanceof CollectionType) ??
57
+ space?.properties[CollectionType.typename]?.target;
58
+ if (!space || !collection) {
59
+ return;
60
+ }
61
+
62
+ const { dispatchPromise: dispatch } = context.requestCapability(Capabilities.IntentDispatcher);
63
+ const result = await dispatch(
64
+ createIntent(SpaceAction.AddObject, {
65
+ target: collection,
66
+ object: create(CollectionType, { name: data.name, objects: [], views: {} }),
67
+ }),
68
+ );
69
+
70
+ return result.data?.object;
71
+ },
72
+ },
73
+ ]);
@@ -0,0 +1,26 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { defineCapability, type Label } from '@dxos/app-framework';
6
+ import { type DeepReadonly } from '@dxos/util';
7
+
8
+ import { SPACE_PLUGIN } from '../meta';
9
+ import { type PluginState } from '../types';
10
+
11
+ export namespace SpaceCapabilities {
12
+ export const State = defineCapability<DeepReadonly<PluginState>>(`${SPACE_PLUGIN}/capability/state`);
13
+ export const MutableState = defineCapability<PluginState>(`${SPACE_PLUGIN}/capability/state`);
14
+
15
+ export type SpaceSettingsPanel = { id: string; label: Label };
16
+ export const SettingsPanel = defineCapability<SpaceSettingsPanel>(`${SPACE_PLUGIN}/capability/settings-panel`);
17
+ }
18
+
19
+ // TODO(wittjosiah): Factor out.
20
+ export namespace ThreadCapabilities {
21
+ type ThreadCapability<T = any> = {
22
+ predicate: (obj: any) => obj is T;
23
+ createSort: (obj: T) => (anchorA: string | undefined, anchorB: string | undefined) => number;
24
+ };
25
+ export const Thread = defineCapability<ThreadCapability>(`${SPACE_PLUGIN}/capability/thread`);
26
+ }
@@ -0,0 +1,26 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { contributes, type PluginsContext, Capabilities } from '@dxos/app-framework';
6
+ import { create, makeRef } from '@dxos/live-object';
7
+ import { Migrations } from '@dxos/migrations';
8
+ import { ClientCapabilities } from '@dxos/plugin-client';
9
+
10
+ import { CollectionType } from '../types';
11
+
12
+ export default async (context: PluginsContext) => {
13
+ const client = context.requestCapability(ClientCapabilities.Client);
14
+ await client.spaces.waitUntilReady();
15
+
16
+ const defaultSpace = client.spaces.default;
17
+ await defaultSpace.waitUntilReady();
18
+
19
+ // Create root collection structure.
20
+ defaultSpace.properties[CollectionType.typename] = makeRef(create(CollectionType, { objects: [], views: {} }));
21
+ if (Migrations.versionProperty) {
22
+ defaultSpace.properties[Migrations.versionProperty] = Migrations.targetVersion;
23
+ }
24
+
25
+ return contributes(Capabilities.Null, null);
26
+ };
@@ -0,0 +1,17 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { lazy } from '@dxos/app-framework';
6
+
7
+ export const AppGraphBuilder = lazy(() => import('./app-graph-builder'));
8
+ export const AppGraphSerializer = lazy(() => import('./app-graph-serializer'));
9
+ export const IdentityCreated = lazy(() => import('./identity-created'));
10
+ export const IntentResolver = lazy(() => import('./intent-resolver'));
11
+ export const ReactRoot = lazy(() => import('./react-root'));
12
+ export const ReactSurface = lazy(() => import('./react-surface'));
13
+ export const SpaceSettings = lazy(() => import('./settings'));
14
+ export const SpaceState = lazy(() => import('./state'));
15
+ export const SpacesReady = lazy(() => import('./spaces-ready'));
16
+
17
+ export * from './capabilities';