@dxos/plugin-thread 0.7.4-staging.f7e8224 → 0.7.5-labs.5f04cf6

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 (197) hide show
  1. package/dist/lib/browser/ThreadContainer-4VLBSBI7.mjs +19 -0
  2. package/dist/lib/browser/ThreadContainer-4VLBSBI7.mjs.map +7 -0
  3. package/dist/lib/browser/app-graph-builder-Q7A2ZRUA.mjs +121 -0
  4. package/dist/lib/browser/app-graph-builder-Q7A2ZRUA.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-2B3EXYZ7.mjs +18 -0
  6. package/dist/lib/browser/chunk-2B3EXYZ7.mjs.map +7 -0
  7. package/dist/lib/browser/{chunk-ENERBDYI.mjs → chunk-636QDHE3.mjs} +3 -3
  8. package/dist/lib/browser/chunk-636QDHE3.mjs.map +7 -0
  9. package/dist/lib/browser/chunk-BNEWNQBO.mjs +71 -0
  10. package/dist/lib/browser/chunk-BNEWNQBO.mjs.map +7 -0
  11. package/dist/lib/browser/{chunk-52HWDLBR.mjs → chunk-GQONSZ3J.mjs} +31 -117
  12. package/dist/lib/browser/chunk-GQONSZ3J.mjs.map +7 -0
  13. package/dist/lib/browser/chunk-NAO3LZMZ.mjs +16 -0
  14. package/dist/lib/browser/chunk-NAO3LZMZ.mjs.map +7 -0
  15. package/dist/lib/browser/chunk-NO5AZLUZ.mjs +81 -0
  16. package/dist/lib/browser/chunk-NO5AZLUZ.mjs.map +7 -0
  17. package/dist/lib/browser/index.mjs +147 -1094
  18. package/dist/lib/browser/index.mjs.map +4 -4
  19. package/dist/lib/browser/intent-resolver-PIEZ3LKQ.mjs +275 -0
  20. package/dist/lib/browser/intent-resolver-PIEZ3LKQ.mjs.map +7 -0
  21. package/dist/lib/browser/markdown-2NBXKXCY.mjs +112 -0
  22. package/dist/lib/browser/markdown-2NBXKXCY.mjs.map +7 -0
  23. package/dist/lib/browser/meta.json +1 -1
  24. package/dist/lib/browser/react-surface-CHCZO4TI.mjs +412 -0
  25. package/dist/lib/browser/react-surface-CHCZO4TI.mjs.map +7 -0
  26. package/dist/lib/browser/settings-TKOPRXSU.mjs +22 -0
  27. package/dist/lib/browser/settings-TKOPRXSU.mjs.map +7 -0
  28. package/dist/lib/browser/state-GNFUURIC.mjs +33 -0
  29. package/dist/lib/browser/state-GNFUURIC.mjs.map +7 -0
  30. package/dist/lib/browser/types.mjs +6 -4
  31. package/dist/lib/node/{ThreadArticle-I7ADXDW3.cjs → ThreadContainer-VOYLV4UL.cjs} +14 -13
  32. package/dist/lib/node/ThreadContainer-VOYLV4UL.cjs.map +7 -0
  33. package/dist/lib/node/app-graph-builder-ZXW5KUDK.cjs +135 -0
  34. package/dist/lib/node/app-graph-builder-ZXW5KUDK.cjs.map +7 -0
  35. package/dist/lib/node/{chunk-YCDM2SZM.cjs → chunk-6PUGKCPR.cjs} +76 -165
  36. package/dist/lib/node/chunk-6PUGKCPR.cjs.map +7 -0
  37. package/dist/lib/node/chunk-AUVKOX5Q.cjs +101 -0
  38. package/dist/lib/node/chunk-AUVKOX5Q.cjs.map +7 -0
  39. package/dist/lib/node/chunk-AWSTZ2KN.cjs +93 -0
  40. package/dist/lib/node/chunk-AWSTZ2KN.cjs.map +7 -0
  41. package/dist/lib/node/{chunk-IC2NICNC.cjs → chunk-F735VMTN.cjs} +7 -7
  42. package/dist/lib/node/chunk-F735VMTN.cjs.map +7 -0
  43. package/dist/lib/node/{meta.cjs → chunk-FPWNZ6TB.cjs} +13 -10
  44. package/dist/lib/node/chunk-FPWNZ6TB.cjs.map +7 -0
  45. package/dist/lib/node/{ThreadMain-MRNJC3TI.cjs → chunk-ZRN7B26W.cjs} +26 -23
  46. package/dist/lib/node/chunk-ZRN7B26W.cjs.map +7 -0
  47. package/dist/lib/node/index.cjs +142 -1083
  48. package/dist/lib/node/index.cjs.map +4 -4
  49. package/dist/lib/node/intent-resolver-X5O3SVLF.cjs +287 -0
  50. package/dist/lib/node/intent-resolver-X5O3SVLF.cjs.map +7 -0
  51. package/dist/lib/node/markdown-FWRKMSKV.cjs +122 -0
  52. package/dist/lib/node/markdown-FWRKMSKV.cjs.map +7 -0
  53. package/dist/lib/node/meta.json +1 -1
  54. package/dist/lib/node/react-surface-LO7S2HPR.cjs +414 -0
  55. package/dist/lib/node/react-surface-LO7S2HPR.cjs.map +7 -0
  56. package/dist/lib/node/settings-TH4YSUVU.cjs +36 -0
  57. package/dist/lib/node/settings-TH4YSUVU.cjs.map +7 -0
  58. package/dist/lib/node/state-TQWEZ6B3.cjs +49 -0
  59. package/dist/lib/node/state-TQWEZ6B3.cjs.map +7 -0
  60. package/dist/lib/node/types.cjs +6 -4
  61. package/dist/lib/node/types.cjs.map +2 -2
  62. package/dist/lib/node-esm/ThreadContainer-7DZY2MWA.mjs +20 -0
  63. package/dist/lib/node-esm/ThreadContainer-7DZY2MWA.mjs.map +7 -0
  64. package/dist/lib/node-esm/app-graph-builder-ATS6HGE6.mjs +122 -0
  65. package/dist/lib/node-esm/app-graph-builder-ATS6HGE6.mjs.map +7 -0
  66. package/dist/lib/node-esm/chunk-B3CF35MY.mjs +82 -0
  67. package/dist/lib/node-esm/chunk-B3CF35MY.mjs.map +7 -0
  68. package/dist/lib/node-esm/{chunk-JG7MXH6O.mjs → chunk-DYFIKKDT.mjs} +31 -117
  69. package/dist/lib/node-esm/chunk-DYFIKKDT.mjs.map +7 -0
  70. package/dist/lib/node-esm/{chunk-7QRGG25F.mjs → chunk-FXUATCLW.mjs} +3 -3
  71. package/dist/lib/node-esm/chunk-FXUATCLW.mjs.map +7 -0
  72. package/dist/lib/node-esm/chunk-GS2OILYF.mjs +20 -0
  73. package/dist/lib/node-esm/chunk-GS2OILYF.mjs.map +7 -0
  74. package/dist/lib/node-esm/chunk-HLWJTUWZ.mjs +73 -0
  75. package/dist/lib/node-esm/chunk-HLWJTUWZ.mjs.map +7 -0
  76. package/dist/lib/node-esm/chunk-ZJITX3TO.mjs +17 -0
  77. package/dist/lib/node-esm/chunk-ZJITX3TO.mjs.map +7 -0
  78. package/dist/lib/node-esm/index.mjs +147 -1094
  79. package/dist/lib/node-esm/index.mjs.map +4 -4
  80. package/dist/lib/node-esm/intent-resolver-XJWT5X6T.mjs +276 -0
  81. package/dist/lib/node-esm/intent-resolver-XJWT5X6T.mjs.map +7 -0
  82. package/dist/lib/node-esm/markdown-6AQFDAW3.mjs +113 -0
  83. package/dist/lib/node-esm/markdown-6AQFDAW3.mjs.map +7 -0
  84. package/dist/lib/node-esm/meta.json +1 -1
  85. package/dist/lib/node-esm/react-surface-LAF3QIPB.mjs +413 -0
  86. package/dist/lib/node-esm/react-surface-LAF3QIPB.mjs.map +7 -0
  87. package/dist/lib/node-esm/settings-KEDRR7RM.mjs +23 -0
  88. package/dist/lib/node-esm/settings-KEDRR7RM.mjs.map +7 -0
  89. package/dist/lib/node-esm/state-7MLR27YG.mjs +34 -0
  90. package/dist/lib/node-esm/state-7MLR27YG.mjs.map +7 -0
  91. package/dist/lib/node-esm/types.mjs +6 -4
  92. package/dist/types/src/ThreadPlugin.d.ts +1 -3
  93. package/dist/types/src/ThreadPlugin.d.ts.map +1 -1
  94. package/dist/types/src/capabilities/app-graph-builder.d.ts +180 -0
  95. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -0
  96. package/dist/types/src/capabilities/capabilities.d.ts +15 -0
  97. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -0
  98. package/dist/types/src/capabilities/index.d.ts +187 -0
  99. package/dist/types/src/capabilities/index.d.ts.map +1 -0
  100. package/dist/types/src/capabilities/intent-resolver.d.ts +4 -0
  101. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -0
  102. package/dist/types/src/capabilities/markdown.d.ts +4 -0
  103. package/dist/types/src/capabilities/markdown.d.ts.map +1 -0
  104. package/dist/types/src/capabilities/react-surface.d.ts +4 -0
  105. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
  106. package/dist/types/src/capabilities/settings.d.ts +4 -0
  107. package/dist/types/src/capabilities/settings.d.ts.map +1 -0
  108. package/dist/types/src/capabilities/state.d.ts +7 -0
  109. package/dist/types/src/capabilities/state.d.ts.map +1 -0
  110. package/dist/types/src/components/Chat.stories.d.ts.map +1 -1
  111. package/dist/types/src/components/ChatContainer.d.ts.map +1 -1
  112. package/dist/types/src/components/CommentContainer.d.ts +1 -1
  113. package/dist/types/src/components/CommentContainer.d.ts.map +1 -1
  114. package/dist/types/src/components/CommentsContainer.d.ts +2 -7
  115. package/dist/types/src/components/CommentsContainer.d.ts.map +1 -1
  116. package/dist/types/src/components/MessageContainer.d.ts.map +1 -1
  117. package/dist/types/src/components/ThreadComplementary.d.ts +1 -6
  118. package/dist/types/src/components/ThreadComplementary.d.ts.map +1 -1
  119. package/dist/types/src/components/ThreadContainer.d.ts +7 -0
  120. package/dist/types/src/components/ThreadContainer.d.ts.map +1 -0
  121. package/dist/types/src/components/index.d.ts +3 -15
  122. package/dist/types/src/components/index.d.ts.map +1 -1
  123. package/dist/types/src/components/testing.d.ts +50 -66
  124. package/dist/types/src/components/testing.d.ts.map +1 -1
  125. package/dist/types/src/components/types.d.ts +5 -5
  126. package/dist/types/src/components/types.d.ts.map +1 -1
  127. package/dist/types/src/extensions/threads.d.ts +2 -2
  128. package/dist/types/src/extensions/threads.d.ts.map +1 -1
  129. package/dist/types/src/hooks/analytics.d.ts.map +1 -1
  130. package/dist/types/src/index.d.ts +1 -3
  131. package/dist/types/src/index.d.ts.map +1 -1
  132. package/dist/types/src/meta.d.ts +1 -2
  133. package/dist/types/src/meta.d.ts.map +1 -1
  134. package/dist/types/src/translations.d.ts +3 -0
  135. package/dist/types/src/translations.d.ts.map +1 -1
  136. package/dist/types/src/types.d.ts +86 -22
  137. package/dist/types/src/types.d.ts.map +1 -1
  138. package/dist/types/tsconfig.tsbuildinfo +1 -0
  139. package/package.json +30 -38
  140. package/src/ThreadPlugin.tsx +109 -620
  141. package/src/capabilities/app-graph-builder.ts +120 -0
  142. package/src/capabilities/capabilities.ts +19 -0
  143. package/src/capabilities/index.ts +14 -0
  144. package/src/capabilities/intent-resolver.ts +223 -0
  145. package/src/capabilities/markdown.ts +18 -0
  146. package/src/capabilities/react-surface.tsx +61 -0
  147. package/src/capabilities/settings.ts +14 -0
  148. package/src/capabilities/state.ts +25 -0
  149. package/src/components/Chat.stories.tsx +26 -22
  150. package/src/components/ChatContainer.tsx +13 -11
  151. package/src/components/CommentContainer.tsx +21 -32
  152. package/src/components/CommentsContainer.tsx +1 -17
  153. package/src/components/MessageContainer.tsx +7 -5
  154. package/src/components/ThreadComplementary.tsx +88 -68
  155. package/src/components/ThreadContainer.tsx +22 -0
  156. package/src/components/index.ts +1 -2
  157. package/src/components/testing.ts +26 -22
  158. package/src/components/types.ts +5 -5
  159. package/src/extensions/threads.ts +16 -25
  160. package/src/hooks/analytics.ts +6 -10
  161. package/src/index.ts +1 -5
  162. package/src/meta.ts +1 -1
  163. package/src/translations.ts +1 -0
  164. package/src/types.ts +70 -38
  165. package/dist/lib/browser/ThreadArticle-6IXTL7CH.mjs +0 -18
  166. package/dist/lib/browser/ThreadArticle-6IXTL7CH.mjs.map +0 -7
  167. package/dist/lib/browser/ThreadMain-4IAS67KE.mjs +0 -26
  168. package/dist/lib/browser/ThreadMain-4IAS67KE.mjs.map +0 -7
  169. package/dist/lib/browser/chunk-52HWDLBR.mjs.map +0 -7
  170. package/dist/lib/browser/chunk-ENERBDYI.mjs.map +0 -7
  171. package/dist/lib/browser/chunk-XUHNZID5.mjs +0 -20
  172. package/dist/lib/browser/chunk-XUHNZID5.mjs.map +0 -7
  173. package/dist/lib/browser/meta.mjs +0 -11
  174. package/dist/lib/browser/meta.mjs.map +0 -7
  175. package/dist/lib/node/ThreadArticle-I7ADXDW3.cjs.map +0 -7
  176. package/dist/lib/node/ThreadMain-MRNJC3TI.cjs.map +0 -7
  177. package/dist/lib/node/chunk-IC2NICNC.cjs.map +0 -7
  178. package/dist/lib/node/chunk-XFO6D2LG.cjs +0 -39
  179. package/dist/lib/node/chunk-XFO6D2LG.cjs.map +0 -7
  180. package/dist/lib/node/chunk-YCDM2SZM.cjs.map +0 -7
  181. package/dist/lib/node/meta.cjs.map +0 -7
  182. package/dist/lib/node-esm/ThreadArticle-CJHYMF2W.mjs +0 -19
  183. package/dist/lib/node-esm/ThreadArticle-CJHYMF2W.mjs.map +0 -7
  184. package/dist/lib/node-esm/ThreadMain-VISGW4IF.mjs +0 -27
  185. package/dist/lib/node-esm/ThreadMain-VISGW4IF.mjs.map +0 -7
  186. package/dist/lib/node-esm/chunk-7QRGG25F.mjs.map +0 -7
  187. package/dist/lib/node-esm/chunk-I5MU54RT.mjs +0 -21
  188. package/dist/lib/node-esm/chunk-I5MU54RT.mjs.map +0 -7
  189. package/dist/lib/node-esm/chunk-JG7MXH6O.mjs.map +0 -7
  190. package/dist/lib/node-esm/meta.mjs +0 -12
  191. package/dist/lib/node-esm/meta.mjs.map +0 -7
  192. package/dist/types/src/components/ThreadArticle.d.ts +0 -5
  193. package/dist/types/src/components/ThreadArticle.d.ts.map +0 -1
  194. package/dist/types/src/components/ThreadMain.d.ts +0 -7
  195. package/dist/types/src/components/ThreadMain.d.ts.map +0 -1
  196. package/src/components/ThreadArticle.tsx +0 -18
  197. package/src/components/ThreadMain.tsx +0 -27
@@ -5,8 +5,7 @@
5
5
  import { CheckCircle, X } from '@phosphor-icons/react';
6
6
  import React, { useCallback, useMemo, useRef, useState } from 'react';
7
7
 
8
- import { create } from '@dxos/live-object';
9
- import { MessageType } from '@dxos/plugin-space/types';
8
+ import { RefArray } from '@dxos/live-object';
10
9
  import { fullyQualifiedId, getSpace, useMembers } from '@dxos/react-client/echo';
11
10
  import { useIdentity } from '@dxos/react-client/halo';
12
11
  import { Button, Tag, Tooltip, useThemeContext, useTranslation } from '@dxos/react-ui';
@@ -56,7 +55,7 @@ const ToggleResolvedButton = ({
56
55
  </Button>
57
56
  </Tooltip.Trigger>
58
57
  <Tooltip.Portal>
59
- <Tooltip.Content classNames={'z-[21]'}>
58
+ <Tooltip.Content>
60
59
  {label}
61
60
  <Tooltip.Arrow />
62
61
  </Tooltip.Content>
@@ -82,7 +81,7 @@ const DeleteThreadButton = ({ onDelete }: { onDelete: () => void }) => {
82
81
  </Button>
83
82
  </Tooltip.Trigger>
84
83
  <Tooltip.Portal>
85
- <Tooltip.Content classNames='z-[21]'>
84
+ <Tooltip.Content classNames='z-30'>
86
85
  {label}
87
86
  <Tooltip.Arrow />
88
87
  </Tooltip.Content>
@@ -94,13 +93,12 @@ const DeleteThreadButton = ({ onDelete }: { onDelete: () => void }) => {
94
93
  export const CommentContainer = ({
95
94
  thread,
96
95
  detached,
97
- context,
98
96
  current,
99
97
  onAttend,
100
- onThreadDelete,
101
- onMessageDelete,
102
- onResolve,
103
98
  onComment,
99
+ onResolve,
100
+ onMessageDelete,
101
+ onThreadDelete,
104
102
  }: ThreadContainerProps) => {
105
103
  const identity = useIdentity()!;
106
104
  const space = getSpace(thread);
@@ -129,31 +127,26 @@ export const CommentContainer = ({
129
127
  const scrollToEnd = (behavior: ScrollBehavior) =>
130
128
  setTimeout(() => threadScrollRef.current?.scrollIntoView({ behavior, block: 'end' }), 10);
131
129
 
132
- const handleCreate: MessageTextboxProps['onSend'] = useCallback(() => {
130
+ const handleAttend = useCallback(() => onAttend?.(thread), [onAttend, thread]);
131
+ const handleResolve = useCallback(() => onResolve?.(thread), [onResolve, thread]);
132
+ const handleMessageDelete = useCallback((id: string) => onMessageDelete?.(thread, id), [onMessageDelete, thread]);
133
+ const handleThreadDelete = useCallback(() => onThreadDelete?.(thread), [onThreadDelete, thread]);
134
+
135
+ const handleComment: MessageTextboxProps['onSend'] = useCallback(() => {
133
136
  if (!messageRef.current) {
134
137
  return false;
135
138
  }
136
139
 
137
- thread.messages.push(
138
- create(MessageType, {
139
- sender: { identityKey: identity.identityKey.toHex() },
140
- timestamp: new Date().toISOString(),
141
- text: messageRef.current,
142
- context,
143
- }),
144
- );
145
- onComment?.(thread);
146
-
140
+ onComment?.(thread, messageRef.current);
147
141
  messageRef.current = '';
148
142
  scrollToEnd('instant');
149
143
  rerenderEditor();
150
144
 
151
- // TODO(burdon): Scroll to bottom.
152
145
  return true;
153
146
  }, [thread, identity]);
154
147
 
155
148
  return (
156
- <Thread onClickCapture={onAttend} onFocusCapture={onAttend} current={current} id={fullyQualifiedId(thread)}>
149
+ <Thread onClickCapture={handleAttend} onFocusCapture={handleAttend} current={current} id={fullyQualifiedId(thread)}>
157
150
  <div
158
151
  role='none'
159
152
  className={mx(
@@ -168,7 +161,7 @@ export const CommentContainer = ({
168
161
  <ThreadHeading detached>{thread.name}</ThreadHeading>
169
162
  </Tooltip.Trigger>
170
163
  <Tooltip.Portal>
171
- <Tooltip.Content classNames='z-[21]' side='top'>
164
+ <Tooltip.Content classNames='z-30' side='top'>
172
165
  {t('detached thread label')}
173
166
  <Tooltip.Arrow />
174
167
  </Tooltip.Content>
@@ -180,18 +173,14 @@ export const CommentContainer = ({
180
173
  <div className='flex flex-row items-center pli-1'>
181
174
  {thread.status === 'staged' && <Tag palette='neutral'>{t('draft button')}</Tag>}
182
175
  {onResolve && !(thread?.status === 'staged') && (
183
- <ToggleResolvedButton isResolved={thread?.status === 'resolved'} onResolve={onResolve} />
176
+ <ToggleResolvedButton isResolved={thread?.status === 'resolved'} onResolve={handleResolve} />
184
177
  )}
185
- {onThreadDelete && <DeleteThreadButton onDelete={onThreadDelete} />}
178
+ {onThreadDelete && <DeleteThreadButton onDelete={handleThreadDelete} />}
186
179
  </div>
187
180
  </div>
188
- {thread.messages.filter(nonNullable).map((message) => (
189
- <MessageContainer
190
- key={message.id}
191
- message={message}
192
- members={members}
193
- onDelete={(id: string) => onMessageDelete?.(id)}
194
- />
181
+ {/** TODO(dmaretskyi): How's `thread.messages` undefined? */}
182
+ {RefArray.allResolvedTargets(thread.messages?.filter(nonNullable) ?? []).map((message) => (
183
+ <MessageContainer key={message.id} message={message} members={members} onDelete={handleMessageDelete} />
195
184
  ))}
196
185
  {/*
197
186
  TODO(wittjosiah): Can't autofocus this generally.
@@ -199,7 +188,7 @@ export const CommentContainer = ({
199
188
  Also, it steals focus from documents when first rendered.
200
189
  Need to find a way to autofocus in one scenario only: when a new thread is created.
201
190
  */}
202
- <MessageTextbox extensions={extensions} onSend={handleCreate} {...textboxMetadata} />
191
+ <MessageTextbox extensions={extensions} onSend={handleComment} {...textboxMetadata} />
203
192
  <ThreadFooter activity={activity}>{t('activity message')}</ThreadFooter>
204
193
  {/* NOTE(thure): This can’t also be the `overflow-anchor` because `ScrollArea` injects an interceding node that contains this necessary ref’d element. */}
205
194
  <div role='none' className='bs-px -mbs-px' ref={threadScrollRef} />
@@ -14,10 +14,7 @@ import { CommentContainer } from './CommentContainer';
14
14
  import { type ThreadContainerProps } from './types';
15
15
  import { THREAD_PLUGIN } from '../meta';
16
16
 
17
- export type ThreadsContainerProps = Omit<
18
- ThreadContainerProps,
19
- 'thread' | 'detached' | 'onAttend' | 'onThreadDelete' | 'onMessageDelete' | 'current' | 'autoFocus'
20
- > & {
17
+ export type ThreadsContainerProps = Omit<ThreadContainerProps, 'thread' | 'detached' | 'current' | 'autoFocus'> & {
21
18
  threads: ThreadType[];
22
19
  /**
23
20
  * Threads that are no longer anchored to a position in the object.
@@ -25,11 +22,6 @@ export type ThreadsContainerProps = Omit<
25
22
  detached?: string[];
26
23
  currentId?: string;
27
24
  showResolvedThreads?: boolean;
28
- onThreadAttend?: (thread: ThreadType) => void;
29
- onThreadDelete?: (thread: ThreadType) => void;
30
- onMessageDelete?: (thread: ThreadType, messageId: string) => void;
31
- onThreadToggleResolved?: (thread: ThreadType) => void;
32
- onComment?: (thread: ThreadType) => void;
33
25
  };
34
26
 
35
27
  /**
@@ -40,10 +32,6 @@ export const CommentsContainer = ({
40
32
  detached = [],
41
33
  currentId,
42
34
  showResolvedThreads,
43
- onThreadAttend,
44
- onThreadDelete,
45
- onMessageDelete,
46
- onThreadToggleResolved,
47
35
  ...props
48
36
  }: ThreadsContainerProps) => {
49
37
  const { t } = useTranslation(THREAD_PLUGIN);
@@ -66,10 +54,6 @@ export const CommentsContainer = ({
66
54
  thread={thread}
67
55
  current={currentId === threadId}
68
56
  detached={detached.includes(threadId)}
69
- {...(onThreadAttend && { onAttend: () => onThreadAttend(thread) })}
70
- {...(onThreadDelete && { onThreadDelete: () => onThreadDelete(thread) })}
71
- {...(onMessageDelete && { onMessageDelete: (messageId: string) => onMessageDelete(thread, messageId) })}
72
- {...(onThreadToggleResolved && { onResolve: () => onThreadToggleResolved(thread) })}
73
57
  {...props}
74
58
  />
75
59
  );
@@ -7,6 +7,7 @@ import { Check, PencilSimple, X } from '@phosphor-icons/react';
7
7
  import React, { forwardRef, useCallback, useEffect, useRef, useState } from 'react';
8
8
 
9
9
  import { Surface } from '@dxos/app-framework';
10
+ import { RefArray } from '@dxos/live-object';
10
11
  import { type MessageType } from '@dxos/plugin-space/types';
11
12
  import { PublicKey } from '@dxos/react-client';
12
13
  import { type ReactiveEchoObject, type Expando, type SpaceMember } from '@dxos/react-client/echo';
@@ -22,7 +23,6 @@ import {
22
23
  mx,
23
24
  } from '@dxos/react-ui-theme';
24
25
  import { MessageHeading, MessageRoot } from '@dxos/react-ui-thread';
25
- import { nonNullable } from '@dxos/util';
26
26
 
27
27
  import { command } from './command-extension';
28
28
  import { useOnEditAnalytics } from '../hooks';
@@ -70,7 +70,7 @@ export const MessageContainer = ({
70
70
  </Button>
71
71
  </Tooltip.Trigger>
72
72
  <Tooltip.Portal>
73
- <Tooltip.Content classNames='z-[21]'>
73
+ <Tooltip.Content>
74
74
  {editLabel}
75
75
  <Tooltip.Arrow />
76
76
  </Tooltip.Content>
@@ -91,7 +91,7 @@ export const MessageContainer = ({
91
91
  </Button>
92
92
  </Tooltip.Trigger>
93
93
  <Tooltip.Portal>
94
- <Tooltip.Content classNames='z-[21]'>
94
+ <Tooltip.Content>
95
95
  {deleteLabel}
96
96
  <Tooltip.Arrow />
97
97
  </Tooltip.Content>
@@ -101,7 +101,9 @@ export const MessageContainer = ({
101
101
  </ButtonGroup>
102
102
  </MessageHeading>
103
103
  <TextboxBlock message={message} isAuthor={userIsAuthor} editing={editing} />
104
- {message.parts?.filter(nonNullable).map((part, index) => <MessagePart key={index} part={part} />)}
104
+ {RefArray.allResolvedTargets(message.parts ?? []).map((part, index) => (
105
+ <MessagePart key={index} part={part} />
106
+ ))}
105
107
  </MessageRoot>
106
108
  );
107
109
  };
@@ -180,7 +182,7 @@ const MessageBlockObjectTile: MosaicTileComponent<ReactiveEchoObject<any>> = for
180
182
  <Surface
181
183
  role='card'
182
184
  limit={1}
183
- data={{ content: item }}
185
+ data={{ subject: item }}
184
186
  draggableProps={draggableProps}
185
187
  fallback={title}
186
188
  {...props}
@@ -2,47 +2,40 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import React, { useEffect, useMemo } from 'react';
5
+ import React, { useCallback, useEffect, useMemo } from 'react';
6
6
 
7
- import { LayoutAction, type Plugin, useIntentDispatcher, useResolvePlugins } from '@dxos/app-framework';
8
- import { type ThreadType } from '@dxos/plugin-space';
9
- import { fullyQualifiedId } from '@dxos/react-client/echo';
7
+ import { createIntent, LayoutAction, useCapabilities, useCapability, useIntentDispatcher } from '@dxos/app-framework';
8
+ import { ThreadCapabilities } from '@dxos/plugin-space';
9
+ import { MessageType, type ThreadType } from '@dxos/plugin-space/types';
10
+ import { create, fullyQualifiedId, makeRef, RefArray } from '@dxos/react-client/echo';
11
+ import { useIdentity } from '@dxos/react-client/halo';
10
12
  import { useAttended } from '@dxos/react-ui-attention';
11
13
  import { nonNullable } from '@dxos/util';
12
14
 
15
+ import { ThreadCapabilities as LocalThreadCapabilities } from '../capabilities';
13
16
  import { CommentsContainer } from '../components';
14
- import { THREAD_PLUGIN } from '../meta';
15
- import { ThreadAction, type ThreadProvides } from '../types';
16
-
17
- const providesThreadsConfig = (plugin: any): Plugin<ThreadProvides<any>> | undefined =>
18
- 'thread' in plugin.provides ? (plugin as Plugin<ThreadProvides<any>>) : undefined;
19
-
20
- export const ThreadComplementary = ({
21
- role,
22
- subject,
23
- drafts,
24
- current,
25
- showResolvedThreads,
26
- }: {
27
- role: string;
28
- subject: any;
29
- drafts: ThreadType[] | undefined;
30
- current?: string;
31
- showResolvedThreads?: boolean;
32
- }) => {
33
- const dispatch = useIntentDispatcher();
34
-
35
- const threadsIntegrators = useResolvePlugins(providesThreadsConfig);
36
- const threadProvides = useMemo(() => threadsIntegrators.map((p) => p.provides.thread), [threadsIntegrators]);
17
+ import { ThreadAction } from '../types';
18
+
19
+ export const ThreadComplementary = ({ subject }: { subject: any }) => {
20
+ const { dispatchPromise: dispatch } = useIntentDispatcher();
21
+ const identity = useIdentity();
22
+
23
+ const { state, getViewState } = useCapability(LocalThreadCapabilities.MutableState);
24
+ const { showResolvedThreads } = getViewState(fullyQualifiedId(subject));
25
+ const drafts = state.drafts[fullyQualifiedId(subject)];
26
+
27
+ const threadsIntegrators = useCapabilities(ThreadCapabilities.Thread);
37
28
  const createSort = useMemo(
38
- () => threadProvides.find((p) => p.predicate(subject))?.createSort,
39
- [threadProvides, subject],
29
+ () => threadsIntegrators.find((p) => p.predicate(subject))?.createSort,
30
+ [threadsIntegrators, subject],
40
31
  );
41
32
  const sort = useMemo(() => createSort?.(subject), [createSort, subject]);
42
33
 
34
+ const threadObjects = RefArray.allResolvedTargets(subject.threads ?? []);
35
+
43
36
  const threads = useMemo(() => {
44
- return subject.threads.concat(drafts ?? []).filter(nonNullable) as ThreadType[];
45
- }, [JSON.stringify(subject.threads), JSON.stringify(drafts)]);
37
+ return threadObjects.concat(drafts ?? []).filter(nonNullable) as ThreadType[];
38
+ }, [JSON.stringify(threadObjects), JSON.stringify(drafts)]);
46
39
 
47
40
  const detachedIds = useMemo(() => {
48
41
  return threads.filter(({ anchor }) => !anchor).map((thread) => fullyQualifiedId(thread));
@@ -58,48 +51,75 @@ export const ThreadComplementary = ({
58
51
  const attended = useAttended();
59
52
  const qualifiedSubjectId = fullyQualifiedId(subject);
60
53
 
54
+ const handleAttend = useCallback(
55
+ (thread: ThreadType) => {
56
+ const threadId = fullyQualifiedId(thread);
57
+ if (state.current !== threadId) {
58
+ state.current = threadId;
59
+
60
+ // TODO(wittjosiah): Should this be a thread-specific intent?
61
+ // The layout doesn't know about threads and this working depends on other plugins conditionally handling it.
62
+ // This may be overloading this intent or highjacking its intended purpose.
63
+ void dispatch(
64
+ createIntent(LayoutAction.ScrollIntoView, {
65
+ id: fullyQualifiedId(subject),
66
+ cursor: thread.anchor,
67
+ ref: threadId,
68
+ }),
69
+ );
70
+ }
71
+ },
72
+ [state.current, dispatch, subject],
73
+ );
74
+
75
+ const handleComment = useCallback(
76
+ async (thread: ThreadType, message: string) => {
77
+ thread.messages.push(
78
+ makeRef(
79
+ create(MessageType, {
80
+ sender: { identityKey: identity?.identityKey.toHex() },
81
+ timestamp: new Date().toISOString(),
82
+ text: message,
83
+ // TODO(wittjosiah): Context based on attention.
84
+ // context: context ? makeRef(context) : undefined,
85
+ }),
86
+ ),
87
+ );
88
+
89
+ await dispatch(createIntent(ThreadAction.OnMessageAdd, { thread, subject }));
90
+
91
+ state.current = fullyQualifiedId(thread);
92
+ },
93
+ [dispatch, identity, subject],
94
+ );
95
+
96
+ const handleResolve = useCallback(
97
+ (thread: ThreadType) => dispatch(createIntent(ThreadAction.ToggleResolved, { thread })),
98
+ [dispatch],
99
+ );
100
+
101
+ const handleThreadDelete = useCallback(
102
+ (thread: ThreadType) => dispatch(createIntent(ThreadAction.Delete, { thread, subject })),
103
+ [dispatch, subject],
104
+ );
105
+
106
+ const handleMessageDelete = useCallback(
107
+ (thread: ThreadType, messageId: string) =>
108
+ dispatch(createIntent(ThreadAction.DeleteMessage, { thread, subject, messageId })),
109
+ [dispatch, subject],
110
+ );
111
+
61
112
  return (
62
113
  <CommentsContainer
63
114
  threads={threads}
64
115
  detached={detachedIds}
65
- currentId={attended.includes(qualifiedSubjectId) ? current : undefined}
116
+ currentId={attended.includes(qualifiedSubjectId) ? state.current : undefined}
66
117
  showResolvedThreads={showResolvedThreads}
67
- onThreadAttend={(thread) => {
68
- const threadId = fullyQualifiedId(thread);
69
- if (current !== threadId) {
70
- current = threadId;
71
-
72
- // TODO(wittjosiah): Should this be a thread-specific intent?
73
- // The layout doesn't know about threads and this working depends on other plugins conditionally handling it.
74
- // This may be overloading this intent or highjacking its intended purpose.
75
- void dispatch?.([
76
- {
77
- action: LayoutAction.SCROLL_INTO_VIEW,
78
- data: {
79
- id: fullyQualifiedId(subject),
80
- thread: threadId,
81
- cursor: thread.anchor,
82
- },
83
- },
84
- ]);
85
- }
86
- }}
87
- onThreadDelete={(thread) => {
88
- return dispatch({ plugin: THREAD_PLUGIN, action: ThreadAction.DELETE, data: { subject, thread } });
89
- }}
90
- onMessageDelete={(thread, messageId) =>
91
- dispatch({
92
- plugin: THREAD_PLUGIN,
93
- action: ThreadAction.DELETE_MESSAGE,
94
- data: { subject, thread, messageId },
95
- })
96
- }
97
- onThreadToggleResolved={(thread) =>
98
- dispatch({ plugin: THREAD_PLUGIN, action: ThreadAction.TOGGLE_RESOLVED, data: { thread } })
99
- }
100
- onComment={(thread) => {
101
- void dispatch({ action: ThreadAction.ON_MESSAGE_ADD, data: { thread, subject } });
102
- }}
118
+ onAttend={handleAttend}
119
+ onComment={handleComment}
120
+ onResolve={handleResolve}
121
+ onMessageDelete={handleMessageDelete}
122
+ onThreadDelete={handleThreadDelete}
103
123
  />
104
124
  );
105
125
  };
@@ -0,0 +1,22 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import React, { type ComponentProps } from 'react';
6
+
7
+ import { StackItem } from '@dxos/react-ui-stack';
8
+
9
+ import { ChatContainer } from './ChatContainer';
10
+
11
+ const ThreadContainer = ({
12
+ role,
13
+ ...props
14
+ }: Pick<ComponentProps<typeof ChatContainer>, 'thread' | 'context'> & { role: string }) => {
15
+ return (
16
+ <StackItem.Content toolbar={false}>
17
+ <ChatContainer {...props} />
18
+ </StackItem.Content>
19
+ );
20
+ };
21
+
22
+ export default ThreadContainer;
@@ -4,8 +4,7 @@
4
4
 
5
5
  import { lazy } from 'react';
6
6
 
7
- export const ThreadMain = lazy(() => import('./ThreadMain'));
8
- export const ThreadArticle = lazy(() => import('./ThreadArticle'));
7
+ export const ThreadContainer = lazy(() => import('./ThreadContainer'));
9
8
 
10
9
  // TODO(wittjosiah): Suspense boundary for sidebar?
11
10
  export * from './CommentContainer';
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { Expando } from '@dxos/echo-schema';
6
6
  import { PublicKey } from '@dxos/keys';
7
- import { create } from '@dxos/live-object';
7
+ import { create, makeRef } from '@dxos/live-object';
8
8
  import { MessageType, ThreadType } from '@dxos/plugin-space/types';
9
9
  import { faker } from '@dxos/random';
10
10
  import { type Identity } from '@dxos/react-client/halo';
@@ -12,20 +12,22 @@ import { type Identity } from '@dxos/react-client/halo';
12
12
  export const createChatThread = (identity: Identity) => {
13
13
  return create(ThreadType, {
14
14
  messages: Array.from({ length: 8 }).map(() =>
15
- create(MessageType, {
16
- sender: {
17
- identityKey: faker.datatype.boolean() ? identity.identityKey.toHex() : PublicKey.random().toHex(),
18
- },
19
- timestamp: faker.date.recent().toISOString(),
20
- text: faker.lorem.sentences(3),
21
- parts: faker.helpers.multiple(
22
- () =>
23
- faker.datatype.boolean({ probability: 0.8 })
24
- ? undefined
25
- : create(Expando, { name: faker.lorem.sentence() }),
26
- { count: { min: 1, max: 3 } },
27
- ),
28
- }),
15
+ makeRef(
16
+ create(MessageType, {
17
+ sender: {
18
+ identityKey: faker.datatype.boolean() ? identity.identityKey.toHex() : PublicKey.random().toHex(),
19
+ },
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
+ ),
29
+ }),
30
+ ),
29
31
  ),
30
32
  });
31
33
  };
@@ -34,13 +36,15 @@ export const createCommentThread = (identity: Identity) => {
34
36
  return create(ThreadType, {
35
37
  messages: faker.helpers.multiple(
36
38
  () =>
37
- create(MessageType, {
38
- sender: {
39
- identityKey: faker.datatype.boolean() ? identity.identityKey.toHex() : PublicKey.random().toHex(),
40
- },
41
- timestamp: new Date().toISOString(),
42
- text: faker.lorem.sentences(3),
43
- }),
39
+ makeRef(
40
+ create(MessageType, {
41
+ sender: {
42
+ identityKey: faker.datatype.boolean() ? identity.identityKey.toHex() : PublicKey.random().toHex(),
43
+ },
44
+ timestamp: new Date().toISOString(),
45
+ text: faker.lorem.sentences(3),
46
+ }),
47
+ ),
44
48
  { count: { min: 2, max: 3 } },
45
49
  ),
46
50
  });
@@ -24,9 +24,9 @@ export type ThreadContainerProps = {
24
24
  autoFocusTextbox?: boolean;
25
25
  onTextboxFocus?: () => void;
26
26
  detached?: boolean;
27
- onAttend?: () => void;
28
- onResolve?: () => void;
29
- onThreadDelete?: () => void;
30
- onMessageDelete?: (messageId: string) => void;
31
- onComment?: (thread: ThreadType) => void;
27
+ onAttend?: (thread: ThreadType) => void;
28
+ onComment?: (thread: ThreadType, message: string) => void;
29
+ onResolve?: (thread: ThreadType) => void;
30
+ onMessageDelete?: (thread: ThreadType, messageId: string) => void;
31
+ onThreadDelete?: (thread: ThreadType) => void;
32
32
  } & Pick<ThreadProps, 'current'>;
@@ -6,12 +6,13 @@ import { type Extension } from '@codemirror/state';
6
6
  import { EditorView } from '@codemirror/view';
7
7
  import { computed, effect } from '@preact/signals-core';
8
8
 
9
- import { type IntentDispatcher } from '@dxos/app-framework';
9
+ import { createIntent, type PromiseIntentDispatcher } from '@dxos/app-framework';
10
+ import { Ref } from '@dxos/echo-schema';
11
+ import { RefArray } from '@dxos/live-object';
10
12
  import { type DocumentType } from '@dxos/plugin-markdown/types';
11
13
  import { ThreadType } from '@dxos/plugin-space/types';
12
14
  import { getSpace, getTextInRange, createDocAccessor, fullyQualifiedId } from '@dxos/react-client/echo';
13
15
  import { comments, createExternalCommentSync } from '@dxos/react-ui-editor';
14
- import { nonNullable } from '@dxos/util';
15
16
 
16
17
  import { ThreadAction, type ThreadState } from '../types';
17
18
 
@@ -19,14 +20,14 @@ import { ThreadAction, type ThreadState } from '../types';
19
20
  const getName = (doc: DocumentType, anchor: string): string | undefined => {
20
21
  if (doc.content) {
21
22
  const [start, end] = anchor.split(':');
22
- return getTextInRange(createDocAccessor(doc.content, ['content']), start, end);
23
+ return getTextInRange(createDocAccessor(doc.content.target!, ['content']), start, end);
23
24
  }
24
25
  };
25
26
 
26
27
  /**
27
28
  * Construct plugins.
28
29
  */
29
- export const threads = (state: ThreadState, doc?: DocumentType, dispatch?: IntentDispatcher): Extension => {
30
+ export const threads = (state: ThreadState, doc?: DocumentType, dispatch?: PromiseIntentDispatcher): Extension => {
30
31
  const space = doc && getSpace(doc);
31
32
  if (!doc || !space || !dispatch) {
32
33
  // Include no-op comments extension here to ensure that the facets are always present when they are expected.
@@ -36,7 +37,7 @@ export const threads = (state: ThreadState, doc?: DocumentType, dispatch?: Inten
36
37
 
37
38
  // TODO(Zan): When we have the deepsignal specific equivalent of this we should use that instead.
38
39
  const threads = computed(() =>
39
- [...doc.threads.filter(nonNullable), ...(state.drafts[fullyQualifiedId(doc)] ?? [])].filter(
40
+ [...RefArray.allResolvedTargets(doc.threads), ...(state.drafts[fullyQualifiedId(doc)] ?? [])].filter(
40
41
  (thread) => !(thread?.status === 'resolved'),
41
42
  ),
42
43
  );
@@ -44,8 +45,8 @@ export const threads = (state: ThreadState, doc?: DocumentType, dispatch?: Inten
44
45
  return [
45
46
  EditorView.updateListener.of((update) => {
46
47
  if (update.docChanged) {
47
- doc.threads.forEach((thread) => {
48
- if (thread?.anchor) {
48
+ RefArray.allResolvedTargets(doc.threads).forEach((thread) => {
49
+ if (thread.anchor) {
49
50
  // Only update if the name has changed, otherwise this will cause an infinite loop.
50
51
  // Skip if the name is empty; this means comment text was deleted, but thread name should remain.
51
52
  const name = getName(doc, thread.anchor);
@@ -70,14 +71,7 @@ export const threads = (state: ThreadState, doc?: DocumentType, dispatch?: Inten
70
71
  id: fullyQualifiedId(doc),
71
72
  onCreate: ({ cursor }) => {
72
73
  const name = getName(doc, cursor);
73
- void dispatch({
74
- action: ThreadAction.CREATE,
75
- data: {
76
- cursor,
77
- name,
78
- subject: doc,
79
- },
80
- });
74
+ void dispatch(createIntent(ThreadAction.Create, { cursor, name, subject: doc }));
81
75
  },
82
76
  onDelete: ({ id }) => {
83
77
  const draft = state.drafts[fullyQualifiedId(doc)];
@@ -88,7 +82,7 @@ export const threads = (state: ThreadState, doc?: DocumentType, dispatch?: Inten
88
82
  }
89
83
  }
90
84
 
91
- const thread = doc.threads.find((thread) => thread?.id === id);
85
+ const thread = doc.threads.find(Ref.hasObjectId(id))?.target;
92
86
  if (thread) {
93
87
  thread.anchor = undefined;
94
88
  }
@@ -96,21 +90,18 @@ export const threads = (state: ThreadState, doc?: DocumentType, dispatch?: Inten
96
90
  onUpdate: ({ id, cursor }) => {
97
91
  const thread =
98
92
  state.drafts[fullyQualifiedId(doc)]?.find((thread) => fullyQualifiedId(thread) === id) ??
99
- doc.threads.find((thread) => thread?.id === id);
93
+ doc.threads.find(Ref.hasObjectId(id))?.target;
100
94
 
101
95
  if (thread instanceof ThreadType && thread.anchor) {
102
96
  thread.name = getName(doc, thread.anchor);
103
97
  thread.anchor = cursor;
104
98
  }
105
99
  },
106
- onSelect: ({ selection: { current, closest } }) => {
107
- void dispatch({
108
- action: ThreadAction.SELECT,
109
- data: {
110
- current: current ?? closest,
111
- skipOpen: true,
112
- },
113
- });
100
+ onSelect: ({ selection }) => {
101
+ const current = selection.current ?? selection.closest;
102
+ if (current) {
103
+ void dispatch(createIntent(ThreadAction.Select, { current, skipOpen: true }));
104
+ }
114
105
  },
115
106
  }),
116
107
  ];
@@ -4,24 +4,20 @@
4
4
 
5
5
  import { useCallback } from 'react';
6
6
 
7
- import { useIntentDispatcher } from '@dxos/app-framework';
7
+ import { createIntent, useIntentDispatcher } from '@dxos/app-framework';
8
8
  import { type ReactiveObject } from '@dxos/live-object';
9
- import { ObservabilityAction } from '@dxos/plugin-observability/meta';
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
12
 
13
13
  export const useAnalyticsCallback = (spaceId: string | undefined, name: string, meta?: any) => {
14
- const dispatch = useIntentDispatcher();
14
+ const { dispatchPromise: dispatch } = useIntentDispatcher();
15
15
 
16
16
  return useCallback(
17
17
  (dynamicMeta?: any) => {
18
- void dispatch({
19
- action: ObservabilityAction.SEND_EVENT,
20
- data: {
21
- name,
22
- properties: { ...meta, ...dynamicMeta, spaceId },
23
- },
24
- });
18
+ void dispatch(
19
+ createIntent(ObservabilityAction.SendEvent, { name, properties: { ...meta, ...dynamicMeta, spaceId } }),
20
+ );
25
21
  },
26
22
  [dispatch, name, meta, spaceId],
27
23
  );