@anvia/react-ui 0.2.0 → 0.4.0
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/README.md +14 -1
- package/dist/attachment/index.js +2 -2
- package/dist/chat/index.d.ts +39 -3
- package/dist/chat/index.js +4 -4
- package/dist/{chunk-RRINF6FN.js → chunk-32QFNRXR.js} +2 -2
- package/dist/chunk-657E5BPN.js +291 -0
- package/dist/chunk-657E5BPN.js.map +1 -0
- package/dist/{chunk-7AS6R7BB.js → chunk-BCCU3VT5.js} +93 -16
- package/dist/chunk-BCCU3VT5.js.map +1 -0
- package/dist/{chunk-AOZN3SWB.js → chunk-FZANAMWH.js} +4 -3
- package/dist/{chunk-AOZN3SWB.js.map → chunk-FZANAMWH.js.map} +1 -1
- package/dist/chunk-GXB4VINL.js +236 -0
- package/dist/chunk-GXB4VINL.js.map +1 -0
- package/dist/{chunk-WK33ER4Q.js → chunk-JUNV3MUR.js} +2 -2
- package/dist/chunk-KO36TK2O.js +1549 -0
- package/dist/chunk-KO36TK2O.js.map +1 -0
- package/dist/chunk-SE7I6ZXJ.js +432 -0
- package/dist/chunk-SE7I6ZXJ.js.map +1 -0
- package/dist/{chunk-LSHD67YK.js → chunk-TOBAQM2G.js} +2 -2
- package/dist/completion/index.js +2 -2
- package/dist/human-input/index.js +2 -2
- package/dist/image/index.d.ts +40 -0
- package/dist/image/index.js +12 -0
- package/dist/image/index.js.map +1 -0
- package/dist/image-DanUxcl2.d.ts +14 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +32 -8
- package/dist/message/index.js +3 -3
- package/dist/selection-toolbar/index.d.ts +21 -0
- package/dist/selection-toolbar/index.js +12 -0
- package/dist/selection-toolbar/index.js.map +1 -0
- package/dist/selection-toolbar-Cwjbuv5S.d.ts +14 -0
- package/dist/shared.d.ts +4 -1
- package/dist/shared.js +13 -3
- package/dist/styles.css +42 -0
- package/dist/thread-C2-Xo08o.d.ts +102 -0
- package/dist/thread-list/index.d.ts +47 -0
- package/dist/thread-list/index.js +18 -0
- package/dist/thread-list/index.js.map +1 -0
- package/dist/thread-list-On8db4XL.d.ts +34 -0
- package/package.json +27 -4
- package/dist/chunk-7AS6R7BB.js.map +0 -1
- package/dist/chunk-AXZB6JSE.js +0 -813
- package/dist/chunk-AXZB6JSE.js.map +0 -1
- package/dist/thread-CluT3m5k.d.ts +0 -31
- /package/dist/{chunk-RRINF6FN.js.map → chunk-32QFNRXR.js.map} +0 -0
- /package/dist/{chunk-WK33ER4Q.js.map → chunk-JUNV3MUR.js.map} +0 -0
- /package/dist/{chunk-LSHD67YK.js.map → chunk-TOBAQM2G.js.map} +0 -0
|
@@ -0,0 +1,1549 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Message
|
|
3
|
+
} from "./chunk-FZANAMWH.js";
|
|
4
|
+
import {
|
|
5
|
+
Attachment
|
|
6
|
+
} from "./chunk-32QFNRXR.js";
|
|
7
|
+
import {
|
|
8
|
+
composeRefs,
|
|
9
|
+
renderPrimitive
|
|
10
|
+
} from "./chunk-T3ZFZ3NP.js";
|
|
11
|
+
import {
|
|
12
|
+
InternalAttachmentProvider,
|
|
13
|
+
InternalComposerProvider,
|
|
14
|
+
InternalMessageProvider,
|
|
15
|
+
InternalThreadProvider,
|
|
16
|
+
useChatContext,
|
|
17
|
+
useComposer,
|
|
18
|
+
useThread
|
|
19
|
+
} from "./chunk-BCCU3VT5.js";
|
|
20
|
+
|
|
21
|
+
// src/chat/composer.tsx
|
|
22
|
+
import { mergeAttributes } from "@tiptap/core";
|
|
23
|
+
import Document from "@tiptap/extension-document";
|
|
24
|
+
import HardBreak from "@tiptap/extension-hard-break";
|
|
25
|
+
import Mention from "@tiptap/extension-mention";
|
|
26
|
+
import Paragraph from "@tiptap/extension-paragraph";
|
|
27
|
+
import Placeholder from "@tiptap/extension-placeholder";
|
|
28
|
+
import Text from "@tiptap/extension-text";
|
|
29
|
+
import { PluginKey } from "@tiptap/pm/state";
|
|
30
|
+
import { EditorContent, useEditor } from "@tiptap/react";
|
|
31
|
+
import {
|
|
32
|
+
forwardRef,
|
|
33
|
+
useCallback,
|
|
34
|
+
useEffect,
|
|
35
|
+
useMemo,
|
|
36
|
+
useRef,
|
|
37
|
+
useState
|
|
38
|
+
} from "react";
|
|
39
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
40
|
+
var ComposerRoot = forwardRef(function ComposerRoot2({
|
|
41
|
+
attachments: attachmentsProp,
|
|
42
|
+
defaultAttachments,
|
|
43
|
+
defaultEntities,
|
|
44
|
+
defaultInput = "",
|
|
45
|
+
defaultQuote,
|
|
46
|
+
entities: entitiesProp,
|
|
47
|
+
input: inputProp,
|
|
48
|
+
onAttachmentsChange,
|
|
49
|
+
onEntitiesChange,
|
|
50
|
+
onInputChange,
|
|
51
|
+
onQuoteChange,
|
|
52
|
+
quote: quoteProp,
|
|
53
|
+
submitMessage,
|
|
54
|
+
triggers: triggersProp,
|
|
55
|
+
onSubmit,
|
|
56
|
+
...props
|
|
57
|
+
}, ref) {
|
|
58
|
+
const chat = useChatContext();
|
|
59
|
+
const [uncontrolledInput, setUncontrolledInput] = useState(defaultInput);
|
|
60
|
+
const [uncontrolledAttachments, setUncontrolledAttachments] = useState(() => [
|
|
61
|
+
...defaultAttachments ?? []
|
|
62
|
+
]);
|
|
63
|
+
const [uncontrolledEntities, setUncontrolledEntities] = useState(() => [
|
|
64
|
+
...defaultEntities ?? []
|
|
65
|
+
]);
|
|
66
|
+
const [uncontrolledQuote, setUncontrolledQuote] = useState(
|
|
67
|
+
defaultQuote
|
|
68
|
+
);
|
|
69
|
+
const [activeTrigger, setActiveTriggerState] = useState();
|
|
70
|
+
const input = inputProp ?? uncontrolledInput;
|
|
71
|
+
const attachments = attachmentsProp ?? uncontrolledAttachments;
|
|
72
|
+
const entities = entitiesProp ?? uncontrolledEntities;
|
|
73
|
+
const triggers = triggersProp ?? EMPTY_COMPOSER_TRIGGERS;
|
|
74
|
+
const rawQuote = quoteProp ?? uncontrolledQuote;
|
|
75
|
+
const rawQuoteMessageId = rawQuote?.messageId;
|
|
76
|
+
const rawQuoteText = rawQuote?.text;
|
|
77
|
+
const quote = useMemo(
|
|
78
|
+
() => rawQuoteMessageId === void 0 || rawQuoteText === void 0 ? void 0 : { messageId: rawQuoteMessageId, text: rawQuoteText },
|
|
79
|
+
[rawQuoteMessageId, rawQuoteText]
|
|
80
|
+
);
|
|
81
|
+
const attachmentsRef = useRef(attachments);
|
|
82
|
+
attachmentsRef.current = attachments;
|
|
83
|
+
const entitiesRef = useRef(entities);
|
|
84
|
+
entitiesRef.current = entities;
|
|
85
|
+
const inputControlled = inputProp !== void 0;
|
|
86
|
+
const attachmentsControlled = attachmentsProp !== void 0;
|
|
87
|
+
const entitiesControlled = entitiesProp !== void 0;
|
|
88
|
+
const quoteControlled = quoteProp !== void 0;
|
|
89
|
+
const hasMessageContent = input.trim().length > 0 || attachments.length > 0 || entities.length > 0 || quote !== void 0;
|
|
90
|
+
const canSubmit = hasMessageContent && chat.status !== "streaming";
|
|
91
|
+
const canStop = chat.status === "streaming";
|
|
92
|
+
const setInput = useCallback(
|
|
93
|
+
(nextInput) => {
|
|
94
|
+
if (!inputControlled) {
|
|
95
|
+
setUncontrolledInput(nextInput);
|
|
96
|
+
}
|
|
97
|
+
onInputChange?.(nextInput);
|
|
98
|
+
},
|
|
99
|
+
[inputControlled, onInputChange]
|
|
100
|
+
);
|
|
101
|
+
const setAttachments = useCallback(
|
|
102
|
+
(update) => {
|
|
103
|
+
const nextAttachments = typeof update === "function" ? update(attachmentsRef.current) : update;
|
|
104
|
+
attachmentsRef.current = nextAttachments;
|
|
105
|
+
if (!attachmentsControlled) {
|
|
106
|
+
setUncontrolledAttachments(nextAttachments);
|
|
107
|
+
}
|
|
108
|
+
onAttachmentsChange?.(nextAttachments);
|
|
109
|
+
},
|
|
110
|
+
[attachmentsControlled, onAttachmentsChange]
|
|
111
|
+
);
|
|
112
|
+
const setEntities = useCallback(
|
|
113
|
+
(update) => {
|
|
114
|
+
const nextEntities = typeof update === "function" ? update(entitiesRef.current) : update;
|
|
115
|
+
entitiesRef.current = nextEntities;
|
|
116
|
+
if (!entitiesControlled) {
|
|
117
|
+
setUncontrolledEntities(nextEntities);
|
|
118
|
+
}
|
|
119
|
+
onEntitiesChange?.(nextEntities);
|
|
120
|
+
},
|
|
121
|
+
[entitiesControlled, onEntitiesChange]
|
|
122
|
+
);
|
|
123
|
+
const setQuote = useCallback(
|
|
124
|
+
(nextQuote) => {
|
|
125
|
+
const normalizedQuote = normalizeQuote(nextQuote);
|
|
126
|
+
if (!quoteControlled) {
|
|
127
|
+
setUncontrolledQuote(normalizedQuote);
|
|
128
|
+
}
|
|
129
|
+
onQuoteChange?.(normalizedQuote);
|
|
130
|
+
},
|
|
131
|
+
[onQuoteChange, quoteControlled]
|
|
132
|
+
);
|
|
133
|
+
const addAttachment = useCallback(
|
|
134
|
+
async (attachment) => {
|
|
135
|
+
const normalized = isFileAttachmentInput(attachment) ? await attachmentFromFile(attachment) : createAttachment(attachment);
|
|
136
|
+
setAttachments((current) => [...current, normalized]);
|
|
137
|
+
},
|
|
138
|
+
[setAttachments]
|
|
139
|
+
);
|
|
140
|
+
const removeAttachment = useCallback(
|
|
141
|
+
(id) => {
|
|
142
|
+
setAttachments((current) => current.filter((attachment) => attachment.id !== id));
|
|
143
|
+
},
|
|
144
|
+
[setAttachments]
|
|
145
|
+
);
|
|
146
|
+
const clearAttachments = useCallback(() => {
|
|
147
|
+
setAttachments([]);
|
|
148
|
+
}, [setAttachments]);
|
|
149
|
+
const clearQuote = useCallback(() => {
|
|
150
|
+
setQuote(void 0);
|
|
151
|
+
}, [setQuote]);
|
|
152
|
+
const clear = useCallback(() => {
|
|
153
|
+
setInput("");
|
|
154
|
+
setAttachments([]);
|
|
155
|
+
setEntities([]);
|
|
156
|
+
setActiveTriggerState(void 0);
|
|
157
|
+
clearQuote();
|
|
158
|
+
}, [clearQuote, setAttachments, setEntities, setInput]);
|
|
159
|
+
const submit = useCallback(async () => {
|
|
160
|
+
const prompt = input;
|
|
161
|
+
if (prompt.trim().length === 0 && attachments.length === 0 && entities.length === 0 && quote === void 0 || chat.status === "streaming") {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (submitMessage !== void 0) {
|
|
165
|
+
await submitMessage({
|
|
166
|
+
input: prompt,
|
|
167
|
+
attachments,
|
|
168
|
+
entities,
|
|
169
|
+
chat,
|
|
170
|
+
quote,
|
|
171
|
+
clear
|
|
172
|
+
});
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const submittedText = quote === void 0 ? prompt : promptWithQuote(prompt, quote);
|
|
176
|
+
const metadata = composerSubmitMetadata(quote, entities);
|
|
177
|
+
if (attachments.length === 0) {
|
|
178
|
+
if (metadata === void 0) {
|
|
179
|
+
await chat.sendMessage(prompt);
|
|
180
|
+
} else {
|
|
181
|
+
await chat.sendMessage({
|
|
182
|
+
text: submittedText,
|
|
183
|
+
metadata
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
} else {
|
|
187
|
+
await chat.sendMessage({
|
|
188
|
+
text: submittedText,
|
|
189
|
+
attachments,
|
|
190
|
+
...metadata === void 0 ? {} : { metadata }
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
clear();
|
|
194
|
+
}, [attachments, chat, clear, entities, input, quote, submitMessage]);
|
|
195
|
+
const setActiveTrigger = useCallback((update) => {
|
|
196
|
+
setActiveTriggerState(update);
|
|
197
|
+
}, []);
|
|
198
|
+
const value = useMemo(
|
|
199
|
+
() => ({
|
|
200
|
+
input,
|
|
201
|
+
setInput,
|
|
202
|
+
attachments,
|
|
203
|
+
setAttachments,
|
|
204
|
+
addAttachment,
|
|
205
|
+
removeAttachment,
|
|
206
|
+
clearAttachments,
|
|
207
|
+
entities,
|
|
208
|
+
setEntities,
|
|
209
|
+
quote,
|
|
210
|
+
setQuote,
|
|
211
|
+
clearQuote,
|
|
212
|
+
triggers,
|
|
213
|
+
activeTrigger,
|
|
214
|
+
setActiveTrigger,
|
|
215
|
+
submit,
|
|
216
|
+
stop: chat.stop,
|
|
217
|
+
status: chat.status,
|
|
218
|
+
canSubmit,
|
|
219
|
+
canStop
|
|
220
|
+
}),
|
|
221
|
+
[
|
|
222
|
+
addAttachment,
|
|
223
|
+
attachments,
|
|
224
|
+
activeTrigger,
|
|
225
|
+
canStop,
|
|
226
|
+
canSubmit,
|
|
227
|
+
chat.status,
|
|
228
|
+
chat.stop,
|
|
229
|
+
clearAttachments,
|
|
230
|
+
clearQuote,
|
|
231
|
+
entities,
|
|
232
|
+
input,
|
|
233
|
+
quote,
|
|
234
|
+
removeAttachment,
|
|
235
|
+
setAttachments,
|
|
236
|
+
setActiveTrigger,
|
|
237
|
+
setEntities,
|
|
238
|
+
setInput,
|
|
239
|
+
setQuote,
|
|
240
|
+
submit,
|
|
241
|
+
triggers
|
|
242
|
+
]
|
|
243
|
+
);
|
|
244
|
+
const handleSubmit = useCallback(
|
|
245
|
+
(event) => {
|
|
246
|
+
onSubmit?.(event);
|
|
247
|
+
if (event.defaultPrevented) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
event.preventDefault();
|
|
251
|
+
void submit();
|
|
252
|
+
},
|
|
253
|
+
[onSubmit, submit]
|
|
254
|
+
);
|
|
255
|
+
return /* @__PURE__ */ jsx(InternalComposerProvider, { value, children: renderPrimitive(
|
|
256
|
+
"form",
|
|
257
|
+
{
|
|
258
|
+
...props,
|
|
259
|
+
onSubmit: handleSubmit,
|
|
260
|
+
"data-anvia-composer": "",
|
|
261
|
+
"data-state": chat.status
|
|
262
|
+
},
|
|
263
|
+
ref
|
|
264
|
+
) });
|
|
265
|
+
});
|
|
266
|
+
var ComposerQuotePreview = forwardRef(
|
|
267
|
+
function ComposerQuotePreview2({ children, ...props }, ref) {
|
|
268
|
+
const composer = useComposer();
|
|
269
|
+
const quote = composer.quote;
|
|
270
|
+
if (quote === void 0) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
const renderedChildren = typeof children === "function" ? children(quote) : children ?? quote.text;
|
|
274
|
+
return renderPrimitive(
|
|
275
|
+
"blockquote",
|
|
276
|
+
{
|
|
277
|
+
...props,
|
|
278
|
+
children: renderedChildren,
|
|
279
|
+
"data-anvia-composer-quote": "",
|
|
280
|
+
"data-message-id": quote.messageId
|
|
281
|
+
},
|
|
282
|
+
ref
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
);
|
|
286
|
+
var ComposerClearQuote = forwardRef(
|
|
287
|
+
function ComposerClearQuote2({ onClick, ...props }, ref) {
|
|
288
|
+
const composer = useComposer();
|
|
289
|
+
const disabled = props.disabled ?? composer.quote === void 0;
|
|
290
|
+
const handleClick = useCallback(
|
|
291
|
+
(event) => {
|
|
292
|
+
onClick?.(event);
|
|
293
|
+
if (event.defaultPrevented || disabled) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
composer.clearQuote();
|
|
297
|
+
},
|
|
298
|
+
[composer, disabled, onClick]
|
|
299
|
+
);
|
|
300
|
+
return renderPrimitive(
|
|
301
|
+
"button",
|
|
302
|
+
{
|
|
303
|
+
...props,
|
|
304
|
+
children: props.children ?? "Clear quote",
|
|
305
|
+
disabled,
|
|
306
|
+
onClick: handleClick,
|
|
307
|
+
type: props.type ?? "button",
|
|
308
|
+
"data-anvia-clear-quote": "",
|
|
309
|
+
"data-state": disabled ? "disabled" : "enabled"
|
|
310
|
+
},
|
|
311
|
+
ref
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
);
|
|
315
|
+
var ComposerInput = forwardRef(function ComposerInput2({
|
|
316
|
+
autoResize = true,
|
|
317
|
+
disabled,
|
|
318
|
+
maxRows: maxRowsProp,
|
|
319
|
+
minRows: minRowsProp,
|
|
320
|
+
onKeyDown,
|
|
321
|
+
placeholder,
|
|
322
|
+
style,
|
|
323
|
+
...props
|
|
324
|
+
}, ref) {
|
|
325
|
+
const composer = useComposer();
|
|
326
|
+
const composerRef = useRef(composer);
|
|
327
|
+
composerRef.current = composer;
|
|
328
|
+
const editorReadyRef = useRef(false);
|
|
329
|
+
const syncingFromEditorRef = useRef(false);
|
|
330
|
+
const triggers = composer.triggers;
|
|
331
|
+
const minRows = normalizeRows(minRowsProp ?? 1, 1);
|
|
332
|
+
const maxRows = normalizeRows(maxRowsProp ?? 6, minRows);
|
|
333
|
+
const extensions = useMemo(
|
|
334
|
+
() => composerInputExtensions({
|
|
335
|
+
composerRef,
|
|
336
|
+
placeholder,
|
|
337
|
+
triggers
|
|
338
|
+
}),
|
|
339
|
+
[placeholder, triggers]
|
|
340
|
+
);
|
|
341
|
+
const editor = useEditor(
|
|
342
|
+
{
|
|
343
|
+
extensions,
|
|
344
|
+
content: plainTextToComposerContent(composer.input),
|
|
345
|
+
editable: composer.status !== "streaming",
|
|
346
|
+
editorProps: {
|
|
347
|
+
attributes: {
|
|
348
|
+
"aria-label": props["aria-label"] ?? "Message",
|
|
349
|
+
"aria-multiline": "true",
|
|
350
|
+
"data-anvia-composer-editor": "",
|
|
351
|
+
role: "textbox"
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
onUpdate: ({ editor: updatedEditor }) => {
|
|
355
|
+
if (!editorReadyRef.current) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
const snapshot = composerSnapshotFromEditor(
|
|
359
|
+
updatedEditor,
|
|
360
|
+
composerRef.current.entities,
|
|
361
|
+
composerRef.current.triggers
|
|
362
|
+
);
|
|
363
|
+
syncingFromEditorRef.current = true;
|
|
364
|
+
composerRef.current.setInput(snapshot.input);
|
|
365
|
+
composerRef.current.setEntities(snapshot.entities);
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
[extensions]
|
|
369
|
+
);
|
|
370
|
+
const ariaDisabled = props["aria-disabled"] === true || props["aria-disabled"] === "true";
|
|
371
|
+
const composerDisabled = disabled === true || ariaDisabled || composer.status === "streaming";
|
|
372
|
+
const inputStyle = useMemo(() => {
|
|
373
|
+
if (!autoResize) {
|
|
374
|
+
return style;
|
|
375
|
+
}
|
|
376
|
+
const rowsStyle = {
|
|
377
|
+
["--anvia-composer-min-rows"]: minRows,
|
|
378
|
+
["--anvia-composer-max-rows"]: maxRows
|
|
379
|
+
};
|
|
380
|
+
return style === void 0 ? rowsStyle : { ...rowsStyle, ...style };
|
|
381
|
+
}, [autoResize, maxRows, minRows, style]);
|
|
382
|
+
const handleKeyDown = useCallback(
|
|
383
|
+
(event) => {
|
|
384
|
+
onKeyDown?.(event);
|
|
385
|
+
if (event.defaultPrevented || event.key !== "Enter" || event.shiftKey || event.nativeEvent.isComposing) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
event.preventDefault();
|
|
389
|
+
void composerRef.current.submit();
|
|
390
|
+
},
|
|
391
|
+
[onKeyDown]
|
|
392
|
+
);
|
|
393
|
+
useEffect(() => {
|
|
394
|
+
if (editor === null) {
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
editor.setEditable(!composerDisabled, false);
|
|
398
|
+
}, [composerDisabled, editor]);
|
|
399
|
+
useEffect(() => {
|
|
400
|
+
if (editor === null) {
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
const handleKeyDown2 = (event) => {
|
|
404
|
+
if (submitComposerFromEditorKeyDown(event, composerRef)) {
|
|
405
|
+
event.stopPropagation();
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
editor.view.dom.addEventListener("keydown", handleKeyDown2, true);
|
|
409
|
+
return () => {
|
|
410
|
+
editor.view.dom.removeEventListener("keydown", handleKeyDown2, true);
|
|
411
|
+
};
|
|
412
|
+
}, [editor]);
|
|
413
|
+
useEffect(() => {
|
|
414
|
+
if (editor === null) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
editor.commands.setContent(plainTextToComposerContent(composerRef.current.input), {
|
|
418
|
+
emitUpdate: false
|
|
419
|
+
});
|
|
420
|
+
editorReadyRef.current = true;
|
|
421
|
+
return () => {
|
|
422
|
+
editorReadyRef.current = false;
|
|
423
|
+
};
|
|
424
|
+
}, [editor]);
|
|
425
|
+
useEffect(() => {
|
|
426
|
+
if (editor === null || !editorReadyRef.current) {
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
if (syncingFromEditorRef.current) {
|
|
430
|
+
syncingFromEditorRef.current = false;
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
const snapshot = composerSnapshotFromEditor(editor, composer.entities, composer.triggers);
|
|
434
|
+
if (snapshot.input === composer.input) {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
editor.commands.setContent(plainTextToComposerContent(composer.input), {
|
|
438
|
+
emitUpdate: false
|
|
439
|
+
});
|
|
440
|
+
}, [composer, editor]);
|
|
441
|
+
return renderPrimitive(
|
|
442
|
+
"div",
|
|
443
|
+
{
|
|
444
|
+
...props,
|
|
445
|
+
children: /* @__PURE__ */ jsx(EditorContent, { editor }),
|
|
446
|
+
"aria-disabled": composerDisabled ? true : props["aria-disabled"],
|
|
447
|
+
"data-anvia-composer-input": "",
|
|
448
|
+
"data-state": composerDisabled ? "disabled" : "enabled",
|
|
449
|
+
"data-auto-resize": autoResize ? "" : void 0,
|
|
450
|
+
onKeyDown: handleKeyDown,
|
|
451
|
+
style: inputStyle
|
|
452
|
+
},
|
|
453
|
+
ref
|
|
454
|
+
);
|
|
455
|
+
});
|
|
456
|
+
var ComposerTextareaInput = forwardRef(
|
|
457
|
+
function ComposerTextareaInput2({
|
|
458
|
+
autoResize = true,
|
|
459
|
+
maxRows: maxRowsProp,
|
|
460
|
+
minRows: minRowsProp,
|
|
461
|
+
onChange,
|
|
462
|
+
onKeyDown,
|
|
463
|
+
rows,
|
|
464
|
+
...props
|
|
465
|
+
}, ref) {
|
|
466
|
+
const composer = useComposer();
|
|
467
|
+
const inputRef = useRef(null);
|
|
468
|
+
const wasAutoResizeRef = useRef(autoResize);
|
|
469
|
+
const composedInputRef = useMemo(() => composeRefs(inputRef, ref), [ref]);
|
|
470
|
+
const minRows = normalizeRows(minRowsProp ?? rows ?? 1, 1);
|
|
471
|
+
const maxRows = normalizeRows(maxRowsProp ?? 6, minRows);
|
|
472
|
+
useEffect(() => {
|
|
473
|
+
const wasAutoResize = wasAutoResizeRef.current;
|
|
474
|
+
wasAutoResizeRef.current = autoResize;
|
|
475
|
+
if (!autoResize) {
|
|
476
|
+
if (wasAutoResize) {
|
|
477
|
+
resetComposerInputSize(inputRef.current);
|
|
478
|
+
}
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
resizeComposerInput(inputRef.current, { maxRows, minRows });
|
|
482
|
+
});
|
|
483
|
+
const handleChange = useCallback(
|
|
484
|
+
(event) => {
|
|
485
|
+
onChange?.(event);
|
|
486
|
+
if (!event.defaultPrevented) {
|
|
487
|
+
composer.setInput(event.currentTarget.value);
|
|
488
|
+
composer.setEntities([]);
|
|
489
|
+
if (autoResize) {
|
|
490
|
+
resizeComposerInput(event.currentTarget, { maxRows, minRows });
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
},
|
|
494
|
+
[autoResize, composer, maxRows, minRows, onChange]
|
|
495
|
+
);
|
|
496
|
+
const handleKeyDown = useCallback(
|
|
497
|
+
(event) => {
|
|
498
|
+
onKeyDown?.(event);
|
|
499
|
+
if (event.defaultPrevented || event.key !== "Enter" || event.shiftKey || event.nativeEvent.isComposing) {
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
event.preventDefault();
|
|
503
|
+
void composer.submit();
|
|
504
|
+
},
|
|
505
|
+
[composer, onKeyDown]
|
|
506
|
+
);
|
|
507
|
+
return renderPrimitive(
|
|
508
|
+
"textarea",
|
|
509
|
+
{
|
|
510
|
+
...props,
|
|
511
|
+
"aria-label": props["aria-label"] ?? "Message",
|
|
512
|
+
disabled: props.disabled ?? composer.status === "streaming",
|
|
513
|
+
onChange: handleChange,
|
|
514
|
+
onKeyDown: handleKeyDown,
|
|
515
|
+
rows: autoResize ? minRows : rows,
|
|
516
|
+
value: composer.input,
|
|
517
|
+
"data-anvia-composer-input": "",
|
|
518
|
+
"data-anvia-composer-textarea-input": ""
|
|
519
|
+
},
|
|
520
|
+
composedInputRef
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
);
|
|
524
|
+
var COMPOSER_ENTITY_NODE = "composerEntity";
|
|
525
|
+
var EMPTY_COMPOSER_TRIGGERS = [];
|
|
526
|
+
var ComposerEntityExtension = Mention.extend({
|
|
527
|
+
name: COMPOSER_ENTITY_NODE,
|
|
528
|
+
addAttributes() {
|
|
529
|
+
return {
|
|
530
|
+
id: {
|
|
531
|
+
default: null,
|
|
532
|
+
parseHTML: (element) => element.getAttribute("data-id"),
|
|
533
|
+
renderHTML: (attributes) => attributes.id === void 0 || attributes.id === null ? {} : { "data-id": String(attributes.id) }
|
|
534
|
+
},
|
|
535
|
+
label: {
|
|
536
|
+
default: null,
|
|
537
|
+
parseHTML: (element) => element.getAttribute("data-label"),
|
|
538
|
+
renderHTML: (attributes) => attributes.label === void 0 || attributes.label === null ? {} : { "data-label": String(attributes.label) }
|
|
539
|
+
},
|
|
540
|
+
mentionSuggestionChar: {
|
|
541
|
+
default: "@",
|
|
542
|
+
parseHTML: (element) => element.getAttribute("data-mention-suggestion-char"),
|
|
543
|
+
renderHTML: (attributes) => ({
|
|
544
|
+
"data-mention-suggestion-char": attributes.mentionSuggestionChar
|
|
545
|
+
})
|
|
546
|
+
},
|
|
547
|
+
triggerId: {
|
|
548
|
+
default: null,
|
|
549
|
+
parseHTML: (element) => element.getAttribute("data-trigger-id"),
|
|
550
|
+
renderHTML: (attributes) => attributes.triggerId === void 0 || attributes.triggerId === null ? {} : { "data-trigger-id": String(attributes.triggerId) }
|
|
551
|
+
},
|
|
552
|
+
text: {
|
|
553
|
+
default: null,
|
|
554
|
+
parseHTML: (element) => element.getAttribute("data-text"),
|
|
555
|
+
renderHTML: (attributes) => attributes.text === void 0 || attributes.text === null ? {} : { "data-text": String(attributes.text) }
|
|
556
|
+
},
|
|
557
|
+
data: {
|
|
558
|
+
default: null,
|
|
559
|
+
parseHTML: (element) => dataFromAttribute(element.getAttribute("data-entity-data")),
|
|
560
|
+
renderHTML: (attributes) => attributes.data === void 0 || attributes.data === null ? {} : { "data-entity-data": JSON.stringify(attributes.data) }
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
function composerInputExtensions({
|
|
566
|
+
composerRef,
|
|
567
|
+
placeholder,
|
|
568
|
+
triggers
|
|
569
|
+
}) {
|
|
570
|
+
return [
|
|
571
|
+
Document,
|
|
572
|
+
Paragraph,
|
|
573
|
+
Text,
|
|
574
|
+
HardBreak,
|
|
575
|
+
Placeholder.configure(placeholder === void 0 ? {} : { placeholder }),
|
|
576
|
+
ComposerEntityExtension.configure({
|
|
577
|
+
HTMLAttributes: {
|
|
578
|
+
"data-anvia-composer-entity": ""
|
|
579
|
+
},
|
|
580
|
+
deleteTriggerWithBackspace: true,
|
|
581
|
+
renderText: ({ node }) => composerEntityText(node.attrs),
|
|
582
|
+
renderHTML: ({ node, options }) => [
|
|
583
|
+
"span",
|
|
584
|
+
mergeAttributes(options.HTMLAttributes, {
|
|
585
|
+
"data-anvia-composer-entity": ""
|
|
586
|
+
}),
|
|
587
|
+
composerEntityText(node.attrs)
|
|
588
|
+
],
|
|
589
|
+
suggestions: triggers.map((trigger) => composerSuggestion(trigger, composerRef))
|
|
590
|
+
})
|
|
591
|
+
];
|
|
592
|
+
}
|
|
593
|
+
function submitComposerFromEditorKeyDown(event, composerRef) {
|
|
594
|
+
if (event.defaultPrevented || event.key !== "Enter" || event.shiftKey || event.isComposing || composerRef.current.activeTrigger !== void 0) {
|
|
595
|
+
return false;
|
|
596
|
+
}
|
|
597
|
+
event.preventDefault();
|
|
598
|
+
void composerRef.current.submit();
|
|
599
|
+
return true;
|
|
600
|
+
}
|
|
601
|
+
function composerSuggestion(trigger, composerRef) {
|
|
602
|
+
const pluginKey = new PluginKey(`anvia-composer-trigger-${trigger.id}`);
|
|
603
|
+
return {
|
|
604
|
+
pluginKey,
|
|
605
|
+
char: trigger.char,
|
|
606
|
+
...trigger.allowSpaces === void 0 ? {} : { allowSpaces: trigger.allowSpaces },
|
|
607
|
+
...trigger.allowedPrefixes === void 0 ? {} : { allowedPrefixes: trigger.allowedPrefixes },
|
|
608
|
+
...trigger.startOfLine === void 0 ? {} : { startOfLine: trigger.startOfLine },
|
|
609
|
+
...trigger.minQueryLength === void 0 ? {} : { minQueryLength: trigger.minQueryLength },
|
|
610
|
+
items: ({ query, signal }) => resolveTriggerItems(trigger, query, composerRef.current, signal),
|
|
611
|
+
command: ({
|
|
612
|
+
editor,
|
|
613
|
+
range,
|
|
614
|
+
props
|
|
615
|
+
}) => {
|
|
616
|
+
const nodeAfter = editor.view.state.selection.$to.nodeAfter;
|
|
617
|
+
const overrideSpace = nodeAfter?.text?.startsWith(" ");
|
|
618
|
+
const insertRange = {
|
|
619
|
+
from: range.from,
|
|
620
|
+
to: overrideSpace ? range.to + 1 : range.to
|
|
621
|
+
};
|
|
622
|
+
editor.chain().focus().insertContentAt(insertRange, [
|
|
623
|
+
{
|
|
624
|
+
type: COMPOSER_ENTITY_NODE,
|
|
625
|
+
attrs: props
|
|
626
|
+
},
|
|
627
|
+
{
|
|
628
|
+
type: "text",
|
|
629
|
+
text: " "
|
|
630
|
+
}
|
|
631
|
+
]).run();
|
|
632
|
+
editor.view.dom.ownerDocument.defaultView?.getSelection()?.collapseToEnd();
|
|
633
|
+
composerRef.current.setActiveTrigger(void 0);
|
|
634
|
+
},
|
|
635
|
+
render: () => ({
|
|
636
|
+
onStart: (props) => {
|
|
637
|
+
updateComposerTriggerState(trigger, props, composerRef);
|
|
638
|
+
},
|
|
639
|
+
onUpdate: (props) => {
|
|
640
|
+
updateComposerTriggerState(trigger, props, composerRef);
|
|
641
|
+
},
|
|
642
|
+
onExit: () => {
|
|
643
|
+
composerRef.current.setActiveTrigger(
|
|
644
|
+
(current) => current?.trigger.id === trigger.id ? void 0 : current
|
|
645
|
+
);
|
|
646
|
+
},
|
|
647
|
+
onKeyDown: (props) => handleComposerTriggerKeyDown(trigger, props, composerRef)
|
|
648
|
+
})
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
function resolveTriggerItems(trigger, query, composer, signal) {
|
|
652
|
+
if (Array.isArray(trigger.items)) {
|
|
653
|
+
const normalizedQuery = query.toLowerCase();
|
|
654
|
+
return trigger.items.filter((item) => item.label.toLowerCase().includes(normalizedQuery));
|
|
655
|
+
}
|
|
656
|
+
return trigger.items({
|
|
657
|
+
trigger,
|
|
658
|
+
query,
|
|
659
|
+
input: composer.input,
|
|
660
|
+
entities: composer.entities,
|
|
661
|
+
signal
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
function updateComposerTriggerState(trigger, props, composerRef) {
|
|
665
|
+
const current = composerRef.current.activeTrigger;
|
|
666
|
+
const selectedIndex = current?.trigger.id === trigger.id ? clampIndex(current.selectedIndex, props.items.length) : 0;
|
|
667
|
+
const rect = rectFromSuggestion(props);
|
|
668
|
+
composerRef.current.setActiveTrigger({
|
|
669
|
+
trigger,
|
|
670
|
+
query: props.query,
|
|
671
|
+
items: props.items,
|
|
672
|
+
loading: props.loading,
|
|
673
|
+
selectedIndex,
|
|
674
|
+
...rect === void 0 ? {} : { rect },
|
|
675
|
+
selectItem: (item) => {
|
|
676
|
+
if (item.disabled) {
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
props.command(triggerItemToEntityAttrs(trigger, item));
|
|
680
|
+
},
|
|
681
|
+
setSelectedIndex: (index) => {
|
|
682
|
+
composerRef.current.setActiveTrigger(
|
|
683
|
+
(active) => active?.trigger.id === trigger.id ? { ...active, selectedIndex: clampIndex(index, active.items.length) } : active
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
function handleComposerTriggerKeyDown(trigger, { event }, composerRef) {
|
|
689
|
+
const activeTrigger = composerRef.current.activeTrigger;
|
|
690
|
+
if (activeTrigger?.trigger.id !== trigger.id) {
|
|
691
|
+
return false;
|
|
692
|
+
}
|
|
693
|
+
if (event.key === "ArrowDown") {
|
|
694
|
+
event.preventDefault();
|
|
695
|
+
activeTrigger.setSelectedIndex(activeTrigger.selectedIndex + 1);
|
|
696
|
+
return true;
|
|
697
|
+
}
|
|
698
|
+
if (event.key === "ArrowUp") {
|
|
699
|
+
event.preventDefault();
|
|
700
|
+
activeTrigger.setSelectedIndex(activeTrigger.selectedIndex - 1);
|
|
701
|
+
return true;
|
|
702
|
+
}
|
|
703
|
+
if (event.key === "Enter" || event.key === "Tab") {
|
|
704
|
+
event.preventDefault();
|
|
705
|
+
const item = activeTrigger.items[activeTrigger.selectedIndex];
|
|
706
|
+
if (item !== void 0) {
|
|
707
|
+
activeTrigger.selectItem(item);
|
|
708
|
+
}
|
|
709
|
+
return true;
|
|
710
|
+
}
|
|
711
|
+
if (event.key === "Escape") {
|
|
712
|
+
composerRef.current.setActiveTrigger(void 0);
|
|
713
|
+
return false;
|
|
714
|
+
}
|
|
715
|
+
return false;
|
|
716
|
+
}
|
|
717
|
+
function triggerItemToEntityAttrs(trigger, item) {
|
|
718
|
+
return {
|
|
719
|
+
id: item.id,
|
|
720
|
+
label: item.label,
|
|
721
|
+
mentionSuggestionChar: trigger.char,
|
|
722
|
+
triggerId: trigger.id,
|
|
723
|
+
text: triggerItemText(trigger, item),
|
|
724
|
+
data: item.data
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
function triggerItemText(trigger, item) {
|
|
728
|
+
return item.text ?? `${trigger.char}${item.label}`;
|
|
729
|
+
}
|
|
730
|
+
function rectFromSuggestion(props) {
|
|
731
|
+
const rect = props.clientRect?.();
|
|
732
|
+
if (rect === void 0 || rect === null) {
|
|
733
|
+
return void 0;
|
|
734
|
+
}
|
|
735
|
+
return {
|
|
736
|
+
top: rect.top,
|
|
737
|
+
right: rect.right,
|
|
738
|
+
bottom: rect.bottom,
|
|
739
|
+
left: rect.left,
|
|
740
|
+
width: rect.width,
|
|
741
|
+
height: rect.height
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
function clampIndex(index, length) {
|
|
745
|
+
if (length <= 0) {
|
|
746
|
+
return 0;
|
|
747
|
+
}
|
|
748
|
+
return (index + length) % length;
|
|
749
|
+
}
|
|
750
|
+
function plainTextToComposerContent(input) {
|
|
751
|
+
const lines = input.length === 0 ? [""] : input.split(/\r?\n/);
|
|
752
|
+
return {
|
|
753
|
+
type: "doc",
|
|
754
|
+
content: lines.map((line) => ({
|
|
755
|
+
type: "paragraph",
|
|
756
|
+
...line.length === 0 ? {} : {
|
|
757
|
+
content: [
|
|
758
|
+
{
|
|
759
|
+
type: "text",
|
|
760
|
+
text: line
|
|
761
|
+
}
|
|
762
|
+
]
|
|
763
|
+
}
|
|
764
|
+
}))
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
function composerSnapshotFromEditor(editor, previousEntities, triggers) {
|
|
768
|
+
return composerSnapshotFromContent(editor.getJSON(), previousEntities, triggers);
|
|
769
|
+
}
|
|
770
|
+
function composerSnapshotFromContent(content, previousEntities, triggers) {
|
|
771
|
+
const entities = [];
|
|
772
|
+
let input = "";
|
|
773
|
+
let blockIndex = 0;
|
|
774
|
+
const appendNode = (node) => {
|
|
775
|
+
if (node.type === "doc") {
|
|
776
|
+
for (const child of node.content ?? []) {
|
|
777
|
+
appendNode(child);
|
|
778
|
+
}
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
if (node.type === "paragraph") {
|
|
782
|
+
if (blockIndex > 0) {
|
|
783
|
+
input += "\n";
|
|
784
|
+
}
|
|
785
|
+
blockIndex += 1;
|
|
786
|
+
for (const child of node.content ?? []) {
|
|
787
|
+
appendNode(child);
|
|
788
|
+
}
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
if (node.type === "text") {
|
|
792
|
+
input += node.text ?? "";
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
if (node.type === "hardBreak") {
|
|
796
|
+
input += "\n";
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
if (node.type === COMPOSER_ENTITY_NODE) {
|
|
800
|
+
const attrs = node.attrs;
|
|
801
|
+
const entity = composerEntityFromAttrs(attrs, input.length, previousEntities, triggers);
|
|
802
|
+
input += entity.text;
|
|
803
|
+
entities.push(entity);
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
for (const child of node.content ?? []) {
|
|
807
|
+
appendNode(child);
|
|
808
|
+
}
|
|
809
|
+
};
|
|
810
|
+
appendNode(content);
|
|
811
|
+
return { input, entities };
|
|
812
|
+
}
|
|
813
|
+
function composerEntityFromAttrs(attrs, from, previousEntities, triggers) {
|
|
814
|
+
const id = stringAttr(attrs?.id) ?? "";
|
|
815
|
+
const trigger = stringAttr(attrs?.mentionSuggestionChar) ?? "@";
|
|
816
|
+
const triggerId = stringAttr(attrs?.triggerId) ?? triggers.find((candidate) => candidate.char === trigger)?.id ?? trigger;
|
|
817
|
+
const label = stringAttr(attrs?.label) ?? id;
|
|
818
|
+
const text = composerEntityText(attrs);
|
|
819
|
+
const previous = previousEntities.find(
|
|
820
|
+
(entity) => entity.id === id && entity.triggerId === triggerId && entity.trigger === trigger
|
|
821
|
+
);
|
|
822
|
+
const data = attrs?.data ?? previous?.data;
|
|
823
|
+
return {
|
|
824
|
+
id,
|
|
825
|
+
triggerId,
|
|
826
|
+
trigger,
|
|
827
|
+
label,
|
|
828
|
+
text,
|
|
829
|
+
range: {
|
|
830
|
+
from,
|
|
831
|
+
to: from + text.length
|
|
832
|
+
},
|
|
833
|
+
...data === void 0 ? {} : { data }
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
function composerEntityText(attrs) {
|
|
837
|
+
const text = stringAttr(attrs?.text);
|
|
838
|
+
if (text !== void 0) {
|
|
839
|
+
return text;
|
|
840
|
+
}
|
|
841
|
+
const trigger = stringAttr(attrs?.mentionSuggestionChar) ?? "@";
|
|
842
|
+
return `${trigger}${stringAttr(attrs?.label) ?? stringAttr(attrs?.id) ?? ""}`;
|
|
843
|
+
}
|
|
844
|
+
function stringAttr(value) {
|
|
845
|
+
return typeof value === "string" ? value : void 0;
|
|
846
|
+
}
|
|
847
|
+
function dataFromAttribute(value) {
|
|
848
|
+
if (value === null) {
|
|
849
|
+
return null;
|
|
850
|
+
}
|
|
851
|
+
try {
|
|
852
|
+
return JSON.parse(value);
|
|
853
|
+
} catch {
|
|
854
|
+
return null;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
function resizeComposerInput(input, { maxRows, minRows }) {
|
|
858
|
+
if (input === null) {
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
const style = getComputedStyle(input);
|
|
862
|
+
const lineHeight = lineHeightFromStyle(style);
|
|
863
|
+
const paddingHeight = pixelValue(style.paddingTop) + pixelValue(style.paddingBottom);
|
|
864
|
+
const borderHeight = pixelValue(style.borderTopWidth) + pixelValue(style.borderBottomWidth);
|
|
865
|
+
const minContentHeight = lineHeight * minRows;
|
|
866
|
+
const maxContentHeight = Math.max(minContentHeight, lineHeight * maxRows);
|
|
867
|
+
input.style.height = "auto";
|
|
868
|
+
const scrollContentHeight = Math.max(input.scrollHeight - paddingHeight, 0);
|
|
869
|
+
const contentHeight = clamp(scrollContentHeight, minContentHeight, maxContentHeight);
|
|
870
|
+
const nextHeight = style.boxSizing === "border-box" ? contentHeight + paddingHeight + borderHeight : contentHeight;
|
|
871
|
+
input.style.height = `${Math.ceil(nextHeight)}px`;
|
|
872
|
+
input.style.overflowY = scrollContentHeight > maxContentHeight ? "auto" : "hidden";
|
|
873
|
+
}
|
|
874
|
+
function resetComposerInputSize(input) {
|
|
875
|
+
if (input === null) {
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
input.style.height = "";
|
|
879
|
+
input.style.overflowY = "";
|
|
880
|
+
}
|
|
881
|
+
function normalizeRows(value, min) {
|
|
882
|
+
if (!Number.isFinite(value)) {
|
|
883
|
+
return min;
|
|
884
|
+
}
|
|
885
|
+
return Math.max(min, Math.floor(value));
|
|
886
|
+
}
|
|
887
|
+
function lineHeightFromStyle(style) {
|
|
888
|
+
return pixelValue(style.lineHeight) || (pixelValue(style.fontSize) || 16) * 1.2;
|
|
889
|
+
}
|
|
890
|
+
function pixelValue(value) {
|
|
891
|
+
const parsed = Number.parseFloat(value);
|
|
892
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
893
|
+
}
|
|
894
|
+
function clamp(value, min, max) {
|
|
895
|
+
return Math.min(Math.max(value, min), max);
|
|
896
|
+
}
|
|
897
|
+
function promptWithQuote(prompt, quote) {
|
|
898
|
+
const quotedText = quote.text.split(/\r?\n/).map((line) => `> ${line}`).join("\n");
|
|
899
|
+
if (prompt.trim().length === 0) {
|
|
900
|
+
return quotedText;
|
|
901
|
+
}
|
|
902
|
+
return `${quotedText}
|
|
903
|
+
|
|
904
|
+
${prompt}`;
|
|
905
|
+
}
|
|
906
|
+
function normalizeQuote(quote) {
|
|
907
|
+
if (quote === void 0) {
|
|
908
|
+
return void 0;
|
|
909
|
+
}
|
|
910
|
+
return {
|
|
911
|
+
text: quote.text,
|
|
912
|
+
messageId: quote.messageId
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
function composerSubmitMetadata(quote, entities) {
|
|
916
|
+
if (quote === void 0 && entities.length === 0) {
|
|
917
|
+
return void 0;
|
|
918
|
+
}
|
|
919
|
+
return {
|
|
920
|
+
...quote === void 0 ? {} : { quote },
|
|
921
|
+
...entities.length === 0 ? {} : {
|
|
922
|
+
composer: {
|
|
923
|
+
entities
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
var ComposerSubmit = forwardRef(
|
|
929
|
+
function ComposerSubmit2(props, ref) {
|
|
930
|
+
const composer = useComposer();
|
|
931
|
+
const disabled = props.disabled ?? !composer.canSubmit;
|
|
932
|
+
return renderPrimitive(
|
|
933
|
+
"button",
|
|
934
|
+
{
|
|
935
|
+
...props,
|
|
936
|
+
children: props.children ?? "Send",
|
|
937
|
+
disabled,
|
|
938
|
+
type: props.type ?? "submit",
|
|
939
|
+
"data-anvia-submit": "",
|
|
940
|
+
"data-state": disabled ? "disabled" : "enabled"
|
|
941
|
+
},
|
|
942
|
+
ref
|
|
943
|
+
);
|
|
944
|
+
}
|
|
945
|
+
);
|
|
946
|
+
var ComposerStop = forwardRef(function ComposerStop2({ onClick, ...props }, ref) {
|
|
947
|
+
const composer = useComposer();
|
|
948
|
+
const disabled = props.disabled ?? !composer.canStop;
|
|
949
|
+
const handleClick = useCallback(
|
|
950
|
+
(event) => {
|
|
951
|
+
onClick?.(event);
|
|
952
|
+
if (event.defaultPrevented || disabled) {
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
composer.stop();
|
|
956
|
+
},
|
|
957
|
+
[composer, disabled, onClick]
|
|
958
|
+
);
|
|
959
|
+
return renderPrimitive(
|
|
960
|
+
"button",
|
|
961
|
+
{
|
|
962
|
+
...props,
|
|
963
|
+
children: props.children ?? "Stop",
|
|
964
|
+
disabled,
|
|
965
|
+
onClick: handleClick,
|
|
966
|
+
type: props.type ?? "button",
|
|
967
|
+
"data-anvia-stop": "",
|
|
968
|
+
"data-state": disabled ? "disabled" : "enabled"
|
|
969
|
+
},
|
|
970
|
+
ref
|
|
971
|
+
);
|
|
972
|
+
});
|
|
973
|
+
var ComposerTriggerMenu = forwardRef(
|
|
974
|
+
function ComposerTriggerMenu2({ children, keepMounted = false, style, ...props }, ref) {
|
|
975
|
+
const composer = useComposer();
|
|
976
|
+
const activeTrigger = composer.activeTrigger;
|
|
977
|
+
const empty = activeTrigger === void 0;
|
|
978
|
+
if (empty && !keepMounted) {
|
|
979
|
+
return null;
|
|
980
|
+
}
|
|
981
|
+
const menuStyle = activeTrigger?.rect === void 0 ? style : {
|
|
982
|
+
position: "fixed",
|
|
983
|
+
top: activeTrigger.rect.bottom,
|
|
984
|
+
left: activeTrigger.rect.left,
|
|
985
|
+
...style
|
|
986
|
+
};
|
|
987
|
+
const renderedChildren = activeTrigger === void 0 ? typeof children === "function" ? null : children : typeof children === "function" ? children(activeTrigger) : children ?? activeTrigger.items.map((item, index) => /* @__PURE__ */ jsx(ComposerTriggerItem, { index }, `${item.id}:${index.toString()}`));
|
|
988
|
+
return renderPrimitive(
|
|
989
|
+
"div",
|
|
990
|
+
{
|
|
991
|
+
...props,
|
|
992
|
+
children: renderedChildren,
|
|
993
|
+
role: props.role ?? "listbox",
|
|
994
|
+
style: menuStyle,
|
|
995
|
+
"data-anvia-composer-trigger-menu": "",
|
|
996
|
+
"data-empty": empty ? "" : void 0,
|
|
997
|
+
"data-loading": activeTrigger?.loading ? "" : void 0,
|
|
998
|
+
"data-trigger": activeTrigger?.trigger.char,
|
|
999
|
+
"data-trigger-id": activeTrigger?.trigger.id
|
|
1000
|
+
},
|
|
1001
|
+
ref
|
|
1002
|
+
);
|
|
1003
|
+
}
|
|
1004
|
+
);
|
|
1005
|
+
var ComposerTriggerItem = forwardRef(
|
|
1006
|
+
function ComposerTriggerItem2({ children, index, item: itemProp, onClick, onMouseEnter, ...props }, ref) {
|
|
1007
|
+
const composer = useComposer();
|
|
1008
|
+
const activeTrigger = composer.activeTrigger;
|
|
1009
|
+
const itemIndex = index ?? 0;
|
|
1010
|
+
const item = itemProp ?? activeTrigger?.items[itemIndex];
|
|
1011
|
+
if (activeTrigger === void 0 || item === void 0) {
|
|
1012
|
+
return null;
|
|
1013
|
+
}
|
|
1014
|
+
const selected = activeTrigger.selectedIndex === itemIndex;
|
|
1015
|
+
const disabled = props.disabled ?? item.disabled ?? false;
|
|
1016
|
+
const renderedChildren = typeof children === "function" ? children(item, activeTrigger) : children ?? item.label;
|
|
1017
|
+
const handleMouseEnter = (event) => {
|
|
1018
|
+
onMouseEnter?.(event);
|
|
1019
|
+
if (!event.defaultPrevented && !disabled) {
|
|
1020
|
+
activeTrigger.setSelectedIndex(itemIndex);
|
|
1021
|
+
}
|
|
1022
|
+
};
|
|
1023
|
+
const handleClick = (event) => {
|
|
1024
|
+
onClick?.(event);
|
|
1025
|
+
if (!event.defaultPrevented && !disabled) {
|
|
1026
|
+
activeTrigger.selectItem(item);
|
|
1027
|
+
}
|
|
1028
|
+
};
|
|
1029
|
+
return renderPrimitive(
|
|
1030
|
+
"button",
|
|
1031
|
+
{
|
|
1032
|
+
...props,
|
|
1033
|
+
children: renderedChildren,
|
|
1034
|
+
disabled,
|
|
1035
|
+
onClick: handleClick,
|
|
1036
|
+
onMouseEnter: handleMouseEnter,
|
|
1037
|
+
role: props.role ?? "option",
|
|
1038
|
+
type: props.type ?? "button",
|
|
1039
|
+
"aria-selected": selected,
|
|
1040
|
+
"data-anvia-composer-trigger-item": "",
|
|
1041
|
+
"data-disabled": disabled ? "" : void 0,
|
|
1042
|
+
"data-item-id": item.id,
|
|
1043
|
+
"data-selected": selected ? "" : void 0,
|
|
1044
|
+
"data-trigger": activeTrigger.trigger.char,
|
|
1045
|
+
"data-trigger-id": activeTrigger.trigger.id
|
|
1046
|
+
},
|
|
1047
|
+
ref
|
|
1048
|
+
);
|
|
1049
|
+
}
|
|
1050
|
+
);
|
|
1051
|
+
var ComposerAttachments = forwardRef(
|
|
1052
|
+
function ComposerAttachments2({ children, keepMounted = false, ...props }, ref) {
|
|
1053
|
+
const composer = useComposer();
|
|
1054
|
+
const empty = composer.attachments.length === 0;
|
|
1055
|
+
if (empty && !keepMounted) {
|
|
1056
|
+
return null;
|
|
1057
|
+
}
|
|
1058
|
+
return renderPrimitive(
|
|
1059
|
+
"div",
|
|
1060
|
+
{
|
|
1061
|
+
...props,
|
|
1062
|
+
children: composer.attachments.map((attachment) => /* @__PURE__ */ jsx(
|
|
1063
|
+
InternalAttachmentProvider,
|
|
1064
|
+
{
|
|
1065
|
+
value: {
|
|
1066
|
+
attachment,
|
|
1067
|
+
remove: () => {
|
|
1068
|
+
composer.removeAttachment(attachment.id);
|
|
1069
|
+
}
|
|
1070
|
+
},
|
|
1071
|
+
children: typeof children === "function" ? children(attachment) : children ?? /* @__PURE__ */ jsx(Attachment.Root, {})
|
|
1072
|
+
},
|
|
1073
|
+
attachment.id
|
|
1074
|
+
)),
|
|
1075
|
+
"data-anvia-composer-attachments": "",
|
|
1076
|
+
"data-empty": empty ? "" : void 0
|
|
1077
|
+
},
|
|
1078
|
+
ref
|
|
1079
|
+
);
|
|
1080
|
+
}
|
|
1081
|
+
);
|
|
1082
|
+
var ComposerAttachmentInput = forwardRef(
|
|
1083
|
+
function ComposerAttachmentInput2({ onChange, ...props }, ref) {
|
|
1084
|
+
const composer = useComposer();
|
|
1085
|
+
const disabled = props.disabled ?? composer.status === "streaming";
|
|
1086
|
+
const handleChange = useCallback(
|
|
1087
|
+
async (event) => {
|
|
1088
|
+
onChange?.(event);
|
|
1089
|
+
if (event.defaultPrevented) {
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
1092
|
+
const files = Array.from(event.currentTarget.files ?? []);
|
|
1093
|
+
event.currentTarget.value = "";
|
|
1094
|
+
for (const file of files) {
|
|
1095
|
+
await composer.addAttachment(file);
|
|
1096
|
+
}
|
|
1097
|
+
},
|
|
1098
|
+
[composer, onChange]
|
|
1099
|
+
);
|
|
1100
|
+
return renderPrimitive(
|
|
1101
|
+
"input",
|
|
1102
|
+
{
|
|
1103
|
+
...props,
|
|
1104
|
+
disabled,
|
|
1105
|
+
onChange: (event) => {
|
|
1106
|
+
void handleChange(event);
|
|
1107
|
+
},
|
|
1108
|
+
type: "file",
|
|
1109
|
+
"data-anvia-attachment-input": "",
|
|
1110
|
+
"data-state": disabled ? "disabled" : "enabled"
|
|
1111
|
+
},
|
|
1112
|
+
ref
|
|
1113
|
+
);
|
|
1114
|
+
}
|
|
1115
|
+
);
|
|
1116
|
+
var ComposerAddAttachment = forwardRef(
|
|
1117
|
+
function ComposerAddAttachment2({ accept, multiple = false, onClick, ...props }, ref) {
|
|
1118
|
+
const composer = useComposer();
|
|
1119
|
+
const inputRef = useRef(null);
|
|
1120
|
+
const disabled = props.disabled ?? composer.status === "streaming";
|
|
1121
|
+
const handleClick = useCallback(
|
|
1122
|
+
(event) => {
|
|
1123
|
+
onClick?.(event);
|
|
1124
|
+
if (event.defaultPrevented || disabled) {
|
|
1125
|
+
return;
|
|
1126
|
+
}
|
|
1127
|
+
inputRef.current?.click();
|
|
1128
|
+
},
|
|
1129
|
+
[disabled, onClick]
|
|
1130
|
+
);
|
|
1131
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1132
|
+
renderPrimitive(
|
|
1133
|
+
"button",
|
|
1134
|
+
{
|
|
1135
|
+
...props,
|
|
1136
|
+
children: props.children ?? "Attach",
|
|
1137
|
+
disabled,
|
|
1138
|
+
onClick: handleClick,
|
|
1139
|
+
type: props.type ?? "button",
|
|
1140
|
+
"data-anvia-add-attachment": "",
|
|
1141
|
+
"data-state": disabled ? "disabled" : "enabled"
|
|
1142
|
+
},
|
|
1143
|
+
ref
|
|
1144
|
+
),
|
|
1145
|
+
/* @__PURE__ */ jsx(
|
|
1146
|
+
ComposerAttachmentInput,
|
|
1147
|
+
{
|
|
1148
|
+
accept,
|
|
1149
|
+
"aria-hidden": "true",
|
|
1150
|
+
hidden: true,
|
|
1151
|
+
multiple,
|
|
1152
|
+
ref: inputRef,
|
|
1153
|
+
tabIndex: -1
|
|
1154
|
+
}
|
|
1155
|
+
)
|
|
1156
|
+
] });
|
|
1157
|
+
}
|
|
1158
|
+
);
|
|
1159
|
+
var ComposerAttachmentDropzone = forwardRef(
|
|
1160
|
+
function ComposerAttachmentDropzone2({ disabled: disabledProp = false, onDragLeave, onDragOver, onDrop, ...props }, ref) {
|
|
1161
|
+
const composer = useComposer();
|
|
1162
|
+
const [dragging, setDragging] = useState(false);
|
|
1163
|
+
const disabled = disabledProp || composer.status === "streaming";
|
|
1164
|
+
const handleDragOver = useCallback(
|
|
1165
|
+
(event) => {
|
|
1166
|
+
onDragOver?.(event);
|
|
1167
|
+
if (event.defaultPrevented || disabled) {
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1170
|
+
event.preventDefault();
|
|
1171
|
+
setDragging(true);
|
|
1172
|
+
},
|
|
1173
|
+
[disabled, onDragOver]
|
|
1174
|
+
);
|
|
1175
|
+
const handleDragLeave = useCallback(
|
|
1176
|
+
(event) => {
|
|
1177
|
+
onDragLeave?.(event);
|
|
1178
|
+
if (!event.defaultPrevented) {
|
|
1179
|
+
setDragging(false);
|
|
1180
|
+
}
|
|
1181
|
+
},
|
|
1182
|
+
[onDragLeave]
|
|
1183
|
+
);
|
|
1184
|
+
const handleDrop = useCallback(
|
|
1185
|
+
(event) => {
|
|
1186
|
+
onDrop?.(event);
|
|
1187
|
+
if (event.defaultPrevented || disabled) {
|
|
1188
|
+
return;
|
|
1189
|
+
}
|
|
1190
|
+
event.preventDefault();
|
|
1191
|
+
setDragging(false);
|
|
1192
|
+
const files = Array.from(event.dataTransfer.files);
|
|
1193
|
+
void Promise.all(files.map((file) => composer.addAttachment(file)));
|
|
1194
|
+
},
|
|
1195
|
+
[composer, disabled, onDrop]
|
|
1196
|
+
);
|
|
1197
|
+
return renderPrimitive(
|
|
1198
|
+
"div",
|
|
1199
|
+
{
|
|
1200
|
+
...props,
|
|
1201
|
+
onDragLeave: handleDragLeave,
|
|
1202
|
+
onDragOver: handleDragOver,
|
|
1203
|
+
onDrop: handleDrop,
|
|
1204
|
+
"data-anvia-attachment-dropzone": "",
|
|
1205
|
+
"data-dragging": dragging ? "" : void 0,
|
|
1206
|
+
"data-state": disabled ? "disabled" : "enabled"
|
|
1207
|
+
},
|
|
1208
|
+
ref
|
|
1209
|
+
);
|
|
1210
|
+
}
|
|
1211
|
+
);
|
|
1212
|
+
function createAttachment(attachment) {
|
|
1213
|
+
return {
|
|
1214
|
+
...attachment,
|
|
1215
|
+
id: attachment.id ?? createAttachmentId()
|
|
1216
|
+
};
|
|
1217
|
+
}
|
|
1218
|
+
function isFileAttachmentInput(attachment) {
|
|
1219
|
+
return typeof File !== "undefined" && attachment instanceof File;
|
|
1220
|
+
}
|
|
1221
|
+
async function attachmentFromFile(file) {
|
|
1222
|
+
return {
|
|
1223
|
+
id: createAttachmentId(),
|
|
1224
|
+
type: file.type.startsWith("image/") ? "image" : "document",
|
|
1225
|
+
name: file.name,
|
|
1226
|
+
mediaType: file.type.length > 0 ? file.type : "application/octet-stream",
|
|
1227
|
+
data: await readFileData(file)
|
|
1228
|
+
};
|
|
1229
|
+
}
|
|
1230
|
+
async function readFileData(file) {
|
|
1231
|
+
const dataUrl = await new Promise((resolve, reject) => {
|
|
1232
|
+
const reader = new FileReader();
|
|
1233
|
+
reader.addEventListener("load", () => {
|
|
1234
|
+
resolve(String(reader.result ?? ""));
|
|
1235
|
+
});
|
|
1236
|
+
reader.addEventListener("error", () => {
|
|
1237
|
+
reject(reader.error ?? new Error("Failed to read attachment."));
|
|
1238
|
+
});
|
|
1239
|
+
reader.readAsDataURL(file);
|
|
1240
|
+
});
|
|
1241
|
+
return dataUrl.includes(",") ? dataUrl.slice(dataUrl.indexOf(",") + 1) : dataUrl;
|
|
1242
|
+
}
|
|
1243
|
+
var nextAttachmentId = 0;
|
|
1244
|
+
function createAttachmentId() {
|
|
1245
|
+
const random = globalThis.crypto?.randomUUID?.();
|
|
1246
|
+
if (random !== void 0) {
|
|
1247
|
+
return `attachment_${random}`;
|
|
1248
|
+
}
|
|
1249
|
+
nextAttachmentId += 1;
|
|
1250
|
+
return `attachment_${nextAttachmentId.toString(36)}`;
|
|
1251
|
+
}
|
|
1252
|
+
var Composer = {
|
|
1253
|
+
Root: ComposerRoot,
|
|
1254
|
+
Quote: ComposerQuotePreview,
|
|
1255
|
+
ClearQuote: ComposerClearQuote,
|
|
1256
|
+
Input: ComposerInput,
|
|
1257
|
+
TextareaInput: ComposerTextareaInput,
|
|
1258
|
+
TriggerMenu: ComposerTriggerMenu,
|
|
1259
|
+
TriggerItem: ComposerTriggerItem,
|
|
1260
|
+
Attachments: ComposerAttachments,
|
|
1261
|
+
AddAttachment: ComposerAddAttachment,
|
|
1262
|
+
AttachmentInput: ComposerAttachmentInput,
|
|
1263
|
+
AttachmentDropzone: ComposerAttachmentDropzone,
|
|
1264
|
+
Submit: ComposerSubmit,
|
|
1265
|
+
Stop: ComposerStop
|
|
1266
|
+
};
|
|
1267
|
+
|
|
1268
|
+
// src/chat/thread.tsx
|
|
1269
|
+
import {
|
|
1270
|
+
Fragment as Fragment2,
|
|
1271
|
+
forwardRef as forwardRef2,
|
|
1272
|
+
useCallback as useCallback2,
|
|
1273
|
+
useEffect as useEffect2,
|
|
1274
|
+
useMemo as useMemo2,
|
|
1275
|
+
useRef as useRef2,
|
|
1276
|
+
useState as useState2
|
|
1277
|
+
} from "react";
|
|
1278
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1279
|
+
var ThreadRoot = forwardRef2(
|
|
1280
|
+
function ThreadRoot2(props, ref) {
|
|
1281
|
+
const chat = useChatContext();
|
|
1282
|
+
const viewportRef = useRef2(null);
|
|
1283
|
+
const [atBottom, setAtBottom] = useState2(true);
|
|
1284
|
+
const scrollToBottom = useCallback2((behavior = "smooth") => {
|
|
1285
|
+
const viewport = viewportRef.current;
|
|
1286
|
+
if (viewport === null) {
|
|
1287
|
+
return;
|
|
1288
|
+
}
|
|
1289
|
+
viewport.scrollTo({ top: viewport.scrollHeight, behavior });
|
|
1290
|
+
}, []);
|
|
1291
|
+
const thread = useMemo2(
|
|
1292
|
+
() => ({ viewportRef, atBottom, setAtBottom, scrollToBottom }),
|
|
1293
|
+
[atBottom, scrollToBottom]
|
|
1294
|
+
);
|
|
1295
|
+
return /* @__PURE__ */ jsx2(InternalThreadProvider, { value: thread, children: renderPrimitive(
|
|
1296
|
+
"div",
|
|
1297
|
+
{
|
|
1298
|
+
...props,
|
|
1299
|
+
"data-anvia-thread": "",
|
|
1300
|
+
"data-state": chat.status
|
|
1301
|
+
},
|
|
1302
|
+
ref
|
|
1303
|
+
) });
|
|
1304
|
+
}
|
|
1305
|
+
);
|
|
1306
|
+
var ThreadViewport = forwardRef2(function ThreadViewport2({ autoScroll = true, onScroll, ...props }, ref) {
|
|
1307
|
+
const chat = useChatContext();
|
|
1308
|
+
const thread = useThread();
|
|
1309
|
+
const messages = chat.messages;
|
|
1310
|
+
const composedRef = useMemo2(
|
|
1311
|
+
() => composeRefs(ref, (node) => {
|
|
1312
|
+
thread.viewportRef.current = node;
|
|
1313
|
+
}),
|
|
1314
|
+
[ref, thread.viewportRef]
|
|
1315
|
+
);
|
|
1316
|
+
useEffect2(() => {
|
|
1317
|
+
const hasMessages = messages.length > 0;
|
|
1318
|
+
if (autoScroll && (thread.atBottom || !hasMessages)) {
|
|
1319
|
+
thread.scrollToBottom("auto");
|
|
1320
|
+
}
|
|
1321
|
+
}, [autoScroll, messages, thread]);
|
|
1322
|
+
const handleScroll = useCallback2(
|
|
1323
|
+
(event) => {
|
|
1324
|
+
onScroll?.(event);
|
|
1325
|
+
const node = event.currentTarget;
|
|
1326
|
+
const distanceFromBottom = node.scrollHeight - node.scrollTop - node.clientHeight;
|
|
1327
|
+
thread.setAtBottom(distanceFromBottom < 8);
|
|
1328
|
+
},
|
|
1329
|
+
[onScroll, thread]
|
|
1330
|
+
);
|
|
1331
|
+
return renderPrimitive(
|
|
1332
|
+
"div",
|
|
1333
|
+
{
|
|
1334
|
+
...props,
|
|
1335
|
+
onScroll: handleScroll,
|
|
1336
|
+
"data-anvia-thread-viewport": "",
|
|
1337
|
+
"data-state": thread.atBottom ? "bottom" : "away"
|
|
1338
|
+
},
|
|
1339
|
+
composedRef
|
|
1340
|
+
);
|
|
1341
|
+
});
|
|
1342
|
+
var ThreadViewportFooter = forwardRef2(
|
|
1343
|
+
function ThreadViewportFooter2(props, ref) {
|
|
1344
|
+
return renderPrimitive(
|
|
1345
|
+
"div",
|
|
1346
|
+
{
|
|
1347
|
+
...props,
|
|
1348
|
+
"data-anvia-thread-viewport-footer": ""
|
|
1349
|
+
},
|
|
1350
|
+
ref
|
|
1351
|
+
);
|
|
1352
|
+
}
|
|
1353
|
+
);
|
|
1354
|
+
var ThreadEmpty = forwardRef2(
|
|
1355
|
+
function ThreadEmpty2(props, ref) {
|
|
1356
|
+
const chat = useChatContext();
|
|
1357
|
+
if (chat.messages.length > 0) {
|
|
1358
|
+
return null;
|
|
1359
|
+
}
|
|
1360
|
+
return renderPrimitive(
|
|
1361
|
+
"div",
|
|
1362
|
+
{
|
|
1363
|
+
...props,
|
|
1364
|
+
"data-anvia-thread-empty": ""
|
|
1365
|
+
},
|
|
1366
|
+
ref
|
|
1367
|
+
);
|
|
1368
|
+
}
|
|
1369
|
+
);
|
|
1370
|
+
var ThreadStatus = forwardRef2(function ThreadStatus2({ children, ...props }, ref) {
|
|
1371
|
+
const chat = useChatContext();
|
|
1372
|
+
const renderedChildren = typeof children === "function" ? children(chat.status) : children;
|
|
1373
|
+
return renderPrimitive(
|
|
1374
|
+
"div",
|
|
1375
|
+
{
|
|
1376
|
+
...props,
|
|
1377
|
+
children: renderedChildren ?? chat.status,
|
|
1378
|
+
"data-anvia-thread-status": "",
|
|
1379
|
+
"data-state": chat.status
|
|
1380
|
+
},
|
|
1381
|
+
ref
|
|
1382
|
+
);
|
|
1383
|
+
});
|
|
1384
|
+
var ThreadLoading = forwardRef2(
|
|
1385
|
+
function ThreadLoading2(props, ref) {
|
|
1386
|
+
const chat = useChatContext();
|
|
1387
|
+
if (chat.status !== "streaming") {
|
|
1388
|
+
return null;
|
|
1389
|
+
}
|
|
1390
|
+
return renderPrimitive(
|
|
1391
|
+
"div",
|
|
1392
|
+
{
|
|
1393
|
+
...props,
|
|
1394
|
+
children: props.children ?? "Loading",
|
|
1395
|
+
"data-anvia-thread-loading": ""
|
|
1396
|
+
},
|
|
1397
|
+
ref
|
|
1398
|
+
);
|
|
1399
|
+
}
|
|
1400
|
+
);
|
|
1401
|
+
var ThreadError = forwardRef2(function ThreadError2({ children, ...props }, ref) {
|
|
1402
|
+
const chat = useChatContext();
|
|
1403
|
+
if (chat.status !== "error" && chat.error === void 0) {
|
|
1404
|
+
return null;
|
|
1405
|
+
}
|
|
1406
|
+
const renderedChildren = typeof children === "function" ? children(chat.error) : children;
|
|
1407
|
+
return renderPrimitive(
|
|
1408
|
+
"div",
|
|
1409
|
+
{
|
|
1410
|
+
...props,
|
|
1411
|
+
children: renderedChildren ?? errorMessage(chat.error),
|
|
1412
|
+
"data-anvia-thread-error": "",
|
|
1413
|
+
role: props.role ?? "alert"
|
|
1414
|
+
},
|
|
1415
|
+
ref
|
|
1416
|
+
);
|
|
1417
|
+
});
|
|
1418
|
+
var ThreadMessages = forwardRef2(function ThreadMessages2({ children, keepMounted = true, ...props }, ref) {
|
|
1419
|
+
const chat = useChatContext();
|
|
1420
|
+
const empty = chat.messages.length === 0;
|
|
1421
|
+
if (empty && !keepMounted) {
|
|
1422
|
+
return null;
|
|
1423
|
+
}
|
|
1424
|
+
return renderPrimitive(
|
|
1425
|
+
"div",
|
|
1426
|
+
{
|
|
1427
|
+
...props,
|
|
1428
|
+
children: chat.messages.map((message) => /* @__PURE__ */ jsx2(InternalMessageProvider, { message, children: typeof children === "function" ? children(message) : children ?? defaultMessage() }, message.id)),
|
|
1429
|
+
"data-anvia-thread-messages": "",
|
|
1430
|
+
"data-empty": empty ? "" : void 0
|
|
1431
|
+
},
|
|
1432
|
+
ref
|
|
1433
|
+
);
|
|
1434
|
+
});
|
|
1435
|
+
var ThreadSuggestions = forwardRef2(
|
|
1436
|
+
function ThreadSuggestions2({ children, keepMounted = false, ...props }, ref) {
|
|
1437
|
+
const chat = useChatContext();
|
|
1438
|
+
const suggestions = chat.suggestions ?? [];
|
|
1439
|
+
const empty = suggestions.length === 0;
|
|
1440
|
+
if (empty && !keepMounted) {
|
|
1441
|
+
return null;
|
|
1442
|
+
}
|
|
1443
|
+
return renderPrimitive(
|
|
1444
|
+
"div",
|
|
1445
|
+
{
|
|
1446
|
+
...props,
|
|
1447
|
+
children: suggestions.map(
|
|
1448
|
+
(suggestion) => typeof children === "function" ? /* @__PURE__ */ jsx2(Fragment2, { children: children(suggestion) }, suggestion.id) : /* @__PURE__ */ jsx2(ThreadSuggestion, { suggestion, children }, suggestion.id)
|
|
1449
|
+
),
|
|
1450
|
+
"data-anvia-thread-suggestions": "",
|
|
1451
|
+
"data-empty": empty ? "" : void 0
|
|
1452
|
+
},
|
|
1453
|
+
ref
|
|
1454
|
+
);
|
|
1455
|
+
}
|
|
1456
|
+
);
|
|
1457
|
+
var ThreadSuggestion = forwardRef2(
|
|
1458
|
+
function ThreadSuggestion2({ onClick, prompt, suggestion, ...props }, ref) {
|
|
1459
|
+
const chat = useChatContext();
|
|
1460
|
+
const suggestionPrompt = prompt ?? suggestion?.prompt ?? "";
|
|
1461
|
+
const disabled = props.disabled ?? (suggestionPrompt.length === 0 || chat.status === "streaming");
|
|
1462
|
+
const handleClick = useCallback2(
|
|
1463
|
+
(event) => {
|
|
1464
|
+
onClick?.(event);
|
|
1465
|
+
if (event.defaultPrevented || disabled) {
|
|
1466
|
+
return;
|
|
1467
|
+
}
|
|
1468
|
+
void chat.sendMessage(suggestionPrompt);
|
|
1469
|
+
},
|
|
1470
|
+
[chat, disabled, onClick, suggestionPrompt]
|
|
1471
|
+
);
|
|
1472
|
+
return renderPrimitive(
|
|
1473
|
+
"button",
|
|
1474
|
+
{
|
|
1475
|
+
...props,
|
|
1476
|
+
children: props.children ?? suggestion?.label ?? suggestionPrompt,
|
|
1477
|
+
disabled,
|
|
1478
|
+
onClick: handleClick,
|
|
1479
|
+
type: props.type ?? "button",
|
|
1480
|
+
"data-anvia-thread-suggestion": "",
|
|
1481
|
+
"data-state": disabled ? "disabled" : "enabled"
|
|
1482
|
+
},
|
|
1483
|
+
ref
|
|
1484
|
+
);
|
|
1485
|
+
}
|
|
1486
|
+
);
|
|
1487
|
+
var ThreadScrollToBottom = forwardRef2(
|
|
1488
|
+
function ThreadScrollToBottom2({ onClick, ...props }, ref) {
|
|
1489
|
+
const thread = useThread();
|
|
1490
|
+
const disabled = props.disabled ?? thread.atBottom;
|
|
1491
|
+
const handleClick = useCallback2(
|
|
1492
|
+
(event) => {
|
|
1493
|
+
onClick?.(event);
|
|
1494
|
+
if (event.defaultPrevented || disabled) {
|
|
1495
|
+
return;
|
|
1496
|
+
}
|
|
1497
|
+
thread.scrollToBottom();
|
|
1498
|
+
},
|
|
1499
|
+
[disabled, onClick, thread]
|
|
1500
|
+
);
|
|
1501
|
+
return renderPrimitive(
|
|
1502
|
+
"button",
|
|
1503
|
+
{
|
|
1504
|
+
...props,
|
|
1505
|
+
children: props.children ?? "Scroll to bottom",
|
|
1506
|
+
disabled,
|
|
1507
|
+
onClick: handleClick,
|
|
1508
|
+
type: props.type ?? "button",
|
|
1509
|
+
"data-anvia-scroll-to-bottom": "",
|
|
1510
|
+
"data-state": thread.atBottom ? "bottom" : "away"
|
|
1511
|
+
},
|
|
1512
|
+
ref
|
|
1513
|
+
);
|
|
1514
|
+
}
|
|
1515
|
+
);
|
|
1516
|
+
function defaultMessage() {
|
|
1517
|
+
return /* @__PURE__ */ jsxs2(Message.Root, { children: [
|
|
1518
|
+
/* @__PURE__ */ jsx2(Message.Content, { children: /* @__PURE__ */ jsx2(Message.Parts, {}) }),
|
|
1519
|
+
/* @__PURE__ */ jsx2(Message.Actions, {})
|
|
1520
|
+
] });
|
|
1521
|
+
}
|
|
1522
|
+
function errorMessage(error) {
|
|
1523
|
+
if (error instanceof Error) {
|
|
1524
|
+
return error.message;
|
|
1525
|
+
}
|
|
1526
|
+
if (typeof error === "string") {
|
|
1527
|
+
return error;
|
|
1528
|
+
}
|
|
1529
|
+
return "Something went wrong.";
|
|
1530
|
+
}
|
|
1531
|
+
var Thread = {
|
|
1532
|
+
Root: ThreadRoot,
|
|
1533
|
+
Viewport: ThreadViewport,
|
|
1534
|
+
ViewportFooter: ThreadViewportFooter,
|
|
1535
|
+
Messages: ThreadMessages,
|
|
1536
|
+
Empty: ThreadEmpty,
|
|
1537
|
+
Status: ThreadStatus,
|
|
1538
|
+
Loading: ThreadLoading,
|
|
1539
|
+
Error: ThreadError,
|
|
1540
|
+
Suggestions: ThreadSuggestions,
|
|
1541
|
+
Suggestion: ThreadSuggestion,
|
|
1542
|
+
ScrollToBottom: ThreadScrollToBottom
|
|
1543
|
+
};
|
|
1544
|
+
|
|
1545
|
+
export {
|
|
1546
|
+
Composer,
|
|
1547
|
+
Thread
|
|
1548
|
+
};
|
|
1549
|
+
//# sourceMappingURL=chunk-KO36TK2O.js.map
|