@dxos/plugin-thread 0.6.11 → 0.6.12-main.568932b

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