@dxos/plugin-thread 0.8.0 → 0.8.1-main.013e445

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 (171) hide show
  1. package/dist/lib/browser/ChannelContainer-YWGW5LTF.mjs +74 -0
  2. package/dist/lib/browser/ChannelContainer-YWGW5LTF.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-YK3ICB7G.mjs → chunk-FZ34L7RM.mjs} +4 -3
  4. package/dist/lib/browser/chunk-FZ34L7RM.mjs.map +7 -0
  5. package/dist/lib/browser/{chunk-BNEWNQBO.mjs → chunk-MC42ZV23.mjs} +16 -4
  6. package/dist/lib/browser/{chunk-BNEWNQBO.mjs.map → chunk-MC42ZV23.mjs.map} +4 -4
  7. package/dist/lib/browser/{chunk-NAO3LZMZ.mjs → chunk-NOOMCX5E.mjs} +3 -2
  8. package/dist/lib/browser/chunk-NOOMCX5E.mjs.map +7 -0
  9. package/dist/lib/browser/{chunk-UPKU23DA.mjs → chunk-VDQ3TPIW.mjs} +28 -19
  10. package/dist/lib/browser/chunk-VDQ3TPIW.mjs.map +7 -0
  11. package/dist/lib/browser/{chunk-636QDHE3.mjs → chunk-WBJLUUOK.mjs} +1 -1
  12. package/dist/lib/browser/{chunk-636QDHE3.mjs.map → chunk-WBJLUUOK.mjs.map} +2 -2
  13. package/dist/lib/browser/index.mjs +38 -27
  14. package/dist/lib/browser/index.mjs.map +4 -4
  15. package/dist/lib/browser/{intent-resolver-FAFQ54NP.mjs → intent-resolver-FCC6BZSE.mjs} +5 -5
  16. package/dist/lib/browser/{intent-resolver-FAFQ54NP.mjs.map → intent-resolver-FCC6BZSE.mjs.map} +2 -2
  17. package/dist/lib/browser/{markdown-RPYOUBVR.mjs → markdown-I7DN4THW.mjs} +6 -6
  18. package/dist/lib/browser/markdown-I7DN4THW.mjs.map +7 -0
  19. package/dist/lib/browser/meta.json +1 -1
  20. package/dist/lib/browser/{react-surface-Y4MUSICN.mjs → react-surface-34FQO5BB.mjs} +23 -31
  21. package/dist/lib/browser/react-surface-34FQO5BB.mjs.map +7 -0
  22. package/dist/lib/browser/{settings-WQTE5AEZ.mjs → settings-G5ZTDTIU.mjs} +3 -3
  23. package/dist/lib/browser/{state-GNFUURIC.mjs → state-HPUFZBB7.mjs} +4 -3
  24. package/dist/lib/browser/state-HPUFZBB7.mjs.map +7 -0
  25. package/dist/lib/browser/types.mjs +2 -2
  26. package/dist/lib/node/ChannelContainer-XQIU3OFX.cjs +98 -0
  27. package/dist/lib/node/ChannelContainer-XQIU3OFX.cjs.map +7 -0
  28. package/dist/lib/node/{chunk-AWSTZ2KN.cjs → chunk-FMKMIGXZ.cjs} +30 -6
  29. package/dist/lib/node/chunk-FMKMIGXZ.cjs.map +7 -0
  30. package/dist/lib/node/{chunk-RQR5Z23G.cjs → chunk-G63K6IDM.cjs} +35 -26
  31. package/dist/lib/node/chunk-G63K6IDM.cjs.map +7 -0
  32. package/dist/lib/node/{chunk-F735VMTN.cjs → chunk-ONLWOUYZ.cjs} +4 -4
  33. package/dist/lib/node/{chunk-F735VMTN.cjs.map → chunk-ONLWOUYZ.cjs.map} +2 -2
  34. package/dist/lib/node/{chunk-FPWNZ6TB.cjs → chunk-VEVJBOWK.cjs} +8 -7
  35. package/dist/lib/node/chunk-VEVJBOWK.cjs.map +7 -0
  36. package/dist/lib/node/{chunk-CMEMMYDL.cjs → chunk-X5TUGJ5K.cjs} +8 -7
  37. package/dist/lib/node/chunk-X5TUGJ5K.cjs.map +7 -0
  38. package/dist/lib/node/index.cjs +74 -64
  39. package/dist/lib/node/index.cjs.map +4 -4
  40. package/dist/lib/node/{intent-resolver-DVBEXJ33.cjs → intent-resolver-KTZSUHCU.cjs} +22 -22
  41. package/dist/lib/node/{intent-resolver-DVBEXJ33.cjs.map → intent-resolver-KTZSUHCU.cjs.map} +2 -2
  42. package/dist/lib/node/{markdown-ELRGISXE.cjs → markdown-EUC6FFCM.cjs} +12 -12
  43. package/dist/lib/node/markdown-EUC6FFCM.cjs.map +7 -0
  44. package/dist/lib/node/meta.json +1 -1
  45. package/dist/lib/node/{react-surface-7HLTN5XS.cjs → react-surface-SF7YHWER.cjs} +44 -52
  46. package/dist/lib/node/react-surface-SF7YHWER.cjs.map +7 -0
  47. package/dist/lib/node/{settings-7VQYARM5.cjs → settings-WQXGBGOV.cjs} +8 -8
  48. package/dist/lib/node/{state-TQWEZ6B3.cjs → state-JMI5ZXZI.cjs} +8 -7
  49. package/dist/lib/node/state-JMI5ZXZI.cjs.map +7 -0
  50. package/dist/lib/node/types.cjs +4 -4
  51. package/dist/lib/node/types.cjs.map +1 -1
  52. package/dist/lib/node-esm/ChannelContainer-G63QTQHU.mjs +75 -0
  53. package/dist/lib/node-esm/ChannelContainer-G63QTQHU.mjs.map +7 -0
  54. package/dist/lib/node-esm/{chunk-FXUATCLW.mjs → chunk-42F2DTMM.mjs} +1 -1
  55. package/dist/lib/node-esm/{chunk-FXUATCLW.mjs.map → chunk-42F2DTMM.mjs.map} +2 -2
  56. package/dist/lib/node-esm/{chunk-HLWJTUWZ.mjs → chunk-C46EUYQO.mjs} +16 -4
  57. package/dist/lib/node-esm/{chunk-HLWJTUWZ.mjs.map → chunk-C46EUYQO.mjs.map} +4 -4
  58. package/dist/lib/node-esm/{chunk-CISLDOH7.mjs → chunk-FIPZFSRG.mjs} +28 -19
  59. package/dist/lib/node-esm/chunk-FIPZFSRG.mjs.map +7 -0
  60. package/dist/lib/node-esm/{chunk-ZJITX3TO.mjs → chunk-TXW5SIBN.mjs} +3 -2
  61. package/dist/lib/node-esm/chunk-TXW5SIBN.mjs.map +7 -0
  62. package/dist/lib/node-esm/{chunk-33NDMQQQ.mjs → chunk-XUAPEJSP.mjs} +4 -3
  63. package/dist/lib/node-esm/chunk-XUAPEJSP.mjs.map +7 -0
  64. package/dist/lib/node-esm/index.mjs +38 -27
  65. package/dist/lib/node-esm/index.mjs.map +4 -4
  66. package/dist/lib/node-esm/{intent-resolver-DHVLBNCC.mjs → intent-resolver-F4GQ2DBX.mjs} +5 -5
  67. package/dist/lib/node-esm/{intent-resolver-DHVLBNCC.mjs.map → intent-resolver-F4GQ2DBX.mjs.map} +2 -2
  68. package/dist/lib/node-esm/{markdown-23OB7EZG.mjs → markdown-LNEGE76H.mjs} +6 -6
  69. package/dist/lib/node-esm/markdown-LNEGE76H.mjs.map +7 -0
  70. package/dist/lib/node-esm/meta.json +1 -1
  71. package/dist/lib/node-esm/{react-surface-AZHTTR6J.mjs → react-surface-W6TBSW7R.mjs} +23 -31
  72. package/dist/lib/node-esm/react-surface-W6TBSW7R.mjs.map +7 -0
  73. package/dist/lib/node-esm/{settings-SGMM7UDT.mjs → settings-3NFSK2ZO.mjs} +3 -3
  74. package/dist/lib/node-esm/{state-7MLR27YG.mjs → state-PSZO57LP.mjs} +4 -3
  75. package/dist/lib/node-esm/state-PSZO57LP.mjs.map +7 -0
  76. package/dist/lib/node-esm/types.mjs +2 -2
  77. package/dist/types/src/ThreadPlugin.d.ts.map +1 -1
  78. package/dist/types/src/capabilities/capabilities.d.ts +5 -0
  79. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  80. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  81. package/dist/types/src/components/ChannelContainer.d.ts +9 -0
  82. package/dist/types/src/components/ChannelContainer.d.ts.map +1 -0
  83. package/dist/types/src/components/Chat.stories.d.ts.map +1 -1
  84. package/dist/types/src/components/ChatContainer.d.ts +3 -2
  85. package/dist/types/src/components/ChatContainer.d.ts.map +1 -1
  86. package/dist/types/src/components/CommentContainer.d.ts +2 -1
  87. package/dist/types/src/components/CommentContainer.d.ts.map +1 -1
  88. package/dist/types/src/components/Comments.stories.d.ts.map +1 -1
  89. package/dist/types/src/components/CommentsContainer.d.ts +2 -1
  90. package/dist/types/src/components/CommentsContainer.d.ts.map +1 -1
  91. package/dist/types/src/components/MessageContainer.d.ts +3 -2
  92. package/dist/types/src/components/MessageContainer.d.ts.map +1 -1
  93. package/dist/types/src/components/ThreadComplementary.d.ts +2 -1
  94. package/dist/types/src/components/ThreadComplementary.d.ts.map +1 -1
  95. package/dist/types/src/components/ThreadSettings.d.ts +2 -1
  96. package/dist/types/src/components/ThreadSettings.d.ts.map +1 -1
  97. package/dist/types/src/components/index.d.ts +3 -2
  98. package/dist/types/src/components/index.d.ts.map +1 -1
  99. package/dist/types/src/components/testing.d.ts +104 -44
  100. package/dist/types/src/components/testing.d.ts.map +1 -1
  101. package/dist/types/src/events.d.ts +4 -0
  102. package/dist/types/src/events.d.ts.map +1 -0
  103. package/dist/types/src/hooks/analytics.d.ts +2 -1
  104. package/dist/types/src/hooks/analytics.d.ts.map +1 -1
  105. package/dist/types/src/index.d.ts +2 -0
  106. package/dist/types/src/index.d.ts.map +1 -1
  107. package/dist/types/src/meta.d.ts +2 -7
  108. package/dist/types/src/meta.d.ts.map +1 -1
  109. package/dist/types/src/translations.d.ts +2 -9
  110. package/dist/types/src/translations.d.ts.map +1 -1
  111. package/dist/types/src/types.d.ts +4 -1
  112. package/dist/types/src/types.d.ts.map +1 -1
  113. package/package.json +32 -30
  114. package/src/ThreadPlugin.tsx +10 -4
  115. package/src/capabilities/capabilities.ts +6 -0
  116. package/src/capabilities/intent-resolver.ts +1 -1
  117. package/src/capabilities/react-surface.tsx +5 -16
  118. package/src/capabilities/state.ts +1 -1
  119. package/src/components/ChannelContainer.tsx +80 -0
  120. package/src/components/Chat.stories.tsx +4 -2
  121. package/src/components/ChatContainer.tsx +5 -5
  122. package/src/components/CommentContainer.tsx +1 -1
  123. package/src/components/Comments.stories.tsx +2 -1
  124. package/src/components/MessageContainer.tsx +15 -13
  125. package/src/components/ThreadComplementary.tsx +5 -4
  126. package/src/components/index.ts +1 -1
  127. package/src/components/testing.ts +15 -12
  128. package/src/events.ts +11 -0
  129. package/src/extensions/threads.ts +2 -2
  130. package/src/hooks/analytics.ts +7 -2
  131. package/src/index.ts +2 -0
  132. package/src/meta.ts +2 -2
  133. package/src/translations.ts +1 -3
  134. package/src/types.ts +4 -1
  135. package/dist/lib/browser/ThreadContainer-6OP5AG2W.mjs +0 -19
  136. package/dist/lib/browser/ThreadContainer-6OP5AG2W.mjs.map +0 -7
  137. package/dist/lib/browser/chunk-NAO3LZMZ.mjs.map +0 -7
  138. package/dist/lib/browser/chunk-UPKU23DA.mjs.map +0 -7
  139. package/dist/lib/browser/chunk-VOMIDDNO.mjs +0 -15
  140. package/dist/lib/browser/chunk-VOMIDDNO.mjs.map +0 -7
  141. package/dist/lib/browser/chunk-YK3ICB7G.mjs.map +0 -7
  142. package/dist/lib/browser/markdown-RPYOUBVR.mjs.map +0 -7
  143. package/dist/lib/browser/react-surface-Y4MUSICN.mjs.map +0 -7
  144. package/dist/lib/browser/state-GNFUURIC.mjs.map +0 -7
  145. package/dist/lib/node/ThreadContainer-LTKKLPXX.cjs +0 -45
  146. package/dist/lib/node/ThreadContainer-LTKKLPXX.cjs.map +0 -7
  147. package/dist/lib/node/chunk-AWSTZ2KN.cjs.map +0 -7
  148. package/dist/lib/node/chunk-BHFHFTHO.cjs +0 -50
  149. package/dist/lib/node/chunk-BHFHFTHO.cjs.map +0 -7
  150. package/dist/lib/node/chunk-CMEMMYDL.cjs.map +0 -7
  151. package/dist/lib/node/chunk-FPWNZ6TB.cjs.map +0 -7
  152. package/dist/lib/node/chunk-RQR5Z23G.cjs.map +0 -7
  153. package/dist/lib/node/markdown-ELRGISXE.cjs.map +0 -7
  154. package/dist/lib/node/react-surface-7HLTN5XS.cjs.map +0 -7
  155. package/dist/lib/node/state-TQWEZ6B3.cjs.map +0 -7
  156. package/dist/lib/node-esm/ThreadContainer-SV74JFRH.mjs +0 -20
  157. package/dist/lib/node-esm/ThreadContainer-SV74JFRH.mjs.map +0 -7
  158. package/dist/lib/node-esm/chunk-33NDMQQQ.mjs.map +0 -7
  159. package/dist/lib/node-esm/chunk-CISLDOH7.mjs.map +0 -7
  160. package/dist/lib/node-esm/chunk-PJPVEO7E.mjs +0 -17
  161. package/dist/lib/node-esm/chunk-PJPVEO7E.mjs.map +0 -7
  162. package/dist/lib/node-esm/chunk-ZJITX3TO.mjs.map +0 -7
  163. package/dist/lib/node-esm/markdown-23OB7EZG.mjs.map +0 -7
  164. package/dist/lib/node-esm/react-surface-AZHTTR6J.mjs.map +0 -7
  165. package/dist/lib/node-esm/state-7MLR27YG.mjs.map +0 -7
  166. package/dist/types/src/components/ThreadContainer.d.ts +0 -7
  167. package/dist/types/src/components/ThreadContainer.d.ts.map +0 -1
  168. package/src/components/ThreadContainer.tsx +0 -22
  169. /package/dist/lib/browser/{settings-WQTE5AEZ.mjs.map → settings-G5ZTDTIU.mjs.map} +0 -0
  170. /package/dist/lib/node/{settings-7VQYARM5.cjs.map → settings-WQXGBGOV.cjs.map} +0 -0
  171. /package/dist/lib/node-esm/{settings-SGMM7UDT.mjs.map → settings-3NFSK2ZO.mjs.map} +0 -0
@@ -220,7 +220,7 @@ export default (context: PluginsContext) =>
220
220
  ],
221
221
  };
222
222
  } else {
223
- if (!messageIndex || !message) {
223
+ if (messageIndex === undefined || !message) {
224
224
  return;
225
225
  }
226
226
 
@@ -10,7 +10,7 @@ import { SettingsStore } from '@dxos/local-storage';
10
10
  import { ChannelType, type ThreadType } from '@dxos/plugin-space/types';
11
11
  import { getSpace } from '@dxos/react-client/echo';
12
12
 
13
- import { ThreadContainer, ThreadComplementary, ThreadSettings } from '../components';
13
+ import { ChannelContainer, ThreadComplementary, ThreadSettings } from '../components';
14
14
  import { THREAD_PLUGIN } from '../meta';
15
15
  import { type ThreadSettingsProps } from '../types';
16
16
 
@@ -21,23 +21,12 @@ export default () =>
21
21
  role: 'article',
22
22
  filter: (data): data is { subject: ChannelType } =>
23
23
  data.subject instanceof ChannelType && !!data.subject.threads[0],
24
- component: ({ data, role }) => {
24
+ component: ({ data: { subject: channel }, role }) => {
25
25
  const layout = useLayout();
26
- const channel = data.subject;
27
- void channel.threads[0].load();
28
- const thread = channel.threads[0].target;
29
- if (!thread) {
30
- return null;
31
- }
32
-
33
26
  const currentPosition = layout.active.findIndex((id) => id === channel.id);
34
- if (currentPosition > 0) {
35
- const objectToTheLeft = layout.active[currentPosition - 1];
36
- const context = getSpace(channel)?.db.getObjectById(objectToTheLeft);
37
- return <ThreadContainer role={role} thread={thread} context={context} />;
38
- }
39
-
40
- return <ThreadContainer role={role} thread={thread} />;
27
+ const objectToTheLeft = layout.active[currentPosition - 1];
28
+ const context = currentPosition > 0 ? getSpace(channel)?.db.getObjectById(objectToTheLeft) : undefined;
29
+ return <ChannelContainer role={role} channel={channel} context={context} />;
41
30
  },
42
31
  }),
43
32
  createSurface({
@@ -11,7 +11,7 @@ import { type ThreadState, type ViewStore } from '../types';
11
11
  const initialViewState = { showResolvedThreads: false };
12
12
 
13
13
  export default () => {
14
- const state = create<ThreadState>({ drafts: {} });
14
+ const state = create<ThreadState>({ activities: {}, drafts: {} });
15
15
  const viewStore = create<ViewStore>({});
16
16
 
17
17
  const getViewState = (subjectId: string) => {
@@ -0,0 +1,80 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import React, { useCallback, type ComponentProps } from 'react';
6
+
7
+ import { Surface, useCapabilities, useCapability } from '@dxos/app-framework';
8
+ import { type NodeArg } from '@dxos/plugin-graph';
9
+ import { type ChannelType } from '@dxos/plugin-space/types';
10
+ import { fullyQualifiedId } from '@dxos/react-client/echo';
11
+ import { createEditorActionGroup } from '@dxos/react-ui-editor';
12
+ import {
13
+ createMenuAction,
14
+ MenuProvider,
15
+ ToolbarMenu,
16
+ type ToolbarMenuActionGroupProperties,
17
+ useMenuActions,
18
+ type MenuAction,
19
+ } from '@dxos/react-ui-menu';
20
+ import { StackItem } from '@dxos/react-ui-stack';
21
+
22
+ import { ChatContainer } from './ChatContainer';
23
+ import { ThreadCapabilities } from '../capabilities';
24
+
25
+ const useActivitiesMenu = (activities: ThreadCapabilities.Activity[], activity: string | undefined) => {
26
+ return useMenuActions(() => ({
27
+ nodes: [
28
+ createEditorActionGroup('activities', {
29
+ variant: 'toggleGroup',
30
+ selectCardinality: 'single',
31
+ value: activity,
32
+ } as ToolbarMenuActionGroupProperties) as NodeArg<any>,
33
+ ...activities.map(({ id, ...properties }) =>
34
+ createMenuAction(id, {
35
+ ...properties,
36
+ checked: true,
37
+ }),
38
+ ),
39
+ ],
40
+ edges: [
41
+ { source: 'root', target: 'activities' },
42
+ ...activities.map((activity) => ({ source: 'activities', target: activity.id })),
43
+ ],
44
+ }));
45
+ };
46
+
47
+ const ChannelContainer = ({
48
+ role,
49
+ channel,
50
+ context,
51
+ }: Pick<ComponentProps<typeof ChatContainer>, 'context'> & { channel: ChannelType; role: string }) => {
52
+ const { state } = useCapability(ThreadCapabilities.MutableState);
53
+ const currentActivity = state.activities[channel.id];
54
+ const activities = useCapabilities(ThreadCapabilities.Activity);
55
+ const menu = useActivitiesMenu(activities, currentActivity);
56
+ const handleAction = useCallback(
57
+ (action: MenuAction) => (state.activities[channel.id] = currentActivity === action.id ? undefined : action.id),
58
+ [currentActivity],
59
+ );
60
+
61
+ const thread = channel.threads[0].target;
62
+ if (!thread) {
63
+ return null;
64
+ }
65
+
66
+ return (
67
+ <StackItem.Content toolbar>
68
+ <MenuProvider {...menu} onAction={handleAction} attendableId={fullyQualifiedId(channel)}>
69
+ <ToolbarMenu />
70
+ </MenuProvider>
71
+ {currentActivity ? (
72
+ <Surface role={`channel-activity--${currentActivity}`} data={{ subject: channel }} />
73
+ ) : (
74
+ <ChatContainer thread={thread} context={context} />
75
+ )}
76
+ </StackItem.Content>
77
+ );
78
+ };
79
+
80
+ export default ChannelContainer;
@@ -7,15 +7,16 @@ import '@dxos-theme';
7
7
  import { type Meta } from '@storybook/react';
8
8
  import React, { useEffect, useState } from 'react';
9
9
 
10
- import { Capabilities, contributes, createSurface } from '@dxos/app-framework';
10
+ import { Capabilities, contributes, createSurface, IntentPlugin } from '@dxos/app-framework';
11
11
  import { withPluginManager } from '@dxos/app-framework/testing';
12
- import { MessageType, ThreadType } from '@dxos/plugin-space/types';
12
+ import { ThreadType } from '@dxos/plugin-space/types';
13
13
  import { faker } from '@dxos/random';
14
14
  import { useClient } from '@dxos/react-client';
15
15
  import { type Space } from '@dxos/react-client/echo';
16
16
  import { useIdentity } from '@dxos/react-client/halo';
17
17
  import { withClientProvider } from '@dxos/react-client/testing';
18
18
  import { Thread } from '@dxos/react-ui-thread';
19
+ import { MessageType } from '@dxos/schema';
19
20
  import { withLayout, withTheme } from '@dxos/storybook-utils';
20
21
 
21
22
  import { ChatContainer } from './ChatContainer';
@@ -58,6 +59,7 @@ const meta: Meta = {
58
59
  render: () => <Story />,
59
60
  decorators: [
60
61
  withPluginManager({
62
+ plugins: [IntentPlugin()],
61
63
  capabilities: [
62
64
  contributes(
63
65
  Capabilities.ReactSurface,
@@ -6,7 +6,6 @@ import React, { useLayoutEffect, useMemo, useRef, useState } from 'react';
6
6
 
7
7
  import { Ref } from '@dxos/echo-schema';
8
8
  import { create, makeRef, RefArray } from '@dxos/live-object';
9
- import { MessageType } from '@dxos/plugin-space/types';
10
9
  import { fullyQualifiedId, getSpace, useMembers } from '@dxos/react-client/echo';
11
10
  import { useIdentity } from '@dxos/react-client/halo';
12
11
  import { Icon, ScrollArea, useThemeContext, useTranslation } from '@dxos/react-ui';
@@ -14,6 +13,7 @@ import { createBasicExtensions, createThemeExtensions, listener } from '@dxos/re
14
13
  import { StackItem } from '@dxos/react-ui-stack';
15
14
  import { mx } from '@dxos/react-ui-theme';
16
15
  import { MessageTextbox, type MessageTextboxProps, Thread, ThreadFooter, threadLayout } from '@dxos/react-ui-thread';
16
+ import { MessageType } from '@dxos/schema';
17
17
 
18
18
  import { MessageContainer } from './MessageContainer';
19
19
  import { command } from './command-extension';
@@ -80,9 +80,9 @@ export const ChatContainer = ({ thread, context, current, autoFocusTextbox }: Th
80
80
  makeRef(
81
81
  create(MessageType, {
82
82
  sender: { identityKey: identity.identityKey.toHex() },
83
- timestamp: new Date().toISOString(),
84
- text: messageRef.current,
85
- context: context ? makeRef(context) : undefined,
83
+ created: new Date().toISOString(),
84
+ blocks: [{ type: 'text', text: messageRef.current }],
85
+ properties: context ? { context: makeRef(context) } : undefined,
86
86
  }),
87
87
  ),
88
88
  );
@@ -110,7 +110,7 @@ export const ChatContainer = ({ thread, context, current, autoFocusTextbox }: Th
110
110
  <ScrollArea.Root classNames='col-span-2'>
111
111
  <ScrollArea.Viewport classNames='overflow-anchored after:overflow-anchor after:block after:bs-px after:-mbs-px [&>div]:min-bs-full [&>div]:!grid [&>div]:grid-rows-[1fr_0]'>
112
112
  <div role='none' className={mx(threadLayout, 'place-self-end')}>
113
- {RefArray.allResolvedTargets(thread.messages ?? []).map((message) => (
113
+ {RefArray.targets(thread.messages ?? []).map((message) => (
114
114
  <MessageContainer key={message.id} message={message} members={members} onDelete={handleDelete} />
115
115
  ))}
116
116
  </div>
@@ -179,7 +179,7 @@ export const CommentContainer = ({
179
179
  </div>
180
180
  </div>
181
181
  {/** TODO(dmaretskyi): How's `thread.messages` undefined? */}
182
- {RefArray.allResolvedTargets(thread.messages?.filter(isNonNullable) ?? []).map((message) => (
182
+ {RefArray.targets(thread.messages?.filter(isNonNullable) ?? []).map((message) => (
183
183
  <MessageContainer key={message.id} message={message} members={members} onDelete={handleMessageDelete} />
184
184
  ))}
185
185
  {/*
@@ -9,11 +9,12 @@ import React, { useEffect, useState } from 'react';
9
9
 
10
10
  import { IntentPlugin } from '@dxos/app-framework';
11
11
  import { withPluginManager } from '@dxos/app-framework/testing';
12
- import { MessageType, ThreadType } from '@dxos/plugin-space/types';
12
+ import { ThreadType } from '@dxos/plugin-space/types';
13
13
  import { faker } from '@dxos/random';
14
14
  import { Filter, fullyQualifiedId, useQuery, useSpace } from '@dxos/react-client/echo';
15
15
  import { useIdentity } from '@dxos/react-client/halo';
16
16
  import { type ClientRepeatedComponentProps, ClientRepeater } from '@dxos/react-client/testing';
17
+ import { MessageType } from '@dxos/schema';
17
18
  import { withLayout, withTheme } from '@dxos/storybook-utils';
18
19
 
19
20
  import { CommentsContainer } from './CommentsContainer';
@@ -8,7 +8,6 @@ import React, { forwardRef, useCallback, useEffect, useRef, useState } from 'rea
8
8
 
9
9
  import { Surface } from '@dxos/app-framework';
10
10
  import { RefArray } from '@dxos/live-object';
11
- import { type MessageType } from '@dxos/plugin-space/types';
12
11
  import { PublicKey } from '@dxos/react-client';
13
12
  import { type ReactiveEchoObject, type Expando, type SpaceMember } from '@dxos/react-client/echo';
14
13
  import { useIdentity, type Identity } from '@dxos/react-client/halo';
@@ -22,6 +21,7 @@ import {
22
21
  mx,
23
22
  } from '@dxos/react-ui-theme';
24
23
  import { MessageHeading, MessageRoot } from '@dxos/react-ui-thread';
24
+ import { type TextContentBlock, type MessageType } from '@dxos/schema';
25
25
 
26
26
  import { command } from './command-extension';
27
27
  import { useOnEditAnalytics } from '../hooks';
@@ -50,6 +50,10 @@ export const MessageContainer = ({
50
50
  const { t } = useTranslation(THREAD_PLUGIN);
51
51
  const editLabel = t(editing ? 'save message label' : 'edit message label');
52
52
  const deleteLabel = t('delete message label');
53
+ const textBlock = message.blocks.find((block) => block.type === 'text');
54
+ const references = message.blocks.filter((block) => block.type === 'reference').map((block) => block.reference);
55
+
56
+ useOnEditAnalytics(message, textBlock, !!editing);
53
57
 
54
58
  return (
55
59
  <MessageRoot {...messageMetadata} classNames={[hoverableControls, hoverableFocusedWithinControls]}>
@@ -99,9 +103,9 @@ export const MessageContainer = ({
99
103
  )}
100
104
  </ButtonGroup>
101
105
  </MessageHeading>
102
- <TextboxBlock message={message} isAuthor={userIsAuthor} editing={editing} />
103
- {RefArray.allResolvedTargets(message.parts ?? []).map((part, index) => (
104
- <MessagePart key={index} part={part} />
106
+ {textBlock && <TextboxBlock block={textBlock} isAuthor={userIsAuthor} editing={editing} />}
107
+ {RefArray.targets(references).map((reference, index) => (
108
+ <MessagePart key={index} part={reference} />
105
109
  ))}
106
110
  </MessageRoot>
107
111
  );
@@ -112,31 +116,31 @@ const MessagePart = ({ part }: { part: Expando }) => {
112
116
  };
113
117
 
114
118
  const TextboxBlock = ({
115
- message,
119
+ block,
116
120
  isAuthor,
117
121
  editing,
118
122
  }: {
119
- message: MessageType;
123
+ block: TextContentBlock;
120
124
  editing?: boolean;
121
125
  isAuthor?: boolean;
122
126
  identity?: Identity;
123
127
  }) => {
124
128
  const { themeMode } = useThemeContext();
125
- const inMemoryContentRef = useRef(message.text);
129
+ const inMemoryContentRef = useRef(block.text);
126
130
 
127
131
  const handleDocumentChange = useCallback((newState: string) => {
128
132
  inMemoryContentRef.current = newState;
129
133
  }, []);
130
134
 
131
135
  const saveDocumentChange = useCallback(() => {
132
- message.text = inMemoryContentRef.current;
133
- }, [message]);
136
+ block.text = inMemoryContentRef.current;
137
+ }, [block]);
134
138
 
135
139
  useOnTransition(editing, true, false, saveDocumentChange);
136
140
 
137
141
  const { parentRef, focusAttributes, view } = useTextEditor(
138
142
  () => ({
139
- initialValue: message.text,
143
+ initialValue: block.text,
140
144
  extensions: [
141
145
  createBasicExtensions({ readonly: !isAuthor || !editing }),
142
146
  createThemeExtensions({ themeMode }),
@@ -148,15 +152,13 @@ const TextboxBlock = ({
148
152
  }),
149
153
  ],
150
154
  }),
151
- [message.text, editing, isAuthor, themeMode, handleDocumentChange],
155
+ [block.text, editing, isAuthor, themeMode, handleDocumentChange],
152
156
  );
153
157
 
154
158
  useEffect(() => {
155
159
  editing && view?.focus();
156
160
  }, [editing, view]);
157
161
 
158
- useOnEditAnalytics(message, !!editing);
159
-
160
162
  return <div role='none' ref={parentRef} className='mie-4' {...focusAttributes} />;
161
163
  };
162
164
 
@@ -6,12 +6,13 @@ import React, { useCallback, useEffect, useMemo } from 'react';
6
6
 
7
7
  import { createIntent, LayoutAction, useCapability, useCapabilities, useIntentDispatcher } from '@dxos/app-framework';
8
8
  import { ThreadCapabilities } from '@dxos/plugin-space';
9
- import { MessageType, type ThreadType } from '@dxos/plugin-space/types';
9
+ import { type ThreadType } from '@dxos/plugin-space/types';
10
10
  import { create, fullyQualifiedId, makeRef, RefArray } from '@dxos/react-client/echo';
11
11
  import { useIdentity } from '@dxos/react-client/halo';
12
12
  import { useTranslation } from '@dxos/react-ui';
13
13
  import { useAttended } from '@dxos/react-ui-attention';
14
14
  import { Tabs } from '@dxos/react-ui-tabs';
15
+ import { MessageType } from '@dxos/schema';
15
16
  import { isNonNullable } from '@dxos/util';
16
17
 
17
18
  import { ThreadCapabilities as LocalThreadCapabilities } from '../capabilities';
@@ -42,7 +43,7 @@ export const ThreadComplementary = ({ subject }: { subject: any }) => {
42
43
  );
43
44
  const sort = useMemo(() => createSort?.(subject), [createSort, subject]);
44
45
 
45
- const threadObjects = RefArray.allResolvedTargets(subject.threads ?? []);
46
+ const threadObjects = RefArray.targets(subject.threads ?? []);
46
47
 
47
48
  const threads = useMemo(() => {
48
49
  return threadObjects.concat(drafts ?? []).filter(isNonNullable) as ThreadType[];
@@ -92,8 +93,8 @@ export const ThreadComplementary = ({ subject }: { subject: any }) => {
92
93
  makeRef(
93
94
  create(MessageType, {
94
95
  sender: { identityKey: identity?.identityKey.toHex() },
95
- timestamp: new Date().toISOString(),
96
- text: message,
96
+ created: new Date().toISOString(),
97
+ blocks: [{ type: 'text', text: message }],
97
98
  // TODO(wittjosiah): Context based on attention.
98
99
  // context: context ? makeRef(context) : undefined,
99
100
  }),
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { lazy } from 'react';
6
6
 
7
- export const ThreadContainer = lazy(() => import('./ThreadContainer'));
7
+ export const ChannelContainer = lazy(() => import('./ChannelContainer'));
8
8
 
9
9
  // TODO(wittjosiah): Suspense boundary for sidebar?
10
10
  export * from './CommentContainer';
@@ -5,9 +5,10 @@
5
5
  import { Expando } from '@dxos/echo-schema';
6
6
  import { PublicKey } from '@dxos/keys';
7
7
  import { create, makeRef } from '@dxos/live-object';
8
- import { MessageType, ThreadType } from '@dxos/plugin-space/types';
8
+ import { ThreadType } from '@dxos/plugin-space/types';
9
9
  import { faker } from '@dxos/random';
10
10
  import { type Identity } from '@dxos/react-client/halo';
11
+ import { MessageType } from '@dxos/schema';
11
12
 
12
13
  export const createChatThread = (identity: Identity) => {
13
14
  return create(ThreadType, {
@@ -17,15 +18,17 @@ export const createChatThread = (identity: Identity) => {
17
18
  sender: {
18
19
  identityKey: faker.datatype.boolean() ? identity.identityKey.toHex() : PublicKey.random().toHex(),
19
20
  },
20
- timestamp: faker.date.recent().toISOString(),
21
- text: faker.lorem.sentences(3),
22
- parts: faker.helpers.multiple(
23
- () =>
24
- faker.datatype.boolean({ probability: 0.8 })
25
- ? undefined
26
- : (makeRef(create(Expando, { name: faker.lorem.sentence() })) as any), // TODO(dmaretskyi): Fix types in schema.
27
- { count: { min: 1, max: 3 } },
28
- ),
21
+ created: faker.date.recent().toISOString(),
22
+ blocks: [
23
+ { type: 'text' as const, text: faker.lorem.sentences(3) },
24
+ ...faker.helpers.multiple(
25
+ () => {
26
+ const reference = makeRef(create(Expando, { name: faker.lorem.sentence() })) as any; // TODO(dmaretskyi): Fix types in schema.
27
+ return { type: 'reference' as const, reference };
28
+ },
29
+ { count: { min: 0, max: 3 } },
30
+ ),
31
+ ],
29
32
  }),
30
33
  ),
31
34
  ),
@@ -41,8 +44,8 @@ export const createCommentThread = (identity: Identity) => {
41
44
  sender: {
42
45
  identityKey: faker.datatype.boolean() ? identity.identityKey.toHex() : PublicKey.random().toHex(),
43
46
  },
44
- timestamp: new Date().toISOString(),
45
- text: faker.lorem.sentences(3),
47
+ created: new Date().toISOString(),
48
+ blocks: [{ type: 'text', text: faker.lorem.sentences(3) }],
46
49
  }),
47
50
  ),
48
51
  { count: { min: 2, max: 3 } },
package/src/events.ts ADDED
@@ -0,0 +1,11 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Events } from '@dxos/app-framework';
6
+
7
+ import { THREAD_PLUGIN } from './meta';
8
+
9
+ export namespace ThreadEvents {
10
+ export const SetupActivity = Events.createStateEvent(`${THREAD_PLUGIN}/setup-activity`);
11
+ }
@@ -37,7 +37,7 @@ export const threads = (state: ThreadState, doc?: DocumentType, dispatch?: Promi
37
37
 
38
38
  // TODO(Zan): When we have the deepsignal specific equivalent of this we should use that instead.
39
39
  const threads = computed(() =>
40
- [...RefArray.allResolvedTargets(doc.threads), ...(state.drafts[fullyQualifiedId(doc)] ?? [])].filter(
40
+ [...RefArray.targets(doc.threads), ...(state.drafts[fullyQualifiedId(doc)] ?? [])].filter(
41
41
  (thread) => !(thread?.status === 'resolved'),
42
42
  ),
43
43
  );
@@ -45,7 +45,7 @@ export const threads = (state: ThreadState, doc?: DocumentType, dispatch?: Promi
45
45
  return [
46
46
  EditorView.updateListener.of((update) => {
47
47
  if (update.docChanged) {
48
- RefArray.allResolvedTargets(doc.threads).forEach((thread) => {
48
+ RefArray.targets(doc.threads).forEach((thread) => {
49
49
  if (thread.anchor) {
50
50
  // Only update if the name has changed, otherwise this will cause an infinite loop.
51
51
  // Skip if the name is empty; this means comment text was deleted, but thread name should remain.
@@ -9,6 +9,7 @@ import { type ReactiveObject } from '@dxos/live-object';
9
9
  import { ObservabilityAction } from '@dxos/plugin-observability/types';
10
10
  import { getSpace } from '@dxos/react-client/echo';
11
11
  import { useOnTransition } from '@dxos/react-ui';
12
+ import { type TextContentBlock } from '@dxos/schema';
12
13
 
13
14
  export const useAnalyticsCallback = (spaceId: string | undefined, name: string, meta?: any) => {
14
15
  const { dispatchPromise: dispatch } = useIntentDispatcher();
@@ -23,12 +24,16 @@ export const useAnalyticsCallback = (spaceId: string | undefined, name: string,
23
24
  );
24
25
  };
25
26
 
26
- export const useOnEditAnalytics = (message: ReactiveObject<any>, editing: boolean) => {
27
+ export const useOnEditAnalytics = (
28
+ message: ReactiveObject<any>,
29
+ textBlock: TextContentBlock | undefined,
30
+ editing: boolean,
31
+ ) => {
27
32
  const space = getSpace(message);
28
33
 
29
34
  const onEditAnalytics = useAnalyticsCallback(space?.id, 'threads.comment-edited', {
30
35
  messageId: message.id,
31
- messageLength: message.text.length,
36
+ messageLength: textBlock?.text.length,
32
37
  });
33
38
 
34
39
  useOnTransition(editing, true, false, onEditAnalytics);
package/src/index.ts CHANGED
@@ -2,6 +2,8 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ export { ThreadCapabilities } from './capabilities';
6
+ export * from './events';
5
7
  export * from './hooks';
6
8
  export * from './meta';
7
9
 
package/src/meta.ts CHANGED
@@ -7,10 +7,10 @@ import { type PluginMeta } from '@dxos/app-framework';
7
7
  export const THREAD_PLUGIN = 'dxos.org/plugin/thread';
8
8
  export const THREAD_ITEM = `${THREAD_PLUGIN}/item`;
9
9
 
10
- export const meta = {
10
+ export const meta: PluginMeta = {
11
11
  id: THREAD_PLUGIN,
12
12
  name: 'Threads',
13
13
  description: 'Add comment threads to objects.',
14
14
  icon: 'ph--chat--regular',
15
15
  source: 'https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-thread',
16
- } satisfies PluginMeta;
16
+ };
@@ -11,13 +11,11 @@ export default [
11
11
  'en-US': {
12
12
  [ChannelType.typename]: {
13
13
  'typename label': 'Channel',
14
+ 'object name placeholder': 'New channel',
14
15
  },
15
16
  [THREAD_PLUGIN]: {
16
17
  'plugin name': 'Threads',
17
- 'channel name placeholder': 'New channel',
18
18
  'channel title label': 'Title',
19
- 'delete channel label': 'Delete channel',
20
- 'create channel label': 'Create channel',
21
19
  'message placeholder': 'Reply…',
22
20
  'activity message': 'Processing…',
23
21
  'anonymous label': 'Anonymous',
package/src/types.ts CHANGED
@@ -3,8 +3,9 @@
3
3
  //
4
4
 
5
5
  import { S } from '@dxos/echo-schema';
6
- import { ChannelType, MessageType, ThreadType } from '@dxos/plugin-space/types';
6
+ import { ChannelType, ThreadType } from '@dxos/plugin-space/types';
7
7
  import { EchoObjectSchema } from '@dxos/react-client/echo';
8
+ import { MessageType } from '@dxos/schema';
8
9
 
9
10
  import { THREAD_PLUGIN } from './meta';
10
11
 
@@ -80,6 +81,8 @@ export type ViewState = { showResolvedThreads: boolean };
80
81
  export type ViewStore = Record<SubjectId, ViewState>;
81
82
 
82
83
  export type ThreadState = {
84
+ /** Current channel activity. */
85
+ activities: Record<string, string | undefined>;
83
86
  /** In-memory draft threads. */
84
87
  drafts: Record<string, ThreadType[]>;
85
88
  current?: string | undefined;
@@ -1,19 +0,0 @@
1
- import {
2
- ChatContainer
3
- } from "./chunk-UPKU23DA.mjs";
4
- import "./chunk-BNEWNQBO.mjs";
5
- import "./chunk-636QDHE3.mjs";
6
-
7
- // packages/plugins/plugin-thread/src/components/ThreadContainer.tsx
8
- import React from "react";
9
- import { StackItem } from "@dxos/react-ui-stack";
10
- var ThreadContainer = ({ role, ...props }) => {
11
- return /* @__PURE__ */ React.createElement(StackItem.Content, {
12
- toolbar: false
13
- }, /* @__PURE__ */ React.createElement(ChatContainer, props));
14
- };
15
- var ThreadContainer_default = ThreadContainer;
16
- export {
17
- ThreadContainer_default as default
18
- };
19
- //# sourceMappingURL=ThreadContainer-6OP5AG2W.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/components/ThreadContainer.tsx"],
4
- "sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport React, { type ComponentProps } from 'react';\n\nimport { StackItem } from '@dxos/react-ui-stack';\n\nimport { ChatContainer } from './ChatContainer';\n\nconst ThreadContainer = ({\n role,\n ...props\n}: Pick<ComponentProps<typeof ChatContainer>, 'thread' | 'context'> & { role: string }) => {\n return (\n <StackItem.Content toolbar={false}>\n <ChatContainer {...props} />\n </StackItem.Content>\n );\n};\n\nexport default ThreadContainer;\n"],
5
- "mappings": ";;;;;;;AAIA,OAAOA,WAAoC;AAE3C,SAASC,iBAAiB;AAI1B,IAAMC,kBAAkB,CAAC,EACvBC,MACA,GAAGC,MAAAA,MACiF;AACpF,SACE,sBAAA,cAACC,UAAUC,SAAO;IAACC,SAAS;KAC1B,sBAAA,cAACC,eAAkBJ,KAAAA,CAAAA;AAGzB;AAEA,IAAA,0BAAeF;",
6
- "names": ["React", "StackItem", "ThreadContainer", "role", "props", "StackItem", "Content", "toolbar", "ChatContainer"]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/capabilities/capabilities.ts"],
4
- "sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport { defineCapability } from '@dxos/app-framework';\nimport { type DeepReadonly } from '@dxos/util';\n\nimport { THREAD_PLUGIN } from '../meta';\nimport { type ViewState, type ThreadState } from '../types';\n\nexport namespace ThreadCapabilities {\n type GetViewState = (subjectId: string) => ViewState;\n export const State = defineCapability<{ state: DeepReadonly<ThreadState>; getViewState: GetViewState }>(\n `${THREAD_PLUGIN}/capability/state`,\n );\n export const MutableState = defineCapability<{ state: ThreadState; getViewState: GetViewState }>(\n `${THREAD_PLUGIN}/capability/state`,\n );\n}\n"],
5
- "mappings": ";;;;;AAIA,SAASA,wBAAwB;;UAMhBC,qBAAAA;sBAEFC,QAAQC,iBACnB,GAAGC,aAAAA,mBAAgC;sBAExBC,eAAeF,iBAC1B,GAAGC,aAAAA,mBAAgC;AAEvC,GARiBH,uBAAAA,qBAAAA,CAAAA,EAAAA;",
6
- "names": ["defineCapability", "ThreadCapabilities", "State", "defineCapability", "THREAD_PLUGIN", "MutableState"]
7
- }