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

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