@dxos/plugin-thread 0.6.12-staging.e11e696 → 0.6.12

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 (81) hide show
  1. package/dist/lib/browser/{ThreadArticle-4VU752F4.mjs → ThreadArticle-O64ZECMF.mjs} +3 -3
  2. package/dist/lib/browser/{ThreadMain-ML3P4XSD.mjs → ThreadMain-ZDCRC6RG.mjs} +3 -3
  3. package/dist/lib/browser/{chunk-7NUYW5IW.mjs → chunk-5IBJO53M.mjs} +5 -2
  4. package/dist/lib/browser/chunk-5IBJO53M.mjs.map +7 -0
  5. package/dist/lib/browser/{chunk-GJRBBPXI.mjs → chunk-GKPZHUT3.mjs} +2 -2
  6. package/dist/lib/browser/{chunk-GJRBBPXI.mjs.map → chunk-GKPZHUT3.mjs.map} +1 -1
  7. package/dist/lib/browser/{chunk-XF5QKI75.mjs → chunk-P7ZE5N5M.mjs} +11 -13
  8. package/dist/lib/browser/chunk-P7ZE5N5M.mjs.map +7 -0
  9. package/dist/lib/browser/index.mjs +234 -233
  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-ERUHPHVT.cjs → ThreadArticle-DO56JXBU.cjs} +7 -7
  15. package/dist/lib/node/{ThreadMain-6DN7FDNB.cjs → ThreadMain-XJX75GHG.cjs} +7 -7
  16. package/dist/lib/node/{chunk-PZVUW4W2.cjs → chunk-5PZNPPQF.cjs} +63 -65
  17. package/dist/lib/node/chunk-5PZNPPQF.cjs.map +7 -0
  18. package/dist/lib/node/{chunk-6EVXCSDS.cjs → chunk-BWBONZGU.cjs} +18 -5
  19. package/dist/lib/node/chunk-BWBONZGU.cjs.map +7 -0
  20. package/dist/lib/node/{chunk-FXD3DYMO.cjs → chunk-D5GOVXHS.cjs} +6 -6
  21. package/dist/lib/node/{chunk-FXD3DYMO.cjs.map → chunk-D5GOVXHS.cjs.map} +1 -1
  22. package/dist/lib/node/index.cjs +316 -313
  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/types/src/ThreadPlugin.d.ts.map +1 -1
  30. package/dist/types/src/components/ChatContainer.d.ts.map +1 -1
  31. package/dist/types/src/components/CommentContainer.d.ts +1 -1
  32. package/dist/types/src/components/CommentContainer.d.ts.map +1 -1
  33. package/dist/types/src/components/CommentsContainer.d.ts +5 -1
  34. package/dist/types/src/components/CommentsContainer.d.ts.map +1 -1
  35. package/dist/types/src/components/MessageContainer.d.ts.map +1 -1
  36. package/dist/types/src/components/testing.d.ts +0 -2
  37. package/dist/types/src/components/testing.d.ts.map +1 -1
  38. package/dist/types/src/meta.d.ts +4 -1
  39. package/dist/types/src/meta.d.ts.map +1 -1
  40. package/dist/types/src/types.d.ts +1 -1
  41. package/dist/types/src/types.d.ts.map +1 -1
  42. package/dist/types/src/types.test.d.ts +2 -0
  43. package/dist/types/src/types.test.d.ts.map +1 -0
  44. package/package.json +37 -40
  45. package/src/ThreadPlugin.tsx +182 -70
  46. package/src/components/ChatContainer.tsx +8 -7
  47. package/src/components/CommentContainer.tsx +19 -18
  48. package/src/components/Comments.stories.tsx +2 -2
  49. package/src/components/CommentsContainer.tsx +31 -18
  50. package/src/components/MessageContainer.tsx +1 -2
  51. package/src/meta.tsx +5 -1
  52. package/src/types.test.ts +13 -0
  53. package/src/types.ts +1 -1
  54. package/dist/lib/browser/chunk-7NUYW5IW.mjs.map +0 -7
  55. package/dist/lib/browser/chunk-XF5QKI75.mjs.map +0 -7
  56. package/dist/lib/node/chunk-6EVXCSDS.cjs.map +0 -7
  57. package/dist/lib/node/chunk-PZVUW4W2.cjs.map +0 -7
  58. package/dist/lib/node-esm/ThreadArticle-NKI5F4W5.mjs +0 -19
  59. package/dist/lib/node-esm/ThreadArticle-NKI5F4W5.mjs.map +0 -7
  60. package/dist/lib/node-esm/ThreadMain-2A3VQLCW.mjs +0 -27
  61. package/dist/lib/node-esm/ThreadMain-2A3VQLCW.mjs.map +0 -7
  62. package/dist/lib/node-esm/chunk-A44C4MTQ.mjs +0 -413
  63. package/dist/lib/node-esm/chunk-A44C4MTQ.mjs.map +0 -7
  64. package/dist/lib/node-esm/chunk-Y475KZTX.mjs +0 -20
  65. package/dist/lib/node-esm/chunk-Y475KZTX.mjs.map +0 -7
  66. package/dist/lib/node-esm/chunk-YO46JGAE.mjs +0 -21
  67. package/dist/lib/node-esm/chunk-YO46JGAE.mjs.map +0 -7
  68. package/dist/lib/node-esm/index.mjs +0 -1102
  69. package/dist/lib/node-esm/index.mjs.map +0 -7
  70. package/dist/lib/node-esm/meta.json +0 -1
  71. package/dist/lib/node-esm/meta.mjs +0 -12
  72. package/dist/lib/node-esm/meta.mjs.map +0 -7
  73. package/dist/lib/node-esm/types.mjs +0 -9
  74. package/dist/lib/node-esm/types.mjs.map +0 -7
  75. package/dist/types/src/components/ThreadComplementary.d.ts +0 -10
  76. package/dist/types/src/components/ThreadComplementary.d.ts.map +0 -1
  77. package/src/components/ThreadComplementary.tsx +0 -113
  78. /package/dist/lib/browser/{ThreadArticle-4VU752F4.mjs.map → ThreadArticle-O64ZECMF.mjs.map} +0 -0
  79. /package/dist/lib/browser/{ThreadMain-ML3P4XSD.mjs.map → ThreadMain-ZDCRC6RG.mjs.map} +0 -0
  80. /package/dist/lib/node/{ThreadArticle-ERUHPHVT.cjs.map → ThreadArticle-DO56JXBU.cjs.map} +0 -0
  81. /package/dist/lib/node/{ThreadMain-6DN7FDNB.cjs.map → ThreadMain-XJX75GHG.cjs.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ThreadAction
3
- } from "./chunk-GJRBBPXI.mjs";
3
+ } from "./chunk-GKPZHUT3.mjs";
4
4
  import {
5
5
  ChatContainer,
6
6
  ChatHeading,
@@ -10,30 +10,34 @@ import {
10
10
  useAnalyticsCallback,
11
11
  useOnEditAnalytics,
12
12
  useStatus
13
- } from "./chunk-XF5QKI75.mjs";
13
+ } from "./chunk-P7ZE5N5M.mjs";
14
14
  import {
15
15
  THREAD_ITEM,
16
16
  THREAD_PLUGIN,
17
17
  meta_default
18
- } from "./chunk-7NUYW5IW.mjs";
18
+ } from "./chunk-5IBJO53M.mjs";
19
19
 
20
20
  // packages/plugins/plugin-thread/src/ThreadPlugin.tsx
21
+ import { Chat } from "@phosphor-icons/react";
21
22
  import { computed, effect } from "@preact/signals-core";
22
- import React6 from "react";
23
- import { LayoutAction as LayoutAction2, NavigationAction, parseIntentPlugin, parseNavigationPlugin, resolvePlugin, isLayoutParts, parseMetadataResolverPlugin } from "@dxos/app-framework";
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";
24
25
  import { getTypename } from "@dxos/echo-schema";
25
26
  import { create as create2 } from "@dxos/echo-schema";
26
27
  import { LocalStorageStore } from "@dxos/local-storage";
27
- import { log } from "@dxos/log";
28
+ import { parseAttentionPlugin } from "@dxos/plugin-attention";
28
29
  import { parseClientPlugin } from "@dxos/plugin-client";
29
- import { createExtension, isActionGroup, toSignal } from "@dxos/plugin-graph";
30
+ import { createExtension, isActionGroup } from "@dxos/plugin-graph";
31
+ import { DocumentType } from "@dxos/plugin-markdown/types";
30
32
  import { ObservabilityAction } from "@dxos/plugin-observability/meta";
31
33
  import { SpaceAction } from "@dxos/plugin-space";
32
34
  import { ThreadType, MessageType as MessageType2, ChannelType } from "@dxos/plugin-space/types";
33
- import { getSpace as getSpace2, getTextInRange, createDocAccessor, fullyQualifiedId as fullyQualifiedId4, loadObjectReferences } from "@dxos/react-client/echo";
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";
34
38
  import { comments, createExternalCommentSync, listener as listener2 } from "@dxos/react-ui-editor";
35
39
  import { translations as threadTranslations } from "@dxos/react-ui-thread";
36
- import { nonNullable as nonNullable3 } from "@dxos/util";
40
+ import { nonNullable as nonNullable2 } from "@dxos/util";
37
41
 
38
42
  // packages/plugins/plugin-thread/src/components/index.ts
39
43
  import React4 from "react";
@@ -54,10 +58,10 @@ var ThreadSettings = ({ settings }) => {
54
58
 
55
59
  // packages/plugins/plugin-thread/src/components/CommentContainer.tsx
56
60
  import { CheckCircle, X } from "@phosphor-icons/react";
57
- import React2, { useCallback, useMemo, useRef, useState } from "react";
61
+ import React2, { useCallback, useEffect, useMemo, useRef, useState } from "react";
58
62
  import { create } from "@dxos/echo-schema";
59
63
  import { MessageType } from "@dxos/plugin-space/types";
60
- import { fullyQualifiedId, getSpace, useMembers } from "@dxos/react-client/echo";
64
+ import { getSpace, useMembers } from "@dxos/react-client/echo";
61
65
  import { useIdentity } from "@dxos/react-client/halo";
62
66
  import { Button, Tag, Tooltip, useThemeContext, useTranslation as useTranslation2 } from "@dxos/react-ui";
63
67
  import { createBasicExtensions, createThemeExtensions, listener } from "@dxos/react-ui-editor";
@@ -110,15 +114,16 @@ var DeleteThreadButton = ({ onDelete }) => {
110
114
  classNames: "z-[21]"
111
115
  }, label, /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null))));
112
116
  };
113
- var CommentContainer = ({ thread, detached, context, current, onAttend, onThreadDelete, onMessageDelete, onResolve, onComment }) => {
117
+ var CommentContainer = ({ thread, detached, context, current, autoFocusTextbox, onAttend, onThreadDelete, onMessageDelete, onResolve, onComment }) => {
114
118
  const identity = useIdentity();
115
119
  const space = getSpace(thread);
116
120
  const members = useMembers(space?.key);
117
- const activity = useStatus(space, fullyQualifiedId(thread));
121
+ const activity = useStatus(space, thread.id);
118
122
  const { t } = useTranslation2(THREAD_PLUGIN);
119
123
  const threadScrollRef = useRef(null);
124
+ const [autoFocus, setAutoFocus] = useState(!!autoFocusTextbox);
120
125
  const { themeMode } = useThemeContext();
121
- const textboxMetadata = getMessageMetadata(fullyQualifiedId(thread), identity);
126
+ const textboxMetadata = getMessageMetadata(thread.id, identity);
122
127
  const [_count, _setCount] = useState(0);
123
128
  const rerenderEditor = () => _setCount((count) => count + 1);
124
129
  const messageRef = useRef("");
@@ -136,6 +141,11 @@ var CommentContainer = ({ thread, detached, context, current, onAttend, onThread
136
141
  ], [
137
142
  _count
138
143
  ]);
144
+ useEffect(() => {
145
+ setAutoFocus(!!autoFocusTextbox);
146
+ }, [
147
+ autoFocusTextbox
148
+ ]);
139
149
  const scrollToEnd = (behavior) => setTimeout(() => threadScrollRef.current?.scrollIntoView({
140
150
  behavior,
141
151
  block: "end"
@@ -154,6 +164,7 @@ var CommentContainer = ({ thread, detached, context, current, onAttend, onThread
154
164
  }));
155
165
  onComment?.(thread);
156
166
  messageRef.current = "";
167
+ setAutoFocus(true);
157
168
  scrollToEnd("instant");
158
169
  rerenderEditor();
159
170
  return true;
@@ -161,11 +172,12 @@ var CommentContainer = ({ thread, detached, context, current, onAttend, onThread
161
172
  thread,
162
173
  identity
163
174
  ]);
175
+ const handleDeleteMessage = (id) => onMessageDelete?.(id);
164
176
  return /* @__PURE__ */ React2.createElement(Thread, {
165
177
  onClickCapture: onAttend,
166
178
  onFocusCapture: onAttend,
167
179
  current,
168
- id: fullyQualifiedId(thread)
180
+ id: thread.id
169
181
  }, /* @__PURE__ */ React2.createElement("div", {
170
182
  role: "none",
171
183
  className: mx("col-span-2 grid grid-cols-[var(--rail-size)_1fr_min-content]", hoverableControls, hoverableFocusedWithinControls)
@@ -189,9 +201,10 @@ var CommentContainer = ({ thread, detached, context, current, onAttend, onThread
189
201
  key: message.id,
190
202
  message,
191
203
  members,
192
- onDelete: (id) => onMessageDelete?.(id)
204
+ onDelete: handleDeleteMessage
193
205
  })), /* @__PURE__ */ React2.createElement(MessageTextbox, {
194
206
  extensions,
207
+ autoFocus,
195
208
  onSend: handleCreate,
196
209
  ...textboxMetadata
197
210
  }), /* @__PURE__ */ React2.createElement(ThreadFooter, {
@@ -204,15 +217,26 @@ var CommentContainer = ({ thread, detached, context, current, onAttend, onThread
204
217
  };
205
218
 
206
219
  // packages/plugins/plugin-thread/src/components/CommentsContainer.tsx
207
- import { ChatText } from "@phosphor-icons/react";
208
- import React3, { useEffect } from "react";
209
- import { fullyQualifiedId as fullyQualifiedId2 } from "@dxos/react-client/echo";
220
+ import { ChatText, Quotes } from "@phosphor-icons/react";
221
+ import React3, { useEffect as useEffect2 } from "react";
210
222
  import { useTranslation as useTranslation3, Trans } from "@dxos/react-ui";
223
+ import { PlankHeading, plankHeadingIconProps } from "@dxos/react-ui-deck";
211
224
  import { descriptionText, mx as mx2 } from "@dxos/react-ui-theme";
212
- var CommentsContainer = ({ threads, detached = [], currentId, showResolvedThreads, onThreadAttend, onThreadDelete, onMessageDelete, onThreadToggleResolved, ...props }) => {
225
+ var CommentsHeading = ({ attendableId }) => {
226
+ 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 }) => {
213
237
  const { t } = useTranslation3(THREAD_PLUGIN);
214
238
  const filteredThreads = showResolvedThreads ? threads : threads.filter((thread) => !(thread?.status === "resolved"));
215
- useEffect(() => {
239
+ useEffect2(() => {
216
240
  if (currentId) {
217
241
  document.getElementById(currentId)?.scrollIntoView({
218
242
  behavior: "smooth",
@@ -222,28 +246,26 @@ var CommentsContainer = ({ threads, detached = [], currentId, showResolvedThread
222
246
  }, [
223
247
  currentId
224
248
  ]);
225
- return /* @__PURE__ */ React3.createElement(React3.Fragment, null, filteredThreads.length > 0 ? filteredThreads.map((thread) => {
226
- const threadId = fullyQualifiedId2(thread);
227
- return /* @__PURE__ */ React3.createElement(CommentContainer, {
228
- key: threadId,
229
- thread,
230
- current: currentId === threadId,
231
- detached: detached.includes(threadId),
232
- ...onThreadAttend && {
233
- onAttend: () => onThreadAttend(thread)
234
- },
235
- ...onThreadDelete && {
236
- onThreadDelete: () => onThreadDelete(thread)
237
- },
238
- ...onMessageDelete && {
239
- onMessageDelete: (messageId) => onMessageDelete(thread, messageId)
240
- },
241
- ...onThreadToggleResolved && {
242
- onResolve: () => onThreadToggleResolved(thread)
243
- },
244
- ...props
245
- });
246
- }) : /* @__PURE__ */ React3.createElement("div", {
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", {
247
269
  role: "alert",
248
270
  className: mx2(descriptionText, "place-self-center border border-dashed border-neutral-400/50 rounded-lg text-center p-4 m-4")
249
271
  }, /* @__PURE__ */ React3.createElement("h2", {
@@ -260,119 +282,8 @@ var CommentsContainer = ({ threads, detached = [], currentId, showResolvedThread
260
282
  };
261
283
 
262
284
  // packages/plugins/plugin-thread/src/components/index.ts
263
- var ThreadMain = React4.lazy(() => import("./ThreadMain-ML3P4XSD.mjs"));
264
- var ThreadArticle = React4.lazy(() => import("./ThreadArticle-4VU752F4.mjs"));
265
-
266
- // packages/plugins/plugin-thread/src/components/ThreadComplementary.tsx
267
- import React5, { useEffect as useEffect2, useMemo as useMemo2 } from "react";
268
- import { LayoutAction, useIntentDispatcher, useResolvePlugins } from "@dxos/app-framework";
269
- import { fullyQualifiedId as fullyQualifiedId3 } from "@dxos/react-client/echo";
270
- import { ScrollArea } from "@dxos/react-ui";
271
- import { useAttended } from "@dxos/react-ui-attention";
272
- import { nonNullable as nonNullable2 } from "@dxos/util";
273
- var providesThreadsConfig = (plugin) => "thread" in plugin.provides ? plugin : void 0;
274
- var ThreadComplementary = ({ role, subject, stagedThreads, current, showResolvedThreads }) => {
275
- const dispatch = useIntentDispatcher();
276
- const threadsIntegrators = useResolvePlugins(providesThreadsConfig);
277
- const threadProvides = useMemo2(() => threadsIntegrators.map((p) => p.provides.thread), [
278
- threadsIntegrators
279
- ]);
280
- const createSort = useMemo2(() => threadProvides.find((p) => p.predicate(subject))?.createSort, [
281
- threadProvides,
282
- subject
283
- ]);
284
- const sort = useMemo2(() => createSort?.(subject), [
285
- createSort,
286
- subject
287
- ]);
288
- const threads = useMemo2(() => {
289
- return subject.threads.concat(stagedThreads ?? []).filter(nonNullable2);
290
- }, [
291
- JSON.stringify(subject.threads),
292
- JSON.stringify(stagedThreads)
293
- ]);
294
- const detachedIds = useMemo2(() => {
295
- return threads.filter(({ anchor }) => !anchor).map((thread) => fullyQualifiedId3(thread));
296
- }, [
297
- threads
298
- ]);
299
- useEffect2(() => {
300
- if (!sort) {
301
- return;
302
- }
303
- threads.sort((a, b) => sort(a?.anchor, b?.anchor));
304
- }, [
305
- sort,
306
- threads
307
- ]);
308
- const attended = useAttended();
309
- const qualifiedSubjectId = fullyQualifiedId3(subject);
310
- return /* @__PURE__ */ React5.createElement("div", {
311
- role: "none",
312
- className: "contents"
313
- }, /* @__PURE__ */ React5.createElement(ScrollArea.Root, {
314
- classNames: "row-span-2"
315
- }, /* @__PURE__ */ React5.createElement(ScrollArea.Viewport, null, /* @__PURE__ */ React5.createElement(CommentsContainer, {
316
- threads,
317
- detached: detachedIds,
318
- currentId: attended.includes(qualifiedSubjectId) ? current : void 0,
319
- showResolvedThreads,
320
- onThreadAttend: (thread) => {
321
- const threadId = fullyQualifiedId3(thread);
322
- if (current !== threadId) {
323
- current = threadId;
324
- void dispatch?.([
325
- {
326
- action: LayoutAction.SCROLL_INTO_VIEW,
327
- data: {
328
- id: fullyQualifiedId3(subject),
329
- thread: threadId,
330
- cursor: thread.anchor
331
- }
332
- }
333
- ]);
334
- }
335
- },
336
- onThreadDelete: (thread) => {
337
- return dispatch({
338
- plugin: THREAD_PLUGIN,
339
- action: ThreadAction.DELETE,
340
- data: {
341
- subject,
342
- thread
343
- }
344
- });
345
- },
346
- onMessageDelete: (thread, messageId) => dispatch({
347
- plugin: THREAD_PLUGIN,
348
- action: ThreadAction.DELETE_MESSAGE,
349
- data: {
350
- subject,
351
- thread,
352
- messageId
353
- }
354
- }),
355
- onThreadToggleResolved: (thread) => dispatch({
356
- plugin: THREAD_PLUGIN,
357
- action: ThreadAction.TOGGLE_RESOLVED,
358
- data: {
359
- thread
360
- }
361
- }),
362
- onComment: (thread) => {
363
- void dispatch({
364
- action: ThreadAction.ON_MESSAGE_ADD,
365
- data: {
366
- thread,
367
- subject
368
- }
369
- });
370
- }
371
- }), /* @__PURE__ */ React5.createElement("div", {
372
- role: "none",
373
- className: "bs-10"
374
- }), /* @__PURE__ */ React5.createElement(ScrollArea.Scrollbar, null, /* @__PURE__ */ React5.createElement(ScrollArea.Thumb, null)))));
375
- };
285
+ var ThreadMain = React4.lazy(() => import("./ThreadMain-ZDCRC6RG.mjs"));
286
+ var ThreadArticle = React4.lazy(() => import("./ThreadArticle-O64ZECMF.mjs"));
376
287
 
377
288
  // packages/plugins/plugin-thread/src/translations.ts
378
289
  var translations_default = [
@@ -411,10 +322,10 @@ var translations_default = [
411
322
  ];
412
323
 
413
324
  // packages/plugins/plugin-thread/src/ThreadPlugin.tsx
414
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/plugins/plugin-thread/src/ThreadPlugin.tsx";
415
325
  var initialViewState = {
416
326
  showResolvedThreads: false
417
327
  };
328
+ var providesThreadsConfig = (plugin) => "thread" in plugin.provides ? plugin : void 0;
418
329
  var ThreadPlugin = () => {
419
330
  const settings = new LocalStorageStore(THREAD_PLUGIN);
420
331
  const state = create2({
@@ -429,8 +340,10 @@ var ThreadPlugin = () => {
429
340
  }
430
341
  return viewStore[subjectId];
431
342
  };
343
+ let attentionPlugin;
432
344
  let navigationPlugin;
433
345
  let intentPlugin;
346
+ let dispatch;
434
347
  const unsubscribeCallbacks = [];
435
348
  return {
436
349
  meta: meta_default,
@@ -441,8 +354,36 @@ var ThreadPlugin = () => {
441
354
  allowUndefined: true
442
355
  })
443
356
  });
357
+ attentionPlugin = resolvePlugin(plugins, parseAttentionPlugin);
444
358
  navigationPlugin = resolvePlugin(plugins, parseNavigationPlugin);
445
359
  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);
446
387
  },
447
388
  unload: async () => {
448
389
  unsubscribeCallbacks.forEach((unsubscribe) => unsubscribe());
@@ -458,7 +399,8 @@ var ThreadPlugin = () => {
458
399
  ns: THREAD_PLUGIN
459
400
  }
460
401
  ],
461
- icon: "ph--chat--regular",
402
+ icon: (props) => /* @__PURE__ */ React5.createElement(Chat, props),
403
+ iconSymbol: "ph--chat--regular",
462
404
  // TODO(wittjosiah): Move out of metadata.
463
405
  loadReferences: (channel) => loadObjectReferences(channel, (channel2) => channel2.threads)
464
406
  },
@@ -505,9 +447,9 @@ var ThreadPlugin = () => {
505
447
  graph: {
506
448
  builder: (plugins) => {
507
449
  const client = resolvePlugin(plugins, parseClientPlugin)?.provides.client;
508
- const dispatch = resolvePlugin(plugins, parseIntentPlugin)?.provides.intent.dispatch;
450
+ const dispatch2 = resolvePlugin(plugins, parseIntentPlugin)?.provides.intent.dispatch;
509
451
  const metadataResolver = resolvePlugin(plugins, parseMetadataResolverPlugin)?.provides.metadata.resolver;
510
- if (!client || !dispatch || !metadataResolver) {
452
+ if (!client || !dispatch2 || !metadataResolver) {
511
453
  return [];
512
454
  }
513
455
  return [
@@ -517,36 +459,29 @@ var ThreadPlugin = () => {
517
459
  if (!id.endsWith("~comments")) {
518
460
  return;
519
461
  }
520
- const subjectId = id.split("~").at(0);
521
- const [spaceId, objectId] = subjectId?.split(":") ?? [];
462
+ const docId = id.split("~").at(0);
463
+ const [spaceId, objectId] = docId?.split(":") ?? [];
522
464
  const space = client.spaces.get().find((space2) => space2.id === spaceId);
523
- const object = toSignal((onChange) => {
524
- const timeout = setTimeout(async () => {
525
- await space?.db.loadObjectById(objectId);
526
- onChange();
527
- });
528
- return () => clearTimeout(timeout);
529
- }, () => space?.db.getObjectById(objectId), subjectId);
530
- if (!object || !subjectId) {
465
+ const doc = space?.db.getObjectById(objectId);
466
+ if (!doc || !docId) {
531
467
  return;
532
468
  }
533
- const meta = metadataResolver(getTypename(object) ?? "");
534
- const label = meta.label?.(object) || object.name || meta.placeholder || [
469
+ const docMeta = metadataResolver(getTypename(doc) ?? "");
470
+ const label = docMeta.label?.(doc) || doc.name || docMeta.placeholder || [
535
471
  "unnamed object threads label",
536
472
  {
537
473
  ns: THREAD_PLUGIN
538
474
  }
539
475
  ];
540
- const viewState = getViewState(subjectId);
476
+ const viewState = getViewState(docId);
541
477
  return {
542
478
  id,
543
479
  type: "orphan-comments-for-subject",
544
- data: null,
480
+ data: doc,
545
481
  properties: {
546
- icon: "ph--chat-text--regular",
482
+ icon: meta_default.iconComponent,
547
483
  label,
548
- showResolvedThreads: viewState.showResolvedThreads,
549
- object
484
+ showResolvedThreads: viewState.showResolvedThreads
550
485
  }
551
486
  };
552
487
  },
@@ -560,7 +495,7 @@ var ThreadPlugin = () => {
560
495
  const toggle = () => {
561
496
  const newToggleState = !viewState.showResolvedThreads;
562
497
  viewState.showResolvedThreads = newToggleState;
563
- void dispatch({
498
+ void dispatch2({
564
499
  action: ObservabilityAction.SEND_EVENT,
565
500
  data: {
566
501
  name: "threads.toggle-show-resolved",
@@ -585,8 +520,7 @@ var ThreadPlugin = () => {
585
520
  ],
586
521
  menuItemType: "toggle",
587
522
  isChecked: viewState.showResolvedThreads,
588
- testId: "threadPlugin.toggleShowResolved",
589
- icon: viewState.showResolvedThreads ? "ph--eye-slash--regular" : "ph--eye--regular"
523
+ testId: "threadPlugin.toggleShowResolved"
590
524
  }
591
525
  }
592
526
  ];
@@ -608,7 +542,7 @@ var ThreadPlugin = () => {
608
542
  {
609
543
  id: `${THREAD_PLUGIN}/create/${node.id}`,
610
544
  data: async () => {
611
- await dispatch([
545
+ await dispatch2([
612
546
  {
613
547
  plugin: THREAD_PLUGIN,
614
548
  action: ThreadAction.CREATE
@@ -631,7 +565,8 @@ var ThreadPlugin = () => {
631
565
  ns: THREAD_PLUGIN
632
566
  }
633
567
  ],
634
- icon: "ph--chat--regular",
568
+ icon: (props) => /* @__PURE__ */ React5.createElement(Chat, props),
569
+ iconSymbol: "ph--chat--regular",
635
570
  testId: "threadPlugin.createObject"
636
571
  }
637
572
  }
@@ -645,18 +580,20 @@ var ThreadPlugin = () => {
645
580
  component: ({ data, role }) => {
646
581
  switch (role) {
647
582
  case "main": {
648
- return data.active instanceof ChannelType && data.active.threads[0] ? /* @__PURE__ */ React6.createElement(ThreadMain, {
583
+ return data.active instanceof ChannelType && data.active.threads[0] ? /* @__PURE__ */ React5.createElement(ThreadMain, {
649
584
  thread: data.active.threads[0]
650
585
  }) : null;
651
586
  }
652
587
  case "settings": {
653
- return data.plugin === meta_default.id ? /* @__PURE__ */ React6.createElement(ThreadSettings, {
588
+ return data.plugin === meta_default.id ? /* @__PURE__ */ React5.createElement(ThreadSettings, {
654
589
  settings: settings.values
655
590
  }) : null;
656
591
  }
657
592
  case "article":
658
593
  case "complementary": {
659
594
  const location = navigationPlugin?.provides.location;
595
+ const threadsIntegrators = useResolvePlugins(providesThreadsConfig);
596
+ const threadProvides = threadsIntegrators.map((p) => p.provides.thread);
660
597
  if (data.object instanceof ChannelType && data.object.threads[0]) {
661
598
  const channel = data.object;
662
599
  if (isLayoutParts(location?.active) && location.active.main) {
@@ -665,32 +602,101 @@ var ThreadPlugin = () => {
665
602
  if (currentPosition > 0) {
666
603
  const objectToTheLeft = layoutEntries[currentPosition - 1];
667
604
  const context = getSpace2(data.object)?.db.getObjectById(objectToTheLeft.id);
668
- return /* @__PURE__ */ React6.createElement(ThreadArticle, {
605
+ return /* @__PURE__ */ React5.createElement(ThreadArticle, {
669
606
  thread: data.object.threads[0],
670
607
  context
671
608
  });
672
609
  }
673
610
  }
674
- return /* @__PURE__ */ React6.createElement(ThreadArticle, {
611
+ return /* @__PURE__ */ React5.createElement(ThreadArticle, {
675
612
  thread: data.object.threads[0]
676
613
  });
677
614
  }
678
615
  if (data.subject instanceof ThreadType) {
679
- return /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(ChatHeading, {
616
+ return /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(ChatHeading, {
680
617
  attendableId: data.subject.id
681
- }), /* @__PURE__ */ React6.createElement(ChatContainer, {
618
+ }), /* @__PURE__ */ React5.createElement(ChatContainer, {
682
619
  thread: data.subject
683
620
  }));
684
621
  }
685
622
  if (data.subject && typeof data.subject === "object" && "threads" in data.subject && Array.isArray(data.subject.threads)) {
686
- const { showResolvedThreads } = getViewState(fullyQualifiedId4(data.subject));
687
- return /* @__PURE__ */ React6.createElement(ThreadComplementary, {
688
- role,
689
- subject: data.subject,
690
- stagedThreads: state.staging[fullyQualifiedId4(data.subject)],
691
- current: state.current,
692
- showResolvedThreads
693
- });
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)))));
694
700
  }
695
701
  }
696
702
  }
@@ -703,20 +709,7 @@ var ThreadPlugin = () => {
703
709
  case ThreadAction.CREATE: {
704
710
  if (intent.data && intent.data.cursor !== void 0) {
705
711
  const { cursor, name, subject } = intent.data;
706
- if (subject?.threads === void 0) {
707
- try {
708
- subject.threads = [];
709
- } catch (e) {
710
- log.error("Subject does not support threads array", subject?.typename, {
711
- F: __dxlog_file,
712
- L: 331,
713
- S: void 0,
714
- C: (f, a) => f(...a)
715
- });
716
- return;
717
- }
718
- }
719
- const subjectId = fullyQualifiedId4(subject);
712
+ const subjectId = fullyQualifiedId(subject);
720
713
  const thread = create2(ThreadType, {
721
714
  name,
722
715
  anchor: cursor,
@@ -735,14 +728,23 @@ var ThreadPlugin = () => {
735
728
  data: thread,
736
729
  intents: [
737
730
  [
731
+ {
732
+ action: NavigationAction.OPEN,
733
+ data: {
734
+ activeParts: {
735
+ complementary: `${subjectId}${SLUG_PATH_SEPARATOR}comments${SLUG_COLLECTION_INDICATOR}`
736
+ }
737
+ }
738
+ },
738
739
  {
739
740
  action: ThreadAction.SELECT,
740
741
  data: {
741
- current: fullyQualifiedId4(thread)
742
+ current: thread.id,
743
+ focus: true
742
744
  }
743
745
  },
744
746
  {
745
- action: LayoutAction2.SET_LAYOUT,
747
+ action: LayoutAction.SET_LAYOUT,
746
748
  data: {
747
749
  element: "complementary",
748
750
  subject: subjectId,
@@ -765,6 +767,7 @@ var ThreadPlugin = () => {
765
767
  }
766
768
  }
767
769
  case ThreadAction.SELECT: {
770
+ state.focus = intent.data?.current === state.current ? state.focus : intent.data?.focus;
768
771
  state.current = intent.data?.current;
769
772
  return {
770
773
  data: true
@@ -801,18 +804,16 @@ var ThreadPlugin = () => {
801
804
  }
802
805
  case ThreadAction.DELETE: {
803
806
  const { subject, thread } = intent.data ?? {};
804
- if (!(thread instanceof ThreadType) || !(subject && typeof subject === "object" && "threads" in subject)) {
807
+ if (!(subject instanceof DocumentType) || !(thread instanceof ThreadType)) {
805
808
  return;
806
809
  }
807
- const subjectId = fullyQualifiedId4(subject);
810
+ const subjectId = fullyQualifiedId(subject);
808
811
  const stagingArea = state.staging[subjectId];
809
812
  if (stagingArea) {
810
813
  const index = stagingArea.findIndex((t) => t.id === thread.id);
811
814
  if (index !== -1) {
812
815
  stagingArea.splice(index, 1);
813
- return {
814
- data: true
815
- };
816
+ return;
816
817
  }
817
818
  }
818
819
  const space = getSpace2(thread);
@@ -873,10 +874,10 @@ var ThreadPlugin = () => {
873
874
  }
874
875
  case ThreadAction.ON_MESSAGE_ADD: {
875
876
  const { thread, subject } = intent.data ?? {};
876
- if (!(thread instanceof ThreadType) || !(subject && typeof subject === "object" && "threads" in subject && Array.isArray(subject.threads))) {
877
+ if (!(thread instanceof ThreadType) || !(subject && typeof subject === "object" && "threads" in subject)) {
877
878
  return;
878
879
  }
879
- const subjectId = fullyQualifiedId4(subject);
880
+ const subjectId = fullyQualifiedId(subject);
880
881
  const space = getSpace2(subject);
881
882
  const intents = [];
882
883
  const analyticsProperties = {
@@ -917,7 +918,7 @@ var ThreadPlugin = () => {
917
918
  case ThreadAction.DELETE_MESSAGE: {
918
919
  const { subject, thread, messageId } = intent.data ?? {};
919
920
  const space = getSpace2(subject);
920
- if (!(thread instanceof ThreadType) || !(subject && typeof subject === "object" && "threads" in subject)) {
921
+ if (!(subject instanceof DocumentType) || !(thread instanceof ThreadType) || !space) {
921
922
  return;
922
923
  }
923
924
  if (!intent.undo) {
@@ -959,7 +960,7 @@ var ThreadPlugin = () => {
959
960
  name: "threads.message.delete",
960
961
  properties: {
961
962
  threadId: thread.id,
962
- spaceId: space?.id
963
+ spaceId: space.id
963
964
  }
964
965
  }
965
966
  }
@@ -983,7 +984,7 @@ var ThreadPlugin = () => {
983
984
  name: "threads.message.undo-delete",
984
985
  properties: {
985
986
  threadId: thread.id,
986
- spaceId: space?.id
987
+ spaceId: space.id
987
988
  }
988
989
  }
989
990
  }
@@ -1005,8 +1006,8 @@ var ThreadPlugin = () => {
1005
1006
  ];
1006
1007
  }
1007
1008
  const threads = computed(() => [
1008
- ...doc.threads.filter(nonNullable3),
1009
- ...state.staging[fullyQualifiedId4(doc)] ?? []
1009
+ ...doc.threads.filter(nonNullable2),
1010
+ ...state.staging[fullyQualifiedId(doc)] ?? []
1010
1011
  ].filter((thread) => !(thread?.status === "resolved")));
1011
1012
  return [
1012
1013
  listener2({
@@ -1024,12 +1025,12 @@ var ThreadPlugin = () => {
1024
1025
  });
1025
1026
  }
1026
1027
  }),
1027
- createExternalCommentSync(fullyQualifiedId4(doc), (sink) => effect(() => sink()), () => threads.value.filter((thread) => thread?.anchor).map((thread) => ({
1028
- id: fullyQualifiedId4(thread),
1028
+ createExternalCommentSync(doc.id, (sink) => effect(() => sink()), () => threads.value.filter((thread) => thread?.anchor).map((thread) => ({
1029
+ id: thread.id,
1029
1030
  cursor: thread.anchor
1030
1031
  }))),
1031
1032
  comments({
1032
- id: fullyQualifiedId4(doc),
1033
+ id: doc.id,
1033
1034
  onCreate: ({ cursor }) => {
1034
1035
  const [start, end] = cursor.split(":");
1035
1036
  const name = doc.content && getTextInRange(createDocAccessor(doc.content, [
@@ -1045,9 +1046,9 @@ var ThreadPlugin = () => {
1045
1046
  });
1046
1047
  },
1047
1048
  onDelete: ({ id }) => {
1048
- const stagingArea = state.staging[fullyQualifiedId4(doc)];
1049
+ const stagingArea = state.staging[fullyQualifiedId(doc)];
1049
1050
  if (stagingArea) {
1050
- const index = stagingArea.findIndex((thread2) => fullyQualifiedId4(thread2) === id);
1051
+ const index = stagingArea.findIndex((thread2) => thread2.id === id);
1051
1052
  if (index !== -1) {
1052
1053
  stagingArea.splice(index, 1);
1053
1054
  }
@@ -1058,7 +1059,7 @@ var ThreadPlugin = () => {
1058
1059
  }
1059
1060
  },
1060
1061
  onUpdate: ({ id, cursor }) => {
1061
- const thread = state.staging[fullyQualifiedId4(doc)]?.find((thread2) => fullyQualifiedId4(thread2) === id) ?? doc.threads.find((thread2) => thread2?.id === id);
1062
+ const thread = state.staging[fullyQualifiedId(doc)]?.find((thread2) => thread2.id === id) ?? doc.threads.find((thread2) => thread2?.id === id);
1062
1063
  if (thread instanceof ThreadType && thread.anchor) {
1063
1064
  const [start, end] = thread.anchor.split(":");
1064
1065
  thread.name = doc.content && getTextInRange(createDocAccessor(doc.content, [
@@ -1068,9 +1069,9 @@ var ThreadPlugin = () => {
1068
1069
  }
1069
1070
  },
1070
1071
  onSelect: ({ selection: { current, closest } }) => {
1071
- const dispatch = intentPlugin?.provides.intent.dispatch;
1072
- if (dispatch) {
1073
- void dispatch([
1072
+ const dispatch2 = intentPlugin?.provides.intent.dispatch;
1073
+ if (dispatch2) {
1074
+ void dispatch2([
1074
1075
  {
1075
1076
  action: ThreadAction.SELECT,
1076
1077
  data: {