@dxos/plugin-thread 0.6.8-main.046e6cf

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 (106) hide show
  1. package/LICENSE +8 -0
  2. package/README.md +15 -0
  3. package/dist/lib/browser/ThreadArticle-J4VCLPMS.mjs +18 -0
  4. package/dist/lib/browser/ThreadArticle-J4VCLPMS.mjs.map +7 -0
  5. package/dist/lib/browser/ThreadMain-VV7MGQZJ.mjs +26 -0
  6. package/dist/lib/browser/ThreadMain-VV7MGQZJ.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-5IBJO53M.mjs +21 -0
  8. package/dist/lib/browser/chunk-5IBJO53M.mjs.map +7 -0
  9. package/dist/lib/browser/chunk-CQ2RYETH.mjs +410 -0
  10. package/dist/lib/browser/chunk-CQ2RYETH.mjs.map +7 -0
  11. package/dist/lib/browser/chunk-TL3JITN7.mjs +19 -0
  12. package/dist/lib/browser/chunk-TL3JITN7.mjs.map +7 -0
  13. package/dist/lib/browser/index.mjs +1062 -0
  14. package/dist/lib/browser/index.mjs.map +7 -0
  15. package/dist/lib/browser/meta.json +1 -0
  16. package/dist/lib/browser/meta.mjs +11 -0
  17. package/dist/lib/browser/meta.mjs.map +7 -0
  18. package/dist/lib/browser/types.mjs +8 -0
  19. package/dist/lib/browser/types.mjs.map +7 -0
  20. package/dist/lib/node/ThreadArticle-CSENFNNT.cjs +44 -0
  21. package/dist/lib/node/ThreadArticle-CSENFNNT.cjs.map +7 -0
  22. package/dist/lib/node/ThreadMain-XP4YSF7U.cjs +52 -0
  23. package/dist/lib/node/ThreadMain-XP4YSF7U.cjs.map +7 -0
  24. package/dist/lib/node/chunk-3VAAUEU5.cjs +431 -0
  25. package/dist/lib/node/chunk-3VAAUEU5.cjs.map +7 -0
  26. package/dist/lib/node/chunk-BWBONZGU.cjs +55 -0
  27. package/dist/lib/node/chunk-BWBONZGU.cjs.map +7 -0
  28. package/dist/lib/node/chunk-WPPLQNMJ.cjs +38 -0
  29. package/dist/lib/node/chunk-WPPLQNMJ.cjs.map +7 -0
  30. package/dist/lib/node/index.cjs +1067 -0
  31. package/dist/lib/node/index.cjs.map +7 -0
  32. package/dist/lib/node/meta.cjs +32 -0
  33. package/dist/lib/node/meta.cjs.map +7 -0
  34. package/dist/lib/node/meta.json +1 -0
  35. package/dist/lib/node/types.cjs +30 -0
  36. package/dist/lib/node/types.cjs.map +7 -0
  37. package/dist/types/src/ThreadPlugin.d.ts +4 -0
  38. package/dist/types/src/ThreadPlugin.d.ts.map +1 -0
  39. package/dist/types/src/components/Chat.stories.d.ts +46 -0
  40. package/dist/types/src/components/Chat.stories.d.ts.map +1 -0
  41. package/dist/types/src/components/ChatContainer.d.ts +7 -0
  42. package/dist/types/src/components/ChatContainer.d.ts.map +1 -0
  43. package/dist/types/src/components/CommentContainer.d.ts +4 -0
  44. package/dist/types/src/components/CommentContainer.d.ts.map +1 -0
  45. package/dist/types/src/components/Comments.stories.d.ts +44 -0
  46. package/dist/types/src/components/Comments.stories.d.ts.map +1 -0
  47. package/dist/types/src/components/CommentsContainer.d.ts +26 -0
  48. package/dist/types/src/components/CommentsContainer.d.ts.map +1 -0
  49. package/dist/types/src/components/MessageContainer.d.ts +9 -0
  50. package/dist/types/src/components/MessageContainer.d.ts.map +1 -0
  51. package/dist/types/src/components/ThreadArticle.d.ts +5 -0
  52. package/dist/types/src/components/ThreadArticle.d.ts.map +1 -0
  53. package/dist/types/src/components/ThreadMain.d.ts +7 -0
  54. package/dist/types/src/components/ThreadMain.d.ts.map +1 -0
  55. package/dist/types/src/components/ThreadSettings.d.ts +6 -0
  56. package/dist/types/src/components/ThreadSettings.d.ts.map +1 -0
  57. package/dist/types/src/components/command-extension.d.ts +3 -0
  58. package/dist/types/src/components/command-extension.d.ts.map +1 -0
  59. package/dist/types/src/components/index.d.ts +22 -0
  60. package/dist/types/src/components/index.d.ts.map +1 -0
  61. package/dist/types/src/components/testing.d.ts +67 -0
  62. package/dist/types/src/components/testing.d.ts.map +1 -0
  63. package/dist/types/src/components/types.d.ts +28 -0
  64. package/dist/types/src/components/types.d.ts.map +1 -0
  65. package/dist/types/src/hooks/analytics.d.ts +4 -0
  66. package/dist/types/src/hooks/analytics.d.ts.map +1 -0
  67. package/dist/types/src/hooks/index.d.ts +3 -0
  68. package/dist/types/src/hooks/index.d.ts.map +1 -0
  69. package/dist/types/src/hooks/useStatus.d.ts +3 -0
  70. package/dist/types/src/hooks/useStatus.d.ts.map +1 -0
  71. package/dist/types/src/index.d.ts +6 -0
  72. package/dist/types/src/index.d.ts.map +1 -0
  73. package/dist/types/src/meta.d.ts +16 -0
  74. package/dist/types/src/meta.d.ts.map +1 -0
  75. package/dist/types/src/translations.d.ts +34 -0
  76. package/dist/types/src/translations.d.ts.map +1 -0
  77. package/dist/types/src/types.d.ts +19 -0
  78. package/dist/types/src/types.d.ts.map +1 -0
  79. package/dist/types/src/types.test.d.ts +2 -0
  80. package/dist/types/src/types.test.d.ts.map +1 -0
  81. package/dist/types/src/util.d.ts +6 -0
  82. package/dist/types/src/util.d.ts.map +1 -0
  83. package/package.json +99 -0
  84. package/src/ThreadPlugin.tsx +744 -0
  85. package/src/components/Chat.stories.tsx +76 -0
  86. package/src/components/ChatContainer.tsx +127 -0
  87. package/src/components/CommentContainer.tsx +206 -0
  88. package/src/components/Comments.stories.tsx +66 -0
  89. package/src/components/CommentsContainer.tsx +114 -0
  90. package/src/components/MessageContainer.tsx +200 -0
  91. package/src/components/ThreadArticle.tsx +18 -0
  92. package/src/components/ThreadMain.tsx +27 -0
  93. package/src/components/ThreadSettings.tsx +23 -0
  94. package/src/components/command-extension.ts +36 -0
  95. package/src/components/index.ts +16 -0
  96. package/src/components/testing.ts +46 -0
  97. package/src/components/types.ts +32 -0
  98. package/src/hooks/analytics.ts +39 -0
  99. package/src/hooks/index.ts +6 -0
  100. package/src/hooks/useStatus.ts +39 -0
  101. package/src/index.ts +12 -0
  102. package/src/meta.tsx +20 -0
  103. package/src/translations.ts +41 -0
  104. package/src/types.test.ts +13 -0
  105. package/src/types.ts +41 -0
  106. package/src/util.ts +26 -0
@@ -0,0 +1,1062 @@
1
+ import {
2
+ ThreadAction
3
+ } from "./chunk-TL3JITN7.mjs";
4
+ import {
5
+ ChatContainer,
6
+ ChatHeading,
7
+ MessageContainer,
8
+ command,
9
+ getMessageMetadata,
10
+ useAnalyticsCallback,
11
+ useOnEditAnalytics,
12
+ useStatus
13
+ } from "./chunk-CQ2RYETH.mjs";
14
+ import {
15
+ THREAD_ITEM,
16
+ THREAD_PLUGIN,
17
+ meta_default
18
+ } from "./chunk-5IBJO53M.mjs";
19
+
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 } from "@dxos/app-framework";
25
+ import { getTypename } from "@dxos/echo-schema";
26
+ import { create as create2 } from "@dxos/echo-schema";
27
+ import { LocalStorageStore } from "@dxos/local-storage";
28
+ import { parseAttentionPlugin } from "@dxos/plugin-attention";
29
+ import { parseClientPlugin } from "@dxos/plugin-client";
30
+ import { createExtension, isActionGroup } from "@dxos/plugin-graph";
31
+ import { DocumentType } from "@dxos/plugin-markdown/types";
32
+ import { ObservabilityAction } from "@dxos/plugin-observability/meta";
33
+ 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, getRangeFromCursor, 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";
39
+ import { translations as threadTranslations } from "@dxos/react-ui-thread";
40
+ import { nonNullable as nonNullable2 } from "@dxos/util";
41
+
42
+ // packages/plugins/plugin-thread/src/components/index.ts
43
+ import React4 from "react";
44
+
45
+ // packages/plugins/plugin-thread/src/components/ThreadSettings.tsx
46
+ import React from "react";
47
+ import { SettingsValue } from "@dxos/plugin-settings";
48
+ import { Input, useTranslation } from "@dxos/react-ui";
49
+ var ThreadSettings = ({ settings }) => {
50
+ const { t } = useTranslation(THREAD_PLUGIN);
51
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(SettingsValue, {
52
+ label: t("settings standalone label")
53
+ }, /* @__PURE__ */ React.createElement(Input.Switch, {
54
+ checked: settings.standalone,
55
+ onCheckedChange: (checked) => settings.standalone = !!checked
56
+ })));
57
+ };
58
+
59
+ // packages/plugins/plugin-thread/src/components/CommentContainer.tsx
60
+ import { CheckCircle, X } from "@phosphor-icons/react";
61
+ import React2, { useCallback, useEffect, useMemo, useRef, useState } from "react";
62
+ import { create } from "@dxos/echo-schema";
63
+ import { MessageType } from "@dxos/plugin-space/types";
64
+ import { getSpace, useMembers } from "@dxos/react-client/echo";
65
+ import { useIdentity } from "@dxos/react-client/halo";
66
+ import { Button, Tag, Tooltip, useThemeContext, useTranslation as useTranslation2 } from "@dxos/react-ui";
67
+ import { createBasicExtensions, createThemeExtensions, listener } from "@dxos/react-ui-editor";
68
+ import { getSize, hoverableControlItem, hoverableControls, hoverableFocusedWithinControls, mx } from "@dxos/react-ui-theme";
69
+ import { MessageTextbox, Thread, ThreadFooter, ThreadHeading } from "@dxos/react-ui-thread";
70
+ import { nonNullable } from "@dxos/util";
71
+ var sizeClass = getSize(4);
72
+ var ToggleResolvedButton = ({ isResolved, onResolve }) => {
73
+ const { t } = useTranslation2(THREAD_PLUGIN);
74
+ const label = t(isResolved ? "mark as unresolved label" : "mark as resolved label");
75
+ return /* @__PURE__ */ React2.createElement(Tooltip.Root, null, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
76
+ asChild: true
77
+ }, /* @__PURE__ */ React2.createElement(Button, {
78
+ variant: "ghost",
79
+ "data-testid": "thread.toggle-resolved",
80
+ onClick: onResolve,
81
+ classNames: [
82
+ "min-bs-0 p-1",
83
+ !isResolved && hoverableControlItem
84
+ ]
85
+ }, /* @__PURE__ */ React2.createElement("span", {
86
+ className: "sr-only"
87
+ }, label), isResolved ? /* @__PURE__ */ React2.createElement(CheckCircle, {
88
+ className: sizeClass,
89
+ weight: "fill"
90
+ }) : /* @__PURE__ */ React2.createElement(CheckCircle, {
91
+ className: sizeClass
92
+ }))), /* @__PURE__ */ React2.createElement(Tooltip.Portal, null, /* @__PURE__ */ React2.createElement(Tooltip.Content, {
93
+ classNames: "z-[21]"
94
+ }, label, /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null))));
95
+ };
96
+ var DeleteThreadButton = ({ onDelete }) => {
97
+ const { t } = useTranslation2(THREAD_PLUGIN);
98
+ const label = t("delete thread label");
99
+ return /* @__PURE__ */ React2.createElement(Tooltip.Root, null, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
100
+ asChild: true
101
+ }, /* @__PURE__ */ React2.createElement(Button, {
102
+ variant: "ghost",
103
+ "data-testid": "thread.delete",
104
+ onClick: onDelete,
105
+ classNames: [
106
+ "min-bs-0 p-1",
107
+ hoverableControlItem
108
+ ]
109
+ }, /* @__PURE__ */ React2.createElement("span", {
110
+ className: "sr-only"
111
+ }, label), /* @__PURE__ */ React2.createElement(X, {
112
+ className: sizeClass
113
+ }))), /* @__PURE__ */ React2.createElement(Tooltip.Portal, null, /* @__PURE__ */ React2.createElement(Tooltip.Content, {
114
+ classNames: "z-[21]"
115
+ }, label, /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null))));
116
+ };
117
+ var CommentContainer = ({ thread, detached, context, current, autoFocusTextbox, onAttend, onThreadDelete, onMessageDelete, onResolve, onComment }) => {
118
+ const identity = useIdentity();
119
+ const space = getSpace(thread);
120
+ const members = useMembers(space?.key);
121
+ const activity = useStatus(space, thread.id);
122
+ const { t } = useTranslation2(THREAD_PLUGIN);
123
+ const threadScrollRef = useRef(null);
124
+ const [autoFocus, setAutoFocus] = useState(!!autoFocusTextbox);
125
+ const { themeMode } = useThemeContext();
126
+ const textboxMetadata = getMessageMetadata(thread.id, identity);
127
+ const [_count, _setCount] = useState(0);
128
+ const rerenderEditor = () => _setCount((count) => count + 1);
129
+ const messageRef = useRef("");
130
+ const extensions = useMemo(() => [
131
+ createBasicExtensions({
132
+ placeholder: t("message placeholder")
133
+ }),
134
+ createThemeExtensions({
135
+ themeMode
136
+ }),
137
+ listener({
138
+ onChange: (text) => messageRef.current = text
139
+ }),
140
+ command
141
+ ], [
142
+ _count
143
+ ]);
144
+ useEffect(() => {
145
+ setAutoFocus(!!autoFocusTextbox);
146
+ }, [
147
+ autoFocusTextbox
148
+ ]);
149
+ const scrollToEnd = (behavior) => setTimeout(() => threadScrollRef.current?.scrollIntoView({
150
+ behavior,
151
+ block: "end"
152
+ }), 10);
153
+ const handleCreate = useCallback(() => {
154
+ if (!messageRef.current) {
155
+ return false;
156
+ }
157
+ thread.messages.push(create(MessageType, {
158
+ sender: {
159
+ identityKey: identity.identityKey.toHex()
160
+ },
161
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
162
+ text: messageRef.current,
163
+ context
164
+ }));
165
+ onComment?.(thread);
166
+ messageRef.current = "";
167
+ setAutoFocus(true);
168
+ scrollToEnd("instant");
169
+ rerenderEditor();
170
+ return true;
171
+ }, [
172
+ thread,
173
+ identity
174
+ ]);
175
+ const handleDeleteMessage = (id) => onMessageDelete?.(id);
176
+ return /* @__PURE__ */ React2.createElement(Thread, {
177
+ onClickCapture: onAttend,
178
+ onFocusCapture: onAttend,
179
+ current,
180
+ id: thread.id
181
+ }, /* @__PURE__ */ React2.createElement("div", {
182
+ role: "none",
183
+ className: mx("col-span-2 grid grid-cols-[var(--rail-size)_1fr_min-content]", hoverableControls, hoverableFocusedWithinControls)
184
+ }, detached ? /* @__PURE__ */ React2.createElement(Tooltip.Root, null, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
185
+ asChild: true
186
+ }, /* @__PURE__ */ React2.createElement(ThreadHeading, {
187
+ detached: true
188
+ }, thread.name ?? t("thread title placeholder"))), /* @__PURE__ */ React2.createElement(Tooltip.Portal, null, /* @__PURE__ */ React2.createElement(Tooltip.Content, {
189
+ classNames: "z-[21]",
190
+ side: "top"
191
+ }, t("detached thread label"), /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null)))) : /* @__PURE__ */ React2.createElement(ThreadHeading, null, thread.name ?? t("thread title placeholder")), /* @__PURE__ */ React2.createElement("div", {
192
+ className: "flex flex-row items-center pli-1"
193
+ }, thread.status === "staged" && /* @__PURE__ */ React2.createElement(Tag, {
194
+ palette: "neutral"
195
+ }, t("draft button")), onResolve && !(thread?.status === "staged") && /* @__PURE__ */ React2.createElement(ToggleResolvedButton, {
196
+ isResolved: thread?.status === "resolved",
197
+ onResolve
198
+ }), onThreadDelete && /* @__PURE__ */ React2.createElement(DeleteThreadButton, {
199
+ onDelete: onThreadDelete
200
+ }))), thread.messages.filter(nonNullable).map((message) => /* @__PURE__ */ React2.createElement(MessageContainer, {
201
+ key: message.id,
202
+ message,
203
+ members,
204
+ onDelete: handleDeleteMessage
205
+ })), /* @__PURE__ */ React2.createElement(MessageTextbox, {
206
+ extensions,
207
+ autoFocus,
208
+ onSend: handleCreate,
209
+ ...textboxMetadata
210
+ }), /* @__PURE__ */ React2.createElement(ThreadFooter, {
211
+ activity
212
+ }, t("activity message")), /* @__PURE__ */ React2.createElement("div", {
213
+ role: "none",
214
+ className: "bs-px -mbs-px",
215
+ ref: threadScrollRef
216
+ }));
217
+ };
218
+
219
+ // packages/plugins/plugin-thread/src/components/CommentsContainer.tsx
220
+ import { ChatText, Quotes } from "@phosphor-icons/react";
221
+ import React3, { useEffect as useEffect2 } from "react";
222
+ import { useTranslation as useTranslation3, Trans } from "@dxos/react-ui";
223
+ import { PlankHeading, plankHeadingIconProps } from "@dxos/react-ui-deck";
224
+ 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 }) => {
237
+ const { t } = useTranslation3(THREAD_PLUGIN);
238
+ const filteredThreads = showResolvedThreads ? threads : threads.filter((thread) => !(thread?.status === "resolved"));
239
+ useEffect2(() => {
240
+ if (currentId) {
241
+ document.getElementById(currentId)?.scrollIntoView({
242
+ behavior: "smooth",
243
+ block: "end"
244
+ });
245
+ }
246
+ }, [
247
+ currentId
248
+ ]);
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", {
269
+ role: "alert",
270
+ className: mx2(descriptionText, "place-self-center border border-dashed border-neutral-400/50 rounded-lg text-center p-4 m-4")
271
+ }, /* @__PURE__ */ React3.createElement("h2", {
272
+ className: "mbe-2 font-medium text-base"
273
+ }, t("no comments title")), /* @__PURE__ */ React3.createElement("p", null, /* @__PURE__ */ React3.createElement(Trans, {
274
+ t,
275
+ i18nKey: "no comments message",
276
+ components: {
277
+ commentIcon: /* @__PURE__ */ React3.createElement(ChatText, {
278
+ className: "inline-block"
279
+ })
280
+ }
281
+ }))));
282
+ };
283
+
284
+ // packages/plugins/plugin-thread/src/components/index.ts
285
+ var ThreadMain = React4.lazy(() => import("./ThreadMain-VV7MGQZJ.mjs"));
286
+ var ThreadArticle = React4.lazy(() => import("./ThreadArticle-J4VCLPMS.mjs"));
287
+
288
+ // packages/plugins/plugin-thread/src/translations.ts
289
+ var translations_default = [
290
+ {
291
+ "en-US": {
292
+ [THREAD_PLUGIN]: {
293
+ "plugin name": "Threads",
294
+ "channel name placeholder": "New channel",
295
+ "channel title label": "Title",
296
+ "delete channel label": "Delete channel",
297
+ "create channel label": "Create channel",
298
+ "message placeholder": "Reply\u2026",
299
+ "settings standalone label": "Enable channel creation (experimental)",
300
+ "activity message": "Processing\u2026",
301
+ "anonymous label": "Anonymous",
302
+ "delete message block label": "Delete message block",
303
+ "detached thread label": "Referenced content was removed",
304
+ "delete thread label": "Delete thread",
305
+ "thread deleted label": "You deleted a thread.",
306
+ "message deleted label": "You deleted a message.",
307
+ "comments heading": "Comments",
308
+ "chat heading": "Chat",
309
+ "draft button": "DRAFT",
310
+ "no comments title": "No comments (yet)",
311
+ "no comments message": "To start a comment thread, select a range of text in a document and click the <commentIcon></commentIcon> Create comment button in the toolbar.",
312
+ "toggle show resolved": "Show resolved",
313
+ "unnamed object threads label": "Threads",
314
+ "mark as resolved label": "Mark as resolved",
315
+ "mark as unresolved label": "Mark as unresolved",
316
+ "save message label": "Save",
317
+ "edit message label": "Edit",
318
+ "delete message label": "Delete message"
319
+ }
320
+ }
321
+ }
322
+ ];
323
+
324
+ // packages/plugins/plugin-thread/src/ThreadPlugin.tsx
325
+ var initialViewState = {
326
+ showResolvedThreads: false
327
+ };
328
+ var ThreadPlugin = () => {
329
+ const settings = new LocalStorageStore(THREAD_PLUGIN);
330
+ const state = create2({
331
+ staging: {}
332
+ });
333
+ const viewStore = create2({});
334
+ const getViewState = (subjectId) => {
335
+ if (!viewStore[subjectId]) {
336
+ viewStore[subjectId] = {
337
+ ...initialViewState
338
+ };
339
+ }
340
+ return viewStore[subjectId];
341
+ };
342
+ let attentionPlugin;
343
+ let navigationPlugin;
344
+ let intentPlugin;
345
+ let dispatch;
346
+ const unsubscribeCallbacks = [];
347
+ return {
348
+ meta: meta_default,
349
+ ready: async (plugins) => {
350
+ settings.prop({
351
+ key: "standalone",
352
+ type: LocalStorageStore.bool({
353
+ allowUndefined: true
354
+ })
355
+ });
356
+ attentionPlugin = resolvePlugin(plugins, parseAttentionPlugin);
357
+ navigationPlugin = resolvePlugin(plugins, parseNavigationPlugin);
358
+ intentPlugin = resolvePlugin(plugins, parseIntentPlugin);
359
+ dispatch = intentPlugin?.provides.intent.dispatch;
360
+ const graphPlugin = resolvePlugin(plugins, parseGraphPlugin);
361
+ const client = resolvePlugin(plugins, parseClientPlugin)?.provides.client;
362
+ if (!client) {
363
+ return;
364
+ }
365
+ const channelsQuery = client.spaces.query(Filter.schema(ChannelType));
366
+ const queryUnsubscribe = channelsQuery.subscribe();
367
+ const unsubscribe = effect(() => {
368
+ const attention = attentionPlugin?.provides.attention;
369
+ if (!attention?.attended) {
370
+ return;
371
+ }
372
+ 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);
373
+ if (firstAttendedNodeWithComments) {
374
+ void intentPlugin?.provides.intent.dispatch({
375
+ action: NavigationAction.OPEN,
376
+ data: {
377
+ activeParts: {
378
+ complementary: `${firstAttendedNodeWithComments.id}${SLUG_PATH_SEPARATOR}comments`
379
+ }
380
+ }
381
+ });
382
+ }
383
+ });
384
+ unsubscribeCallbacks.push(queryUnsubscribe);
385
+ unsubscribeCallbacks.push(unsubscribe);
386
+ },
387
+ unload: async () => {
388
+ unsubscribeCallbacks.forEach((unsubscribe) => unsubscribe());
389
+ },
390
+ provides: {
391
+ settings: settings.values,
392
+ metadata: {
393
+ records: {
394
+ [ChannelType.typename]: {
395
+ placeholder: [
396
+ "channel title placeholder",
397
+ {
398
+ ns: THREAD_PLUGIN
399
+ }
400
+ ],
401
+ icon: (props) => /* @__PURE__ */ React5.createElement(Chat, props),
402
+ iconSymbol: "ph--chat--regular",
403
+ // TODO(wittjosiah): Move out of metadata.
404
+ loadReferences: (channel) => loadObjectReferences(channel, (channel2) => channel2.threads)
405
+ },
406
+ [ThreadType.typename]: {
407
+ // TODO(wittjosiah): Move out of metadata.
408
+ loadReferences: (thread) => loadObjectReferences(thread, (thread2) => thread2.messages)
409
+ },
410
+ [MessageType2.typename]: {
411
+ // TODO(wittjosiah): Move out of metadata.
412
+ loadReferences: (message) => []
413
+ },
414
+ [THREAD_ITEM]: {
415
+ parse: (item, type) => {
416
+ switch (type) {
417
+ case "node":
418
+ return {
419
+ id: item.id,
420
+ label: item.title,
421
+ data: item
422
+ };
423
+ case "object":
424
+ return item;
425
+ case "view-object":
426
+ return {
427
+ id: `${item.id}-view`,
428
+ object: item
429
+ };
430
+ }
431
+ }
432
+ }
433
+ }
434
+ },
435
+ translations: [
436
+ ...translations_default,
437
+ ...threadTranslations
438
+ ],
439
+ echo: {
440
+ schema: [
441
+ ChannelType,
442
+ ThreadType,
443
+ MessageType2
444
+ ]
445
+ },
446
+ graph: {
447
+ builder: (plugins) => {
448
+ const client = resolvePlugin(plugins, parseClientPlugin)?.provides.client;
449
+ const dispatch2 = resolvePlugin(plugins, parseIntentPlugin)?.provides.intent.dispatch;
450
+ const metadataResolver = resolvePlugin(plugins, parseMetadataResolverPlugin)?.provides.metadata.resolver;
451
+ if (!client || !dispatch2 || !metadataResolver) {
452
+ return [];
453
+ }
454
+ return [
455
+ createExtension({
456
+ id: `${THREAD_PLUGIN}/comments-for-subject`,
457
+ resolver: ({ id }) => {
458
+ if (!id.endsWith("~comments")) {
459
+ return;
460
+ }
461
+ const docId = id.split("~").at(0);
462
+ const [spaceId, objectId] = docId?.split(":") ?? [];
463
+ const space = client.spaces.get().find((space2) => space2.id === spaceId);
464
+ const doc = space?.db.getObjectById(objectId);
465
+ if (!doc || !docId) {
466
+ return;
467
+ }
468
+ const docMeta = metadataResolver(getTypename(doc) ?? "");
469
+ const label = docMeta.label?.(doc) || doc.name || docMeta.placeholder || [
470
+ "unnamed object threads label",
471
+ {
472
+ ns: THREAD_PLUGIN
473
+ }
474
+ ];
475
+ const viewState = getViewState(docId);
476
+ return {
477
+ id,
478
+ type: "orphan-comments-for-subject",
479
+ data: doc,
480
+ properties: {
481
+ icon: meta_default.iconComponent,
482
+ label,
483
+ showResolvedThreads: viewState.showResolvedThreads
484
+ }
485
+ };
486
+ },
487
+ actions: ({ node }) => {
488
+ const dataId = node.id.split("~").at(0);
489
+ if (!node.id.endsWith("~comments") || !dataId) {
490
+ return;
491
+ }
492
+ const [spaceId, objectId] = dataId.split(":");
493
+ const viewState = getViewState(dataId);
494
+ const toggle = () => {
495
+ const newToggleState = !viewState.showResolvedThreads;
496
+ viewState.showResolvedThreads = newToggleState;
497
+ void dispatch2({
498
+ action: ObservabilityAction.SEND_EVENT,
499
+ data: {
500
+ name: "threads.toggle-show-resolved",
501
+ properties: {
502
+ spaceId,
503
+ threadId: objectId,
504
+ showResolved: newToggleState
505
+ }
506
+ }
507
+ });
508
+ };
509
+ return [
510
+ {
511
+ id: `${THREAD_PLUGIN}/toggle-show-resolved/${node.id}`,
512
+ data: toggle,
513
+ properties: {
514
+ label: [
515
+ "toggle show resolved",
516
+ {
517
+ ns: THREAD_PLUGIN
518
+ }
519
+ ],
520
+ menuItemType: "toggle",
521
+ isChecked: viewState.showResolvedThreads,
522
+ testId: "threadPlugin.toggleShowResolved"
523
+ }
524
+ }
525
+ ];
526
+ }
527
+ }),
528
+ createExtension({
529
+ id: ThreadAction.CREATE,
530
+ filter: (node) => !!settings.values.standalone && isActionGroup(node) && node.id.startsWith(SpaceAction.ADD_OBJECT),
531
+ actions: ({ node }) => {
532
+ const id = node.id.split("/").at(-1);
533
+ const [spaceId, objectId] = id?.split(":") ?? [];
534
+ const space = client.spaces.get().find((space2) => space2.id === spaceId);
535
+ const object = objectId && space?.db.getObjectById(objectId);
536
+ const target = objectId ? object : space;
537
+ if (!target) {
538
+ return;
539
+ }
540
+ return [
541
+ {
542
+ id: `${THREAD_PLUGIN}/create/${node.id}`,
543
+ data: async () => {
544
+ await dispatch2([
545
+ {
546
+ plugin: THREAD_PLUGIN,
547
+ action: ThreadAction.CREATE
548
+ },
549
+ {
550
+ action: SpaceAction.ADD_OBJECT,
551
+ data: {
552
+ target
553
+ }
554
+ },
555
+ {
556
+ action: NavigationAction.OPEN
557
+ }
558
+ ]);
559
+ },
560
+ properties: {
561
+ label: [
562
+ "create channel label",
563
+ {
564
+ ns: THREAD_PLUGIN
565
+ }
566
+ ],
567
+ icon: (props) => /* @__PURE__ */ React5.createElement(Chat, props),
568
+ iconSymbol: "ph--chat--regular",
569
+ testId: "threadPlugin.createObject"
570
+ }
571
+ }
572
+ ];
573
+ }
574
+ })
575
+ ];
576
+ }
577
+ },
578
+ surface: {
579
+ component: ({ data, role }) => {
580
+ switch (role) {
581
+ case "main": {
582
+ return data.active instanceof ChannelType && data.active.threads[0] ? /* @__PURE__ */ React5.createElement(ThreadMain, {
583
+ thread: data.active.threads[0]
584
+ }) : null;
585
+ }
586
+ case "settings": {
587
+ return data.plugin === meta_default.id ? /* @__PURE__ */ React5.createElement(ThreadSettings, {
588
+ settings: settings.values
589
+ }) : null;
590
+ }
591
+ case "article":
592
+ case "complementary": {
593
+ const location = navigationPlugin?.provides.location;
594
+ if (data.object instanceof ChannelType && data.object.threads[0]) {
595
+ const channel = data.object;
596
+ if (isLayoutParts(location?.active) && location.active.main) {
597
+ const layoutEntries = location.active.main;
598
+ const currentPosition = layoutEntries.findIndex((entry) => channel.id === entry.id);
599
+ if (currentPosition > 0) {
600
+ const objectToTheLeft = layoutEntries[currentPosition - 1];
601
+ const context = getSpace2(data.object)?.db.getObjectById(objectToTheLeft.id);
602
+ return /* @__PURE__ */ React5.createElement(ThreadArticle, {
603
+ thread: data.object.threads[0],
604
+ context
605
+ });
606
+ }
607
+ }
608
+ return /* @__PURE__ */ React5.createElement(ThreadArticle, {
609
+ thread: data.object.threads[0]
610
+ });
611
+ }
612
+ if (data.subject instanceof DocumentType) {
613
+ const doc = data.subject;
614
+ const accessor = doc.content ? createDocAccessor(doc.content, [
615
+ "content"
616
+ ]) : void 0;
617
+ if (!accessor) {
618
+ return null;
619
+ }
620
+ const getStartPosition = (cursor) => {
621
+ const range = cursor ? getRangeFromCursor(accessor, cursor) : void 0;
622
+ return range?.start ?? Number.MAX_SAFE_INTEGER;
623
+ };
624
+ const threads = doc.threads.concat(state.staging[data.subject.id]).filter(nonNullable2).sort((a, b) => getStartPosition(a.anchor) - getStartPosition(b.anchor));
625
+ const detached = data.subject.threads.filter(nonNullable2).filter(({ anchor }) => !anchor).map((thread) => thread.id);
626
+ const qualifiedSubjectId = fullyQualifiedId(doc);
627
+ const attention = attentionPlugin?.provides.attention?.attended ?? /* @__PURE__ */ new Set([
628
+ qualifiedSubjectId
629
+ ]);
630
+ const attendableAttrs = createAttendableAttributes(qualifiedSubjectId);
631
+ const space = getSpace2(doc);
632
+ const { showResolvedThreads } = getViewState(qualifiedSubjectId);
633
+ const dispatchAnalytic = (name, meta) => {
634
+ void dispatch?.({
635
+ action: ObservabilityAction.SEND_EVENT,
636
+ data: {
637
+ name,
638
+ properties: {
639
+ ...meta,
640
+ space: space?.id
641
+ }
642
+ }
643
+ });
644
+ };
645
+ return /* @__PURE__ */ React5.createElement("div", {
646
+ role: "none",
647
+ className: "contents group/attention",
648
+ ...attendableAttrs
649
+ }, role === "complementary" && /* @__PURE__ */ React5.createElement(CommentsHeading, {
650
+ attendableId: qualifiedSubjectId
651
+ }), /* @__PURE__ */ React5.createElement(ScrollArea.Root, {
652
+ classNames: "row-span-2"
653
+ }, /* @__PURE__ */ React5.createElement(ScrollArea.Viewport, null, /* @__PURE__ */ React5.createElement(CommentsContainer, {
654
+ threads,
655
+ detached,
656
+ currentId: attention.has(qualifiedSubjectId) ? state.current : void 0,
657
+ autoFocusCurrentTextbox: state.focus,
658
+ showResolvedThreads,
659
+ onThreadAttend: (thread) => {
660
+ if (state.current !== thread.id) {
661
+ state.current = thread.id;
662
+ void dispatch?.({
663
+ action: LayoutAction.SCROLL_INTO_VIEW,
664
+ data: {
665
+ id: thread.id
666
+ }
667
+ });
668
+ }
669
+ },
670
+ onThreadDelete: (thread) => dispatch?.({
671
+ plugin: THREAD_PLUGIN,
672
+ action: ThreadAction.DELETE,
673
+ data: {
674
+ document: data.subject,
675
+ thread
676
+ }
677
+ }),
678
+ onMessageDelete: (thread, messageId) => dispatch?.({
679
+ plugin: THREAD_PLUGIN,
680
+ action: ThreadAction.DELETE_MESSAGE,
681
+ data: {
682
+ document: data.subject,
683
+ thread,
684
+ messageId
685
+ }
686
+ }),
687
+ onThreadToggleResolved: (thread) => dispatch?.({
688
+ plugin: THREAD_PLUGIN,
689
+ action: ThreadAction.TOGGLE_RESOLVED,
690
+ data: {
691
+ thread
692
+ }
693
+ }),
694
+ onComment: (thread) => {
695
+ const doc2 = data.subject;
696
+ if (state.staging[doc2.id]?.find((t) => t === thread)) {
697
+ thread.status = "active";
698
+ doc2.threads ? doc2.threads.push(thread) : doc2.threads = [
699
+ thread
700
+ ];
701
+ state.staging[doc2.id] = state.staging[doc2.id]?.filter((t) => t.id !== thread.id);
702
+ dispatchAnalytic("threads.thread-created", {
703
+ threadId: thread.id
704
+ });
705
+ }
706
+ dispatchAnalytic("threads.message-added", {
707
+ threadId: thread.id,
708
+ threadLength: thread.messages.length
709
+ });
710
+ }
711
+ }), /* @__PURE__ */ React5.createElement("div", {
712
+ role: "none",
713
+ className: "bs-10"
714
+ }), /* @__PURE__ */ React5.createElement(ScrollArea.Scrollbar, null, /* @__PURE__ */ React5.createElement(ScrollArea.Thumb, null)))));
715
+ } else if (data.subject instanceof ThreadType) {
716
+ return /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(ChatHeading, {
717
+ attendableId: data.subject.id
718
+ }), /* @__PURE__ */ React5.createElement(ChatContainer, {
719
+ thread: data.subject
720
+ }));
721
+ }
722
+ }
723
+ }
724
+ return null;
725
+ }
726
+ },
727
+ intent: {
728
+ resolver: async (intent) => {
729
+ switch (intent.action) {
730
+ case ThreadAction.CREATE: {
731
+ return {
732
+ data: create2(ChannelType, {
733
+ threads: [
734
+ create2(ThreadType, {
735
+ messages: []
736
+ })
737
+ ]
738
+ })
739
+ };
740
+ }
741
+ case ThreadAction.SELECT: {
742
+ state.focus = intent.data?.current === state.current ? state.focus : intent.data?.focus;
743
+ state.current = intent.data?.current;
744
+ return {
745
+ data: true
746
+ };
747
+ }
748
+ case ThreadAction.TOGGLE_RESOLVED: {
749
+ const { thread } = intent.data ?? {};
750
+ if (!(thread instanceof ThreadType)) {
751
+ return;
752
+ }
753
+ if (thread.status === "active" || thread.status === void 0) {
754
+ thread.status = "resolved";
755
+ } else if (thread.status === "resolved") {
756
+ thread.status = "active";
757
+ }
758
+ const space = getSpace2(thread);
759
+ const spaceId = space?.id;
760
+ return {
761
+ intents: [
762
+ [
763
+ {
764
+ action: ObservabilityAction.SEND_EVENT,
765
+ data: {
766
+ name: "threads.toggle-resolved",
767
+ properties: {
768
+ threadId: thread.id,
769
+ spaceId
770
+ }
771
+ }
772
+ }
773
+ ]
774
+ ]
775
+ };
776
+ }
777
+ case ThreadAction.DELETE: {
778
+ const { document: doc, thread } = intent.data ?? {};
779
+ if (!(doc instanceof DocumentType) || !(thread instanceof ThreadType)) {
780
+ return;
781
+ }
782
+ const stagingArea = state.staging[doc.id];
783
+ if (stagingArea) {
784
+ const index = state.staging[doc.id]?.findIndex((t) => t.id === thread.id);
785
+ if (index !== -1) {
786
+ state.staging[doc.id]?.splice(index, 1);
787
+ return;
788
+ }
789
+ }
790
+ const space = getSpace2(thread);
791
+ if (!space || !doc.threads) {
792
+ return;
793
+ }
794
+ if (!intent.undo) {
795
+ const index = doc.threads.findIndex((t) => t?.id === thread.id);
796
+ const cursor = doc.threads[index]?.anchor;
797
+ if (index !== -1) {
798
+ doc.threads?.splice(index, 1);
799
+ }
800
+ space.db.remove(thread);
801
+ return {
802
+ undoable: {
803
+ message: translations_default[0]["en-US"][THREAD_PLUGIN]["thread deleted label"],
804
+ data: {
805
+ cursor
806
+ }
807
+ },
808
+ intents: [
809
+ [
810
+ {
811
+ action: ObservabilityAction.SEND_EVENT,
812
+ data: {
813
+ name: "threads.delete",
814
+ properties: {
815
+ threadId: thread.id,
816
+ spaceId: space.id
817
+ }
818
+ }
819
+ }
820
+ ]
821
+ ]
822
+ };
823
+ } else if (intent.undo) {
824
+ const savedThread = space.db.add(thread);
825
+ doc.threads.push(savedThread);
826
+ return {
827
+ data: true,
828
+ intents: [
829
+ [
830
+ {
831
+ action: ObservabilityAction.SEND_EVENT,
832
+ data: {
833
+ name: "threads.undo-delete",
834
+ properties: {
835
+ threadId: thread.id,
836
+ spaceId: space.id
837
+ }
838
+ }
839
+ }
840
+ ]
841
+ ]
842
+ };
843
+ }
844
+ break;
845
+ }
846
+ case ThreadAction.DELETE_MESSAGE: {
847
+ const { document: doc, thread, messageId } = intent.data ?? {};
848
+ const space = getSpace2(doc);
849
+ if (!(doc instanceof DocumentType) || !(thread instanceof ThreadType) || !space) {
850
+ return;
851
+ }
852
+ if (!intent.undo) {
853
+ const message = thread.messages.find((m) => m?.id === messageId);
854
+ const messageIndex = thread.messages.findIndex((m) => m?.id === messageId);
855
+ if (messageIndex === -1 || !message) {
856
+ return void 0;
857
+ }
858
+ if (messageIndex === 0 && thread.messages.length === 1) {
859
+ return {
860
+ intents: [
861
+ [
862
+ {
863
+ action: ThreadAction.DELETE,
864
+ data: {
865
+ document: doc,
866
+ thread
867
+ }
868
+ }
869
+ ]
870
+ ]
871
+ };
872
+ } else {
873
+ thread.messages.splice(messageIndex, 1);
874
+ }
875
+ return {
876
+ undoable: {
877
+ message: translations_default[0]["en-US"][THREAD_PLUGIN]["message deleted label"],
878
+ data: {
879
+ message,
880
+ messageIndex
881
+ }
882
+ },
883
+ intents: [
884
+ [
885
+ {
886
+ action: ObservabilityAction.SEND_EVENT,
887
+ data: {
888
+ name: "threads.message.delete",
889
+ properties: {
890
+ threadId: thread.id,
891
+ spaceId: space.id
892
+ }
893
+ }
894
+ }
895
+ ]
896
+ ]
897
+ };
898
+ } else if (intent.undo) {
899
+ const message = intent.data?.message;
900
+ const messageIndex = intent.data?.messageIndex;
901
+ if (!(message instanceof MessageType2) || !(typeof messageIndex === "number")) {
902
+ return;
903
+ }
904
+ thread.messages.splice(messageIndex, 0, message);
905
+ return {
906
+ data: true,
907
+ intents: [
908
+ [
909
+ {
910
+ action: ObservabilityAction.SEND_EVENT,
911
+ data: {
912
+ name: "threads.message.undo-delete",
913
+ properties: {
914
+ threadId: thread.id,
915
+ spaceId: space.id
916
+ }
917
+ }
918
+ }
919
+ ]
920
+ ]
921
+ };
922
+ }
923
+ }
924
+ }
925
+ }
926
+ },
927
+ markdown: {
928
+ // TODO(burdon): Factor out extension factory into separate file (for simplicity).
929
+ extensions: ({ document: doc }) => {
930
+ const space = doc && getSpace2(doc);
931
+ if (!doc || !space) {
932
+ return [
933
+ comments()
934
+ ];
935
+ }
936
+ const threads = computed(() => [
937
+ ...doc.threads.filter(nonNullable2),
938
+ ...state.staging[doc.id] ?? []
939
+ ].filter((thread) => !(thread?.status === "resolved")));
940
+ return [
941
+ listener2({
942
+ onChange: () => {
943
+ doc.threads.forEach((thread) => {
944
+ if (thread?.anchor) {
945
+ const [start, end] = thread.anchor.split(":");
946
+ const name = doc.content && getTextInRange(createDocAccessor(doc.content, [
947
+ "content"
948
+ ]), start, end);
949
+ if (name && name !== thread.name) {
950
+ thread.name = name;
951
+ }
952
+ }
953
+ });
954
+ }
955
+ }),
956
+ createExternalCommentSync(doc.id, (sink) => effect(() => sink()), () => threads.value.filter((thread) => thread?.anchor).map((thread) => ({
957
+ id: thread.id,
958
+ cursor: thread.anchor
959
+ }))),
960
+ comments({
961
+ id: doc.id,
962
+ onCreate: ({ cursor }) => {
963
+ const [start, end] = cursor.split(":");
964
+ const name = doc.content && getTextInRange(createDocAccessor(doc.content, [
965
+ "content"
966
+ ]), start, end);
967
+ const thread = create2(ThreadType, {
968
+ name,
969
+ anchor: cursor,
970
+ messages: [],
971
+ status: "staged"
972
+ });
973
+ const stagingArea = state.staging[doc.id];
974
+ if (stagingArea) {
975
+ stagingArea.push(thread);
976
+ } else {
977
+ state.staging[doc.id] = [
978
+ thread
979
+ ];
980
+ }
981
+ void intentPlugin?.provides.intent.dispatch([
982
+ {
983
+ action: NavigationAction.OPEN,
984
+ data: {
985
+ activeParts: {
986
+ complementary: `${fullyQualifiedId(doc)}${SLUG_PATH_SEPARATOR}comments${SLUG_COLLECTION_INDICATOR}`
987
+ }
988
+ }
989
+ },
990
+ {
991
+ action: ThreadAction.SELECT,
992
+ data: {
993
+ current: thread.id,
994
+ focus: true
995
+ }
996
+ },
997
+ {
998
+ action: LayoutAction.SET_LAYOUT,
999
+ data: {
1000
+ element: "complementary",
1001
+ subject: doc,
1002
+ state: true
1003
+ }
1004
+ }
1005
+ ]);
1006
+ return thread.id;
1007
+ },
1008
+ onDelete: ({ id }) => {
1009
+ const stagingArea = state.staging[doc.id];
1010
+ if (stagingArea) {
1011
+ const index = stagingArea.findIndex((thread2) => thread2.id === id);
1012
+ if (index !== -1) {
1013
+ stagingArea.splice(index, 1);
1014
+ }
1015
+ }
1016
+ const thread = doc.threads.find((thread2) => thread2?.id === id);
1017
+ if (thread) {
1018
+ thread.anchor = void 0;
1019
+ }
1020
+ },
1021
+ onUpdate: ({ id, cursor }) => {
1022
+ const thread = state.staging[doc.id]?.find((thread2) => thread2.id === id) ?? doc.threads.find((thread2) => thread2?.id === id);
1023
+ if (thread instanceof ThreadType && thread.anchor) {
1024
+ const [start, end] = thread.anchor.split(":");
1025
+ thread.name = doc.content && getTextInRange(createDocAccessor(doc.content, [
1026
+ "content"
1027
+ ]), start, end);
1028
+ thread.anchor = cursor;
1029
+ }
1030
+ },
1031
+ onSelect: ({ selection: { current, closest } }) => {
1032
+ const dispatch2 = intentPlugin?.provides.intent.dispatch;
1033
+ if (dispatch2) {
1034
+ void dispatch2([
1035
+ {
1036
+ action: ThreadAction.SELECT,
1037
+ data: {
1038
+ current: current ?? closest
1039
+ }
1040
+ }
1041
+ ]);
1042
+ }
1043
+ }
1044
+ })
1045
+ ];
1046
+ }
1047
+ }
1048
+ }
1049
+ };
1050
+ };
1051
+
1052
+ // packages/plugins/plugin-thread/src/index.ts
1053
+ var src_default = ThreadPlugin;
1054
+ export {
1055
+ ThreadAction,
1056
+ ThreadPlugin,
1057
+ src_default as default,
1058
+ useAnalyticsCallback,
1059
+ useOnEditAnalytics,
1060
+ useStatus
1061
+ };
1062
+ //# sourceMappingURL=index.mjs.map