@dxos/plugin-thread 0.6.11 → 0.6.12-main.2d19bf1

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 (89) hide show
  1. package/dist/lib/browser/{ThreadArticle-O64ZECMF.mjs → ThreadArticle-GBPIH47W.mjs} +3 -3
  2. package/dist/lib/browser/{ThreadMain-ZDCRC6RG.mjs → ThreadMain-PGF7W5EV.mjs} +3 -3
  3. package/dist/lib/browser/chunk-5G3KL6TC.mjs +16 -0
  4. package/dist/lib/browser/chunk-5G3KL6TC.mjs.map +7 -0
  5. package/dist/lib/browser/{chunk-GKPZHUT3.mjs → chunk-JFQZ5W2I.mjs} +2 -2
  6. package/dist/lib/browser/{chunk-GKPZHUT3.mjs.map → chunk-JFQZ5W2I.mjs.map} +1 -1
  7. package/dist/lib/browser/{chunk-P7ZE5N5M.mjs → chunk-O4YRHIZC.mjs} +13 -11
  8. package/dist/lib/browser/chunk-O4YRHIZC.mjs.map +7 -0
  9. package/dist/lib/browser/index.mjs +360 -341
  10. package/dist/lib/browser/index.mjs.map +4 -4
  11. package/dist/lib/browser/meta.json +1 -1
  12. package/dist/lib/browser/meta.mjs +1 -1
  13. package/dist/lib/browser/types.mjs +2 -2
  14. package/dist/lib/node/{ThreadArticle-DO56JXBU.cjs → ThreadArticle-GH5UBWFU.cjs} +7 -7
  15. package/dist/lib/node/{ThreadMain-XJX75GHG.cjs → ThreadMain-CO6I3DNX.cjs} +7 -7
  16. package/dist/lib/node/{chunk-5PZNPPQF.cjs → chunk-FDYRG7HE.cjs} +65 -63
  17. package/dist/lib/node/chunk-FDYRG7HE.cjs.map +7 -0
  18. package/dist/lib/node/{chunk-D5GOVXHS.cjs → chunk-FOZUQZF4.cjs} +6 -6
  19. package/dist/lib/node/{chunk-D5GOVXHS.cjs.map → chunk-FOZUQZF4.cjs.map} +1 -1
  20. package/dist/lib/node/{chunk-BWBONZGU.cjs → chunk-JIJ7Z6NO.cjs} +7 -22
  21. package/dist/lib/node/chunk-JIJ7Z6NO.cjs.map +7 -0
  22. package/dist/lib/node/index.cjs +433 -418
  23. package/dist/lib/node/index.cjs.map +4 -4
  24. package/dist/lib/node/meta.cjs +4 -4
  25. package/dist/lib/node/meta.cjs.map +1 -1
  26. package/dist/lib/node/meta.json +1 -1
  27. package/dist/lib/node/types.cjs +3 -3
  28. package/dist/lib/node/types.cjs.map +1 -1
  29. package/dist/lib/node-esm/ThreadArticle-ZTYD52TR.mjs +19 -0
  30. package/dist/lib/node-esm/ThreadArticle-ZTYD52TR.mjs.map +7 -0
  31. package/dist/lib/node-esm/ThreadMain-LEHGOF7R.mjs +27 -0
  32. package/dist/lib/node-esm/ThreadMain-LEHGOF7R.mjs.map +7 -0
  33. package/dist/lib/node-esm/chunk-DC5ZA6H5.mjs +18 -0
  34. package/dist/lib/node-esm/chunk-DC5ZA6H5.mjs.map +7 -0
  35. package/dist/lib/node-esm/chunk-XOVJ65DG.mjs +413 -0
  36. package/dist/lib/node-esm/chunk-XOVJ65DG.mjs.map +7 -0
  37. package/dist/lib/node-esm/chunk-ZXIPFXSA.mjs +21 -0
  38. package/dist/lib/node-esm/chunk-ZXIPFXSA.mjs.map +7 -0
  39. package/dist/lib/node-esm/index.mjs +1122 -0
  40. package/dist/lib/node-esm/index.mjs.map +7 -0
  41. package/dist/lib/node-esm/meta.json +1 -0
  42. package/dist/lib/node-esm/meta.mjs +12 -0
  43. package/dist/lib/node-esm/meta.mjs.map +7 -0
  44. package/dist/lib/node-esm/types.mjs +9 -0
  45. package/dist/lib/node-esm/types.mjs.map +7 -0
  46. package/dist/types/src/ThreadPlugin.d.ts.map +1 -1
  47. package/dist/types/src/components/ChatContainer.d.ts.map +1 -1
  48. package/dist/types/src/components/CommentContainer.d.ts +1 -1
  49. package/dist/types/src/components/CommentContainer.d.ts.map +1 -1
  50. package/dist/types/src/components/CommentsContainer.d.ts +1 -5
  51. package/dist/types/src/components/CommentsContainer.d.ts.map +1 -1
  52. package/dist/types/src/components/MessageContainer.d.ts.map +1 -1
  53. package/dist/types/src/components/ThreadComplementary.d.ts +10 -0
  54. package/dist/types/src/components/ThreadComplementary.d.ts.map +1 -0
  55. package/dist/types/src/components/testing.d.ts +2 -0
  56. package/dist/types/src/components/testing.d.ts.map +1 -1
  57. package/dist/types/src/extensions/index.d.ts +2 -0
  58. package/dist/types/src/extensions/index.d.ts.map +1 -0
  59. package/dist/types/src/extensions/threads.d.ts +9 -0
  60. package/dist/types/src/extensions/threads.d.ts.map +1 -0
  61. package/dist/types/src/meta.d.ts +3 -9
  62. package/dist/types/src/meta.d.ts.map +1 -1
  63. package/dist/types/src/types.d.ts +6 -1
  64. package/dist/types/src/types.d.ts.map +1 -1
  65. package/package.json +40 -37
  66. package/src/ThreadPlugin.tsx +115 -310
  67. package/src/components/ChatContainer.tsx +7 -8
  68. package/src/components/CommentContainer.tsx +18 -19
  69. package/src/components/Comments.stories.tsx +2 -2
  70. package/src/components/CommentsContainer.tsx +18 -31
  71. package/src/components/MessageContainer.tsx +2 -1
  72. package/src/components/ThreadComplementary.tsx +113 -0
  73. package/src/extensions/index.ts +5 -0
  74. package/src/extensions/threads.ts +117 -0
  75. package/src/meta.ts +15 -0
  76. package/src/types.ts +8 -3
  77. package/dist/lib/browser/chunk-5IBJO53M.mjs +0 -21
  78. package/dist/lib/browser/chunk-5IBJO53M.mjs.map +0 -7
  79. package/dist/lib/browser/chunk-P7ZE5N5M.mjs.map +0 -7
  80. package/dist/lib/node/chunk-5PZNPPQF.cjs.map +0 -7
  81. package/dist/lib/node/chunk-BWBONZGU.cjs.map +0 -7
  82. package/dist/types/src/types.test.d.ts +0 -2
  83. package/dist/types/src/types.test.d.ts.map +0 -1
  84. package/src/meta.tsx +0 -20
  85. package/src/types.test.ts +0 -13
  86. /package/dist/lib/browser/{ThreadArticle-O64ZECMF.mjs.map → ThreadArticle-GBPIH47W.mjs.map} +0 -0
  87. /package/dist/lib/browser/{ThreadMain-ZDCRC6RG.mjs.map → ThreadMain-PGF7W5EV.mjs.map} +0 -0
  88. /package/dist/lib/node/{ThreadArticle-DO56JXBU.cjs.map → ThreadArticle-GH5UBWFU.cjs.map} +0 -0
  89. /package/dist/lib/node/{ThreadMain-XJX75GHG.cjs.map → ThreadMain-CO6I3DNX.cjs.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ThreadAction
3
- } from "./chunk-GKPZHUT3.mjs";
3
+ } from "./chunk-JFQZ5W2I.mjs";
4
4
  import {
5
5
  ChatContainer,
6
6
  ChatHeading,
@@ -10,34 +10,27 @@ import {
10
10
  useAnalyticsCallback,
11
11
  useOnEditAnalytics,
12
12
  useStatus
13
- } from "./chunk-P7ZE5N5M.mjs";
13
+ } from "./chunk-O4YRHIZC.mjs";
14
14
  import {
15
15
  THREAD_ITEM,
16
16
  THREAD_PLUGIN,
17
17
  meta_default
18
- } from "./chunk-5IBJO53M.mjs";
18
+ } from "./chunk-5G3KL6TC.mjs";
19
19
 
20
20
  // packages/plugins/plugin-thread/src/ThreadPlugin.tsx
21
- import { Chat } from "@phosphor-icons/react";
22
- import { computed, effect } from "@preact/signals-core";
23
- import React5 from "react";
24
- import { LayoutAction, NavigationAction, parseIntentPlugin, parseNavigationPlugin, resolvePlugin, parseGraphPlugin, SLUG_PATH_SEPARATOR, SLUG_COLLECTION_INDICATOR, isLayoutParts, parseMetadataResolverPlugin, useResolvePlugins } from "@dxos/app-framework";
21
+ import React6 from "react";
22
+ import { NavigationAction, parseIntentPlugin, parseNavigationPlugin, resolvePlugin, isLayoutParts, parseMetadataResolverPlugin } from "@dxos/app-framework";
25
23
  import { getTypename } from "@dxos/echo-schema";
26
24
  import { create as create2 } from "@dxos/echo-schema";
27
25
  import { LocalStorageStore } from "@dxos/local-storage";
28
- import { parseAttentionPlugin } from "@dxos/plugin-attention";
26
+ import { log } from "@dxos/log";
29
27
  import { parseClientPlugin } from "@dxos/plugin-client";
30
- import { createExtension, isActionGroup } from "@dxos/plugin-graph";
31
- import { DocumentType } from "@dxos/plugin-markdown/types";
28
+ import { createExtension, isActionGroup, toSignal } from "@dxos/plugin-graph";
32
29
  import { ObservabilityAction } from "@dxos/plugin-observability/meta";
33
30
  import { SpaceAction } from "@dxos/plugin-space";
34
- import { ThreadType, MessageType as MessageType2, ChannelType } from "@dxos/plugin-space/types";
35
- import { getSpace as getSpace2, getTextInRange, Filter, createDocAccessor, fullyQualifiedId, loadObjectReferences } from "@dxos/react-client/echo";
36
- import { ScrollArea } from "@dxos/react-ui";
37
- import { createAttendableAttributes } from "@dxos/react-ui-attention";
38
- import { comments, createExternalCommentSync, listener as listener2 } from "@dxos/react-ui-editor";
31
+ import { ThreadType as ThreadType2, MessageType as MessageType2, ChannelType } from "@dxos/plugin-space/types";
32
+ import { getSpace as getSpace3, fullyQualifiedId as fullyQualifiedId5, loadObjectReferences } from "@dxos/react-client/echo";
39
33
  import { translations as threadTranslations } from "@dxos/react-ui-thread";
40
- import { nonNullable as nonNullable2 } from "@dxos/util";
41
34
 
42
35
  // packages/plugins/plugin-thread/src/components/index.ts
43
36
  import React4 from "react";
@@ -58,10 +51,10 @@ var ThreadSettings = ({ settings }) => {
58
51
 
59
52
  // packages/plugins/plugin-thread/src/components/CommentContainer.tsx
60
53
  import { CheckCircle, X } from "@phosphor-icons/react";
61
- import React2, { useCallback, useEffect, useMemo, useRef, useState } from "react";
54
+ import React2, { useCallback, useMemo, useRef, useState } from "react";
62
55
  import { create } from "@dxos/echo-schema";
63
56
  import { MessageType } from "@dxos/plugin-space/types";
64
- import { getSpace, useMembers } from "@dxos/react-client/echo";
57
+ import { fullyQualifiedId, getSpace, useMembers } from "@dxos/react-client/echo";
65
58
  import { useIdentity } from "@dxos/react-client/halo";
66
59
  import { Button, Tag, Tooltip, useThemeContext, useTranslation as useTranslation2 } from "@dxos/react-ui";
67
60
  import { createBasicExtensions, createThemeExtensions, listener } from "@dxos/react-ui-editor";
@@ -114,16 +107,15 @@ var DeleteThreadButton = ({ onDelete }) => {
114
107
  classNames: "z-[21]"
115
108
  }, label, /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null))));
116
109
  };
117
- var CommentContainer = ({ thread, detached, context, current, autoFocusTextbox, onAttend, onThreadDelete, onMessageDelete, onResolve, onComment }) => {
110
+ var CommentContainer = ({ thread, detached, context, current, onAttend, onThreadDelete, onMessageDelete, onResolve, onComment }) => {
118
111
  const identity = useIdentity();
119
112
  const space = getSpace(thread);
120
113
  const members = useMembers(space?.key);
121
- const activity = useStatus(space, thread.id);
114
+ const activity = useStatus(space, fullyQualifiedId(thread));
122
115
  const { t } = useTranslation2(THREAD_PLUGIN);
123
116
  const threadScrollRef = useRef(null);
124
- const [autoFocus, setAutoFocus] = useState(!!autoFocusTextbox);
125
117
  const { themeMode } = useThemeContext();
126
- const textboxMetadata = getMessageMetadata(thread.id, identity);
118
+ const textboxMetadata = getMessageMetadata(fullyQualifiedId(thread), identity);
127
119
  const [_count, _setCount] = useState(0);
128
120
  const rerenderEditor = () => _setCount((count) => count + 1);
129
121
  const messageRef = useRef("");
@@ -141,11 +133,6 @@ var CommentContainer = ({ thread, detached, context, current, autoFocusTextbox,
141
133
  ], [
142
134
  _count
143
135
  ]);
144
- useEffect(() => {
145
- setAutoFocus(!!autoFocusTextbox);
146
- }, [
147
- autoFocusTextbox
148
- ]);
149
136
  const scrollToEnd = (behavior) => setTimeout(() => threadScrollRef.current?.scrollIntoView({
150
137
  behavior,
151
138
  block: "end"
@@ -164,7 +151,6 @@ var CommentContainer = ({ thread, detached, context, current, autoFocusTextbox,
164
151
  }));
165
152
  onComment?.(thread);
166
153
  messageRef.current = "";
167
- setAutoFocus(true);
168
154
  scrollToEnd("instant");
169
155
  rerenderEditor();
170
156
  return true;
@@ -172,12 +158,11 @@ var CommentContainer = ({ thread, detached, context, current, autoFocusTextbox,
172
158
  thread,
173
159
  identity
174
160
  ]);
175
- const handleDeleteMessage = (id) => onMessageDelete?.(id);
176
161
  return /* @__PURE__ */ React2.createElement(Thread, {
177
162
  onClickCapture: onAttend,
178
163
  onFocusCapture: onAttend,
179
164
  current,
180
- id: thread.id
165
+ id: fullyQualifiedId(thread)
181
166
  }, /* @__PURE__ */ React2.createElement("div", {
182
167
  role: "none",
183
168
  className: mx("col-span-2 grid grid-cols-[var(--rail-size)_1fr_min-content]", hoverableControls, hoverableFocusedWithinControls)
@@ -201,10 +186,9 @@ var CommentContainer = ({ thread, detached, context, current, autoFocusTextbox,
201
186
  key: message.id,
202
187
  message,
203
188
  members,
204
- onDelete: handleDeleteMessage
189
+ onDelete: (id) => onMessageDelete?.(id)
205
190
  })), /* @__PURE__ */ React2.createElement(MessageTextbox, {
206
191
  extensions,
207
- autoFocus,
208
192
  onSend: handleCreate,
209
193
  ...textboxMetadata
210
194
  }), /* @__PURE__ */ React2.createElement(ThreadFooter, {
@@ -217,26 +201,15 @@ var CommentContainer = ({ thread, detached, context, current, autoFocusTextbox,
217
201
  };
218
202
 
219
203
  // packages/plugins/plugin-thread/src/components/CommentsContainer.tsx
220
- import { ChatText, Quotes } from "@phosphor-icons/react";
221
- import React3, { useEffect as useEffect2 } from "react";
204
+ import { ChatText } from "@phosphor-icons/react";
205
+ import React3, { useEffect } from "react";
206
+ import { fullyQualifiedId as fullyQualifiedId2 } from "@dxos/react-client/echo";
222
207
  import { useTranslation as useTranslation3, Trans } from "@dxos/react-ui";
223
- import { PlankHeading, plankHeadingIconProps } from "@dxos/react-ui-deck";
224
208
  import { descriptionText, mx as mx2 } from "@dxos/react-ui-theme";
225
- var CommentsHeading = ({ attendableId }) => {
209
+ var CommentsContainer = ({ threads: threads2, detached = [], currentId, showResolvedThreads, onThreadAttend, onThreadDelete, onMessageDelete, onThreadToggleResolved, ...props }) => {
226
210
  const { t } = useTranslation3(THREAD_PLUGIN);
227
- return /* @__PURE__ */ React3.createElement("div", {
228
- role: "none",
229
- className: "flex items-center"
230
- }, /* @__PURE__ */ React3.createElement(PlankHeading.Button, {
231
- attendableId
232
- }, /* @__PURE__ */ React3.createElement(Quotes, plankHeadingIconProps)), /* @__PURE__ */ React3.createElement(PlankHeading.Label, {
233
- attendableId
234
- }, t("comments heading")));
235
- };
236
- var CommentsContainer = ({ threads, detached = [], currentId, autoFocusCurrentTextbox, showResolvedThreads, onThreadAttend, onThreadDelete, onMessageDelete, onThreadToggleResolved, ...props }) => {
237
- const { t } = useTranslation3(THREAD_PLUGIN);
238
- const filteredThreads = showResolvedThreads ? threads : threads.filter((thread) => !(thread?.status === "resolved"));
239
- useEffect2(() => {
211
+ const filteredThreads = showResolvedThreads ? threads2 : threads2.filter((thread) => !(thread?.status === "resolved"));
212
+ useEffect(() => {
240
213
  if (currentId) {
241
214
  document.getElementById(currentId)?.scrollIntoView({
242
215
  behavior: "smooth",
@@ -246,26 +219,28 @@ var CommentsContainer = ({ threads, detached = [], currentId, autoFocusCurrentTe
246
219
  }, [
247
220
  currentId
248
221
  ]);
249
- return /* @__PURE__ */ React3.createElement(React3.Fragment, null, filteredThreads.length > 0 ? filteredThreads.map((thread) => /* @__PURE__ */ React3.createElement(CommentContainer, {
250
- key: thread.id,
251
- thread,
252
- current: currentId === thread.id,
253
- detached: detached.includes(thread.id),
254
- autoFocusTextbox: autoFocusCurrentTextbox && currentId === thread.id,
255
- ...onThreadAttend && {
256
- onAttend: () => onThreadAttend(thread)
257
- },
258
- ...onThreadDelete && {
259
- onThreadDelete: () => onThreadDelete(thread)
260
- },
261
- ...onMessageDelete && {
262
- onMessageDelete: (messageId) => onMessageDelete(thread, messageId)
263
- },
264
- ...onThreadToggleResolved && {
265
- onResolve: () => onThreadToggleResolved(thread)
266
- },
267
- ...props
268
- })) : /* @__PURE__ */ React3.createElement("div", {
222
+ return /* @__PURE__ */ React3.createElement(React3.Fragment, null, filteredThreads.length > 0 ? filteredThreads.map((thread) => {
223
+ const threadId = fullyQualifiedId2(thread);
224
+ return /* @__PURE__ */ React3.createElement(CommentContainer, {
225
+ key: threadId,
226
+ thread,
227
+ current: currentId === threadId,
228
+ detached: detached.includes(threadId),
229
+ ...onThreadAttend && {
230
+ onAttend: () => onThreadAttend(thread)
231
+ },
232
+ ...onThreadDelete && {
233
+ onThreadDelete: () => onThreadDelete(thread)
234
+ },
235
+ ...onMessageDelete && {
236
+ onMessageDelete: (messageId) => onMessageDelete(thread, messageId)
237
+ },
238
+ ...onThreadToggleResolved && {
239
+ onResolve: () => onThreadToggleResolved(thread)
240
+ },
241
+ ...props
242
+ });
243
+ }) : /* @__PURE__ */ React3.createElement("div", {
269
244
  role: "alert",
270
245
  className: mx2(descriptionText, "place-self-center border border-dashed border-neutral-400/50 rounded-lg text-center p-4 m-4")
271
246
  }, /* @__PURE__ */ React3.createElement("h2", {
@@ -282,8 +257,208 @@ var CommentsContainer = ({ threads, detached = [], currentId, autoFocusCurrentTe
282
257
  };
283
258
 
284
259
  // packages/plugins/plugin-thread/src/components/index.ts
285
- var ThreadMain = React4.lazy(() => import("./ThreadMain-ZDCRC6RG.mjs"));
286
- var ThreadArticle = React4.lazy(() => import("./ThreadArticle-O64ZECMF.mjs"));
260
+ var ThreadMain = React4.lazy(() => import("./ThreadMain-PGF7W5EV.mjs"));
261
+ var ThreadArticle = React4.lazy(() => import("./ThreadArticle-GBPIH47W.mjs"));
262
+
263
+ // packages/plugins/plugin-thread/src/components/ThreadComplementary.tsx
264
+ import React5, { useEffect as useEffect2, useMemo as useMemo2 } from "react";
265
+ import { LayoutAction, useIntentDispatcher, useResolvePlugins } from "@dxos/app-framework";
266
+ import { fullyQualifiedId as fullyQualifiedId3 } from "@dxos/react-client/echo";
267
+ import { ScrollArea } from "@dxos/react-ui";
268
+ import { useAttended } from "@dxos/react-ui-attention";
269
+ import { nonNullable as nonNullable2 } from "@dxos/util";
270
+ var providesThreadsConfig = (plugin) => "thread" in plugin.provides ? plugin : void 0;
271
+ var ThreadComplementary = ({ role, subject, drafts, current, showResolvedThreads }) => {
272
+ const dispatch = useIntentDispatcher();
273
+ const threadsIntegrators = useResolvePlugins(providesThreadsConfig);
274
+ const threadProvides = useMemo2(() => threadsIntegrators.map((p) => p.provides.thread), [
275
+ threadsIntegrators
276
+ ]);
277
+ const createSort = useMemo2(() => threadProvides.find((p) => p.predicate(subject))?.createSort, [
278
+ threadProvides,
279
+ subject
280
+ ]);
281
+ const sort = useMemo2(() => createSort?.(subject), [
282
+ createSort,
283
+ subject
284
+ ]);
285
+ const threads2 = useMemo2(() => {
286
+ return subject.threads.concat(drafts ?? []).filter(nonNullable2);
287
+ }, [
288
+ JSON.stringify(subject.threads),
289
+ JSON.stringify(drafts)
290
+ ]);
291
+ const detachedIds = useMemo2(() => {
292
+ return threads2.filter(({ anchor }) => !anchor).map((thread) => fullyQualifiedId3(thread));
293
+ }, [
294
+ threads2
295
+ ]);
296
+ useEffect2(() => {
297
+ if (!sort) {
298
+ return;
299
+ }
300
+ threads2.sort((a, b) => sort(a?.anchor, b?.anchor));
301
+ }, [
302
+ sort,
303
+ threads2
304
+ ]);
305
+ const attended = useAttended();
306
+ const qualifiedSubjectId = fullyQualifiedId3(subject);
307
+ return /* @__PURE__ */ React5.createElement("div", {
308
+ role: "none",
309
+ className: "contents"
310
+ }, /* @__PURE__ */ React5.createElement(ScrollArea.Root, {
311
+ classNames: "row-span-2"
312
+ }, /* @__PURE__ */ React5.createElement(ScrollArea.Viewport, null, /* @__PURE__ */ React5.createElement(CommentsContainer, {
313
+ threads: threads2,
314
+ detached: detachedIds,
315
+ currentId: attended.includes(qualifiedSubjectId) ? current : void 0,
316
+ showResolvedThreads,
317
+ onThreadAttend: (thread) => {
318
+ const threadId = fullyQualifiedId3(thread);
319
+ if (current !== threadId) {
320
+ current = threadId;
321
+ void dispatch?.([
322
+ {
323
+ action: LayoutAction.SCROLL_INTO_VIEW,
324
+ data: {
325
+ id: fullyQualifiedId3(subject),
326
+ thread: threadId,
327
+ cursor: thread.anchor
328
+ }
329
+ }
330
+ ]);
331
+ }
332
+ },
333
+ onThreadDelete: (thread) => {
334
+ return dispatch({
335
+ plugin: THREAD_PLUGIN,
336
+ action: ThreadAction.DELETE,
337
+ data: {
338
+ subject,
339
+ thread
340
+ }
341
+ });
342
+ },
343
+ onMessageDelete: (thread, messageId) => dispatch({
344
+ plugin: THREAD_PLUGIN,
345
+ action: ThreadAction.DELETE_MESSAGE,
346
+ data: {
347
+ subject,
348
+ thread,
349
+ messageId
350
+ }
351
+ }),
352
+ onThreadToggleResolved: (thread) => dispatch({
353
+ plugin: THREAD_PLUGIN,
354
+ action: ThreadAction.TOGGLE_RESOLVED,
355
+ data: {
356
+ thread
357
+ }
358
+ }),
359
+ onComment: (thread) => {
360
+ void dispatch({
361
+ action: ThreadAction.ON_MESSAGE_ADD,
362
+ data: {
363
+ thread,
364
+ subject
365
+ }
366
+ });
367
+ }
368
+ }), /* @__PURE__ */ React5.createElement("div", {
369
+ role: "none",
370
+ className: "bs-10"
371
+ }), /* @__PURE__ */ React5.createElement(ScrollArea.Scrollbar, null, /* @__PURE__ */ React5.createElement(ScrollArea.Thumb, null)))));
372
+ };
373
+
374
+ // packages/plugins/plugin-thread/src/extensions/threads.ts
375
+ import { EditorView } from "@codemirror/view";
376
+ import { computed, effect } from "@preact/signals-core";
377
+ import { ThreadType } from "@dxos/plugin-space/types";
378
+ import { getSpace as getSpace2, getTextInRange, createDocAccessor, fullyQualifiedId as fullyQualifiedId4 } from "@dxos/react-client/echo";
379
+ import { comments, createExternalCommentSync } from "@dxos/react-ui-editor";
380
+ import { nonNullable as nonNullable3 } from "@dxos/util";
381
+ var getName = (doc, anchor) => {
382
+ if (doc.content) {
383
+ const [start, end] = anchor.split(":");
384
+ return getTextInRange(createDocAccessor(doc.content, [
385
+ "content"
386
+ ]), start, end);
387
+ }
388
+ };
389
+ var threads = (state, doc, dispatch) => {
390
+ const space = doc && getSpace2(doc);
391
+ if (!doc || !space || !dispatch) {
392
+ return [
393
+ comments()
394
+ ];
395
+ }
396
+ const threads2 = computed(() => [
397
+ ...doc.threads.filter(nonNullable3),
398
+ ...state.drafts[fullyQualifiedId4(doc)] ?? []
399
+ ].filter((thread) => !(thread?.status === "resolved")));
400
+ return [
401
+ EditorView.updateListener.of((update) => {
402
+ if (update.docChanged) {
403
+ doc.threads.forEach((thread) => {
404
+ if (thread?.anchor) {
405
+ const name = getName(doc, thread.anchor);
406
+ if (name && name !== thread.name) {
407
+ thread.name = name;
408
+ }
409
+ }
410
+ });
411
+ }
412
+ }),
413
+ createExternalCommentSync(fullyQualifiedId4(doc), (sink) => effect(() => sink()), () => threads2.value.filter((thread) => thread?.anchor).map((thread) => ({
414
+ id: fullyQualifiedId4(thread),
415
+ cursor: thread.anchor
416
+ }))),
417
+ comments({
418
+ id: fullyQualifiedId4(doc),
419
+ onCreate: ({ cursor }) => {
420
+ const name = getName(doc, cursor);
421
+ void dispatch({
422
+ action: ThreadAction.CREATE,
423
+ data: {
424
+ cursor,
425
+ name,
426
+ subject: doc
427
+ }
428
+ });
429
+ },
430
+ onDelete: ({ id }) => {
431
+ const draft = state.drafts[fullyQualifiedId4(doc)];
432
+ if (draft) {
433
+ const index = draft.findIndex((thread2) => fullyQualifiedId4(thread2) === id);
434
+ if (index !== -1) {
435
+ draft.splice(index, 1);
436
+ }
437
+ }
438
+ const thread = doc.threads.find((thread2) => thread2?.id === id);
439
+ if (thread) {
440
+ thread.anchor = void 0;
441
+ }
442
+ },
443
+ onUpdate: ({ id, cursor }) => {
444
+ const thread = state.drafts[fullyQualifiedId4(doc)]?.find((thread2) => fullyQualifiedId4(thread2) === id) ?? doc.threads.find((thread2) => thread2?.id === id);
445
+ if (thread instanceof ThreadType && thread.anchor) {
446
+ thread.name = getName(doc, thread.anchor);
447
+ thread.anchor = cursor;
448
+ }
449
+ },
450
+ onSelect: ({ selection: { current, closest } }) => {
451
+ void dispatch({
452
+ action: ThreadAction.SELECT,
453
+ data: {
454
+ current: current ?? closest,
455
+ skipOpen: true
456
+ }
457
+ });
458
+ }
459
+ })
460
+ ];
461
+ };
287
462
 
288
463
  // packages/plugins/plugin-thread/src/translations.ts
289
464
  var translations_default = [
@@ -322,14 +497,14 @@ var translations_default = [
322
497
  ];
323
498
 
324
499
  // packages/plugins/plugin-thread/src/ThreadPlugin.tsx
500
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/plugins/plugin-thread/src/ThreadPlugin.tsx";
325
501
  var initialViewState = {
326
502
  showResolvedThreads: false
327
503
  };
328
- var providesThreadsConfig = (plugin) => "thread" in plugin.provides ? plugin : void 0;
329
504
  var ThreadPlugin = () => {
330
505
  const settings = new LocalStorageStore(THREAD_PLUGIN);
331
506
  const state = create2({
332
- staging: {}
507
+ drafts: {}
333
508
  });
334
509
  const viewStore = create2({});
335
510
  const getViewState = (subjectId) => {
@@ -340,10 +515,8 @@ var ThreadPlugin = () => {
340
515
  }
341
516
  return viewStore[subjectId];
342
517
  };
343
- let attentionPlugin;
344
518
  let navigationPlugin;
345
519
  let intentPlugin;
346
- let dispatch;
347
520
  const unsubscribeCallbacks = [];
348
521
  return {
349
522
  meta: meta_default,
@@ -354,36 +527,8 @@ var ThreadPlugin = () => {
354
527
  allowUndefined: true
355
528
  })
356
529
  });
357
- attentionPlugin = resolvePlugin(plugins, parseAttentionPlugin);
358
530
  navigationPlugin = resolvePlugin(plugins, parseNavigationPlugin);
359
531
  intentPlugin = resolvePlugin(plugins, parseIntentPlugin);
360
- dispatch = intentPlugin?.provides.intent.dispatch;
361
- const graphPlugin = resolvePlugin(plugins, parseGraphPlugin);
362
- const client = resolvePlugin(plugins, parseClientPlugin)?.provides.client;
363
- if (!client) {
364
- return;
365
- }
366
- const channelsQuery = client.spaces.query(Filter.schema(ChannelType));
367
- const queryUnsubscribe = channelsQuery.subscribe();
368
- const unsubscribe = effect(() => {
369
- const attention = attentionPlugin?.provides.attention;
370
- if (!attention?.attended) {
371
- return;
372
- }
373
- const firstAttendedNodeWithComments = Array.from(attention.attended).map((id) => graphPlugin?.provides.graph.findNode(id)).find((node) => node?.data instanceof DocumentType && (node.data.threads?.length ?? 0) > 0);
374
- if (firstAttendedNodeWithComments) {
375
- void intentPlugin?.provides.intent.dispatch({
376
- action: NavigationAction.OPEN,
377
- data: {
378
- activeParts: {
379
- complementary: `${firstAttendedNodeWithComments.id}${SLUG_PATH_SEPARATOR}comments`
380
- }
381
- }
382
- });
383
- }
384
- });
385
- unsubscribeCallbacks.push(queryUnsubscribe);
386
- unsubscribeCallbacks.push(unsubscribe);
387
532
  },
388
533
  unload: async () => {
389
534
  unsubscribeCallbacks.forEach((unsubscribe) => unsubscribe());
@@ -399,12 +544,11 @@ var ThreadPlugin = () => {
399
544
  ns: THREAD_PLUGIN
400
545
  }
401
546
  ],
402
- icon: (props) => /* @__PURE__ */ React5.createElement(Chat, props),
403
- iconSymbol: "ph--chat--regular",
547
+ icon: "ph--chat--regular",
404
548
  // TODO(wittjosiah): Move out of metadata.
405
549
  loadReferences: (channel) => loadObjectReferences(channel, (channel2) => channel2.threads)
406
550
  },
407
- [ThreadType.typename]: {
551
+ [ThreadType2.typename]: {
408
552
  // TODO(wittjosiah): Move out of metadata.
409
553
  loadReferences: (thread) => loadObjectReferences(thread, (thread2) => thread2.messages)
410
554
  },
@@ -440,16 +584,16 @@ var ThreadPlugin = () => {
440
584
  echo: {
441
585
  schema: [
442
586
  ChannelType,
443
- ThreadType,
587
+ ThreadType2,
444
588
  MessageType2
445
589
  ]
446
590
  },
447
591
  graph: {
448
592
  builder: (plugins) => {
449
593
  const client = resolvePlugin(plugins, parseClientPlugin)?.provides.client;
450
- const dispatch2 = resolvePlugin(plugins, parseIntentPlugin)?.provides.intent.dispatch;
594
+ const dispatch = resolvePlugin(plugins, parseIntentPlugin)?.provides.intent.dispatch;
451
595
  const metadataResolver = resolvePlugin(plugins, parseMetadataResolverPlugin)?.provides.metadata.resolver;
452
- if (!client || !dispatch2 || !metadataResolver) {
596
+ if (!client || !dispatch || !metadataResolver) {
453
597
  return [];
454
598
  }
455
599
  return [
@@ -459,29 +603,36 @@ var ThreadPlugin = () => {
459
603
  if (!id.endsWith("~comments")) {
460
604
  return;
461
605
  }
462
- const docId = id.split("~").at(0);
463
- const [spaceId, objectId] = docId?.split(":") ?? [];
606
+ const subjectId = id.split("~").at(0);
607
+ const [spaceId, objectId] = subjectId?.split(":") ?? [];
464
608
  const space = client.spaces.get().find((space2) => space2.id === spaceId);
465
- const doc = space?.db.getObjectById(objectId);
466
- if (!doc || !docId) {
609
+ const object = toSignal((onChange) => {
610
+ const timeout = setTimeout(async () => {
611
+ await space?.db.loadObjectById(objectId);
612
+ onChange();
613
+ });
614
+ return () => clearTimeout(timeout);
615
+ }, () => space?.db.getObjectById(objectId), subjectId);
616
+ if (!object || !subjectId) {
467
617
  return;
468
618
  }
469
- const docMeta = metadataResolver(getTypename(doc) ?? "");
470
- const label = docMeta.label?.(doc) || doc.name || docMeta.placeholder || [
619
+ const meta = metadataResolver(getTypename(object) ?? "");
620
+ const label = meta.label?.(object) || object.name || meta.placeholder || [
471
621
  "unnamed object threads label",
472
622
  {
473
623
  ns: THREAD_PLUGIN
474
624
  }
475
625
  ];
476
- const viewState = getViewState(docId);
626
+ const viewState = getViewState(subjectId);
477
627
  return {
478
628
  id,
479
629
  type: "orphan-comments-for-subject",
480
- data: doc,
630
+ data: null,
481
631
  properties: {
482
- icon: meta_default.iconComponent,
632
+ icon: "ph--chat-text--regular",
483
633
  label,
484
- showResolvedThreads: viewState.showResolvedThreads
634
+ showResolvedThreads: viewState.showResolvedThreads,
635
+ object
485
636
  }
486
637
  };
487
638
  },
@@ -495,7 +646,7 @@ var ThreadPlugin = () => {
495
646
  const toggle = () => {
496
647
  const newToggleState = !viewState.showResolvedThreads;
497
648
  viewState.showResolvedThreads = newToggleState;
498
- void dispatch2({
649
+ void dispatch({
499
650
  action: ObservabilityAction.SEND_EVENT,
500
651
  data: {
501
652
  name: "threads.toggle-show-resolved",
@@ -520,7 +671,8 @@ var ThreadPlugin = () => {
520
671
  ],
521
672
  menuItemType: "toggle",
522
673
  isChecked: viewState.showResolvedThreads,
523
- testId: "threadPlugin.toggleShowResolved"
674
+ testId: "threadPlugin.toggleShowResolved",
675
+ icon: viewState.showResolvedThreads ? "ph--eye-slash--regular" : "ph--eye--regular"
524
676
  }
525
677
  }
526
678
  ];
@@ -542,7 +694,7 @@ var ThreadPlugin = () => {
542
694
  {
543
695
  id: `${THREAD_PLUGIN}/create/${node.id}`,
544
696
  data: async () => {
545
- await dispatch2([
697
+ await dispatch([
546
698
  {
547
699
  plugin: THREAD_PLUGIN,
548
700
  action: ThreadAction.CREATE
@@ -565,8 +717,7 @@ var ThreadPlugin = () => {
565
717
  ns: THREAD_PLUGIN
566
718
  }
567
719
  ],
568
- icon: (props) => /* @__PURE__ */ React5.createElement(Chat, props),
569
- iconSymbol: "ph--chat--regular",
720
+ icon: "ph--chat--regular",
570
721
  testId: "threadPlugin.createObject"
571
722
  }
572
723
  }
@@ -580,20 +731,17 @@ var ThreadPlugin = () => {
580
731
  component: ({ data, role }) => {
581
732
  switch (role) {
582
733
  case "main": {
583
- return data.active instanceof ChannelType && data.active.threads[0] ? /* @__PURE__ */ React5.createElement(ThreadMain, {
734
+ return data.active instanceof ChannelType && data.active.threads[0] ? /* @__PURE__ */ React6.createElement(ThreadMain, {
584
735
  thread: data.active.threads[0]
585
736
  }) : null;
586
737
  }
587
738
  case "settings": {
588
- return data.plugin === meta_default.id ? /* @__PURE__ */ React5.createElement(ThreadSettings, {
739
+ return data.plugin === meta_default.id ? /* @__PURE__ */ React6.createElement(ThreadSettings, {
589
740
  settings: settings.values
590
741
  }) : null;
591
742
  }
592
- case "article":
593
- case "complementary": {
743
+ case "article": {
594
744
  const location = navigationPlugin?.provides.location;
595
- const threadsIntegrators = useResolvePlugins(providesThreadsConfig);
596
- const threadProvides = threadsIntegrators.map((p) => p.provides.thread);
597
745
  if (data.object instanceof ChannelType && data.object.threads[0]) {
598
746
  const channel = data.object;
599
747
  if (isLayoutParts(location?.active) && location.active.main) {
@@ -601,103 +749,38 @@ var ThreadPlugin = () => {
601
749
  const currentPosition = layoutEntries.findIndex((entry) => channel.id === entry.id);
602
750
  if (currentPosition > 0) {
603
751
  const objectToTheLeft = layoutEntries[currentPosition - 1];
604
- const context = getSpace2(data.object)?.db.getObjectById(objectToTheLeft.id);
605
- return /* @__PURE__ */ React5.createElement(ThreadArticle, {
752
+ const context = getSpace3(data.object)?.db.getObjectById(objectToTheLeft.id);
753
+ return /* @__PURE__ */ React6.createElement(ThreadArticle, {
606
754
  thread: data.object.threads[0],
607
755
  context
608
756
  });
609
757
  }
610
758
  }
611
- return /* @__PURE__ */ React5.createElement(ThreadArticle, {
759
+ return /* @__PURE__ */ React6.createElement(ThreadArticle, {
612
760
  thread: data.object.threads[0]
613
761
  });
614
762
  }
615
- if (data.subject instanceof ThreadType) {
616
- return /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(ChatHeading, {
763
+ if (data.subject instanceof ThreadType2) {
764
+ return /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(ChatHeading, {
617
765
  attendableId: data.subject.id
618
- }), /* @__PURE__ */ React5.createElement(ChatContainer, {
766
+ }), /* @__PURE__ */ React6.createElement(ChatContainer, {
619
767
  thread: data.subject
620
768
  }));
621
769
  }
770
+ break;
771
+ }
772
+ case "complementary--comments": {
622
773
  if (data.subject && typeof data.subject === "object" && "threads" in data.subject && Array.isArray(data.subject.threads)) {
623
- const threads = data.subject.threads.concat(state.staging[fullyQualifiedId(data.subject)]).filter(nonNullable2);
624
- const createSort = threadProvides.find((p) => p.predicate(data.subject))?.createSort;
625
- if (createSort) {
626
- const sort = createSort(data.subject);
627
- threads.sort((a, b) => sort(a.anchor, b.anchor));
628
- }
629
- const detached = data.subject.threads.filter(nonNullable2).filter(({ anchor }) => !anchor).map((thread) => thread.id);
630
- const qualifiedSubjectId = fullyQualifiedId(data.subject);
631
- const attention = attentionPlugin?.provides.attention?.attended ?? /* @__PURE__ */ new Set([
632
- qualifiedSubjectId
633
- ]);
634
- const attendableAttrs = createAttendableAttributes(qualifiedSubjectId);
635
- const { showResolvedThreads } = getViewState(qualifiedSubjectId);
636
- return /* @__PURE__ */ React5.createElement("div", {
637
- role: "none",
638
- className: "contents group/attention",
639
- ...attendableAttrs
640
- }, role === "complementary" && /* @__PURE__ */ React5.createElement(CommentsHeading, {
641
- attendableId: qualifiedSubjectId
642
- }), /* @__PURE__ */ React5.createElement(ScrollArea.Root, {
643
- classNames: "row-span-2"
644
- }, /* @__PURE__ */ React5.createElement(ScrollArea.Viewport, null, /* @__PURE__ */ React5.createElement(CommentsContainer, {
645
- threads,
646
- detached,
647
- currentId: attention.has(qualifiedSubjectId) ? state.current : void 0,
648
- autoFocusCurrentTextbox: state.focus,
649
- showResolvedThreads,
650
- onThreadAttend: (thread) => {
651
- if (state.current !== thread.id) {
652
- state.current = thread.id;
653
- void dispatch?.({
654
- action: LayoutAction.SCROLL_INTO_VIEW,
655
- data: {
656
- id: fullyQualifiedId(data.subject),
657
- thread: fullyQualifiedId(thread),
658
- cursor: thread.anchor
659
- }
660
- });
661
- }
662
- },
663
- onThreadDelete: (thread) => dispatch?.({
664
- plugin: THREAD_PLUGIN,
665
- action: ThreadAction.DELETE,
666
- data: {
667
- subject: data.subject,
668
- thread
669
- }
670
- }),
671
- onMessageDelete: (thread, messageId) => dispatch?.({
672
- plugin: THREAD_PLUGIN,
673
- action: ThreadAction.DELETE_MESSAGE,
674
- data: {
675
- subject: data.subject,
676
- thread,
677
- messageId
678
- }
679
- }),
680
- onThreadToggleResolved: (thread) => dispatch?.({
681
- plugin: THREAD_PLUGIN,
682
- action: ThreadAction.TOGGLE_RESOLVED,
683
- data: {
684
- thread
685
- }
686
- }),
687
- onComment: (thread) => {
688
- void intentPlugin?.provides.intent.dispatch({
689
- action: ThreadAction.ON_MESSAGE_ADD,
690
- data: {
691
- thread,
692
- subject: data.subject
693
- }
694
- });
695
- }
696
- }), /* @__PURE__ */ React5.createElement("div", {
697
- role: "none",
698
- className: "bs-10"
699
- }), /* @__PURE__ */ React5.createElement(ScrollArea.Scrollbar, null, /* @__PURE__ */ React5.createElement(ScrollArea.Thumb, null)))));
774
+ const { showResolvedThreads } = getViewState(fullyQualifiedId5(data.subject));
775
+ return /* @__PURE__ */ React6.createElement(ThreadComplementary, {
776
+ role,
777
+ subject: data.subject,
778
+ drafts: state.drafts[fullyQualifiedId5(data.subject)],
779
+ current: state.current,
780
+ showResolvedThreads
781
+ });
700
782
  }
783
+ break;
701
784
  }
702
785
  }
703
786
  return null;
@@ -709,18 +792,31 @@ var ThreadPlugin = () => {
709
792
  case ThreadAction.CREATE: {
710
793
  if (intent.data && intent.data.cursor !== void 0) {
711
794
  const { cursor, name, subject } = intent.data;
712
- const subjectId = fullyQualifiedId(subject);
713
- const thread = create2(ThreadType, {
795
+ if (subject?.threads === void 0) {
796
+ try {
797
+ subject.threads = [];
798
+ } catch (err) {
799
+ log.error("Subject does not support threads array", subject?.typename, {
800
+ F: __dxlog_file,
801
+ L: 322,
802
+ S: void 0,
803
+ C: (f, a) => f(...a)
804
+ });
805
+ return;
806
+ }
807
+ }
808
+ const subjectId = fullyQualifiedId5(subject);
809
+ const thread = create2(ThreadType2, {
714
810
  name,
715
811
  anchor: cursor,
716
812
  messages: [],
717
813
  status: "staged"
718
814
  });
719
- const stagingArea = state.staging[subjectId];
720
- if (stagingArea) {
721
- stagingArea.push(thread);
815
+ const draft = state.drafts[subjectId];
816
+ if (draft) {
817
+ draft.push(thread);
722
818
  } else {
723
- state.staging[subjectId] = [
819
+ state.drafts[subjectId] = [
724
820
  thread
725
821
  ];
726
822
  }
@@ -728,27 +824,20 @@ var ThreadPlugin = () => {
728
824
  data: thread,
729
825
  intents: [
730
826
  [
731
- {
732
- action: NavigationAction.OPEN,
733
- data: {
734
- activeParts: {
735
- complementary: `${subjectId}${SLUG_PATH_SEPARATOR}comments${SLUG_COLLECTION_INDICATOR}`
736
- }
737
- }
738
- },
739
827
  {
740
828
  action: ThreadAction.SELECT,
741
829
  data: {
742
- current: thread.id,
743
- focus: true
830
+ current: fullyQualifiedId5(thread)
744
831
  }
745
- },
832
+ }
833
+ ],
834
+ [
746
835
  {
747
- action: LayoutAction.SET_LAYOUT,
836
+ action: NavigationAction.OPEN,
748
837
  data: {
749
- element: "complementary",
750
- subject: subjectId,
751
- state: true
838
+ activeParts: {
839
+ complementary: "comments"
840
+ }
752
841
  }
753
842
  }
754
843
  ]
@@ -758,7 +847,7 @@ var ThreadPlugin = () => {
758
847
  return {
759
848
  data: create2(ChannelType, {
760
849
  threads: [
761
- create2(ThreadType, {
850
+ create2(ThreadType2, {
762
851
  messages: []
763
852
  })
764
853
  ]
@@ -767,15 +856,26 @@ var ThreadPlugin = () => {
767
856
  }
768
857
  }
769
858
  case ThreadAction.SELECT: {
770
- state.focus = intent.data?.current === state.current ? state.focus : intent.data?.focus;
771
859
  state.current = intent.data?.current;
772
860
  return {
773
- data: true
861
+ data: true,
862
+ intents: !intent.data?.skipOpen ? [
863
+ [
864
+ {
865
+ action: NavigationAction.OPEN,
866
+ data: {
867
+ activeParts: {
868
+ complementary: "comments"
869
+ }
870
+ }
871
+ }
872
+ ]
873
+ ] : []
774
874
  };
775
875
  }
776
876
  case ThreadAction.TOGGLE_RESOLVED: {
777
877
  const { thread } = intent.data ?? {};
778
- if (!(thread instanceof ThreadType)) {
878
+ if (!(thread instanceof ThreadType2)) {
779
879
  return;
780
880
  }
781
881
  if (thread.status === "active" || thread.status === void 0) {
@@ -783,9 +883,10 @@ var ThreadPlugin = () => {
783
883
  } else if (thread.status === "resolved") {
784
884
  thread.status = "active";
785
885
  }
786
- const space = getSpace2(thread);
886
+ const space = getSpace3(thread);
787
887
  const spaceId = space?.id;
788
888
  return {
889
+ data: true,
789
890
  intents: [
790
891
  [
791
892
  {
@@ -804,19 +905,21 @@ var ThreadPlugin = () => {
804
905
  }
805
906
  case ThreadAction.DELETE: {
806
907
  const { subject, thread } = intent.data ?? {};
807
- if (!(subject instanceof DocumentType) || !(thread instanceof ThreadType)) {
908
+ if (!(thread instanceof ThreadType2) || !(subject && typeof subject === "object" && "threads" in subject)) {
808
909
  return;
809
910
  }
810
- const subjectId = fullyQualifiedId(subject);
811
- const stagingArea = state.staging[subjectId];
812
- if (stagingArea) {
813
- const index = stagingArea.findIndex((t) => t.id === thread.id);
911
+ const subjectId = fullyQualifiedId5(subject);
912
+ const draft = state.drafts[subjectId];
913
+ if (draft) {
914
+ const index = draft.findIndex((t) => t.id === thread.id);
814
915
  if (index !== -1) {
815
- stagingArea.splice(index, 1);
816
- return;
916
+ draft.splice(index, 1);
917
+ return {
918
+ data: true
919
+ };
817
920
  }
818
921
  }
819
- const space = getSpace2(thread);
922
+ const space = getSpace3(thread);
820
923
  if (!space || !subject.threads) {
821
924
  return;
822
925
  }
@@ -874,22 +977,22 @@ var ThreadPlugin = () => {
874
977
  }
875
978
  case ThreadAction.ON_MESSAGE_ADD: {
876
979
  const { thread, subject } = intent.data ?? {};
877
- if (!(thread instanceof ThreadType) || !(subject && typeof subject === "object" && "threads" in subject)) {
980
+ if (!(thread instanceof ThreadType2) || !(subject && typeof subject === "object" && "threads" in subject && Array.isArray(subject.threads))) {
878
981
  return;
879
982
  }
880
- const subjectId = fullyQualifiedId(subject);
881
- const space = getSpace2(subject);
983
+ const subjectId = fullyQualifiedId5(subject);
984
+ const space = getSpace3(subject);
882
985
  const intents = [];
883
986
  const analyticsProperties = {
884
987
  threadId: thread.id,
885
988
  spaceId: space?.id
886
989
  };
887
- if (state.staging[subjectId]?.find((t) => t === thread)) {
990
+ if (state.drafts[subjectId]?.find((t) => t === thread)) {
888
991
  thread.status = "active";
889
992
  subject.threads ? subject.threads.push(thread) : subject.threads = [
890
993
  thread
891
994
  ];
892
- state.staging[subjectId] = state.staging[subjectId]?.filter((t) => t.id !== thread.id);
995
+ state.drafts[subjectId] = state.drafts[subjectId]?.filter(({ id }) => id !== thread.id);
893
996
  intents.push({
894
997
  action: ObservabilityAction.SEND_EVENT,
895
998
  data: {
@@ -917,8 +1020,8 @@ var ThreadPlugin = () => {
917
1020
  }
918
1021
  case ThreadAction.DELETE_MESSAGE: {
919
1022
  const { subject, thread, messageId } = intent.data ?? {};
920
- const space = getSpace2(subject);
921
- if (!(subject instanceof DocumentType) || !(thread instanceof ThreadType) || !space) {
1023
+ const space = getSpace3(subject);
1024
+ if (!(thread instanceof ThreadType2) || !(subject && typeof subject === "object" && "threads" in subject)) {
922
1025
  return;
923
1026
  }
924
1027
  if (!intent.undo) {
@@ -960,7 +1063,7 @@ var ThreadPlugin = () => {
960
1063
  name: "threads.message.delete",
961
1064
  properties: {
962
1065
  threadId: thread.id,
963
- spaceId: space.id
1066
+ spaceId: space?.id
964
1067
  }
965
1068
  }
966
1069
  }
@@ -984,7 +1087,7 @@ var ThreadPlugin = () => {
984
1087
  name: "threads.message.undo-delete",
985
1088
  properties: {
986
1089
  threadId: thread.id,
987
- spaceId: space.id
1090
+ spaceId: space?.id
988
1091
  }
989
1092
  }
990
1093
  }
@@ -997,92 +1100,8 @@ var ThreadPlugin = () => {
997
1100
  }
998
1101
  },
999
1102
  markdown: {
1000
- // TODO(burdon): Factor out extension factory into separate file (for simplicity).
1001
1103
  extensions: ({ document: doc }) => {
1002
- const space = doc && getSpace2(doc);
1003
- if (!doc || !space) {
1004
- return [
1005
- comments()
1006
- ];
1007
- }
1008
- const threads = computed(() => [
1009
- ...doc.threads.filter(nonNullable2),
1010
- ...state.staging[fullyQualifiedId(doc)] ?? []
1011
- ].filter((thread) => !(thread?.status === "resolved")));
1012
- return [
1013
- listener2({
1014
- onChange: () => {
1015
- doc.threads.forEach((thread) => {
1016
- if (thread?.anchor) {
1017
- const [start, end] = thread.anchor.split(":");
1018
- const name = doc.content && getTextInRange(createDocAccessor(doc.content, [
1019
- "content"
1020
- ]), start, end);
1021
- if (name && name !== thread.name) {
1022
- thread.name = name;
1023
- }
1024
- }
1025
- });
1026
- }
1027
- }),
1028
- createExternalCommentSync(doc.id, (sink) => effect(() => sink()), () => threads.value.filter((thread) => thread?.anchor).map((thread) => ({
1029
- id: thread.id,
1030
- cursor: thread.anchor
1031
- }))),
1032
- comments({
1033
- id: doc.id,
1034
- onCreate: ({ cursor }) => {
1035
- const [start, end] = cursor.split(":");
1036
- const name = doc.content && getTextInRange(createDocAccessor(doc.content, [
1037
- "content"
1038
- ]), start, end);
1039
- void intentPlugin?.provides.intent.dispatch({
1040
- action: ThreadAction.CREATE,
1041
- data: {
1042
- cursor,
1043
- name,
1044
- subject: doc
1045
- }
1046
- });
1047
- },
1048
- onDelete: ({ id }) => {
1049
- const stagingArea = state.staging[fullyQualifiedId(doc)];
1050
- if (stagingArea) {
1051
- const index = stagingArea.findIndex((thread2) => thread2.id === id);
1052
- if (index !== -1) {
1053
- stagingArea.splice(index, 1);
1054
- }
1055
- }
1056
- const thread = doc.threads.find((thread2) => thread2?.id === id);
1057
- if (thread) {
1058
- thread.anchor = void 0;
1059
- }
1060
- },
1061
- onUpdate: ({ id, cursor }) => {
1062
- const thread = state.staging[fullyQualifiedId(doc)]?.find((thread2) => thread2.id === id) ?? doc.threads.find((thread2) => thread2?.id === id);
1063
- if (thread instanceof ThreadType && thread.anchor) {
1064
- const [start, end] = thread.anchor.split(":");
1065
- thread.name = doc.content && getTextInRange(createDocAccessor(doc.content, [
1066
- "content"
1067
- ]), start, end);
1068
- thread.anchor = cursor;
1069
- }
1070
- },
1071
- onSelect: ({ selection: { current, closest } }) => {
1072
- const dispatch2 = intentPlugin?.provides.intent.dispatch;
1073
- if (dispatch2) {
1074
- void dispatch2([
1075
- {
1076
- action: ThreadAction.SELECT,
1077
- data: {
1078
- current: current ?? closest
1079
- }
1080
- }
1081
- ]);
1082
- }
1083
- }
1084
- })
1085
- ];
1104
+ return threads(state, doc, intentPlugin?.provides.intent.dispatch);
1086
1105
  }
1087
1106
  }
1088
1107
  }