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

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