@assistant-ui/react 0.0.13 → 0.0.15
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/index.d.mts +37 -29
- package/dist/index.d.ts +37 -29
- package/dist/index.js +317 -259
- package/dist/index.mjs +322 -263
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
@@ -35,7 +35,7 @@ var useAssistantContext = () => {
|
|
35
35
|
const context = useContext(AssistantContext);
|
36
36
|
if (!context)
|
37
37
|
throw new Error(
|
38
|
-
"
|
38
|
+
"This component must be used within a AssistantProvider."
|
39
39
|
);
|
40
40
|
return context;
|
41
41
|
};
|
@@ -44,14 +44,10 @@ var useAssistantContext = () => {
|
|
44
44
|
var useThreadIf = (props) => {
|
45
45
|
const { useThread } = useAssistantContext();
|
46
46
|
return useThread((thread) => {
|
47
|
-
if (props.empty === true && thread.messages.length !== 0)
|
48
|
-
|
49
|
-
if (props.
|
50
|
-
|
51
|
-
if (props.running === true && !thread.isRunning)
|
52
|
-
return false;
|
53
|
-
if (props.running === false && thread.isRunning)
|
54
|
-
return false;
|
47
|
+
if (props.empty === true && thread.messages.length !== 0) return false;
|
48
|
+
if (props.empty === false && thread.messages.length === 0) return false;
|
49
|
+
if (props.running === true && !thread.isRunning) return false;
|
50
|
+
if (props.running === false && thread.isRunning) return false;
|
55
51
|
return true;
|
56
52
|
});
|
57
53
|
};
|
@@ -81,8 +77,7 @@ var useOnResizeContent = (ref, callback) => {
|
|
81
77
|
callbackRef.current = callback;
|
82
78
|
useLayoutEffect(() => {
|
83
79
|
const el = ref.current;
|
84
|
-
if (!el)
|
85
|
-
return;
|
80
|
+
if (!el) return;
|
86
81
|
const resizeObserver = new ResizeObserver(() => {
|
87
82
|
callbackRef.current();
|
88
83
|
});
|
@@ -137,16 +132,14 @@ var ThreadViewport = forwardRef2(({ autoScroll = true, onScroll, children, ...re
|
|
137
132
|
const lastScrollTop = useRef3(0);
|
138
133
|
const scrollToBottom = () => {
|
139
134
|
const div = messagesEndRef.current;
|
140
|
-
if (!div || !autoScroll)
|
141
|
-
return;
|
135
|
+
if (!div || !autoScroll) return;
|
142
136
|
const behavior = firstRenderRef.current ? "instant" : "auto";
|
143
137
|
firstRenderRef.current = false;
|
144
138
|
useViewport.setState({ isAtBottom: true });
|
145
139
|
div.scrollIntoView({ behavior });
|
146
140
|
};
|
147
141
|
useOnResizeContent(divRef, () => {
|
148
|
-
if (!useViewport.getState().isAtBottom)
|
149
|
-
return;
|
142
|
+
if (!useViewport.getState().isAtBottom) return;
|
150
143
|
scrollToBottom();
|
151
144
|
});
|
152
145
|
useOnScrollToBottom(() => {
|
@@ -154,8 +147,7 @@ var ThreadViewport = forwardRef2(({ autoScroll = true, onScroll, children, ...re
|
|
154
147
|
});
|
155
148
|
const handleScroll = () => {
|
156
149
|
const div = divRef.current;
|
157
|
-
if (!div)
|
158
|
-
return;
|
150
|
+
if (!div) return;
|
159
151
|
const isAtBottom = useViewport.getState().isAtBottom;
|
160
152
|
const newIsAtBottom = div.scrollHeight - div.scrollTop <= div.clientHeight;
|
161
153
|
if (!newIsAtBottom && lastScrollTop.current < div.scrollTop) {
|
@@ -187,7 +179,7 @@ var MessageContext = createContext2(null);
|
|
187
179
|
var useMessageContext = () => {
|
188
180
|
const context = useContext2(MessageContext);
|
189
181
|
if (!context)
|
190
|
-
throw new Error("
|
182
|
+
throw new Error("This component must be used within a MessageProvider.");
|
191
183
|
return context;
|
192
184
|
};
|
193
185
|
|
@@ -205,10 +197,8 @@ var useComposerContext = () => {
|
|
205
197
|
var useComposerIf = (props) => {
|
206
198
|
const { useComposer } = useComposerContext();
|
207
199
|
return useComposer((composer) => {
|
208
|
-
if (props.editing === true && !composer.isEditing)
|
209
|
-
|
210
|
-
if (props.editing === false && composer.isEditing)
|
211
|
-
return false;
|
200
|
+
if (props.editing === true && !composer.isEditing) return false;
|
201
|
+
if (props.editing === false && composer.isEditing) return false;
|
212
202
|
return true;
|
213
203
|
});
|
214
204
|
};
|
@@ -230,6 +220,14 @@ __export(message_exports, {
|
|
230
220
|
import { useMemo, useState } from "react";
|
231
221
|
import { create as create2 } from "zustand";
|
232
222
|
|
223
|
+
// src/utils/context/getMessageText.tsx
|
224
|
+
var getMessageText = (message) => {
|
225
|
+
const textParts = message.content.filter(
|
226
|
+
(part) => part.type === "text"
|
227
|
+
);
|
228
|
+
return textParts.map((part) => part.text).join("\n\n");
|
229
|
+
};
|
230
|
+
|
233
231
|
// src/utils/context/stores/ComposerStore.ts
|
234
232
|
import {
|
235
233
|
create
|
@@ -256,8 +254,7 @@ var makeMessageComposerStore = ({
|
|
256
254
|
onSend(value);
|
257
255
|
},
|
258
256
|
cancel: () => {
|
259
|
-
if (!get().isEditing)
|
260
|
-
return false;
|
257
|
+
if (!get().isEditing) return false;
|
261
258
|
set({ isEditing: false });
|
262
259
|
return true;
|
263
260
|
}
|
@@ -276,8 +273,7 @@ var makeThreadComposerStore = (useThread) => create()((set, get, store) => {
|
|
276
273
|
},
|
277
274
|
cancel: () => {
|
278
275
|
const thread = useThread.getState();
|
279
|
-
if (!thread.isRunning)
|
280
|
-
return false;
|
276
|
+
if (!thread.isRunning) return false;
|
281
277
|
useThread.getState().cancelRun();
|
282
278
|
return true;
|
283
279
|
}
|
@@ -292,31 +288,42 @@ var getIsLast = (thread, message) => {
|
|
292
288
|
var useMessageContext2 = () => {
|
293
289
|
const { useThread } = useAssistantContext();
|
294
290
|
const [context] = useState(() => {
|
295
|
-
const useMessage = create2(() => ({
|
291
|
+
const useMessage = create2((set) => ({
|
296
292
|
message: null,
|
293
|
+
parentId: null,
|
297
294
|
branches: [],
|
298
295
|
isLast: false,
|
299
296
|
isCopied: false,
|
300
297
|
isHovering: false,
|
301
|
-
setIsCopied: () => {
|
298
|
+
setIsCopied: (value) => {
|
299
|
+
set({ isCopied: value });
|
302
300
|
},
|
303
|
-
setIsHovering: () => {
|
301
|
+
setIsHovering: (value) => {
|
302
|
+
set({ isHovering: value });
|
304
303
|
}
|
305
304
|
}));
|
306
305
|
const useComposer = makeMessageComposerStore({
|
307
306
|
onEdit: () => {
|
308
307
|
const message = useMessage.getState().message;
|
309
308
|
if (message.role !== "user")
|
310
|
-
throw new Error(
|
311
|
-
|
312
|
-
|
313
|
-
|
309
|
+
throw new Error(
|
310
|
+
"Tried to edit a non-user message. Editing is only supported for user messages. This is likely an internal bug in assistant-ui."
|
311
|
+
);
|
312
|
+
const text = getMessageText(message);
|
313
|
+
return text;
|
314
314
|
},
|
315
315
|
onSend: (text) => {
|
316
|
-
const message = useMessage.getState()
|
316
|
+
const { message, parentId } = useMessage.getState();
|
317
|
+
if (message.role !== "user")
|
318
|
+
throw new Error(
|
319
|
+
"Tried to edit a non-user message. Editing is only supported for user messages. This is likely an internal bug in assistant-ui."
|
320
|
+
);
|
321
|
+
const nonTextParts = message.content.filter(
|
322
|
+
(part) => part.type !== "text" && part.type !== "ui"
|
323
|
+
);
|
317
324
|
useThread.getState().append({
|
318
|
-
parentId
|
319
|
-
content: [{ type: "text", text }]
|
325
|
+
parentId,
|
326
|
+
content: [{ type: "text", text }, ...nonTextParts]
|
320
327
|
});
|
321
328
|
}
|
322
329
|
});
|
@@ -326,28 +333,21 @@ var useMessageContext2 = () => {
|
|
326
333
|
};
|
327
334
|
var MessageProvider = ({
|
328
335
|
message,
|
336
|
+
parentId,
|
329
337
|
children
|
330
338
|
}) => {
|
331
339
|
const { useThread } = useAssistantContext();
|
332
340
|
const context = useMessageContext2();
|
333
341
|
const isLast = useThread((thread) => getIsLast(thread, message));
|
334
342
|
const branches = useThread((thread) => thread.getBranches(message.id));
|
335
|
-
const [isCopied, setIsCopied] = useState(false);
|
336
|
-
const [isHovering, setIsHovering] = useState(false);
|
337
343
|
useMemo(() => {
|
338
|
-
context.useMessage.setState(
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
setIsCopied,
|
346
|
-
setIsHovering
|
347
|
-
},
|
348
|
-
true
|
349
|
-
);
|
350
|
-
}, [context, message, branches, isLast, isCopied, isHovering]);
|
344
|
+
context.useMessage.setState({
|
345
|
+
message,
|
346
|
+
parentId,
|
347
|
+
branches,
|
348
|
+
isLast
|
349
|
+
});
|
350
|
+
}, [context, message, parentId, branches, isLast]);
|
351
351
|
return /* @__PURE__ */ jsx4(MessageContext.Provider, { value: context, children });
|
352
352
|
};
|
353
353
|
|
@@ -384,18 +384,12 @@ var MessageRoot = forwardRef3(
|
|
384
384
|
var useMessageIf = (props) => {
|
385
385
|
const { useMessage } = useMessageContext();
|
386
386
|
return useMessage(({ message, branches, isLast, isCopied, isHovering }) => {
|
387
|
-
if (props.hasBranches === true && branches.length < 2)
|
388
|
-
|
389
|
-
if (props.
|
390
|
-
|
391
|
-
if (props.
|
392
|
-
|
393
|
-
if (props.lastOrHover === true && !isHovering && !isLast)
|
394
|
-
return false;
|
395
|
-
if (props.copied === true && !isCopied)
|
396
|
-
return false;
|
397
|
-
if (props.copied === false && isCopied)
|
398
|
-
return false;
|
387
|
+
if (props.hasBranches === true && branches.length < 2) return false;
|
388
|
+
if (props.user && message.role !== "user") return false;
|
389
|
+
if (props.assistant && message.role !== "assistant") return false;
|
390
|
+
if (props.lastOrHover === true && !isHovering && !isLast) return false;
|
391
|
+
if (props.copied === true && !isCopied) return false;
|
392
|
+
if (props.copied === false && isCopied) return false;
|
399
393
|
return true;
|
400
394
|
});
|
401
395
|
};
|
@@ -457,18 +451,23 @@ var ThreadMessages = ({ components }) => {
|
|
457
451
|
const thread = useThread();
|
458
452
|
const messages = thread.messages;
|
459
453
|
const { UserMessage, EditComposer, AssistantMessage } = getComponents(components);
|
460
|
-
if (messages.length === 0)
|
461
|
-
return null;
|
454
|
+
if (messages.length === 0) return null;
|
462
455
|
return /* @__PURE__ */ jsx7(Fragment2, { children: messages.map((message, idx) => {
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
456
|
+
const parentId = messages[idx - 1]?.id ?? null;
|
457
|
+
return /* @__PURE__ */ jsxs2(
|
458
|
+
MessageProvider,
|
459
|
+
{
|
460
|
+
message,
|
461
|
+
parentId,
|
462
|
+
children: [
|
463
|
+
/* @__PURE__ */ jsxs2(MessageIf, { user: true, children: [
|
464
|
+
/* @__PURE__ */ jsx7(ComposerIf, { editing: false, children: /* @__PURE__ */ jsx7(UserMessage, {}) }),
|
465
|
+
/* @__PURE__ */ jsx7(ComposerIf, { editing: true, children: /* @__PURE__ */ jsx7(EditComposer, {}) })
|
466
|
+
] }),
|
467
|
+
/* @__PURE__ */ jsx7(MessageIf, { assistant: true, children: /* @__PURE__ */ jsx7(AssistantMessage, {}) })
|
468
|
+
]
|
469
|
+
},
|
470
|
+
parentId ?? "__ROOT__"
|
472
471
|
);
|
473
472
|
}) });
|
474
473
|
};
|
@@ -552,8 +551,7 @@ var ComposerRoot = forwardRef6(
|
|
552
551
|
const ref = useComposedRefs2(forwardedRef, formRef);
|
553
552
|
const handleSubmit = (e) => {
|
554
553
|
const composerState = useComposer.getState();
|
555
|
-
if (!composerState.isEditing)
|
556
|
-
return;
|
554
|
+
if (!composerState.isEditing) return;
|
557
555
|
e.preventDefault();
|
558
556
|
composerState.send();
|
559
557
|
useViewport.getState().scrollToBottom();
|
@@ -582,18 +580,16 @@ import {
|
|
582
580
|
import TextareaAutosize from "react-textarea-autosize";
|
583
581
|
import { jsx as jsx11 } from "react/jsx-runtime";
|
584
582
|
var ComposerInput = forwardRef7(
|
585
|
-
({ autoFocus, asChild, disabled, onChange, onKeyDown, ...rest }, forwardedRef) => {
|
583
|
+
({ autoFocus = false, asChild, disabled, onChange, onKeyDown, ...rest }, forwardedRef) => {
|
586
584
|
const { useThread, useViewport } = useAssistantContext();
|
587
585
|
const { useComposer, type } = useComposerContext();
|
588
586
|
const value = useComposer((c) => {
|
589
|
-
if (!c.isEditing)
|
590
|
-
return "";
|
587
|
+
if (!c.isEditing) return "";
|
591
588
|
return c.value;
|
592
589
|
});
|
593
590
|
const Component = asChild ? Slot : TextareaAutosize;
|
594
591
|
const handleKeyPress = (e) => {
|
595
|
-
if (disabled)
|
596
|
-
return;
|
592
|
+
if (disabled) return;
|
597
593
|
const composer = useComposer.getState();
|
598
594
|
if (e.key === "Escape") {
|
599
595
|
if (useComposer.getState().cancel()) {
|
@@ -610,11 +606,10 @@ var ComposerInput = forwardRef7(
|
|
610
606
|
};
|
611
607
|
const textareaRef = useRef5(null);
|
612
608
|
const ref = useComposedRefs3(forwardedRef, textareaRef);
|
613
|
-
const autoFocusEnabled = autoFocus
|
609
|
+
const autoFocusEnabled = autoFocus && !disabled;
|
614
610
|
const focus = useCallback(() => {
|
615
611
|
const textarea = textareaRef.current;
|
616
|
-
if (!textarea || !autoFocusEnabled)
|
617
|
-
return;
|
612
|
+
if (!textarea || !autoFocusEnabled) return;
|
618
613
|
textarea.focus();
|
619
614
|
textarea.setSelectionRange(
|
620
615
|
textareaRef.current.value.length,
|
@@ -636,8 +631,7 @@ var ComposerInput = forwardRef7(
|
|
636
631
|
disabled,
|
637
632
|
onChange: composeEventHandlers6(onChange, (e) => {
|
638
633
|
const composerState = useComposer.getState();
|
639
|
-
if (!composerState.isEditing)
|
640
|
-
return;
|
634
|
+
if (!composerState.isEditing) return;
|
641
635
|
return composerState.setValue(e.target.value);
|
642
636
|
}),
|
643
637
|
onKeyDown: composeEventHandlers6(onKeyDown, handleKeyPress)
|
@@ -735,8 +729,7 @@ var useGoToNextBranch = () => {
|
|
735
729
|
[useMessage, useComposer],
|
736
730
|
(m, c) => c.isEditing || m.branches.indexOf(m.message.id) + 1 >= m.branches.length
|
737
731
|
);
|
738
|
-
if (disabled)
|
739
|
-
return null;
|
732
|
+
if (disabled) return null;
|
740
733
|
return () => {
|
741
734
|
const { message, branches } = useMessage.getState();
|
742
735
|
useThread.getState().switchToBranch(branches[branches.indexOf(message.id) + 1]);
|
@@ -779,8 +772,7 @@ var useGoToPreviousBranch = () => {
|
|
779
772
|
[useMessage, useComposer],
|
780
773
|
(m, c) => c.isEditing || m.branches.indexOf(m.message.id) <= 0
|
781
774
|
);
|
782
|
-
if (disabled)
|
783
|
-
return null;
|
775
|
+
if (disabled) return null;
|
784
776
|
return () => {
|
785
777
|
const { message, branches } = useMessage.getState();
|
786
778
|
useThread.getState().switchToBranch(
|
@@ -840,20 +832,16 @@ var ActionBarRoot = forwardRef12(({ hideWhenRunning, autohide, autohideFloat, ..
|
|
840
832
|
const hideAndfloatStatus = useCombinedStore(
|
841
833
|
[useThread, useMessage],
|
842
834
|
(t, m) => {
|
843
|
-
if (hideWhenRunning && t.isRunning)
|
844
|
-
return "hidden" /* Hidden */;
|
835
|
+
if (hideWhenRunning && t.isRunning) return "hidden" /* Hidden */;
|
845
836
|
const autohideEnabled = autohide === "always" || autohide === "not-last" && !m.isLast;
|
846
|
-
if (!autohideEnabled)
|
847
|
-
|
848
|
-
if (!m.isHovering)
|
849
|
-
return "hidden" /* Hidden */;
|
837
|
+
if (!autohideEnabled) return "normal" /* Normal */;
|
838
|
+
if (!m.isHovering) return "hidden" /* Hidden */;
|
850
839
|
if (autohideFloat === "always" || autohideFloat === "single-branch" && m.branches.length <= 1)
|
851
840
|
return "floating" /* Floating */;
|
852
841
|
return "normal" /* Normal */;
|
853
842
|
}
|
854
843
|
);
|
855
|
-
if (hideAndfloatStatus === "hidden" /* Hidden */)
|
856
|
-
return null;
|
844
|
+
if (hideAndfloatStatus === "hidden" /* Hidden */) return null;
|
857
845
|
return /* @__PURE__ */ jsx18(
|
858
846
|
Primitive11.div,
|
859
847
|
{
|
@@ -867,14 +855,18 @@ var ActionBarRoot = forwardRef12(({ hideWhenRunning, autohide, autohideFloat, ..
|
|
867
855
|
// src/actions/useCopyMessage.tsx
|
868
856
|
var useCopyMessage = ({ copiedDuration = 3e3 }) => {
|
869
857
|
const { useMessage, useComposer } = useMessageContext();
|
870
|
-
const
|
871
|
-
|
872
|
-
|
858
|
+
const hasCopyableContent = useCombinedStore(
|
859
|
+
[useMessage, useComposer],
|
860
|
+
(m, c) => {
|
861
|
+
return c.isEditing || m.message.content.some((c2) => c2.type === "text");
|
862
|
+
}
|
863
|
+
);
|
864
|
+
if (!hasCopyableContent) return null;
|
873
865
|
return () => {
|
866
|
+
const { isEditing, value: composerValue } = useComposer.getState();
|
874
867
|
const { message, setIsCopied } = useMessage.getState();
|
875
|
-
|
876
|
-
|
877
|
-
navigator.clipboard.writeText(message.content[0].text);
|
868
|
+
const valueToCopy = isEditing ? composerValue : getMessageText(message);
|
869
|
+
navigator.clipboard.writeText(valueToCopy);
|
878
870
|
setIsCopied(true);
|
879
871
|
setTimeout(() => setIsCopied(false), copiedDuration);
|
880
872
|
};
|
@@ -891,13 +883,10 @@ var useReloadMessage = () => {
|
|
891
883
|
[useThread, useMessage],
|
892
884
|
(t, m) => t.isRunning || m.message.role !== "assistant"
|
893
885
|
);
|
894
|
-
if (disabled)
|
895
|
-
return null;
|
886
|
+
if (disabled) return null;
|
896
887
|
return () => {
|
897
|
-
const
|
898
|
-
|
899
|
-
throw new Error("Reloading is only supported on assistant messages");
|
900
|
-
useThread.getState().startRun(message.parentId);
|
888
|
+
const { parentId } = useMessage.getState();
|
889
|
+
useThread.getState().startRun(parentId);
|
901
890
|
useViewport.getState().scrollToBottom();
|
902
891
|
};
|
903
892
|
};
|
@@ -912,8 +901,7 @@ var useBeginMessageEdit = () => {
|
|
912
901
|
[useMessage, useComposer],
|
913
902
|
(m, c) => m.message.role !== "user" || c.isEditing
|
914
903
|
);
|
915
|
-
if (disabled)
|
916
|
-
return null;
|
904
|
+
if (disabled) return null;
|
917
905
|
return () => {
|
918
906
|
const { edit } = useComposer.getState();
|
919
907
|
edit();
|
@@ -924,7 +912,7 @@ var useBeginMessageEdit = () => {
|
|
924
912
|
var ActionBarEdit = createActionButton(useBeginMessageEdit);
|
925
913
|
|
926
914
|
// src/adapters/vercel/VercelAIAssistantProvider.tsx
|
927
|
-
import {
|
915
|
+
import { useMemo as useMemo4 } from "react";
|
928
916
|
|
929
917
|
// src/adapters/vercel/useDummyAIAssistantContext.tsx
|
930
918
|
import { useState as useState2 } from "react";
|
@@ -961,13 +949,13 @@ var makeDummyThreadStore = () => {
|
|
961
949
|
switchToBranch: () => {
|
962
950
|
throw new Error("Not implemented");
|
963
951
|
},
|
964
|
-
append:
|
952
|
+
append: () => {
|
965
953
|
throw new Error("Not implemented");
|
966
954
|
},
|
967
|
-
|
955
|
+
startRun: () => {
|
968
956
|
throw new Error("Not implemented");
|
969
957
|
},
|
970
|
-
|
958
|
+
cancelRun: () => {
|
971
959
|
throw new Error("Not implemented");
|
972
960
|
}
|
973
961
|
}));
|
@@ -982,7 +970,7 @@ var useDummyAIAssistantContext = () => {
|
|
982
970
|
return context;
|
983
971
|
};
|
984
972
|
|
985
|
-
// src/adapters/vercel/
|
973
|
+
// src/adapters/vercel/useVercelAIThreadState.tsx
|
986
974
|
import { useCallback as useCallback2, useMemo as useMemo3, useRef as useRef6, useState as useState3 } from "react";
|
987
975
|
|
988
976
|
// src/adapters/MessageRepository.tsx
|
@@ -995,8 +983,7 @@ var optimisticPrefix = "__optimistic__";
|
|
995
983
|
var generateOptimisticId = () => `${optimisticPrefix}${generateId()}`;
|
996
984
|
var isOptimisticId = (id) => id.startsWith(optimisticPrefix);
|
997
985
|
var findHead = (message) => {
|
998
|
-
if (message.next)
|
999
|
-
return findHead(message.next);
|
986
|
+
if (message.next) return findHead(message.next);
|
1000
987
|
return message;
|
1001
988
|
};
|
1002
989
|
var MessageRepository = class {
|
@@ -1011,19 +998,21 @@ var MessageRepository = class {
|
|
1011
998
|
}
|
1012
999
|
return messages;
|
1013
1000
|
}
|
1014
|
-
addOrUpdateMessage(message) {
|
1001
|
+
addOrUpdateMessage(parentId, message) {
|
1015
1002
|
const item = this.messages.get(message.id);
|
1016
1003
|
if (item) {
|
1017
|
-
if (item.current.
|
1004
|
+
if ((item.prev?.current.id ?? null) !== parentId) {
|
1018
1005
|
this.deleteMessage(message.id);
|
1019
1006
|
} else {
|
1020
1007
|
item.current = message;
|
1021
1008
|
return;
|
1022
1009
|
}
|
1023
1010
|
}
|
1024
|
-
const prev =
|
1011
|
+
const prev = parentId ? this.messages.get(parentId) : null;
|
1025
1012
|
if (prev === void 0)
|
1026
|
-
throw new Error(
|
1013
|
+
throw new Error(
|
1014
|
+
"MessageRepository(addOrUpdateMessage): Parent message not found. This is likely an internal bug in assistant-ui."
|
1015
|
+
);
|
1027
1016
|
const newItem = {
|
1028
1017
|
prev,
|
1029
1018
|
current: message,
|
@@ -1045,7 +1034,9 @@ var MessageRepository = class {
|
|
1045
1034
|
deleteMessage(messageId) {
|
1046
1035
|
const message = this.messages.get(messageId);
|
1047
1036
|
if (!message)
|
1048
|
-
throw new Error(
|
1037
|
+
throw new Error(
|
1038
|
+
"MessageRepository(deleteMessage): Message not found. This is likely an internal bug in assistant-ui."
|
1039
|
+
);
|
1049
1040
|
if (message.children.length > 0) {
|
1050
1041
|
for (const child of message.children) {
|
1051
1042
|
this.deleteMessage(child);
|
@@ -1060,7 +1051,9 @@ var MessageRepository = class {
|
|
1060
1051
|
const childId = message.prev.children.at(-1);
|
1061
1052
|
const child = childId ? this.messages.get(childId) : null;
|
1062
1053
|
if (child === void 0)
|
1063
|
-
throw new Error(
|
1054
|
+
throw new Error(
|
1055
|
+
"MessageRepository(deleteMessage): Child message not found. This is likely an internal bug in assistant-ui."
|
1056
|
+
);
|
1064
1057
|
message.prev.next = child;
|
1065
1058
|
}
|
1066
1059
|
} else {
|
@@ -1070,16 +1063,16 @@ var MessageRepository = class {
|
|
1070
1063
|
this.head = message.prev ? findHead(message.prev) : null;
|
1071
1064
|
}
|
1072
1065
|
}
|
1073
|
-
getOptimisticId
|
1066
|
+
getOptimisticId() {
|
1074
1067
|
let optimisticId;
|
1075
1068
|
do {
|
1076
1069
|
optimisticId = generateOptimisticId();
|
1077
1070
|
} while (this.messages.has(optimisticId));
|
1078
1071
|
return optimisticId;
|
1079
|
-
}
|
1072
|
+
}
|
1080
1073
|
commitOptimisticRun(parentId) {
|
1081
1074
|
const optimisticId = this.getOptimisticId();
|
1082
|
-
this.addOrUpdateMessage({
|
1075
|
+
this.addOrUpdateMessage(parentId, {
|
1083
1076
|
id: optimisticId,
|
1084
1077
|
role: "assistant",
|
1085
1078
|
content: [
|
@@ -1088,7 +1081,6 @@ var MessageRepository = class {
|
|
1088
1081
|
text: ""
|
1089
1082
|
}
|
1090
1083
|
],
|
1091
|
-
parentId,
|
1092
1084
|
createdAt: /* @__PURE__ */ new Date()
|
1093
1085
|
});
|
1094
1086
|
return optimisticId;
|
@@ -1096,7 +1088,9 @@ var MessageRepository = class {
|
|
1096
1088
|
getBranches(messageId) {
|
1097
1089
|
const message = this.messages.get(messageId);
|
1098
1090
|
if (!message)
|
1099
|
-
throw new Error(
|
1091
|
+
throw new Error(
|
1092
|
+
"MessageRepository(getBranches): Message not found. This is likely an internal bug in assistant-ui."
|
1093
|
+
);
|
1100
1094
|
if (message.prev) {
|
1101
1095
|
return message.prev.children;
|
1102
1096
|
}
|
@@ -1105,7 +1099,9 @@ var MessageRepository = class {
|
|
1105
1099
|
switchToBranch(messageId) {
|
1106
1100
|
const message = this.messages.get(messageId);
|
1107
1101
|
if (!message)
|
1108
|
-
throw new Error(
|
1102
|
+
throw new Error(
|
1103
|
+
"MessageRepository(switchToBranch): Branch not found. This is likely an internal bug in assistant-ui."
|
1104
|
+
);
|
1109
1105
|
if (message.prev) {
|
1110
1106
|
message.prev.next = message;
|
1111
1107
|
}
|
@@ -1115,7 +1111,9 @@ var MessageRepository = class {
|
|
1115
1111
|
if (messageId) {
|
1116
1112
|
const message = this.messages.get(messageId);
|
1117
1113
|
if (!message)
|
1118
|
-
throw new Error(
|
1114
|
+
throw new Error(
|
1115
|
+
"MessageRepository(resetHead): Branch not found. This is likely an internal bug in assistant-ui."
|
1116
|
+
);
|
1119
1117
|
this.head = message;
|
1120
1118
|
for (let current = message; current; current = current.prev) {
|
1121
1119
|
if (current.prev) {
|
@@ -1128,151 +1126,171 @@ var MessageRepository = class {
|
|
1128
1126
|
}
|
1129
1127
|
};
|
1130
1128
|
|
1131
|
-
// src/adapters/
|
1129
|
+
// src/adapters/ThreadMessageConverter.tsx
|
1130
|
+
var ThreadMessageConverter = class {
|
1131
|
+
constructor(converter2) {
|
1132
|
+
this.converter = converter2;
|
1133
|
+
}
|
1134
|
+
cache = /* @__PURE__ */ new WeakMap();
|
1135
|
+
convertMessages(messages) {
|
1136
|
+
return messages.map((m) => {
|
1137
|
+
const cached = this.cache.get(m);
|
1138
|
+
if (cached) return cached;
|
1139
|
+
const newMessage = this.converter(m);
|
1140
|
+
this.cache.set(m, newMessage);
|
1141
|
+
return newMessage;
|
1142
|
+
});
|
1143
|
+
}
|
1144
|
+
};
|
1145
|
+
|
1146
|
+
// src/adapters/vercel/useVercelAIThreadState.tsx
|
1147
|
+
var vercelToThreadMessage = (message) => {
|
1148
|
+
if (message.role !== "user" && message.role !== "assistant")
|
1149
|
+
throw new Error(
|
1150
|
+
`You have a message with an unsupported role. The role ${message.role} is not supported.`
|
1151
|
+
);
|
1152
|
+
return {
|
1153
|
+
id: message.id,
|
1154
|
+
role: message.role,
|
1155
|
+
content: [
|
1156
|
+
...message.content ? [{ type: "text", text: message.content }] : [],
|
1157
|
+
...message.toolInvocations?.map((t) => ({
|
1158
|
+
type: "tool-call",
|
1159
|
+
name: t.toolName,
|
1160
|
+
args: t.args,
|
1161
|
+
result: "result" in t ? t.result : void 0
|
1162
|
+
})) ?? []
|
1163
|
+
],
|
1164
|
+
// ignore type mismatch for now
|
1165
|
+
createdAt: message.createdAt ?? /* @__PURE__ */ new Date(),
|
1166
|
+
innerMessage: message
|
1167
|
+
};
|
1168
|
+
};
|
1169
|
+
var converter = new ThreadMessageConverter(vercelToThreadMessage);
|
1132
1170
|
var sliceMessagesUntil = (messages, messageId) => {
|
1133
|
-
if (messageId == null)
|
1134
|
-
|
1135
|
-
if (isOptimisticId(messageId))
|
1136
|
-
return messages;
|
1171
|
+
if (messageId == null) return [];
|
1172
|
+
if (isOptimisticId(messageId)) return messages;
|
1137
1173
|
const messageIdx = messages.findIndex((m) => m.id === messageId);
|
1138
1174
|
if (messageIdx === -1)
|
1139
|
-
throw new Error(
|
1175
|
+
throw new Error(
|
1176
|
+
"useVercelAIThreadState: Message not found. This is liekly an internal bug in assistant-ui."
|
1177
|
+
);
|
1140
1178
|
return messages.slice(0, messageIdx + 1);
|
1141
1179
|
};
|
1142
1180
|
var hasUpcomingMessage = (isRunning, messages) => {
|
1143
1181
|
return isRunning && messages[messages.length - 1]?.role !== "assistant";
|
1144
1182
|
};
|
1145
|
-
var
|
1183
|
+
var getIsRunning = (vercel) => {
|
1184
|
+
if ("isLoading" in vercel) return vercel.isLoading;
|
1185
|
+
return vercel.status === "in_progress";
|
1186
|
+
};
|
1187
|
+
var useVercelAIThreadState = (vercel) => {
|
1146
1188
|
const [data] = useState3(() => new MessageRepository());
|
1147
|
-
const
|
1189
|
+
const vercelRef = useRef6(vercel);
|
1190
|
+
vercelRef.current = vercel;
|
1191
|
+
const isRunning = getIsRunning(vercelRef.current);
|
1148
1192
|
const assistantOptimisticIdRef = useRef6(null);
|
1149
|
-
const
|
1150
|
-
|
1151
|
-
|
1193
|
+
const messages = useMemo3(() => {
|
1194
|
+
const vm = converter.convertMessages(vercel.messages);
|
1195
|
+
for (let i = 0; i < vm.length; i++) {
|
1196
|
+
const message = vm[i];
|
1197
|
+
const parent = vm[i - 1];
|
1198
|
+
data.addOrUpdateMessage(parent?.id ?? null, message);
|
1152
1199
|
}
|
1153
1200
|
if (assistantOptimisticIdRef.current) {
|
1154
1201
|
data.deleteMessage(assistantOptimisticIdRef.current);
|
1155
1202
|
assistantOptimisticIdRef.current = null;
|
1156
1203
|
}
|
1157
|
-
if (hasUpcomingMessage(isRunning,
|
1204
|
+
if (hasUpcomingMessage(isRunning, vm)) {
|
1158
1205
|
assistantOptimisticIdRef.current = data.commitOptimisticRun(
|
1159
|
-
|
1206
|
+
vm.at(-1)?.id ?? null
|
1160
1207
|
);
|
1161
1208
|
}
|
1162
|
-
data.resetHead(
|
1163
|
-
assistantOptimisticIdRef.current ?? messages.at(-1)?.id ?? null
|
1164
|
-
);
|
1209
|
+
data.resetHead(assistantOptimisticIdRef.current ?? vm.at(-1)?.id ?? null);
|
1165
1210
|
return data.getMessages();
|
1166
|
-
}, [data, isRunning, messages]);
|
1167
|
-
const
|
1211
|
+
}, [data, isRunning, vercel.messages]);
|
1212
|
+
const getBranches2 = useCallback2(
|
1168
1213
|
(messageId) => {
|
1169
1214
|
return data.getBranches(messageId);
|
1170
1215
|
},
|
1171
1216
|
[data]
|
1172
1217
|
);
|
1173
|
-
const
|
1218
|
+
const switchToBranch2 = useCallback2(
|
1174
1219
|
(messageId) => {
|
1175
1220
|
data.switchToBranch(messageId);
|
1176
|
-
|
1221
|
+
vercelRef.current.setMessages(
|
1177
1222
|
data.getMessages().filter((m) => !isOptimisticId(m.id)).map((m) => m.innerMessage)
|
1178
1223
|
);
|
1179
1224
|
},
|
1180
|
-
[data
|
1181
|
-
);
|
1182
|
-
const reloadMaybe = "reload" in chat ? chat.reload : void 0;
|
1183
|
-
const startRun = useCallback2(
|
1184
|
-
async (parentId) => {
|
1185
|
-
if (!reloadMaybe)
|
1186
|
-
throw new Error("Reload not supported by Vercel AI SDK's useAssistant");
|
1187
|
-
const newMessages = sliceMessagesUntil(chat.messages, parentId);
|
1188
|
-
chat.setMessages(newMessages);
|
1189
|
-
await reloadMaybe();
|
1190
|
-
},
|
1191
|
-
[chat.messages, chat.setMessages, reloadMaybe]
|
1192
|
-
);
|
1193
|
-
const append = useCallback2(
|
1194
|
-
async (message) => {
|
1195
|
-
if (message.content.length !== 1 || message.content[0]?.type !== "text")
|
1196
|
-
throw new Error("Only text content is supported by Vercel AI SDK");
|
1197
|
-
const newMessages = sliceMessagesUntil(chat.messages, message.parentId);
|
1198
|
-
chat.setMessages(newMessages);
|
1199
|
-
await chat.append({
|
1200
|
-
role: "user",
|
1201
|
-
content: message.content[0].text
|
1202
|
-
});
|
1203
|
-
},
|
1204
|
-
[chat.messages, chat.setMessages, chat.append]
|
1225
|
+
[data]
|
1205
1226
|
);
|
1227
|
+
const startRun = useCallback2(async (parentId) => {
|
1228
|
+
const reloadMaybe = "reload" in vercelRef.current ? vercelRef.current.reload : void 0;
|
1229
|
+
if (!reloadMaybe)
|
1230
|
+
throw new Error(
|
1231
|
+
"Reload is not supported by Vercel AI SDK's useAssistant."
|
1232
|
+
);
|
1233
|
+
const newMessages = sliceMessagesUntil(
|
1234
|
+
vercelRef.current.messages,
|
1235
|
+
parentId
|
1236
|
+
);
|
1237
|
+
vercelRef.current.setMessages(newMessages);
|
1238
|
+
await reloadMaybe();
|
1239
|
+
}, []);
|
1240
|
+
const append = useCallback2(async (message) => {
|
1241
|
+
if (message.content.length !== 1 || message.content[0]?.type !== "text")
|
1242
|
+
throw new Error("Only text content is supported by Vercel AI SDK.");
|
1243
|
+
const newMessages = sliceMessagesUntil(
|
1244
|
+
vercelRef.current.messages,
|
1245
|
+
message.parentId
|
1246
|
+
);
|
1247
|
+
vercelRef.current.setMessages(newMessages);
|
1248
|
+
await vercelRef.current.append({
|
1249
|
+
role: "user",
|
1250
|
+
content: message.content[0].text
|
1251
|
+
});
|
1252
|
+
}, []);
|
1253
|
+
const cancelRun2 = useCallback2(() => {
|
1254
|
+
const lastMessage = vercelRef.current.messages.at(-1);
|
1255
|
+
vercelRef.current.stop();
|
1256
|
+
if (lastMessage?.role === "user") {
|
1257
|
+
vercelRef.current.setInput(lastMessage.content);
|
1258
|
+
}
|
1259
|
+
}, []);
|
1206
1260
|
return useMemo3(
|
1207
1261
|
() => ({
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1262
|
+
isRunning,
|
1263
|
+
messages,
|
1264
|
+
getBranches: getBranches2,
|
1265
|
+
switchToBranch: switchToBranch2,
|
1211
1266
|
append,
|
1212
|
-
startRun
|
1267
|
+
startRun,
|
1268
|
+
cancelRun: cancelRun2
|
1213
1269
|
}),
|
1214
|
-
[
|
1270
|
+
[
|
1271
|
+
isRunning,
|
1272
|
+
messages,
|
1273
|
+
getBranches2,
|
1274
|
+
switchToBranch2,
|
1275
|
+
append,
|
1276
|
+
startRun,
|
1277
|
+
cancelRun2
|
1278
|
+
]
|
1215
1279
|
);
|
1216
1280
|
};
|
1217
1281
|
|
1218
1282
|
// src/adapters/vercel/VercelAIAssistantProvider.tsx
|
1219
1283
|
import { jsx as jsx19 } from "react/jsx-runtime";
|
1220
|
-
var ThreadMessageCache = /* @__PURE__ */ new WeakMap();
|
1221
|
-
var vercelToThreadMessage = (message, parentId) => {
|
1222
|
-
if (message.role !== "user" && message.role !== "assistant")
|
1223
|
-
throw new Error("Unsupported role");
|
1224
|
-
return {
|
1225
|
-
parentId,
|
1226
|
-
id: message.id,
|
1227
|
-
role: message.role,
|
1228
|
-
content: [{ type: "text", text: message.content }],
|
1229
|
-
createdAt: message.createdAt ?? /* @__PURE__ */ new Date(),
|
1230
|
-
innerMessage: message
|
1231
|
-
};
|
1232
|
-
};
|
1233
|
-
var vercelToCachedThreadMessages = (messages) => {
|
1234
|
-
return messages.map((m, idx) => {
|
1235
|
-
const cached = ThreadMessageCache.get(m);
|
1236
|
-
const parentId = messages[idx - 1]?.id ?? null;
|
1237
|
-
if (cached && cached.parentId === parentId)
|
1238
|
-
return cached;
|
1239
|
-
const newMessage = vercelToThreadMessage(m, parentId);
|
1240
|
-
ThreadMessageCache.set(m, newMessage);
|
1241
|
-
return newMessage;
|
1242
|
-
});
|
1243
|
-
};
|
1244
1284
|
var VercelAIAssistantProvider = ({
|
1245
1285
|
children,
|
1246
1286
|
...rest
|
1247
1287
|
}) => {
|
1248
1288
|
const context = useDummyAIAssistantContext();
|
1249
1289
|
const vercel = "chat" in rest ? rest.chat : rest.assistant;
|
1250
|
-
const
|
1251
|
-
return vercelToCachedThreadMessages(vercel.messages);
|
1252
|
-
}, [vercel.messages]);
|
1253
|
-
const branches = useVercelAIBranches(vercel, messages);
|
1254
|
-
const cancelRun = useCallback3(() => {
|
1255
|
-
const lastMessage = vercel.messages.at(-1);
|
1256
|
-
vercel.stop();
|
1257
|
-
if (lastMessage?.role === "user") {
|
1258
|
-
vercel.setInput(lastMessage.content);
|
1259
|
-
}
|
1260
|
-
}, [vercel.messages, vercel.stop, vercel.setInput]);
|
1261
|
-
const isRunning = "isLoading" in vercel ? vercel.isLoading : vercel.status === "in_progress";
|
1290
|
+
const threadState = useVercelAIThreadState(vercel);
|
1262
1291
|
useMemo4(() => {
|
1263
|
-
context.useThread.setState(
|
1264
|
-
|
1265
|
-
messages: branches.messages,
|
1266
|
-
isRunning,
|
1267
|
-
getBranches: branches.getBranches,
|
1268
|
-
switchToBranch: branches.switchToBranch,
|
1269
|
-
append: branches.append,
|
1270
|
-
startRun: branches.startRun,
|
1271
|
-
cancelRun
|
1272
|
-
},
|
1273
|
-
true
|
1274
|
-
);
|
1275
|
-
}, [context, isRunning, cancelRun, branches]);
|
1292
|
+
context.useThread.setState(threadState, true);
|
1293
|
+
}, [context, threadState]);
|
1276
1294
|
useMemo4(() => {
|
1277
1295
|
context.useComposer.setState({
|
1278
1296
|
value: vercel.input,
|
@@ -1284,55 +1302,96 @@ var VercelAIAssistantProvider = ({
|
|
1284
1302
|
|
1285
1303
|
// src/adapters/vercel/VercelRSCAssistantProvider.tsx
|
1286
1304
|
import {
|
1287
|
-
useCallback as
|
1288
|
-
useMemo as useMemo5
|
1305
|
+
useCallback as useCallback3,
|
1306
|
+
useMemo as useMemo5,
|
1307
|
+
useState as useState4
|
1289
1308
|
} from "react";
|
1290
1309
|
import { jsx as jsx20 } from "react/jsx-runtime";
|
1291
|
-
var
|
1292
|
-
var vercelToThreadMessage2 = (parentId, message) => {
|
1293
|
-
if (message.role !== "user" && message.role !== "assistant")
|
1294
|
-
throw new Error("Unsupported role");
|
1310
|
+
var vercelToThreadMessage2 = (message) => {
|
1295
1311
|
return {
|
1296
|
-
parentId,
|
1297
1312
|
id: message.id,
|
1298
1313
|
role: message.role,
|
1299
1314
|
content: [{ type: "ui", display: message.display }],
|
1300
1315
|
createdAt: message.createdAt ?? /* @__PURE__ */ new Date()
|
1301
1316
|
};
|
1302
1317
|
};
|
1303
|
-
var
|
1304
|
-
|
1305
|
-
|
1306
|
-
const parentId = messages[idx - 1]?.id ?? null;
|
1307
|
-
if (cached && cached.parentId === parentId)
|
1308
|
-
return cached;
|
1309
|
-
const newMessage = vercelToThreadMessage2(parentId, m);
|
1310
|
-
ThreadMessageCache2.set(m, newMessage);
|
1311
|
-
return newMessage;
|
1312
|
-
});
|
1318
|
+
var EMPTY_BRANCHES = [];
|
1319
|
+
var getBranches = () => {
|
1320
|
+
return EMPTY_BRANCHES;
|
1313
1321
|
};
|
1314
|
-
var
|
1322
|
+
var switchToBranch = () => {
|
1323
|
+
throw new Error(
|
1324
|
+
"Branch switching is not supported by VercelRSCAssistantProvider."
|
1325
|
+
);
|
1326
|
+
};
|
1327
|
+
var cancelRun = () => {
|
1328
|
+
if (process.env["NODE_ENV"] === "development") {
|
1329
|
+
console.warn(
|
1330
|
+
"Run cancellation is not supported by VercelRSCAssistantProvider."
|
1331
|
+
);
|
1332
|
+
}
|
1333
|
+
};
|
1334
|
+
var VercelRSCAssistantProvider = ({
|
1335
|
+
children,
|
1336
|
+
convertMessage,
|
1337
|
+
messages: vercelMessages,
|
1338
|
+
append: appendCallback,
|
1339
|
+
edit,
|
1340
|
+
reload
|
1341
|
+
}) => {
|
1315
1342
|
const context = useDummyAIAssistantContext();
|
1343
|
+
const [isRunning, setIsRunning] = useState4(false);
|
1344
|
+
const withRunning = useCallback3((callback) => {
|
1345
|
+
setIsRunning(true);
|
1346
|
+
return callback.finally(() => setIsRunning(false));
|
1347
|
+
}, []);
|
1348
|
+
const converter2 = useMemo5(() => {
|
1349
|
+
const rscConverter = convertMessage ?? ((m) => m);
|
1350
|
+
return new ThreadMessageConverter((m) => {
|
1351
|
+
return vercelToThreadMessage2(rscConverter(m));
|
1352
|
+
});
|
1353
|
+
}, [convertMessage]);
|
1316
1354
|
const messages = useMemo5(() => {
|
1317
|
-
return
|
1318
|
-
}, [vercelMessages]);
|
1319
|
-
const append =
|
1355
|
+
return converter2.convertMessages(vercelMessages);
|
1356
|
+
}, [converter2, vercelMessages]);
|
1357
|
+
const append = useCallback3(
|
1320
1358
|
async (message) => {
|
1321
|
-
if (message.parentId !== (context.useThread.getState().messages.at(-1)?.id ?? null))
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1359
|
+
if (message.parentId !== (context.useThread.getState().messages.at(-1)?.id ?? null)) {
|
1360
|
+
if (!edit)
|
1361
|
+
throw new Error(
|
1362
|
+
"Message editing is not enabled, please provide an edit callback to VercelRSCAssistantProvider."
|
1363
|
+
);
|
1364
|
+
await withRunning(edit(message));
|
1365
|
+
} else {
|
1366
|
+
await withRunning(appendCallback(message));
|
1325
1367
|
}
|
1326
|
-
await vercelAppend(message);
|
1327
1368
|
},
|
1328
|
-
[context,
|
1369
|
+
[context, withRunning, appendCallback, edit]
|
1370
|
+
);
|
1371
|
+
const startRun = useCallback3(
|
1372
|
+
async (parentId) => {
|
1373
|
+
if (!reload)
|
1374
|
+
throw new Error(
|
1375
|
+
"Message reloading is not enabled, please provide a reload callback to VercelRSCAssistantProvider."
|
1376
|
+
);
|
1377
|
+
await withRunning(reload(parentId));
|
1378
|
+
},
|
1379
|
+
[withRunning, reload]
|
1329
1380
|
);
|
1330
1381
|
useMemo5(() => {
|
1331
|
-
context.useThread.setState(
|
1332
|
-
|
1333
|
-
|
1334
|
-
|
1335
|
-
|
1382
|
+
context.useThread.setState(
|
1383
|
+
{
|
1384
|
+
messages,
|
1385
|
+
isRunning,
|
1386
|
+
getBranches,
|
1387
|
+
switchToBranch,
|
1388
|
+
append,
|
1389
|
+
startRun,
|
1390
|
+
cancelRun
|
1391
|
+
},
|
1392
|
+
true
|
1393
|
+
);
|
1394
|
+
}, [context, messages, isRunning, append, startRun]);
|
1336
1395
|
return /* @__PURE__ */ jsx20(AssistantContext.Provider, { value: context, children });
|
1337
1396
|
};
|
1338
1397
|
export {
|