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