@dxos/plugin-space 0.7.4 → 0.7.5-labs.071a3e2

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 (257) hide show
  1. package/dist/lib/browser/app-graph-builder-TTM2YZVS.mjs +367 -0
  2. package/dist/lib/browser/app-graph-builder-TTM2YZVS.mjs.map +7 -0
  3. package/dist/lib/browser/app-graph-serializer-ZGM5NDXE.mjs +80 -0
  4. package/dist/lib/browser/app-graph-serializer-ZGM5NDXE.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-2NMUVDMZ.mjs +1715 -0
  6. package/dist/lib/browser/chunk-2NMUVDMZ.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-ENRYFGYE.mjs +133 -0
  8. package/dist/lib/browser/chunk-ENRYFGYE.mjs.map +7 -0
  9. package/dist/lib/browser/chunk-H2AR4OLP.mjs +316 -0
  10. package/dist/lib/browser/chunk-H2AR4OLP.mjs.map +7 -0
  11. package/dist/lib/browser/chunk-PQXZCNAU.mjs +13 -0
  12. package/dist/lib/browser/chunk-PQXZCNAU.mjs.map +7 -0
  13. package/dist/lib/browser/chunk-RLZQJD47.mjs +22 -0
  14. package/dist/lib/browser/chunk-RLZQJD47.mjs.map +7 -0
  15. package/dist/lib/browser/chunk-S5IGZNXJ.mjs +528 -0
  16. package/dist/lib/browser/chunk-S5IGZNXJ.mjs.map +7 -0
  17. package/dist/lib/browser/identity-created-VICTPQX7.mjs +28 -0
  18. package/dist/lib/browser/identity-created-VICTPQX7.mjs.map +7 -0
  19. package/dist/lib/browser/index.mjs +193 -3521
  20. package/dist/lib/browser/index.mjs.map +4 -4
  21. package/dist/lib/browser/intent-resolver-A5274MUR.mjs +537 -0
  22. package/dist/lib/browser/intent-resolver-A5274MUR.mjs.map +7 -0
  23. package/dist/lib/browser/meta.json +1 -1
  24. package/dist/lib/browser/react-root-N6QTWYCV.mjs +28 -0
  25. package/dist/lib/browser/react-root-N6QTWYCV.mjs.map +7 -0
  26. package/dist/lib/browser/react-surface-STMNA7W7.mjs +231 -0
  27. package/dist/lib/browser/react-surface-STMNA7W7.mjs.map +7 -0
  28. package/dist/lib/browser/settings-HN5UIYQO.mjs +24 -0
  29. package/dist/lib/browser/settings-HN5UIYQO.mjs.map +7 -0
  30. package/dist/lib/browser/spaces-ready-F57ITJDR.mjs +199 -0
  31. package/dist/lib/browser/spaces-ready-F57ITJDR.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 +14 -5
  35. package/dist/lib/node/app-graph-builder-6N4TEVHH.cjs +370 -0
  36. package/dist/lib/node/app-graph-builder-6N4TEVHH.cjs.map +7 -0
  37. package/dist/lib/node/app-graph-serializer-AWKVTYAB.cjs +88 -0
  38. package/dist/lib/node/app-graph-serializer-AWKVTYAB.cjs.map +7 -0
  39. package/dist/lib/node/chunk-2RCJT3P2.cjs +1712 -0
  40. package/dist/lib/node/chunk-2RCJT3P2.cjs.map +7 -0
  41. package/dist/lib/node/chunk-I2LRRRMV.cjs +150 -0
  42. package/dist/lib/node/chunk-I2LRRRMV.cjs.map +7 -0
  43. package/dist/lib/node/chunk-MMXP2NHE.cjs +556 -0
  44. package/dist/lib/node/chunk-MMXP2NHE.cjs.map +7 -0
  45. package/dist/lib/node/chunk-SPCSJ2CY.cjs +345 -0
  46. package/dist/lib/node/chunk-SPCSJ2CY.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/{meta.cjs → chunk-WZR6OAN3.cjs} +13 -13
  50. package/dist/lib/node/chunk-WZR6OAN3.cjs.map +7 -0
  51. package/dist/lib/node/identity-created-JNDKMFKI.cjs +44 -0
  52. package/dist/lib/node/identity-created-JNDKMFKI.cjs.map +7 -0
  53. package/dist/lib/node/index.cjs +176 -3506
  54. package/dist/lib/node/index.cjs.map +4 -4
  55. package/dist/lib/node/intent-resolver-NVTAESKB.cjs +536 -0
  56. package/dist/lib/node/intent-resolver-NVTAESKB.cjs.map +7 -0
  57. package/dist/lib/node/meta.json +1 -1
  58. package/dist/lib/node/react-root-YCHSAYQE.cjs +50 -0
  59. package/dist/lib/node/react-root-YCHSAYQE.cjs.map +7 -0
  60. package/dist/lib/node/react-surface-ANSZ4FKK.cjs +229 -0
  61. package/dist/lib/node/react-surface-ANSZ4FKK.cjs.map +7 -0
  62. package/dist/lib/node/settings-RBBL22DJ.cjs +38 -0
  63. package/dist/lib/node/settings-RBBL22DJ.cjs.map +7 -0
  64. package/dist/lib/node/spaces-ready-WHU4J6E5.cjs +210 -0
  65. package/dist/lib/node/spaces-ready-WHU4J6E5.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 +23 -14
  69. package/dist/lib/node/types/index.cjs.map +2 -2
  70. package/dist/lib/node-esm/app-graph-builder-MS6BI5EW.mjs +368 -0
  71. package/dist/lib/node-esm/app-graph-builder-MS6BI5EW.mjs.map +7 -0
  72. package/dist/lib/node-esm/app-graph-serializer-AWAWDSCM.mjs +81 -0
  73. package/dist/lib/node-esm/app-graph-serializer-AWAWDSCM.mjs.map +7 -0
  74. package/dist/lib/node-esm/chunk-4HICD7AU.mjs +1716 -0
  75. package/dist/lib/node-esm/chunk-4HICD7AU.mjs.map +7 -0
  76. package/dist/lib/node-esm/chunk-77RE7Y5J.mjs +529 -0
  77. package/dist/lib/node-esm/chunk-77RE7Y5J.mjs.map +7 -0
  78. package/dist/lib/node-esm/chunk-ESWV7ICX.mjs +134 -0
  79. package/dist/lib/node-esm/chunk-ESWV7ICX.mjs.map +7 -0
  80. package/dist/lib/node-esm/chunk-ICCM4YRJ.mjs +15 -0
  81. package/dist/lib/node-esm/chunk-ICCM4YRJ.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-S6VAU6VJ.mjs +317 -0
  85. package/dist/lib/node-esm/chunk-S6VAU6VJ.mjs.map +7 -0
  86. package/dist/lib/node-esm/identity-created-3AUSSVEK.mjs +29 -0
  87. package/dist/lib/node-esm/identity-created-3AUSSVEK.mjs.map +7 -0
  88. package/dist/lib/node-esm/index.mjs +193 -3521
  89. package/dist/lib/node-esm/index.mjs.map +4 -4
  90. package/dist/lib/node-esm/intent-resolver-KJ67TU34.mjs +538 -0
  91. package/dist/lib/node-esm/intent-resolver-KJ67TU34.mjs.map +7 -0
  92. package/dist/lib/node-esm/meta.json +1 -1
  93. package/dist/lib/node-esm/react-root-NBQQKAZD.mjs +29 -0
  94. package/dist/lib/node-esm/react-root-NBQQKAZD.mjs.map +7 -0
  95. package/dist/lib/node-esm/react-surface-G2H5T2D2.mjs +232 -0
  96. package/dist/lib/node-esm/react-surface-G2H5T2D2.mjs.map +7 -0
  97. package/dist/lib/node-esm/settings-VBAUB37B.mjs +25 -0
  98. package/dist/lib/node-esm/settings-VBAUB37B.mjs.map +7 -0
  99. package/dist/lib/node-esm/spaces-ready-ABADUX2P.mjs +200 -0
  100. package/dist/lib/node-esm/spaces-ready-ABADUX2P.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 +14 -5
  104. package/dist/types/src/SpacePlugin.d.ts +3 -22
  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 +196 -0
  115. package/dist/types/src/capabilities/index.d.ts.map +1 -0
  116. package/dist/types/src/capabilities/intent-resolver.d.ts +9 -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 +5 -4
  138. package/dist/types/src/components/CreateDialog/CreateObjectDialog.d.ts.map +1 -1
  139. package/dist/types/src/components/CreateDialog/CreateObjectPanel.d.ts +9 -9
  140. package/dist/types/src/components/CreateDialog/CreateObjectPanel.d.ts.map +1 -1
  141. package/dist/types/src/components/CreateDialog/CreateSpaceDialog.d.ts +1 -0
  142. package/dist/types/src/components/CreateDialog/CreateSpaceDialog.d.ts.map +1 -1
  143. package/dist/types/src/components/JoinDialog.d.ts +1 -0
  144. package/dist/types/src/components/JoinDialog.d.ts.map +1 -1
  145. package/dist/types/src/components/PopoverRenameObject.d.ts +1 -0
  146. package/dist/types/src/components/PopoverRenameObject.d.ts.map +1 -1
  147. package/dist/types/src/components/PopoverRenameSpace.d.ts +1 -0
  148. package/dist/types/src/components/PopoverRenameSpace.d.ts.map +1 -1
  149. package/dist/types/src/components/ShareSpaceButton.d.ts.map +1 -1
  150. package/dist/types/src/components/SpacePluginSettings.d.ts.map +1 -1
  151. package/dist/types/src/components/SpacePresence.d.ts +9 -6
  152. package/dist/types/src/components/SpacePresence.d.ts.map +1 -1
  153. package/dist/types/src/components/SpacePresence.stories.d.ts +1 -1
  154. package/dist/types/src/components/SpacePresence.stories.d.ts.map +1 -1
  155. package/dist/types/src/components/SpaceSettings/SpaceSettingsDialog.d.ts +1 -0
  156. package/dist/types/src/components/SpaceSettings/SpaceSettingsDialog.d.ts.map +1 -1
  157. package/dist/types/src/components/SpaceSettings/SpaceSettingsDialog.stories.d.ts.map +1 -1
  158. package/dist/types/src/components/SpaceSettings/SpaceSettingsPanel.d.ts +4 -3
  159. package/dist/types/src/components/SpaceSettings/SpaceSettingsPanel.d.ts.map +1 -1
  160. package/dist/types/src/components/SpaceSettings/SpaceSettingsPanel.stories.d.ts.map +1 -1
  161. package/dist/types/src/components/SyncStatus/InlineSyncStatus.d.ts +3 -3
  162. package/dist/types/src/components/SyncStatus/InlineSyncStatus.d.ts.map +1 -1
  163. package/dist/types/src/components/SyncStatus/SyncStatus.d.ts.map +1 -1
  164. package/dist/types/src/components/SyncStatus/SyncStatusDetail.stories.d.ts +2 -2
  165. package/dist/types/src/components/SyncStatus/SyncStatusDetail.stories.d.ts.map +1 -1
  166. package/dist/types/src/components/index.d.ts +2 -1
  167. package/dist/types/src/components/index.d.ts.map +1 -1
  168. package/dist/types/src/events.d.ts +5 -0
  169. package/dist/types/src/events.d.ts.map +1 -0
  170. package/dist/types/src/hooks/index.d.ts +2 -0
  171. package/dist/types/src/hooks/index.d.ts.map +1 -0
  172. package/dist/types/src/hooks/usePath.d.ts +11 -0
  173. package/dist/types/src/hooks/usePath.d.ts.map +1 -0
  174. package/dist/types/src/index.d.ts +3 -5
  175. package/dist/types/src/index.d.ts.map +1 -1
  176. package/dist/types/src/meta.d.ts +2 -27
  177. package/dist/types/src/meta.d.ts.map +1 -1
  178. package/dist/types/src/translations.d.ts +18 -3
  179. package/dist/types/src/translations.d.ts.map +1 -1
  180. package/dist/types/src/types/collection.d.ts +8 -12
  181. package/dist/types/src/types/collection.d.ts.map +1 -1
  182. package/dist/types/src/types/thread.d.ts +180 -186
  183. package/dist/types/src/types/thread.d.ts.map +1 -1
  184. package/dist/types/src/types/types.d.ts +228 -16
  185. package/dist/types/src/types/types.d.ts.map +1 -1
  186. package/dist/types/src/util.d.ts +9 -8
  187. package/dist/types/src/util.d.ts.map +1 -1
  188. package/dist/types/tsconfig.tsbuildinfo +1 -0
  189. package/package.json +39 -46
  190. package/src/SpacePlugin.tsx +121 -1538
  191. package/src/capabilities/app-graph-builder.ts +393 -0
  192. package/src/capabilities/app-graph-serializer.ts +73 -0
  193. package/src/capabilities/capabilities.ts +26 -0
  194. package/src/capabilities/identity-created.ts +26 -0
  195. package/src/capabilities/index.ts +17 -0
  196. package/src/capabilities/intent-resolver.ts +518 -0
  197. package/src/capabilities/react-root.tsx +20 -0
  198. package/src/capabilities/react-surface.tsx +226 -0
  199. package/src/capabilities/settings.ts +17 -0
  200. package/src/capabilities/spaces-ready.ts +230 -0
  201. package/src/capabilities/state.ts +45 -0
  202. package/src/components/AdvancedObjectSettings/AdvancedObjectSettings.tsx +72 -0
  203. package/src/components/AdvancedObjectSettings/ForeignKeys.tsx +51 -0
  204. package/src/components/AdvancedObjectSettings/index.ts +5 -0
  205. package/src/components/AwaitingObject.tsx +15 -19
  206. package/src/components/{DefaultObjectSettings.tsx → BaseObjectSettings.tsx} +2 -2
  207. package/src/components/CreateDialog/CreateObjectDialog.tsx +49 -38
  208. package/src/components/CreateDialog/CreateObjectPanel.tsx +137 -101
  209. package/src/components/CreateDialog/CreateSpaceDialog.tsx +22 -17
  210. package/src/components/JoinDialog.tsx +40 -48
  211. package/src/components/PersistenceStatus.tsx +1 -1
  212. package/src/components/PopoverRenameObject.tsx +2 -0
  213. package/src/components/PopoverRenameSpace.tsx +2 -0
  214. package/src/components/ShareSpaceButton.tsx +5 -4
  215. package/src/components/SpacePluginSettings.tsx +5 -16
  216. package/src/components/SpacePresence.stories.tsx +27 -19
  217. package/src/components/SpacePresence.tsx +41 -21
  218. package/src/components/SpaceSettings/SpaceSettingsDialog.stories.tsx +2 -3
  219. package/src/components/SpaceSettings/SpaceSettingsDialog.tsx +19 -2
  220. package/src/components/SpaceSettings/SpaceSettingsPanel.stories.tsx +7 -5
  221. package/src/components/SpaceSettings/SpaceSettingsPanel.tsx +7 -7
  222. package/src/components/SyncStatus/InlineSyncStatus.tsx +37 -27
  223. package/src/components/SyncStatus/SyncStatus.tsx +2 -1
  224. package/src/components/SyncStatus/SyncStatusDetail.stories.tsx +55 -51
  225. package/src/components/index.ts +2 -1
  226. package/src/events.ts +12 -0
  227. package/src/hooks/index.ts +5 -0
  228. package/src/hooks/usePath.ts +44 -0
  229. package/src/index.ts +3 -7
  230. package/src/meta.ts +2 -29
  231. package/src/translations.ts +7 -2
  232. package/src/types/collection.ts +3 -3
  233. package/src/types/thread.ts +6 -6
  234. package/src/types/types.ts +177 -42
  235. package/src/util.tsx +82 -65
  236. package/dist/lib/browser/chunk-FTKV32QZ.mjs +0 -43
  237. package/dist/lib/browser/chunk-FTKV32QZ.mjs.map +0 -7
  238. package/dist/lib/browser/chunk-MWKXNS5S.mjs +0 -124
  239. package/dist/lib/browser/chunk-MWKXNS5S.mjs.map +0 -7
  240. package/dist/lib/browser/meta.mjs +0 -15
  241. package/dist/lib/browser/meta.mjs.map +0 -7
  242. package/dist/lib/node/chunk-6SNOZF7Y.cjs +0 -152
  243. package/dist/lib/node/chunk-6SNOZF7Y.cjs.map +0 -7
  244. package/dist/lib/node/chunk-QNVEU2UD.cjs +0 -69
  245. package/dist/lib/node/chunk-QNVEU2UD.cjs.map +0 -7
  246. package/dist/lib/node/meta.cjs.map +0 -7
  247. package/dist/lib/node-esm/chunk-OHEAWSCA.mjs +0 -126
  248. package/dist/lib/node-esm/chunk-OHEAWSCA.mjs.map +0 -7
  249. package/dist/lib/node-esm/chunk-UMV7XREB.mjs +0 -45
  250. package/dist/lib/node-esm/chunk-UMV7XREB.mjs.map +0 -7
  251. package/dist/lib/node-esm/meta.mjs +0 -16
  252. package/dist/lib/node-esm/meta.mjs.map +0 -7
  253. package/dist/types/src/components/DefaultObjectSettings.d.ts +0 -7
  254. package/dist/types/src/components/DefaultObjectSettings.d.ts.map +0 -1
  255. package/dist/types/src/components/SyncStatus/InlineSyncStatus.stories.d.ts +0 -6
  256. package/dist/types/src/components/SyncStatus/InlineSyncStatus.stories.d.ts.map +0 -1
  257. package/src/components/SyncStatus/InlineSyncStatus.stories.tsx +0 -57
@@ -0,0 +1,226 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import React, { useCallback } from 'react';
6
+
7
+ import {
8
+ Capabilities,
9
+ contributes,
10
+ createSurface,
11
+ Surface,
12
+ useCapability,
13
+ useCapabilities,
14
+ usePluginManager,
15
+ } from '@dxos/app-framework';
16
+ import { SettingsStore } from '@dxos/local-storage';
17
+ import { ClientCapabilities } from '@dxos/plugin-client';
18
+ import {
19
+ getSpace,
20
+ isEchoObject,
21
+ isReactiveObject,
22
+ isSpace,
23
+ SpaceState,
24
+ type ReactiveEchoObject,
25
+ type Space,
26
+ } from '@dxos/react-client/echo';
27
+ import { type JoinPanelProps } from '@dxos/shell/react';
28
+
29
+ import { SpaceCapabilities } from './capabilities';
30
+ import {
31
+ AdvancedObjectSettings,
32
+ BaseObjectSettings,
33
+ CollectionMain,
34
+ CollectionSection,
35
+ CREATE_OBJECT_DIALOG,
36
+ CREATE_SPACE_DIALOG,
37
+ CreateObjectDialog,
38
+ CreateSpaceDialog,
39
+ InlineSyncStatus,
40
+ JOIN_DIALOG,
41
+ JoinDialog,
42
+ MenuFooter,
43
+ POPOVER_RENAME_OBJECT,
44
+ POPOVER_RENAME_SPACE,
45
+ PopoverRenameObject,
46
+ PopoverRenameSpace,
47
+ SmallPresenceLive,
48
+ SPACE_SETTINGS_DIALOG,
49
+ SpacePluginSettings,
50
+ SpacePresence,
51
+ SpaceSettingsDialog,
52
+ SpaceSettingsPanel,
53
+ SyncStatus,
54
+ type CreateObjectDialogProps,
55
+ type SpaceSettingsDialogProps,
56
+ } from '../components';
57
+ import { SPACE_PLUGIN } from '../meta';
58
+ import { CollectionType, type SpaceSettingsProps } from '../types';
59
+
60
+ type ReactSurfaceOptions = {
61
+ createInvitationUrl: (invitationCode: string) => string;
62
+ };
63
+
64
+ export default ({ createInvitationUrl }: ReactSurfaceOptions) =>
65
+ contributes(Capabilities.ReactSurface, [
66
+ createSurface({
67
+ id: `${SPACE_PLUGIN}/article`,
68
+ role: 'article',
69
+ filter: (data): data is { subject: Space } =>
70
+ // TODO(wittjosiah): Need to avoid shotgun parsing space state everywhere.
71
+ isSpace(data.subject) && data.subject.state.get() === SpaceState.SPACE_READY,
72
+ component: ({ data, role, ...rest }) => (
73
+ <Surface
74
+ data={{ id: data.subject.id, subject: data.subject.properties[CollectionType.typename]?.target }}
75
+ role={role}
76
+ {...rest}
77
+ />
78
+ ),
79
+ }),
80
+ createSurface({
81
+ id: `${SPACE_PLUGIN}/collection-fallback`,
82
+ role: 'article',
83
+ position: 'fallback',
84
+ filter: (data): data is { subject: CollectionType } => data.subject instanceof CollectionType,
85
+ component: ({ data }) => <CollectionMain collection={data.subject} />,
86
+ }),
87
+ createSurface({
88
+ id: `${SPACE_PLUGIN}/settings-panel`,
89
+ // TODO(burdon): Add role name syntax to minimal plugin docs.
90
+ role: 'complementary--settings',
91
+ filter: (data): data is { subject: Space } => isSpace(data.subject),
92
+ component: ({ data }) => <SpaceSettingsPanel space={data.subject} />,
93
+ }),
94
+ createSurface({
95
+ id: `${SPACE_PLUGIN}/object-settings-base-panel`,
96
+ role: 'complementary--settings',
97
+ position: 'hoist',
98
+ filter: (data): data is { subject: ReactiveEchoObject<any> } => isEchoObject(data.subject),
99
+ component: ({ data }) => <BaseObjectSettings object={data.subject} />,
100
+ }),
101
+ createSurface({
102
+ id: `${SPACE_PLUGIN}/object-settings-advanced-panel`,
103
+ role: 'complementary--settings',
104
+ position: 'fallback',
105
+ filter: (data): data is { subject: ReactiveEchoObject<any> } => isEchoObject(data.subject),
106
+ component: ({ data }) => <AdvancedObjectSettings object={data.subject} />,
107
+ }),
108
+ createSurface({
109
+ id: SPACE_SETTINGS_DIALOG,
110
+ role: 'dialog',
111
+ filter: (data): data is { props: SpaceSettingsDialogProps } => data.component === SPACE_SETTINGS_DIALOG,
112
+ component: ({ data }) => <SpaceSettingsDialog {...data.props} createInvitationUrl={createInvitationUrl} />,
113
+ }),
114
+ createSurface({
115
+ id: JOIN_DIALOG,
116
+ role: 'dialog',
117
+ filter: (data): data is { props: JoinPanelProps } => data.component === JOIN_DIALOG,
118
+ component: ({ data }) => <JoinDialog {...data.props} />,
119
+ }),
120
+ createSurface({
121
+ id: CREATE_SPACE_DIALOG,
122
+ role: 'dialog',
123
+ filter: (data): data is any => data.component === CREATE_SPACE_DIALOG,
124
+ component: () => <CreateSpaceDialog />,
125
+ }),
126
+ createSurface({
127
+ id: CREATE_OBJECT_DIALOG,
128
+ role: 'dialog',
129
+ filter: (data): data is { props: Partial<CreateObjectDialogProps> } => data.component === CREATE_OBJECT_DIALOG,
130
+ component: ({ data }) => {
131
+ const schemas = useCapabilities(ClientCapabilities.Schema).flat();
132
+ const manager = usePluginManager();
133
+
134
+ const resolve = useCallback(
135
+ (typename: string) => {
136
+ return (
137
+ manager.context.requestCapabilities(Capabilities.Metadata).find(({ id }) => id === typename)?.metadata ??
138
+ {}
139
+ );
140
+ },
141
+ [manager],
142
+ );
143
+
144
+ return <CreateObjectDialog schemas={schemas} resolve={resolve} {...data.props} />;
145
+ },
146
+ }),
147
+ createSurface({
148
+ id: POPOVER_RENAME_SPACE,
149
+ role: 'popover',
150
+ filter: (data): data is { props: Space } => data.component === POPOVER_RENAME_SPACE && isSpace(data.props),
151
+ component: ({ data }) => <PopoverRenameSpace space={data.props} />,
152
+ }),
153
+ createSurface({
154
+ id: POPOVER_RENAME_OBJECT,
155
+ role: 'popover',
156
+ filter: (data): data is { props: ReactiveEchoObject<any> } =>
157
+ data.component === POPOVER_RENAME_OBJECT && isReactiveObject(data.props),
158
+ component: ({ data }) => <PopoverRenameObject object={data.props} />,
159
+ }),
160
+ createSurface({
161
+ id: `${SPACE_PLUGIN}/navtree-presence`,
162
+ role: 'navtree-item-end',
163
+ filter: (data): data is { id: string; subject: ReactiveEchoObject<any>; open?: boolean } =>
164
+ typeof data.id === 'string' && isEchoObject(data.subject),
165
+ component: ({ data }) => {
166
+ // TODO(wittjosiah): Doesn't need to be mutable but readonly type messes with ComplexMap.
167
+ const state = useCapability(SpaceCapabilities.MutableState);
168
+ return <SmallPresenceLive id={data.id} open={data.open} viewers={state.viewersByObject[data.id]} />;
169
+ },
170
+ }),
171
+ createSurface({
172
+ // TODO(wittjosiah): Attention glyph for non-echo items should be handled elsewhere.
173
+ id: `${SPACE_PLUGIN}/navtree-presence-fallback`,
174
+ role: 'navtree-item-end',
175
+ position: 'fallback',
176
+ filter: (data): data is { id: string; open?: boolean } => typeof data.id === 'string',
177
+ component: ({ data }) => <SmallPresenceLive id={data.id} open={data.open} />,
178
+ }),
179
+ createSurface({
180
+ id: `${SPACE_PLUGIN}/navtree-sync-status`,
181
+ role: 'navtree-item-end',
182
+ filter: (data): data is { subject: Space; open?: boolean } => isSpace(data.subject),
183
+ component: ({ data }) => <InlineSyncStatus space={data.subject} open={data.open} />,
184
+ }),
185
+ createSurface({
186
+ id: `${SPACE_PLUGIN}/navbar-presence`,
187
+ role: 'navbar-end',
188
+ position: 'hoist',
189
+ filter: (data): data is { subject: Space | ReactiveEchoObject<any> } =>
190
+ isSpace(data.subject) || isEchoObject(data.subject),
191
+ component: ({ data }) => {
192
+ const space = isSpace(data.subject) ? data.subject : getSpace(data.subject);
193
+ const object = isSpace(data.subject)
194
+ ? data.subject.state.get() === SpaceState.SPACE_READY
195
+ ? (space?.properties[CollectionType.typename]?.target as CollectionType)
196
+ : undefined
197
+ : data.subject;
198
+
199
+ return object ? <SpacePresence object={object} /> : null;
200
+ },
201
+ }),
202
+ createSurface({
203
+ id: `${SPACE_PLUGIN}/collection-section`,
204
+ role: 'section',
205
+ filter: (data): data is { subject: CollectionType } => data.subject instanceof CollectionType,
206
+ component: ({ data }) => <CollectionSection collection={data.subject} />,
207
+ }),
208
+ createSurface({
209
+ id: `${SPACE_PLUGIN}/settings`,
210
+ role: 'article',
211
+ filter: (data): data is { subject: SettingsStore<SpaceSettingsProps> } =>
212
+ data.subject instanceof SettingsStore && data.subject.prefix === SPACE_PLUGIN,
213
+ component: ({ data: { subject } }) => <SpacePluginSettings settings={subject.value} />,
214
+ }),
215
+ createSurface({
216
+ id: `${SPACE_PLUGIN}/menu-footer`,
217
+ role: 'menu-footer',
218
+ filter: (data): data is { subject: ReactiveEchoObject<any> } => isEchoObject(data.subject),
219
+ component: ({ data }) => <MenuFooter object={data.subject} />,
220
+ }),
221
+ createSurface({
222
+ id: `${SPACE_PLUGIN}/status`,
223
+ role: 'status',
224
+ component: () => <SyncStatus />,
225
+ }),
226
+ ]);
@@ -0,0 +1,17 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Capabilities, contributes } from '@dxos/app-framework';
6
+ import { create } from '@dxos/live-object';
7
+
8
+ import { SPACE_PLUGIN } from '../meta';
9
+ import { type SpaceSettingsProps, SpaceSettingsSchema } from '../types';
10
+
11
+ export default () => {
12
+ const settings = create<SpaceSettingsProps>({
13
+ showHidden: false,
14
+ });
15
+
16
+ return contributes(Capabilities.Settings, { schema: SpaceSettingsSchema, prefix: SPACE_PLUGIN, value: settings });
17
+ };
@@ -0,0 +1,230 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { contributes, createIntent, type PluginsContext, Capabilities, LayoutAction } from '@dxos/app-framework';
6
+ import { EventSubscriptions } from '@dxos/async';
7
+ import { Expando } from '@dxos/echo-schema';
8
+ import { scheduledEffect } from '@dxos/echo-signals/core';
9
+ import { create } from '@dxos/live-object';
10
+ import { log } from '@dxos/log';
11
+ import { AttentionCapabilities } from '@dxos/plugin-attention';
12
+ import { ClientCapabilities } from '@dxos/plugin-client';
13
+ import { DeckCapabilities } from '@dxos/plugin-deck';
14
+ import { EdgeReplicationSetting } from '@dxos/protocols/proto/dxos/echo/metadata';
15
+ import { PublicKey } from '@dxos/react-client';
16
+ import { Filter, FQ_ID_LENGTH, parseFullyQualifiedId, SpaceState } from '@dxos/react-client/echo';
17
+ import { ComplexMap, reduceGroupBy } from '@dxos/util';
18
+
19
+ import { SpaceCapabilities } from './capabilities';
20
+ import { SpaceAction } from '../types';
21
+ import { COMPOSER_SPACE_LOCK, SHARED } from '../util';
22
+
23
+ const ACTIVE_NODE_BROADCAST_INTERVAL = 30_000;
24
+ const WAIT_FOR_OBJECT_TIMEOUT = 1000;
25
+
26
+ export default async (context: PluginsContext) => {
27
+ const subscriptions = new EventSubscriptions();
28
+ const spaceSubscriptions = new EventSubscriptions();
29
+
30
+ const { dispatchPromise: dispatch } = context.requestCapability(Capabilities.IntentDispatcher);
31
+ const { graph } = context.requestCapability(Capabilities.AppGraph);
32
+ const layout = context.requestCapability(Capabilities.Layout);
33
+ const deck = context.requestCapability(DeckCapabilities.DeckState);
34
+ const attention = context.requestCapability(AttentionCapabilities.Attention);
35
+ const state = context.requestCapability(SpaceCapabilities.MutableState);
36
+ const client = context.requestCapability(ClientCapabilities.Client);
37
+
38
+ const defaultSpace = client.spaces.default;
39
+ await defaultSpace.waitUntilReady();
40
+
41
+ if (deck.activeDeck === 'default') {
42
+ await dispatch(createIntent(LayoutAction.SwitchWorkspace, { part: 'workspace', subject: defaultSpace.id }));
43
+ }
44
+
45
+ // Initialize space sharing lock in default space.
46
+ if (typeof defaultSpace.properties[COMPOSER_SPACE_LOCK] !== 'boolean') {
47
+ defaultSpace.properties[COMPOSER_SPACE_LOCK] = true;
48
+ }
49
+
50
+ const {
51
+ objects: [spacesOrder],
52
+ } = await defaultSpace.db.query(Filter.schema(Expando, { key: SHARED })).run();
53
+ if (!spacesOrder) {
54
+ // TODO(wittjosiah): Cannot be a Folder because Spaces are not TypedObjects so can't be saved in the database.
55
+ // Instead, we store order as an array of space ids.
56
+ defaultSpace.db.add(create({ key: SHARED, order: [] }));
57
+ }
58
+
59
+ // Await missing objects.
60
+ subscriptions.add(
61
+ scheduledEffect(
62
+ () => ({ active: layout.active }),
63
+ ({ active }) => {
64
+ if (active.length !== 1) {
65
+ return;
66
+ }
67
+
68
+ const node = graph.findNode(active[0]);
69
+ if (!node && active[0].length === FQ_ID_LENGTH) {
70
+ const timeout = setTimeout(async () => {
71
+ const node = graph.findNode(active[0]);
72
+ if (!node) {
73
+ await dispatch(createIntent(SpaceAction.WaitForObject, { id: active[0] }));
74
+ }
75
+ }, WAIT_FOR_OBJECT_TIMEOUT);
76
+
77
+ return () => clearTimeout(timeout);
78
+ }
79
+ },
80
+ ),
81
+ );
82
+
83
+ // Cache space names.
84
+ subscriptions.add(
85
+ client.spaces.subscribe(async (spaces) => {
86
+ // TODO(wittjosiah): Remove. This is a hack to be able to migrate the default space properties.
87
+ if (defaultSpace.state.get() === SpaceState.SPACE_REQUIRES_MIGRATION) {
88
+ await defaultSpace.internal.migrate();
89
+ }
90
+
91
+ spaces
92
+ .filter((space) => space.state.get() === SpaceState.SPACE_READY)
93
+ .forEach((space) => {
94
+ subscriptions.add(
95
+ scheduledEffect(
96
+ () => ({ name: space.properties.name }),
97
+ ({ name }) => (state.spaceNames[space.id] = name),
98
+ ),
99
+ );
100
+ });
101
+ }).unsubscribe,
102
+ );
103
+
104
+ // Broadcast active node to other peers in the space.
105
+ subscriptions.add(
106
+ scheduledEffect(
107
+ () => ({ current: attention.current, active: layout.active, inactive: layout.inactive }),
108
+ ({ current, active, inactive }) => {
109
+ const send = () => {
110
+ const spaces = client.spaces.get();
111
+ const identity = client.halo.identity.get();
112
+ if (identity) {
113
+ // Group parts by space for efficient messaging.
114
+ const idsBySpace = reduceGroupBy(active, (id) => {
115
+ try {
116
+ const [spaceId] = parseFullyQualifiedId(id);
117
+ return spaceId;
118
+ } catch {
119
+ return null;
120
+ }
121
+ });
122
+
123
+ const removedBySpace = reduceGroupBy(inactive, (id) => {
124
+ try {
125
+ const [spaceId] = parseFullyQualifiedId(id);
126
+ return spaceId;
127
+ } catch {
128
+ return null;
129
+ }
130
+ });
131
+
132
+ // NOTE: Ensure all spaces are included so that we send the correct `removed` object arrays.
133
+ for (const space of spaces) {
134
+ if (!idsBySpace.has(space.id)) {
135
+ idsBySpace.set(space.id, []);
136
+ }
137
+ }
138
+
139
+ for (const [spaceId, added] of idsBySpace) {
140
+ const removed = removedBySpace.get(spaceId) ?? [];
141
+ const space = spaces.find((space) => space.id === spaceId);
142
+ if (!space) {
143
+ continue;
144
+ }
145
+
146
+ void space
147
+ .postMessage('viewing', {
148
+ identityKey: identity.identityKey.toHex(),
149
+ attended: current,
150
+ added,
151
+ removed,
152
+ })
153
+ // TODO(burdon): This seems defensive; why would this fail? Backoff interval.
154
+ .catch((err) => {
155
+ log.warn('Failed to broadcast active node for presence.', { err: err.message });
156
+ });
157
+ }
158
+ }
159
+ };
160
+
161
+ send();
162
+ // Send at interval to allow peers to expire entries if they become disconnected.
163
+ const interval = setInterval(() => send(), ACTIVE_NODE_BROADCAST_INTERVAL);
164
+ return () => clearInterval(interval);
165
+ },
166
+ ),
167
+ );
168
+
169
+ // Listen for active nodes from other peers in the space.
170
+ subscriptions.add(
171
+ client.spaces.subscribe((spaces) => {
172
+ spaceSubscriptions.clear();
173
+ spaces.forEach((space) => {
174
+ spaceSubscriptions.add(
175
+ space.listen('viewing', (message) => {
176
+ const { added, removed, attended } = message.payload;
177
+
178
+ const identityKey = PublicKey.safeFrom(message.payload.identityKey);
179
+ const currentIdentity = client.halo.identity.get();
180
+ if (
181
+ identityKey &&
182
+ !currentIdentity?.identityKey.equals(identityKey) &&
183
+ Array.isArray(added) &&
184
+ Array.isArray(removed)
185
+ ) {
186
+ added.forEach((id) => {
187
+ if (typeof id === 'string') {
188
+ if (!(id in state.viewersByObject)) {
189
+ state.viewersByObject[id] = new ComplexMap(PublicKey.hash);
190
+ }
191
+ state.viewersByObject[id]!.set(identityKey, {
192
+ lastSeen: Date.now(),
193
+ currentlyAttended: new Set(attended).has(id),
194
+ });
195
+ if (!state.viewersByIdentity.has(identityKey)) {
196
+ state.viewersByIdentity.set(identityKey, new Set());
197
+ }
198
+ state.viewersByIdentity.get(identityKey)!.add(id);
199
+ }
200
+ });
201
+
202
+ removed.forEach((id) => {
203
+ if (typeof id === 'string') {
204
+ state.viewersByObject[id]?.delete(identityKey);
205
+ state.viewersByIdentity.get(identityKey)?.delete(id);
206
+ // It’s okay for these to be empty sets/maps, reduces churn.
207
+ }
208
+ });
209
+ }
210
+ }),
211
+ );
212
+ });
213
+ }).unsubscribe,
214
+ );
215
+
216
+ // Enable edge replication for all spaces.
217
+ try {
218
+ await Promise.all(
219
+ client.spaces.get().map((space) => space.internal.setEdgeReplicationPreference(EdgeReplicationSetting.ENABLED)),
220
+ );
221
+ state.enabledEdgeReplication = true;
222
+ } catch (err) {
223
+ log.catch(err);
224
+ }
225
+
226
+ return contributes(Capabilities.Null, null, () => {
227
+ spaceSubscriptions.clear();
228
+ subscriptions.clear();
229
+ });
230
+ };
@@ -0,0 +1,45 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { effect } from '@preact/signals-core';
6
+
7
+ import { Capabilities, contributes, type PluginsContext } from '@dxos/app-framework';
8
+ import { PublicKey } from '@dxos/keys';
9
+ import { LocalStorageStore } from '@dxos/local-storage';
10
+ import { ComplexMap } from '@dxos/util';
11
+
12
+ import { SpaceCapabilities } from './capabilities';
13
+ import { SPACE_PLUGIN } from '../meta';
14
+ import { type PluginState } from '../types';
15
+
16
+ export default (context: PluginsContext) => {
17
+ const state = new LocalStorageStore<PluginState>(SPACE_PLUGIN, {
18
+ awaiting: undefined,
19
+ spaceNames: {},
20
+ viewersByObject: {},
21
+ // TODO(wittjosiah): Stop using (Complex)Map inside reactive object.
22
+ viewersByIdentity: new ComplexMap(PublicKey.hash),
23
+ sdkMigrationRunning: {},
24
+ navigableCollections: false,
25
+ enabledEdgeReplication: false,
26
+ });
27
+
28
+ state
29
+ .prop({ key: 'spaceNames', type: LocalStorageStore.json<Record<string, string>>() })
30
+ .prop({ key: 'enabledEdgeReplication', type: LocalStorageStore.bool() });
31
+
32
+ const manager = context.requestCapability(Capabilities.PluginManager);
33
+ const unsubscribe = effect(() => {
34
+ // TODO(wittjosiah): Find a way to make this capability-based.
35
+ const enabled = manager.enabled.includes('dxos.org/plugin/stack');
36
+ if (enabled !== state.values.navigableCollections) {
37
+ state.values.navigableCollections = enabled;
38
+ }
39
+ });
40
+
41
+ return contributes(SpaceCapabilities.State, state.values, () => {
42
+ unsubscribe();
43
+ state.close();
44
+ });
45
+ };
@@ -0,0 +1,72 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import React, { useCallback, useState } from 'react';
6
+
7
+ import { type ForeignKey, ForeignKeySchema } from '@dxos/echo-schema';
8
+ import { getMeta, type ReactiveEchoObject } from '@dxos/react-client/echo';
9
+ import { IconButton, useTranslation, Separator } from '@dxos/react-ui';
10
+ import { Form } from '@dxos/react-ui-form';
11
+
12
+ import { ForeignKeys } from './ForeignKeys';
13
+ import { SPACE_PLUGIN } from '../../meta';
14
+
15
+ const initialValues = {
16
+ source: '',
17
+ id: '',
18
+ };
19
+
20
+ export type AdvancedObjectSettingsProps = {
21
+ object: ReactiveEchoObject<any>;
22
+ };
23
+
24
+ export const AdvancedObjectSettings = ({ object }: AdvancedObjectSettingsProps) => {
25
+ const { t } = useTranslation(SPACE_PLUGIN);
26
+ const [adding, setAdding] = useState(false);
27
+ const keys = getMeta(object).keys;
28
+
29
+ const handleNew = useCallback(() => setAdding(true), []);
30
+ const handleCancel = useCallback(() => setAdding(false), []);
31
+ const handleSave = useCallback(
32
+ (key: ForeignKey) => {
33
+ const index = keys.findIndex(({ source, id }) => source === key.source && id === key.id);
34
+ if (index === -1) {
35
+ keys.push(key);
36
+ }
37
+ setAdding(false);
38
+ },
39
+ [keys],
40
+ );
41
+ const handleDelete = useCallback(
42
+ (key: ForeignKey) => {
43
+ const index = keys.findIndex(({ source, id }) => source === key.source && id === key.id);
44
+ if (index !== -1) {
45
+ keys.splice(index, 1);
46
+ }
47
+ },
48
+ [keys],
49
+ );
50
+
51
+ // TODO(wittjosiah): This should be wrapped in an "Advanced" accordion.
52
+ return (
53
+ <>
54
+ <Separator />
55
+ <div className='p-2 flex flex-col gap-4'>
56
+ <h2>{t('advanced settings label')}</h2>
57
+ <div className='flex items-center'>
58
+ <h3 className='text-sm font-semibold'>{t('foreign keys')}</h3>
59
+ <div className='grow' />
60
+ <IconButton
61
+ classNames={adding && 'invisible'}
62
+ icon='ph--plus--regular'
63
+ label={t('add key')}
64
+ onClick={handleNew}
65
+ />
66
+ </div>
67
+ {!adding && <ForeignKeys keys={keys} onDelete={handleDelete} />}
68
+ </div>
69
+ {adding && <Form schema={ForeignKeySchema} values={initialValues} onSave={handleSave} onCancel={handleCancel} />}
70
+ </>
71
+ );
72
+ };
@@ -0,0 +1,51 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import React, { useCallback } from 'react';
6
+
7
+ import { type ForeignKey } from '@dxos/echo-schema';
8
+ import { IconButton, List, ListItem, useTranslation } from '@dxos/react-ui';
9
+
10
+ import { SPACE_PLUGIN } from '../../meta';
11
+
12
+ export type ForeignKeysProps = {
13
+ keys: ForeignKey[];
14
+ onDelete?: (key: ForeignKey) => void;
15
+ };
16
+
17
+ // TODO(wittjosiah): This is a clone of `TokenManager`. Consider a form variant for arrays of read-only objects.
18
+ export const ForeignKeys = ({ keys, onDelete }: ForeignKeysProps) => {
19
+ return (
20
+ <List classNames='flex flex-col gap-2'>
21
+ {keys.map((key) => (
22
+ <KeyItem key={key.id} forignKey={key} onDelete={onDelete} />
23
+ ))}
24
+ </List>
25
+ );
26
+ };
27
+
28
+ type KeyItemProps = {
29
+ forignKey: ForeignKey;
30
+ onDelete?: (key: ForeignKey) => void;
31
+ };
32
+
33
+ const KeyItem = ({ forignKey, onDelete }: KeyItemProps) => {
34
+ const { t } = useTranslation(SPACE_PLUGIN);
35
+
36
+ const handleDelete = useCallback(() => {
37
+ onDelete?.(forignKey);
38
+ }, [forignKey, onDelete]);
39
+
40
+ return (
41
+ <ListItem.Root classNames='px-2'>
42
+ <ListItem.Heading classNames='flex flex-col grow truncate'>
43
+ <div>{forignKey.source}</div>
44
+ <div className='text-description text-sm truncate'>{forignKey.id}</div>
45
+ </ListItem.Heading>
46
+ <ListItem.Endcap>
47
+ <IconButton iconOnly icon='ph--x--regular' variant='ghost' label={t('delete key')} onClick={handleDelete} />
48
+ </ListItem.Endcap>
49
+ </ListItem.Root>
50
+ );
51
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export * from './AdvancedObjectSettings';