@dxos/plugin-space 0.6.14-main.2b6a0f3 → 0.6.14-main.f49f251

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 (46) hide show
  1. package/dist/lib/browser/{chunk-47SVNCZM.mjs → chunk-FOI7DAUV.mjs} +1 -1
  2. package/dist/lib/browser/{chunk-47SVNCZM.mjs.map → chunk-FOI7DAUV.mjs.map} +2 -2
  3. package/dist/lib/browser/index.mjs +208 -108
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/types/index.mjs +1 -1
  7. package/dist/lib/node/{chunk-CTYDNFGG.cjs → chunk-OTDRTHT4.cjs} +4 -4
  8. package/dist/lib/node/{chunk-CTYDNFGG.cjs.map → chunk-OTDRTHT4.cjs.map} +2 -2
  9. package/dist/lib/node/index.cjs +234 -135
  10. package/dist/lib/node/index.cjs.map +4 -4
  11. package/dist/lib/node/meta.json +1 -1
  12. package/dist/lib/node/types/index.cjs +11 -11
  13. package/dist/lib/node/types/index.cjs.map +1 -1
  14. package/dist/lib/node-esm/{chunk-PLPMYTLC.mjs → chunk-FYDGMPSC.mjs} +1 -1
  15. package/dist/lib/node-esm/{chunk-PLPMYTLC.mjs.map → chunk-FYDGMPSC.mjs.map} +2 -2
  16. package/dist/lib/node-esm/index.mjs +208 -108
  17. package/dist/lib/node-esm/index.mjs.map +4 -4
  18. package/dist/lib/node-esm/meta.json +1 -1
  19. package/dist/lib/node-esm/types/index.mjs +1 -1
  20. package/dist/types/src/SpacePlugin.d.ts.map +1 -1
  21. package/dist/types/src/components/DefaultObjectSettings.d.ts +1 -1
  22. package/dist/types/src/components/DefaultObjectSettings.d.ts.map +1 -1
  23. package/dist/types/src/components/SpacePresence.d.ts +4 -2
  24. package/dist/types/src/components/SpacePresence.d.ts.map +1 -1
  25. package/dist/types/src/components/SpaceSettingsPanel.d.ts +7 -0
  26. package/dist/types/src/components/SpaceSettingsPanel.d.ts.map +1 -0
  27. package/dist/types/src/components/SyncStatus/SyncStatus.stories.d.ts.map +1 -1
  28. package/dist/types/src/components/index.d.ts +1 -0
  29. package/dist/types/src/components/index.d.ts.map +1 -1
  30. package/dist/types/src/translations.d.ts +2 -0
  31. package/dist/types/src/translations.d.ts.map +1 -1
  32. package/dist/types/src/types/types.d.ts +3 -0
  33. package/dist/types/src/types/types.d.ts.map +1 -1
  34. package/dist/types/src/util.d.ts +1 -1
  35. package/dist/types/src/util.d.ts.map +1 -1
  36. package/package.json +40 -49
  37. package/src/SpacePlugin.tsx +62 -32
  38. package/src/components/DefaultObjectSettings.tsx +3 -2
  39. package/src/components/SpacePresence.tsx +33 -22
  40. package/src/components/SpaceSettings.tsx +5 -5
  41. package/src/components/SpaceSettingsPanel.tsx +59 -0
  42. package/src/components/SyncStatus/SyncStatus.tsx +1 -1
  43. package/src/components/index.ts +1 -0
  44. package/src/translations.ts +4 -2
  45. package/src/types/types.ts +3 -1
  46. package/src/util.tsx +3 -3
@@ -34,8 +34,8 @@ import { type Node, createExtension, memoize, toSignal } from '@dxos/plugin-grap
34
34
  import { ObservabilityAction } from '@dxos/plugin-observability/meta';
35
35
  import { type Client, PublicKey } from '@dxos/react-client';
36
36
  import {
37
- Expando,
38
37
  type EchoReactiveObject,
38
+ Expando,
39
39
  Filter,
40
40
  type PropertiesTypeProps,
41
41
  type Space,
@@ -47,7 +47,7 @@ import {
47
47
  isEchoObject,
48
48
  isSpace,
49
49
  loadObjectReferences,
50
- parseFullyQualifiedId,
50
+ parseId,
51
51
  } from '@dxos/react-client/echo';
52
52
  import { Dialog } from '@dxos/react-ui';
53
53
  import { ClipboardProvider, InvitationManager, type InvitationManagerProps, osTranslations } from '@dxos/shell/react';
@@ -68,6 +68,7 @@ import {
68
68
  SmallPresenceLive,
69
69
  SpacePresence,
70
70
  SpaceSettings,
71
+ SpaceSettingsPanel,
71
72
  SyncStatus,
72
73
  } from './components';
73
74
  import meta, { SPACE_PLUGIN, SpaceAction } from './meta';
@@ -128,6 +129,7 @@ export const SpacePlugin = ({
128
129
  awaiting: undefined,
129
130
  spaceNames: {},
130
131
  viewersByObject: {},
132
+ // TODO(wittjosiah): Stop using (Complex)Map inside reactive object.
131
133
  viewersByIdentity: new ComplexMap(PublicKey.hash),
132
134
  sdkMigrationRunning: {},
133
135
  });
@@ -284,17 +286,8 @@ export const SpacePlugin = ({
284
286
  return {
285
287
  meta,
286
288
  ready: async (plugins) => {
287
- settings.prop({
288
- key: 'showHidden',
289
- storageKey: 'show-hidden',
290
- type: LocalStorageStore.bool({ allowUndefined: true }),
291
- });
292
-
293
- state.prop({
294
- key: 'spaceNames',
295
- storageKey: 'space-names',
296
- type: LocalStorageStore.json<Record<string, string>>(),
297
- });
289
+ settings.prop({ key: 'showHidden', type: LocalStorageStore.bool({ allowUndefined: true }) });
290
+ state.prop({ key: 'spaceNames', type: LocalStorageStore.json<Record<string, string>>() });
298
291
 
299
292
  navigationPlugin = resolvePlugin(plugins, parseNavigationPlugin);
300
293
  attentionPlugin = resolvePlugin(plugins, parseAttentionPlugin);
@@ -375,12 +368,19 @@ export const SpacePlugin = ({
375
368
  {...rest}
376
369
  />
377
370
  ) : primary instanceof CollectionType ? (
378
- { node: <CollectionMain collection={primary} />, disposition: 'fallback' }
371
+ {
372
+ node: <CollectionMain collection={primary} />,
373
+ disposition: 'fallback',
374
+ }
379
375
  ) : typeof primary === 'string' && primary.length === OBJECT_ID_LENGTH ? (
380
376
  <MissingObject id={primary} />
381
377
  ) : null;
382
378
  case 'complementary--settings':
383
- return isEchoObject(data.subject) ? <DefaultObjectSettings object={data.subject} /> : null;
379
+ return isSpace(data.subject) ? (
380
+ <SpaceSettingsPanel space={data.subject} />
381
+ ) : isEchoObject(data.subject) ? (
382
+ { node: <DefaultObjectSettings object={data.subject} />, disposition: 'fallback' }
383
+ ) : null;
384
384
  case 'dialog':
385
385
  if (data.component === 'dxos.org/plugin/space/InvitationManagerDialog') {
386
386
  return (
@@ -390,10 +390,9 @@ export const SpacePlugin = ({
390
390
  </ClipboardProvider>
391
391
  </Dialog.Content>
392
392
  );
393
- } else {
394
- return null;
395
393
  }
396
- case 'popover':
394
+ return null;
395
+ case 'popover': {
397
396
  if (data.component === 'dxos.org/plugin/space/RenameSpacePopover' && isSpace(data.subject)) {
398
397
  return <PopoverRenameSpace space={data.subject} />;
399
398
  }
@@ -401,12 +400,16 @@ export const SpacePlugin = ({
401
400
  return <PopoverRenameObject object={data.subject} />;
402
401
  }
403
402
  return null;
403
+ }
404
404
  // TODO(burdon): Add role name syntax to minimal plugin docs.
405
405
  case 'presence--glyph': {
406
406
  return isReactiveObject(data.object) ? (
407
- <SmallPresenceLive viewers={state.values.viewersByObject[fullyQualifiedId(data.object)]} />
407
+ <SmallPresenceLive
408
+ id={data.id as string}
409
+ viewers={state.values.viewersByObject[fullyQualifiedId(data.object)]}
410
+ />
408
411
  ) : (
409
- <SmallPresence count={0} />
412
+ <SmallPresence id={data.id as string} count={0} />
410
413
  );
411
414
  }
412
415
  case 'navbar-start': {
@@ -423,6 +426,7 @@ export const SpacePlugin = ({
423
426
  ? (space?.properties[CollectionType.typename] as CollectionType)
424
427
  : undefined
425
428
  : data.object;
429
+
426
430
  return space && object
427
431
  ? {
428
432
  node: (
@@ -440,10 +444,10 @@ export const SpacePlugin = ({
440
444
  case 'settings':
441
445
  return data.plugin === meta.id ? <SpaceSettings settings={settings.values} /> : null;
442
446
  case 'menu-footer':
443
- if (!isEchoObject(data.object)) {
444
- return null;
445
- } else {
447
+ if (isEchoObject(data.object)) {
446
448
  return <MenuFooter object={data.object} />;
449
+ } else {
450
+ return null;
447
451
  }
448
452
  case 'status': {
449
453
  return (
@@ -468,8 +472,7 @@ export const SpacePlugin = ({
468
472
  const dispatch = intentPlugin?.provides.intent.dispatch;
469
473
  const resolve = metadataPlugin?.provides.metadata.resolver;
470
474
  const graph = graphPlugin?.provides.graph;
471
-
472
- if (!graph || !dispatch || !resolve || !client) {
475
+ if (!client || !dispatch || !resolve || !graph) {
473
476
  return [];
474
477
  }
475
478
 
@@ -503,7 +506,6 @@ export const SpacePlugin = ({
503
506
  type: SPACES,
504
507
  properties: {
505
508
  label: ['spaces label', { ns: SPACE_PLUGIN }],
506
- palette: 'teal',
507
509
  testId: 'spacePlugin.spaces',
508
510
  role: 'branch',
509
511
  childrenPersistenceClass: 'echo',
@@ -553,8 +555,9 @@ export const SpacePlugin = ({
553
555
  properties: {
554
556
  label: ['create space label', { ns: SPACE_PLUGIN }],
555
557
  icon: 'ph--plus--regular',
556
- disposition: 'toolbar',
558
+ disposition: 'item',
557
559
  testId: 'spacePlugin.createSpace',
560
+ className: 'pbs-4',
558
561
  },
559
562
  },
560
563
  {
@@ -573,7 +576,9 @@ export const SpacePlugin = ({
573
576
  properties: {
574
577
  label: ['join space label', { ns: SPACE_PLUGIN }],
575
578
  icon: 'ph--sign-in--regular',
579
+ disposition: 'item',
576
580
  testId: 'spacePlugin.joinSpace',
581
+ className: 'pbe-4',
577
582
  },
578
583
  },
579
584
  ],
@@ -727,9 +732,33 @@ export const SpacePlugin = ({
727
732
  return;
728
733
  }
729
734
 
735
+ const type = 'orphan-settings-for-subject';
736
+ const icon = 'ph--gear--regular';
737
+
730
738
  const [subjectId] = id.split('~');
731
- const [spaceId, objectId] = parseFullyQualifiedId(subjectId);
739
+ const { spaceId, objectId } = parseId(subjectId);
732
740
  const space = client.spaces.get().find((space) => space.id === spaceId);
741
+ if (!objectId) {
742
+ const label = space
743
+ ? space.properties.name || ['unnamed space label', { ns: SPACE_PLUGIN }]
744
+ : ['unnamed object settings label', { ns: SPACE_PLUGIN }];
745
+
746
+ // TODO(wittjosiah): Support comments for arbitrary subjects.
747
+ // This is to ensure that the comments panel is not stuck on an old object.
748
+ return {
749
+ id,
750
+ type,
751
+ data: null,
752
+ properties: {
753
+ icon,
754
+ label,
755
+ showResolvedThreads: false,
756
+ object: null,
757
+ space,
758
+ },
759
+ };
760
+ }
761
+
733
762
  const object = toSignal(
734
763
  (onChange) => {
735
764
  const timeout = setTimeout(async () => {
@@ -753,10 +782,10 @@ export const SpacePlugin = ({
753
782
 
754
783
  return {
755
784
  id,
756
- type: 'orphan-settings-for-subject',
785
+ type,
757
786
  data: null,
758
787
  properties: {
759
- icon: 'ph--gear--regular',
788
+ icon,
760
789
  label,
761
790
  object,
762
791
  },
@@ -1286,11 +1315,12 @@ export const SpacePlugin = ({
1286
1315
  case SpaceAction.DUPLICATE_OBJECT: {
1287
1316
  const originalObject = intent.data?.object ?? intent.data?.result;
1288
1317
  const resolve = resolvePlugin(plugins, parseMetadataResolverPlugin)?.provides.metadata.resolver;
1289
- if (!isEchoObject(originalObject) || !resolve) {
1318
+ const space = isSpace(intent.data?.target) ? intent.data?.target : getSpace(intent.data?.target);
1319
+ if (!isEchoObject(originalObject) || !resolve || !space) {
1290
1320
  return;
1291
1321
  }
1292
1322
 
1293
- const newObject = await cloneObject(originalObject, resolve);
1323
+ const newObject = await cloneObject(originalObject, resolve, space);
1294
1324
  return {
1295
1325
  intents: [
1296
1326
  [{ action: SpaceAction.ADD_OBJECT, data: { object: newObject, target: intent.data?.target } }],
@@ -4,7 +4,7 @@
4
4
 
5
5
  import React from 'react';
6
6
 
7
- import { type EchoReactiveObject } from '@dxos/client/echo';
7
+ import { type EchoReactiveObject } from '@dxos/react-client/echo';
8
8
  import { Input, useTranslation } from '@dxos/react-ui';
9
9
 
10
10
  import { SPACE_PLUGIN } from '../meta';
@@ -15,13 +15,14 @@ export type DefaultObjectSettingsProps = {
15
15
 
16
16
  export const DefaultObjectSettings = ({ object }: DefaultObjectSettingsProps) => {
17
17
  const { t } = useTranslation(SPACE_PLUGIN);
18
+ // TODO(burdon): Standardize forms.
18
19
  return (
19
20
  <div role='form' className='flex flex-col w-full p-2 gap-1'>
20
21
  <Input.Root>
21
22
  <Input.Label>{t('name label')}</Input.Label>
22
23
  <Input.TextInput
23
24
  placeholder={t('name placeholder')}
24
- value={object.name}
25
+ value={object.name ?? ''}
25
26
  onChange={(event) => {
26
27
  object.name = event.target.value;
27
28
  }}
@@ -17,13 +17,12 @@ import {
17
17
  type Size,
18
18
  type ThemedClassName,
19
19
  Tooltip,
20
- useDensityContext,
21
20
  useTranslation,
22
21
  List,
23
22
  ListItem,
24
23
  useDefaultValue,
25
24
  } from '@dxos/react-ui';
26
- import { AttentionGlyph } from '@dxos/react-ui-attention';
25
+ import { AttentionGlyph, useAttention } from '@dxos/react-ui-attention';
27
26
  import { ComplexMap, keyToFallback } from '@dxos/util';
28
27
 
29
28
  import { SPACE_PLUGIN } from '../meta';
@@ -40,7 +39,6 @@ const noViewers = new ComplexMap<PublicKey, ObjectViewerProps>(PublicKey.hash);
40
39
  const getName = (identity: Identity) => identity.profile?.displayName ?? generateName(identity.identityKey.toHex());
41
40
 
42
41
  export const SpacePresence = ({ object, spaceKey }: { object: Expando; spaceKey?: PublicKey }) => {
43
- const density = useDensityContext();
44
42
  const spacePlugin = usePlugin<SpacePluginProvides>(SPACE_PLUGIN);
45
43
  const client = useClient();
46
44
  const identity = useIdentity();
@@ -86,11 +84,7 @@ export const SpacePresence = ({ object, spaceKey }: { object: Expando; spaceKey?
86
84
  })
87
85
  .toSorted((a, b) => a.lastSeen - b.lastSeen);
88
86
 
89
- return density === 'fine' ? (
90
- <SmallPresence count={membersForObject.length} />
91
- ) : (
92
- <FullPresence members={membersForObject} />
93
- );
87
+ return <FullPresence members={membersForObject} />;
94
88
  };
95
89
 
96
90
  export type Member = SpaceMember & {
@@ -200,28 +194,45 @@ const PrensenceAvatar = ({ identity, showName, match, group, index, onClick }: P
200
194
  );
201
195
  };
202
196
 
203
- export const SmallPresenceLive = ({ viewers }: { viewers?: ComplexMap<PublicKey, ObjectViewerProps> }) => {
204
- const [moment, setMoment] = useState(Date.now());
197
+ export const SmallPresenceLive = ({
198
+ id,
199
+ viewers,
200
+ }: {
201
+ id?: string;
202
+ viewers?: ComplexMap<PublicKey, ObjectViewerProps>;
203
+ }) => {
204
+ const getActiveViewers = (viewers: ComplexMap<PublicKey, ObjectViewerProps>): ObjectViewerProps[] => {
205
+ const moment = Date.now();
206
+ return Array.from(viewers.values()).filter(({ lastSeen }) => moment - lastSeen < ACTIVITY_DURATION);
207
+ };
205
208
 
206
- // NOTE(thure): This is necessary so Presence updates without any underlying data updating.
207
- useEffect(() => {
208
- const interval = setInterval(() => setMoment(Date.now()), REFRESH_INTERVAL);
209
- return () => clearInterval(interval);
210
- }, []);
211
-
212
- const activeViewers = viewers
213
- ? Array.from(viewers.values()).filter(({ lastSeen }) => moment - lastSeen < ACTIVITY_DURATION)
214
- : [];
209
+ const [activeViewers, setActiveViewers] = useState(viewers ? getActiveViewers(viewers) : []);
215
210
 
216
- return <SmallPresence count={activeViewers.length} />;
211
+ useEffect(() => {
212
+ if (viewers) {
213
+ setActiveViewers(getActiveViewers(viewers));
214
+ const interval = setInterval(() => {
215
+ setActiveViewers(getActiveViewers(viewers));
216
+ }, REFRESH_INTERVAL);
217
+ return () => clearInterval(interval);
218
+ }
219
+ }, [viewers]);
220
+
221
+ return <SmallPresence id={id} count={activeViewers.length} />;
217
222
  };
218
223
 
219
- export const SmallPresence = ({ count }: { count: number }) => {
224
+ export const SmallPresence = ({ id, count }: { id?: string; count: number }) => {
220
225
  const { t } = useTranslation(SPACE_PLUGIN);
226
+ const { hasAttention, isAncestor, isRelated } = useAttention(id);
227
+ const attention = hasAttention || isAncestor || isRelated;
228
+
221
229
  return (
222
230
  <Tooltip.Root>
223
231
  <Tooltip.Trigger asChild>
224
- <AttentionGlyph presence={count > 1 ? 'many' : count === 1 ? 'one' : 'none'} classNames='self-center mie-1' />
232
+ {/* TODO(wittjosiah): Don't depend on data attribute just pass prop to AttentionGlyph. */}
233
+ <div role='none' className='flex' data-attention={attention}>
234
+ <AttentionGlyph presence={count > 1 ? 'many' : count === 1 ? 'one' : 'none'} classNames='self-center mie-1' />
235
+ </div>
225
236
  </Tooltip.Trigger>
226
237
  <Tooltip.Portal>
227
238
  <Tooltip.Content side='bottom' classNames='z-[70]'>
@@ -5,8 +5,8 @@
5
5
  import React from 'react';
6
6
 
7
7
  import { useIntentDispatcher, useResolvePlugins } from '@dxos/app-framework';
8
- import { SettingsValue } from '@dxos/plugin-settings';
9
8
  import { Input, Select, toLocalizedString, useTranslation } from '@dxos/react-ui';
9
+ import { FormInput } from '@dxos/react-ui-data';
10
10
 
11
11
  import { SpaceAction, SPACE_PLUGIN } from '../meta';
12
12
  import { parseSpaceInitPlugin, type SpaceSettingsProps } from '../types';
@@ -18,7 +18,7 @@ export const SpaceSettings = ({ settings }: { settings: SpaceSettingsProps }) =>
18
18
 
19
19
  return (
20
20
  <>
21
- <SettingsValue label={t('show hidden spaces label')}>
21
+ <FormInput label={t('show hidden spaces label')}>
22
22
  <Input.Switch
23
23
  checked={settings.showHidden}
24
24
  onCheckedChange={(checked) =>
@@ -29,9 +29,9 @@ export const SpaceSettings = ({ settings }: { settings: SpaceSettingsProps }) =>
29
29
  })
30
30
  }
31
31
  />
32
- </SettingsValue>
32
+ </FormInput>
33
33
 
34
- <SettingsValue label={t('default on space create label')}>
34
+ <FormInput label={t('default on space create label')}>
35
35
  <Select.Root
36
36
  value={settings.onSpaceCreate}
37
37
  onValueChange={(value) => {
@@ -57,7 +57,7 @@ export const SpaceSettings = ({ settings }: { settings: SpaceSettingsProps }) =>
57
57
  </Select.Content>
58
58
  </Select.Portal>
59
59
  </Select.Root>
60
- </SettingsValue>
60
+ </FormInput>
61
61
  </>
62
62
  );
63
63
  };
@@ -0,0 +1,59 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import React, { useCallback, useState } from 'react';
6
+
7
+ import { log } from '@dxos/log';
8
+ import { EdgeReplicationSetting } from '@dxos/protocols/proto/dxos/echo/metadata';
9
+ import { type Space } from '@dxos/react-client/echo';
10
+ import { Input, useTranslation } from '@dxos/react-ui';
11
+
12
+ import { SPACE_PLUGIN } from '../meta';
13
+
14
+ export type SpaceSettingsPanelProps = {
15
+ space: Space;
16
+ };
17
+
18
+ export const SpaceSettingsPanel = ({ space }: SpaceSettingsPanelProps) => {
19
+ const { t } = useTranslation(SPACE_PLUGIN);
20
+ const [edgeReplication, setEdgeReplication] = useState(
21
+ space.internal.data.edgeReplication === EdgeReplicationSetting.ENABLED,
22
+ );
23
+
24
+ const toggleEdgeReplication = useCallback(
25
+ async (next: boolean) => {
26
+ setEdgeReplication(next);
27
+ await space?.internal
28
+ .setEdgeReplicationPreference(next ? EdgeReplicationSetting.ENABLED : EdgeReplicationSetting.DISABLED)
29
+ .catch((err) => {
30
+ log.catch(err);
31
+ setEdgeReplication(!next);
32
+ });
33
+ },
34
+ [space],
35
+ );
36
+
37
+ return (
38
+ <div role='form' className='flex flex-col w-full p-2 gap-4'>
39
+ <Input.Root>
40
+ <div role='none' className='flex flex-col gap-1'>
41
+ <Input.Label>{t('name label')}</Input.Label>
42
+ <Input.TextInput
43
+ placeholder={t('name placeholder')}
44
+ value={space.properties.name}
45
+ onChange={(event) => {
46
+ space.properties.name = event.target.value;
47
+ }}
48
+ />
49
+ </div>
50
+ </Input.Root>
51
+ <Input.Root>
52
+ <div role='none' className='flex justify-between'>
53
+ <Input.Label>{t('edge replication label')}</Input.Label>
54
+ <Input.Switch checked={edgeReplication} onCheckedChange={toggleEdgeReplication} />
55
+ </div>
56
+ </Input.Root>
57
+ </div>
58
+ );
59
+ };
@@ -63,7 +63,7 @@ export const SyncStatusIndicator = ({ state }: { state: SpaceSyncStateMap }) =>
63
63
  classNames={classNames}
64
64
  />
65
65
  </Popover.Trigger>
66
- <Popover.Content>
66
+ <Popover.Content sideOffset={16}>
67
67
  <SyncStatusDetail state={state} summary={summary} debug={false} />
68
68
  </Popover.Content>
69
69
  </Popover.Root>
@@ -15,5 +15,6 @@ export * from './ShareSpaceButton';
15
15
  export * from './SpaceMain';
16
16
  export * from './SpacePresence';
17
17
  export * from './SpaceSettings';
18
+ export * from './SpaceSettingsPanel';
18
19
  export * from './SaveStatus';
19
20
  export * from './SyncStatus';
@@ -10,8 +10,8 @@ export default [
10
10
  [SPACE_PLUGIN]: {
11
11
  'plugin name': 'Spaces',
12
12
  'first run message': 'Nothing selected.',
13
- 'create space label': 'Create a new space',
14
- 'join space label': 'Join a space',
13
+ 'create space label': 'Create space',
14
+ 'join space label': 'Join space',
15
15
  'empty space message': 'No documents',
16
16
  'empty tree message': 'No spaces',
17
17
  'unnamed space label': 'New space',
@@ -92,6 +92,8 @@ export default [
92
92
  'join success label': 'Successfully joined space',
93
93
  'name label': 'Name',
94
94
  'name placeholder': 'Name',
95
+ 'unnamed object settings label': 'Settings',
96
+ 'edge replication label': 'Enable EDGE Replication',
95
97
  },
96
98
  },
97
99
  },
@@ -15,6 +15,7 @@ import type {
15
15
  import { type Expando } from '@dxos/echo-schema';
16
16
  import { type SchemaProvides } from '@dxos/plugin-client';
17
17
  import { type PublicKey } from '@dxos/react-client';
18
+ import { type Space } from '@dxos/react-client/echo';
18
19
  import { type Label } from '@dxos/react-ui';
19
20
  import { type ComplexMap } from '@dxos/util';
20
21
 
@@ -99,7 +100,8 @@ export interface TypedObjectSerializer<T extends Expando = Expando> {
99
100
 
100
101
  /**
101
102
  * @param params.content
103
+ * @param params.space Space to use for deserializing schema references.
102
104
  * @param params.newId Generate new ID for deserialized object.
103
105
  */
104
- deserialize(params: { content: string; newId?: boolean }): Promise<T>;
106
+ deserialize(params: { content: string; space: Space; newId?: boolean }): Promise<T>;
105
107
  }
package/src/util.tsx CHANGED
@@ -154,7 +154,7 @@ const getCollectionGraphNodePartials = ({
154
154
  },
155
155
  onCopy: async (child: Node<EchoReactiveObject<any>>, index?: number) => {
156
156
  // Create clone of child and add to destination space.
157
- const newObject = await cloneObject(child.data, resolve);
157
+ const newObject = await cloneObject(child.data, resolve, space);
158
158
  space.db.add(newObject);
159
159
  if (typeof index !== 'undefined') {
160
160
  collection.objects.splice(index, 0, newObject);
@@ -596,12 +596,12 @@ export const getNestedObjects = async (
596
596
  * @deprecated Workaround for ECHO not supporting clone.
597
597
  */
598
598
  // TODO(burdon): Remove.
599
- export const cloneObject = async (object: Expando, resolve: MetadataResolver): Promise<Expando> => {
599
+ export const cloneObject = async (object: Expando, resolve: MetadataResolver, newSpace: Space): Promise<Expando> => {
600
600
  const schema = getSchema(object);
601
601
  const typename = schema ? getObjectAnnotation(schema)?.typename ?? EXPANDO_TYPENAME : EXPANDO_TYPENAME;
602
602
  const metadata = resolve(typename);
603
603
  const serializer = metadata.serializer;
604
604
  invariant(serializer, `No serializer for type: ${typename}`);
605
605
  const content = await serializer.serialize({ object });
606
- return serializer.deserialize({ content, newId: true });
606
+ return serializer.deserialize({ content, space: newSpace, newId: true });
607
607
  };