@assistant-ui/react 0.0.9 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/index.d.mts +10 -10
- package/dist/index.d.ts +10 -10
- package/dist/index.js +131 -95
- package/dist/index.mjs +134 -96
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
@@ -21,7 +21,7 @@ type RequireAtLeastOne<T, Keys extends keyof T = keyof T> = Pick<T, Exclude<keyo
|
|
21
21
|
|
22
22
|
type ThreadIfFilters = {
|
23
23
|
empty: boolean | undefined;
|
24
|
-
|
24
|
+
running: boolean | undefined;
|
25
25
|
};
|
26
26
|
type ThreadIfProps = PropsWithChildren<RequireAtLeastOne<ThreadIfFilters>>;
|
27
27
|
declare const ThreadIf: FC<ThreadIfProps>;
|
@@ -91,17 +91,16 @@ declare namespace index$3 {
|
|
91
91
|
export { ComposerCancel as Cancel, ComposerIf as If, ComposerInput as Input, ComposerRoot as Root, ComposerSend as Send };
|
92
92
|
}
|
93
93
|
|
94
|
-
type
|
94
|
+
type BaseComposerState = {
|
95
|
+
value: string;
|
96
|
+
setValue: (value: string) => void;
|
97
|
+
};
|
98
|
+
type MessageComposerState = BaseComposerState & {
|
95
99
|
isEditing: boolean;
|
96
|
-
canCancel:
|
100
|
+
canCancel: true;
|
97
101
|
edit: () => void;
|
98
102
|
send: () => void;
|
99
103
|
cancel: () => void;
|
100
|
-
value: string;
|
101
|
-
setValue: (value: string) => void;
|
102
|
-
};
|
103
|
-
type ComposerStore = {
|
104
|
-
useComposer: UseBoundStore<StoreApi<ComposerState>>;
|
105
104
|
};
|
106
105
|
|
107
106
|
type ThreadMessageTextPart = {
|
@@ -226,7 +225,7 @@ declare const ActionBarRoot: react.ForwardRefExoticComponent<Pick<Omit<react.Det
|
|
226
225
|
} & {
|
227
226
|
asChild?: boolean;
|
228
227
|
}, "key" | keyof react.HTMLAttributes<HTMLDivElement> | "asChild"> & {
|
229
|
-
|
228
|
+
hideWhenRunning?: boolean;
|
230
229
|
autohide?: "always" | "not-last" | "never";
|
231
230
|
autohideFloat?: "always" | "single-branch" | "never";
|
232
231
|
} & react.RefAttributes<HTMLDivElement>>;
|
@@ -283,8 +282,9 @@ type MessageState = {
|
|
283
282
|
isHovering: boolean;
|
284
283
|
setIsHovering: (value: boolean) => void;
|
285
284
|
};
|
286
|
-
type MessageStore =
|
285
|
+
type MessageStore = {
|
287
286
|
useMessage: UseBoundStore<StoreApi<MessageState>>;
|
287
|
+
useComposer: UseBoundStore<StoreApi<MessageComposerState>>;
|
288
288
|
};
|
289
289
|
|
290
290
|
declare const useMessageContext: () => MessageStore;
|
package/dist/index.d.ts
CHANGED
@@ -21,7 +21,7 @@ type RequireAtLeastOne<T, Keys extends keyof T = keyof T> = Pick<T, Exclude<keyo
|
|
21
21
|
|
22
22
|
type ThreadIfFilters = {
|
23
23
|
empty: boolean | undefined;
|
24
|
-
|
24
|
+
running: boolean | undefined;
|
25
25
|
};
|
26
26
|
type ThreadIfProps = PropsWithChildren<RequireAtLeastOne<ThreadIfFilters>>;
|
27
27
|
declare const ThreadIf: FC<ThreadIfProps>;
|
@@ -91,17 +91,16 @@ declare namespace index$3 {
|
|
91
91
|
export { ComposerCancel as Cancel, ComposerIf as If, ComposerInput as Input, ComposerRoot as Root, ComposerSend as Send };
|
92
92
|
}
|
93
93
|
|
94
|
-
type
|
94
|
+
type BaseComposerState = {
|
95
|
+
value: string;
|
96
|
+
setValue: (value: string) => void;
|
97
|
+
};
|
98
|
+
type MessageComposerState = BaseComposerState & {
|
95
99
|
isEditing: boolean;
|
96
|
-
canCancel:
|
100
|
+
canCancel: true;
|
97
101
|
edit: () => void;
|
98
102
|
send: () => void;
|
99
103
|
cancel: () => void;
|
100
|
-
value: string;
|
101
|
-
setValue: (value: string) => void;
|
102
|
-
};
|
103
|
-
type ComposerStore = {
|
104
|
-
useComposer: UseBoundStore<StoreApi<ComposerState>>;
|
105
104
|
};
|
106
105
|
|
107
106
|
type ThreadMessageTextPart = {
|
@@ -226,7 +225,7 @@ declare const ActionBarRoot: react.ForwardRefExoticComponent<Pick<Omit<react.Det
|
|
226
225
|
} & {
|
227
226
|
asChild?: boolean;
|
228
227
|
}, "key" | keyof react.HTMLAttributes<HTMLDivElement> | "asChild"> & {
|
229
|
-
|
228
|
+
hideWhenRunning?: boolean;
|
230
229
|
autohide?: "always" | "not-last" | "never";
|
231
230
|
autohideFloat?: "always" | "single-branch" | "never";
|
232
231
|
} & react.RefAttributes<HTMLDivElement>>;
|
@@ -283,8 +282,9 @@ type MessageState = {
|
|
283
282
|
isHovering: boolean;
|
284
283
|
setIsHovering: (value: boolean) => void;
|
285
284
|
};
|
286
|
-
type MessageStore =
|
285
|
+
type MessageStore = {
|
287
286
|
useMessage: UseBoundStore<StoreApi<MessageState>>;
|
287
|
+
useComposer: UseBoundStore<StoreApi<MessageComposerState>>;
|
288
288
|
};
|
289
289
|
|
290
290
|
declare const useMessageContext: () => MessageStore;
|
package/dist/index.js
CHANGED
@@ -86,9 +86,9 @@ var useThreadIf = (props) => {
|
|
86
86
|
return false;
|
87
87
|
if (props.empty === false && thread.messages.length === 0)
|
88
88
|
return false;
|
89
|
-
if (props.
|
89
|
+
if (props.running === true && !thread.isRunning)
|
90
90
|
return false;
|
91
|
-
if (props.
|
91
|
+
if (props.running === false && thread.isRunning)
|
92
92
|
return false;
|
93
93
|
return true;
|
94
94
|
});
|
@@ -153,12 +153,12 @@ var import_react4 = require("react");
|
|
153
153
|
var useOnScrollToBottom = (callback) => {
|
154
154
|
const callbackRef = (0, import_react4.useRef)(callback);
|
155
155
|
callbackRef.current = callback;
|
156
|
-
const {
|
156
|
+
const { useViewport } = useAssistantContext();
|
157
157
|
(0, import_react4.useEffect)(() => {
|
158
|
-
return
|
158
|
+
return useViewport.getState().onScrollToBottom(() => {
|
159
159
|
callbackRef.current();
|
160
160
|
});
|
161
|
-
}, [
|
161
|
+
}, [useViewport]);
|
162
162
|
};
|
163
163
|
|
164
164
|
// src/primitives/thread/ThreadViewport.tsx
|
@@ -166,7 +166,7 @@ var ThreadViewport = (0, import_react5.forwardRef)(({ autoScroll = true, onScrol
|
|
166
166
|
const messagesEndRef = (0, import_react5.useRef)(null);
|
167
167
|
const divRef = (0, import_react5.useRef)(null);
|
168
168
|
const ref = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, divRef);
|
169
|
-
const {
|
169
|
+
const { useViewport } = useAssistantContext();
|
170
170
|
const firstRenderRef = (0, import_react5.useRef)(true);
|
171
171
|
const lastScrollTop = (0, import_react5.useRef)(0);
|
172
172
|
const scrollToBottom = () => {
|
@@ -175,11 +175,11 @@ var ThreadViewport = (0, import_react5.forwardRef)(({ autoScroll = true, onScrol
|
|
175
175
|
return;
|
176
176
|
const behavior = firstRenderRef.current ? "instant" : "auto";
|
177
177
|
firstRenderRef.current = false;
|
178
|
-
|
178
|
+
useViewport.setState({ isAtBottom: true });
|
179
179
|
div.scrollIntoView({ behavior });
|
180
180
|
};
|
181
181
|
useOnResizeContent(divRef, () => {
|
182
|
-
if (!
|
182
|
+
if (!useViewport.getState().isAtBottom)
|
183
183
|
return;
|
184
184
|
scrollToBottom();
|
185
185
|
});
|
@@ -190,11 +190,11 @@ var ThreadViewport = (0, import_react5.forwardRef)(({ autoScroll = true, onScrol
|
|
190
190
|
const div = divRef.current;
|
191
191
|
if (!div)
|
192
192
|
return;
|
193
|
-
const isAtBottom =
|
193
|
+
const isAtBottom = useViewport.getState().isAtBottom;
|
194
194
|
const newIsAtBottom = div.scrollHeight - div.scrollTop <= div.clientHeight;
|
195
195
|
if (!newIsAtBottom && lastScrollTop.current < div.scrollTop) {
|
196
196
|
} else if (newIsAtBottom !== isAtBottom) {
|
197
|
-
|
197
|
+
useViewport.setState({ isAtBottom: newIsAtBottom });
|
198
198
|
}
|
199
199
|
lastScrollTop.current = div.scrollTop;
|
200
200
|
};
|
@@ -310,13 +310,13 @@ var useVercelAIBranches = (chat, context) => {
|
|
310
310
|
[data, chat.messages, chat.setMessages]
|
311
311
|
);
|
312
312
|
const reloadMaybe = "reload" in chat ? chat.reload : void 0;
|
313
|
-
const
|
314
|
-
async (
|
313
|
+
const startRun = (0, import_react6.useCallback)(
|
314
|
+
async (parentId) => {
|
315
315
|
if (!reloadMaybe)
|
316
316
|
throw new Error("Reload not supported by Vercel AI SDK's useAssistant");
|
317
|
-
const newMessages = sliceMessagesUntil(chat.messages,
|
317
|
+
const newMessages = sliceMessagesUntil(chat.messages, parentId);
|
318
318
|
chat.setMessages(newMessages);
|
319
|
-
context.
|
319
|
+
context.useViewport.getState().scrollToBottom();
|
320
320
|
await reloadMaybe();
|
321
321
|
},
|
322
322
|
[context, chat.messages, chat.setMessages, reloadMaybe]
|
@@ -327,7 +327,7 @@ var useVercelAIBranches = (chat, context) => {
|
|
327
327
|
chat.setMessages(newMessages);
|
328
328
|
if (message.content.length !== 1 || message.content[0]?.type !== "text")
|
329
329
|
throw new Error("Only text content is currently supported");
|
330
|
-
context.
|
330
|
+
context.useViewport.getState().scrollToBottom();
|
331
331
|
await chat.append({
|
332
332
|
role: "user",
|
333
333
|
content: message.content[0].text
|
@@ -340,13 +340,13 @@ var useVercelAIBranches = (chat, context) => {
|
|
340
340
|
getBranchState,
|
341
341
|
switchToBranch,
|
342
342
|
append,
|
343
|
-
|
343
|
+
startRun
|
344
344
|
}),
|
345
|
-
[getBranchState, switchToBranch, append,
|
345
|
+
[getBranchState, switchToBranch, append, startRun]
|
346
346
|
);
|
347
347
|
};
|
348
348
|
var hasUpcomingMessage = (thread) => {
|
349
|
-
return thread.
|
349
|
+
return thread.isRunning && thread.messages[thread.messages.length - 1]?.role !== "assistant";
|
350
350
|
};
|
351
351
|
|
352
352
|
// src/utils/context/useComposerContext.ts
|
@@ -399,7 +399,54 @@ __export(message_exports, {
|
|
399
399
|
|
400
400
|
// src/primitives/message/MessageProvider.tsx
|
401
401
|
var import_react9 = require("react");
|
402
|
+
var import_zustand2 = require("zustand");
|
403
|
+
|
404
|
+
// src/utils/context/stores/ComposerStore.ts
|
402
405
|
var import_zustand = require("zustand");
|
406
|
+
var makeBaseComposer = (set) => ({
|
407
|
+
value: "",
|
408
|
+
setValue: (value) => {
|
409
|
+
set({ value });
|
410
|
+
}
|
411
|
+
});
|
412
|
+
var makeMessageComposerStore = ({
|
413
|
+
onEdit,
|
414
|
+
onSend
|
415
|
+
}) => (0, import_zustand.create)()((set, get, store) => ({
|
416
|
+
...makeBaseComposer(set, get, store),
|
417
|
+
canCancel: true,
|
418
|
+
isEditing: false,
|
419
|
+
edit: () => {
|
420
|
+
const value = onEdit();
|
421
|
+
set({ isEditing: true, value });
|
422
|
+
},
|
423
|
+
send: () => {
|
424
|
+
const value = get().value;
|
425
|
+
set({ isEditing: false });
|
426
|
+
return onSend(value);
|
427
|
+
},
|
428
|
+
cancel: () => {
|
429
|
+
set({ isEditing: false });
|
430
|
+
}
|
431
|
+
}));
|
432
|
+
var makeThreadComposerStore = ({
|
433
|
+
onSend,
|
434
|
+
onCancel
|
435
|
+
}) => (0, import_zustand.create)()((set, get, store) => ({
|
436
|
+
...makeBaseComposer(set, get, store),
|
437
|
+
isEditing: true,
|
438
|
+
canCancel: false,
|
439
|
+
send: () => {
|
440
|
+
const value = get().value;
|
441
|
+
set({ value: "", canCancel: true });
|
442
|
+
onSend(value).then(() => {
|
443
|
+
set({ canCancel: false });
|
444
|
+
});
|
445
|
+
},
|
446
|
+
cancel: onCancel
|
447
|
+
}));
|
448
|
+
|
449
|
+
// src/primitives/message/MessageProvider.tsx
|
403
450
|
var getIsLast = (thread, message) => {
|
404
451
|
const hasUpcoming = hasUpcomingMessage(thread);
|
405
452
|
return hasUpcoming ? message.id === UPCOMING_MESSAGE_ID : thread.messages[thread.messages.length - 1]?.id === message.id;
|
@@ -407,7 +454,7 @@ var getIsLast = (thread, message) => {
|
|
407
454
|
var useMessageContext2 = () => {
|
408
455
|
const [context] = (0, import_react9.useState)(() => {
|
409
456
|
const { useThread } = useAssistantContext();
|
410
|
-
const useMessage = (0,
|
457
|
+
const useMessage = (0, import_zustand2.create)(() => ({
|
411
458
|
message: null,
|
412
459
|
isLast: false,
|
413
460
|
isCopied: false,
|
@@ -417,34 +464,23 @@ var useMessageContext2 = () => {
|
|
417
464
|
setIsHovering: () => {
|
418
465
|
}
|
419
466
|
}));
|
420
|
-
const useComposer = (
|
421
|
-
|
422
|
-
canCancel: true,
|
423
|
-
edit: () => {
|
467
|
+
const useComposer = makeMessageComposerStore({
|
468
|
+
onEdit: () => {
|
424
469
|
const message = useMessage.getState().message;
|
425
470
|
if (message.role !== "user")
|
426
471
|
throw new Error("Editing is only supported for user messages");
|
427
472
|
if (message.content[0]?.type !== "text")
|
428
473
|
throw new Error("Editing is only supported for text-only messages");
|
429
|
-
return
|
430
|
-
isEditing: true,
|
431
|
-
value: message.content[0].text
|
432
|
-
});
|
474
|
+
return message.content[0].text;
|
433
475
|
},
|
434
|
-
|
435
|
-
send: () => {
|
476
|
+
onSend: (text) => {
|
436
477
|
const message = useMessage.getState().message;
|
437
|
-
|
438
|
-
throw new Error("Editing is only supported for user messages");
|
439
|
-
useThread.getState().append({
|
478
|
+
return useThread.getState().append({
|
440
479
|
parentId: message.parentId,
|
441
|
-
content: [{ type: "text", text
|
480
|
+
content: [{ type: "text", text }]
|
442
481
|
});
|
443
|
-
|
444
|
-
|
445
|
-
value: "",
|
446
|
-
setValue: (value) => set({ value })
|
447
|
-
}));
|
482
|
+
}
|
483
|
+
});
|
448
484
|
return { useMessage, useComposer };
|
449
485
|
});
|
450
486
|
return context;
|
@@ -605,11 +641,10 @@ var import_primitive3 = require("@radix-ui/primitive");
|
|
605
641
|
var import_react_primitive4 = require("@radix-ui/react-primitive");
|
606
642
|
var import_react11 = require("react");
|
607
643
|
var ThreadScrollToBottom = (0, import_react11.forwardRef)(({ onClick, ...rest }, ref) => {
|
608
|
-
const {
|
609
|
-
const isAtBottom =
|
644
|
+
const { useViewport } = useAssistantContext();
|
645
|
+
const isAtBottom = useViewport((s) => s.isAtBottom);
|
610
646
|
const handleScrollToBottom = () => {
|
611
|
-
|
612
|
-
thread.scrollToBottom();
|
647
|
+
useViewport.getState().scrollToBottom();
|
613
648
|
};
|
614
649
|
return /* @__PURE__ */ React.createElement(
|
615
650
|
import_react_primitive4.Primitive.button,
|
@@ -685,8 +720,8 @@ var ComposerInput = (0, import_react13.forwardRef)(
|
|
685
720
|
useComposer.getState().cancel();
|
686
721
|
}
|
687
722
|
if (e.key === "Enter" && e.shiftKey === false) {
|
688
|
-
const
|
689
|
-
if (!
|
723
|
+
const isRunning = useThread.getState().isRunning;
|
724
|
+
if (!isRunning) {
|
690
725
|
e.preventDefault();
|
691
726
|
composer.send();
|
692
727
|
}
|
@@ -813,7 +848,7 @@ var useGoToNextBranch = () => {
|
|
813
848
|
const { useComposer, useMessage } = useMessageContext();
|
814
849
|
const disabled = useCombinedStore(
|
815
850
|
[useThread, useComposer, useMessage],
|
816
|
-
(t, c, m) => t.
|
851
|
+
(t, c, m) => t.isRunning || c.isEditing || m.message.branchId + 1 >= m.message.branchCount
|
817
852
|
);
|
818
853
|
if (disabled)
|
819
854
|
return null;
|
@@ -854,7 +889,7 @@ var useGoToPreviousBranch = () => {
|
|
854
889
|
const { useComposer, useMessage } = useMessageContext();
|
855
890
|
const disabled = useCombinedStore(
|
856
891
|
[useThread, useComposer, useMessage],
|
857
|
-
(t, c, m) => t.
|
892
|
+
(t, c, m) => t.isRunning || c.isEditing || m.message.branchId <= 0
|
858
893
|
);
|
859
894
|
if (disabled)
|
860
895
|
return null;
|
@@ -900,13 +935,13 @@ __export(actionBar_exports, {
|
|
900
935
|
// src/primitives/actionBar/ActionBarRoot.tsx
|
901
936
|
var import_react_primitive10 = require("@radix-ui/react-primitive");
|
902
937
|
var import_react20 = require("react");
|
903
|
-
var ActionBarRoot = (0, import_react20.forwardRef)(({
|
938
|
+
var ActionBarRoot = (0, import_react20.forwardRef)(({ hideWhenRunning, autohide, autohideFloat, ...rest }, ref) => {
|
904
939
|
const { useThread } = useAssistantContext();
|
905
940
|
const { useMessage } = useMessageContext();
|
906
941
|
const hideAndfloatStatus = useCombinedStore(
|
907
942
|
[useThread, useMessage],
|
908
943
|
(t, m) => {
|
909
|
-
if (
|
944
|
+
if (hideWhenRunning && t.isRunning)
|
910
945
|
return "hidden" /* Hidden */;
|
911
946
|
const autohideEnabled = autohide === "always" || autohide === "not-last" && !m.isLast;
|
912
947
|
if (!autohideEnabled)
|
@@ -955,7 +990,7 @@ var useReloadMessage = () => {
|
|
955
990
|
const { useMessage } = useMessageContext();
|
956
991
|
const disabled = useCombinedStore(
|
957
992
|
[useThread, useMessage],
|
958
|
-
(t, m) => t.
|
993
|
+
(t, m) => t.isRunning || m.message.role !== "assistant"
|
959
994
|
);
|
960
995
|
if (disabled)
|
961
996
|
return null;
|
@@ -963,7 +998,7 @@ var useReloadMessage = () => {
|
|
963
998
|
const message = useMessage.getState().message;
|
964
999
|
if (message.role !== "assistant")
|
965
1000
|
throw new Error("Reloading is only supported on assistant messages");
|
966
|
-
useThread.getState().
|
1001
|
+
useThread.getState().startRun(message.parentId);
|
967
1002
|
};
|
968
1003
|
};
|
969
1004
|
|
@@ -993,60 +1028,61 @@ var import_react22 = require("react");
|
|
993
1028
|
|
994
1029
|
// src/adapters/vercel/useDummyAIAssistantContext.tsx
|
995
1030
|
var import_react21 = require("react");
|
996
|
-
var
|
1031
|
+
var import_zustand4 = require("zustand");
|
1032
|
+
|
1033
|
+
// src/utils/context/stores/ViewportStore.tsx
|
1034
|
+
var import_zustand3 = require("zustand");
|
1035
|
+
var makeViewportStore = () => {
|
1036
|
+
const scrollToBottomListeners = /* @__PURE__ */ new Set();
|
1037
|
+
return (0, import_zustand3.create)(() => ({
|
1038
|
+
isAtBottom: true,
|
1039
|
+
scrollToBottom: () => {
|
1040
|
+
for (const listener of scrollToBottomListeners) {
|
1041
|
+
listener();
|
1042
|
+
}
|
1043
|
+
},
|
1044
|
+
onScrollToBottom: (callback) => {
|
1045
|
+
scrollToBottomListeners.add(callback);
|
1046
|
+
return () => {
|
1047
|
+
scrollToBottomListeners.delete(callback);
|
1048
|
+
};
|
1049
|
+
}
|
1050
|
+
}));
|
1051
|
+
};
|
1052
|
+
|
1053
|
+
// src/adapters/vercel/useDummyAIAssistantContext.tsx
|
997
1054
|
var useDummyAIAssistantContext = () => {
|
998
1055
|
const [context] = (0, import_react21.useState)(() => {
|
999
|
-
const
|
1000
|
-
|
1056
|
+
const useThread = (0, import_zustand4.create)()(() => ({
|
1057
|
+
id: "",
|
1001
1058
|
messages: [],
|
1002
|
-
|
1059
|
+
isRunning: false,
|
1003
1060
|
append: async () => {
|
1004
1061
|
throw new Error("Not implemented");
|
1005
1062
|
},
|
1006
|
-
|
1063
|
+
cancelRun: () => {
|
1007
1064
|
throw new Error("Not implemented");
|
1008
1065
|
},
|
1009
1066
|
switchToBranch: () => {
|
1010
1067
|
throw new Error("Not implemented");
|
1011
1068
|
},
|
1012
|
-
|
1069
|
+
startRun: async () => {
|
1013
1070
|
throw new Error("Not implemented");
|
1014
|
-
},
|
1015
|
-
isAtBottom: true,
|
1016
|
-
scrollToBottom: () => {
|
1017
|
-
for (const listener of scrollToBottomListeners) {
|
1018
|
-
listener();
|
1019
|
-
}
|
1020
|
-
},
|
1021
|
-
onScrollToBottom: (callback) => {
|
1022
|
-
scrollToBottomListeners.add(callback);
|
1023
|
-
return () => {
|
1024
|
-
scrollToBottomListeners.delete(callback);
|
1025
|
-
};
|
1026
1071
|
}
|
1027
1072
|
}));
|
1028
|
-
const
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
setValue: (value) => {
|
1033
|
-
useComposer.setState({ value });
|
1034
|
-
},
|
1035
|
-
edit: () => {
|
1036
|
-
throw new Error("Not implemented");
|
1037
|
-
},
|
1038
|
-
send: () => {
|
1039
|
-
useThread.getState().append({
|
1073
|
+
const useViewport = makeViewportStore();
|
1074
|
+
const useComposer = makeThreadComposerStore({
|
1075
|
+
onSend: async (text) => {
|
1076
|
+
await useThread.getState().append({
|
1040
1077
|
parentId: useThread.getState().messages.at(-1)?.id ?? ROOT_PARENT_ID,
|
1041
|
-
content: [{ type: "text", text
|
1078
|
+
content: [{ type: "text", text }]
|
1042
1079
|
});
|
1043
|
-
useComposer.getState().setValue("");
|
1044
1080
|
},
|
1045
|
-
|
1046
|
-
useThread.getState().
|
1081
|
+
onCancel: () => {
|
1082
|
+
useThread.getState().cancelRun();
|
1047
1083
|
}
|
1048
|
-
})
|
1049
|
-
return { useThread, useComposer };
|
1084
|
+
});
|
1085
|
+
return { useThread, useViewport, useComposer };
|
1050
1086
|
});
|
1051
1087
|
return context;
|
1052
1088
|
};
|
@@ -1096,31 +1132,31 @@ var VercelAIAssistantProvider = ({
|
|
1096
1132
|
branches.getBranchState
|
1097
1133
|
);
|
1098
1134
|
}, [vercel.messages, branches.getBranchState]);
|
1099
|
-
const
|
1135
|
+
const cancelRun = (0, import_react22.useCallback)(() => {
|
1100
1136
|
const lastMessage = vercel.messages.at(-1);
|
1101
1137
|
vercel.stop();
|
1102
1138
|
if (lastMessage?.role === "user") {
|
1103
1139
|
vercel.setInput(lastMessage.content);
|
1104
1140
|
}
|
1105
1141
|
}, [vercel.messages, vercel.stop, vercel.setInput]);
|
1106
|
-
const
|
1142
|
+
const isRunning = "isLoading" in vercel ? vercel.isLoading : vercel.status === "in_progress";
|
1107
1143
|
(0, import_react22.useMemo)(() => {
|
1108
1144
|
context.useThread.setState({
|
1109
1145
|
messages,
|
1110
|
-
|
1111
|
-
|
1146
|
+
isRunning,
|
1147
|
+
cancelRun,
|
1112
1148
|
switchToBranch: branches.switchToBranch,
|
1113
1149
|
append: branches.append,
|
1114
|
-
|
1150
|
+
startRun: branches.startRun
|
1115
1151
|
});
|
1116
|
-
}, [context, messages,
|
1152
|
+
}, [context, messages, isRunning, cancelRun, branches]);
|
1117
1153
|
(0, import_react22.useMemo)(() => {
|
1118
1154
|
context.useComposer.setState({
|
1119
|
-
canCancel:
|
1155
|
+
canCancel: isRunning,
|
1120
1156
|
value: vercel.input,
|
1121
1157
|
setValue: vercel.setInput
|
1122
1158
|
});
|
1123
|
-
}, [context,
|
1159
|
+
}, [context, isRunning, vercel.input, vercel.setInput]);
|
1124
1160
|
return /* @__PURE__ */ React.createElement(AssistantContext.Provider, { value: context }, children);
|
1125
1161
|
};
|
1126
1162
|
|
@@ -1167,7 +1203,7 @@ var VercelRSCAssistantProvider = ({
|
|
1167
1203
|
if (message.content[0]?.type !== "text") {
|
1168
1204
|
throw new Error("Only text content is currently supported");
|
1169
1205
|
}
|
1170
|
-
context.
|
1206
|
+
context.useViewport.getState().scrollToBottom();
|
1171
1207
|
await vercelAppend(message);
|
1172
1208
|
},
|
1173
1209
|
[context, vercelAppend]
|
package/dist/index.mjs
CHANGED
@@ -46,9 +46,9 @@ var useThreadIf = (props) => {
|
|
46
46
|
return false;
|
47
47
|
if (props.empty === false && thread.messages.length === 0)
|
48
48
|
return false;
|
49
|
-
if (props.
|
49
|
+
if (props.running === true && !thread.isRunning)
|
50
50
|
return false;
|
51
|
-
if (props.
|
51
|
+
if (props.running === false && thread.isRunning)
|
52
52
|
return false;
|
53
53
|
return true;
|
54
54
|
});
|
@@ -115,12 +115,12 @@ import { useEffect, useRef as useRef2 } from "react";
|
|
115
115
|
var useOnScrollToBottom = (callback) => {
|
116
116
|
const callbackRef = useRef2(callback);
|
117
117
|
callbackRef.current = callback;
|
118
|
-
const {
|
118
|
+
const { useViewport } = useAssistantContext();
|
119
119
|
useEffect(() => {
|
120
|
-
return
|
120
|
+
return useViewport.getState().onScrollToBottom(() => {
|
121
121
|
callbackRef.current();
|
122
122
|
});
|
123
|
-
}, [
|
123
|
+
}, [useViewport]);
|
124
124
|
};
|
125
125
|
|
126
126
|
// src/primitives/thread/ThreadViewport.tsx
|
@@ -128,7 +128,7 @@ var ThreadViewport = forwardRef2(({ autoScroll = true, onScroll, children, ...re
|
|
128
128
|
const messagesEndRef = useRef3(null);
|
129
129
|
const divRef = useRef3(null);
|
130
130
|
const ref = useComposedRefs(forwardedRef, divRef);
|
131
|
-
const {
|
131
|
+
const { useViewport } = useAssistantContext();
|
132
132
|
const firstRenderRef = useRef3(true);
|
133
133
|
const lastScrollTop = useRef3(0);
|
134
134
|
const scrollToBottom = () => {
|
@@ -137,11 +137,11 @@ var ThreadViewport = forwardRef2(({ autoScroll = true, onScroll, children, ...re
|
|
137
137
|
return;
|
138
138
|
const behavior = firstRenderRef.current ? "instant" : "auto";
|
139
139
|
firstRenderRef.current = false;
|
140
|
-
|
140
|
+
useViewport.setState({ isAtBottom: true });
|
141
141
|
div.scrollIntoView({ behavior });
|
142
142
|
};
|
143
143
|
useOnResizeContent(divRef, () => {
|
144
|
-
if (!
|
144
|
+
if (!useViewport.getState().isAtBottom)
|
145
145
|
return;
|
146
146
|
scrollToBottom();
|
147
147
|
});
|
@@ -152,11 +152,11 @@ var ThreadViewport = forwardRef2(({ autoScroll = true, onScroll, children, ...re
|
|
152
152
|
const div = divRef.current;
|
153
153
|
if (!div)
|
154
154
|
return;
|
155
|
-
const isAtBottom =
|
155
|
+
const isAtBottom = useViewport.getState().isAtBottom;
|
156
156
|
const newIsAtBottom = div.scrollHeight - div.scrollTop <= div.clientHeight;
|
157
157
|
if (!newIsAtBottom && lastScrollTop.current < div.scrollTop) {
|
158
158
|
} else if (newIsAtBottom !== isAtBottom) {
|
159
|
-
|
159
|
+
useViewport.setState({ isAtBottom: newIsAtBottom });
|
160
160
|
}
|
161
161
|
lastScrollTop.current = div.scrollTop;
|
162
162
|
};
|
@@ -272,13 +272,13 @@ var useVercelAIBranches = (chat, context) => {
|
|
272
272
|
[data, chat.messages, chat.setMessages]
|
273
273
|
);
|
274
274
|
const reloadMaybe = "reload" in chat ? chat.reload : void 0;
|
275
|
-
const
|
276
|
-
async (
|
275
|
+
const startRun = useCallback(
|
276
|
+
async (parentId) => {
|
277
277
|
if (!reloadMaybe)
|
278
278
|
throw new Error("Reload not supported by Vercel AI SDK's useAssistant");
|
279
|
-
const newMessages = sliceMessagesUntil(chat.messages,
|
279
|
+
const newMessages = sliceMessagesUntil(chat.messages, parentId);
|
280
280
|
chat.setMessages(newMessages);
|
281
|
-
context.
|
281
|
+
context.useViewport.getState().scrollToBottom();
|
282
282
|
await reloadMaybe();
|
283
283
|
},
|
284
284
|
[context, chat.messages, chat.setMessages, reloadMaybe]
|
@@ -289,7 +289,7 @@ var useVercelAIBranches = (chat, context) => {
|
|
289
289
|
chat.setMessages(newMessages);
|
290
290
|
if (message.content.length !== 1 || message.content[0]?.type !== "text")
|
291
291
|
throw new Error("Only text content is currently supported");
|
292
|
-
context.
|
292
|
+
context.useViewport.getState().scrollToBottom();
|
293
293
|
await chat.append({
|
294
294
|
role: "user",
|
295
295
|
content: message.content[0].text
|
@@ -302,13 +302,13 @@ var useVercelAIBranches = (chat, context) => {
|
|
302
302
|
getBranchState,
|
303
303
|
switchToBranch,
|
304
304
|
append,
|
305
|
-
|
305
|
+
startRun
|
306
306
|
}),
|
307
|
-
[getBranchState, switchToBranch, append,
|
307
|
+
[getBranchState, switchToBranch, append, startRun]
|
308
308
|
);
|
309
309
|
};
|
310
310
|
var hasUpcomingMessage = (thread) => {
|
311
|
-
return thread.
|
311
|
+
return thread.isRunning && thread.messages[thread.messages.length - 1]?.role !== "assistant";
|
312
312
|
};
|
313
313
|
|
314
314
|
// src/utils/context/useComposerContext.ts
|
@@ -361,7 +361,56 @@ __export(message_exports, {
|
|
361
361
|
|
362
362
|
// src/primitives/message/MessageProvider.tsx
|
363
363
|
import { useMemo as useMemo2, useState } from "react";
|
364
|
-
import { create } from "zustand";
|
364
|
+
import { create as create2 } from "zustand";
|
365
|
+
|
366
|
+
// src/utils/context/stores/ComposerStore.ts
|
367
|
+
import {
|
368
|
+
create
|
369
|
+
} from "zustand";
|
370
|
+
var makeBaseComposer = (set) => ({
|
371
|
+
value: "",
|
372
|
+
setValue: (value) => {
|
373
|
+
set({ value });
|
374
|
+
}
|
375
|
+
});
|
376
|
+
var makeMessageComposerStore = ({
|
377
|
+
onEdit,
|
378
|
+
onSend
|
379
|
+
}) => create()((set, get, store) => ({
|
380
|
+
...makeBaseComposer(set, get, store),
|
381
|
+
canCancel: true,
|
382
|
+
isEditing: false,
|
383
|
+
edit: () => {
|
384
|
+
const value = onEdit();
|
385
|
+
set({ isEditing: true, value });
|
386
|
+
},
|
387
|
+
send: () => {
|
388
|
+
const value = get().value;
|
389
|
+
set({ isEditing: false });
|
390
|
+
return onSend(value);
|
391
|
+
},
|
392
|
+
cancel: () => {
|
393
|
+
set({ isEditing: false });
|
394
|
+
}
|
395
|
+
}));
|
396
|
+
var makeThreadComposerStore = ({
|
397
|
+
onSend,
|
398
|
+
onCancel
|
399
|
+
}) => create()((set, get, store) => ({
|
400
|
+
...makeBaseComposer(set, get, store),
|
401
|
+
isEditing: true,
|
402
|
+
canCancel: false,
|
403
|
+
send: () => {
|
404
|
+
const value = get().value;
|
405
|
+
set({ value: "", canCancel: true });
|
406
|
+
onSend(value).then(() => {
|
407
|
+
set({ canCancel: false });
|
408
|
+
});
|
409
|
+
},
|
410
|
+
cancel: onCancel
|
411
|
+
}));
|
412
|
+
|
413
|
+
// src/primitives/message/MessageProvider.tsx
|
365
414
|
var getIsLast = (thread, message) => {
|
366
415
|
const hasUpcoming = hasUpcomingMessage(thread);
|
367
416
|
return hasUpcoming ? message.id === UPCOMING_MESSAGE_ID : thread.messages[thread.messages.length - 1]?.id === message.id;
|
@@ -369,7 +418,7 @@ var getIsLast = (thread, message) => {
|
|
369
418
|
var useMessageContext2 = () => {
|
370
419
|
const [context] = useState(() => {
|
371
420
|
const { useThread } = useAssistantContext();
|
372
|
-
const useMessage =
|
421
|
+
const useMessage = create2(() => ({
|
373
422
|
message: null,
|
374
423
|
isLast: false,
|
375
424
|
isCopied: false,
|
@@ -379,34 +428,23 @@ var useMessageContext2 = () => {
|
|
379
428
|
setIsHovering: () => {
|
380
429
|
}
|
381
430
|
}));
|
382
|
-
const useComposer =
|
383
|
-
|
384
|
-
canCancel: true,
|
385
|
-
edit: () => {
|
431
|
+
const useComposer = makeMessageComposerStore({
|
432
|
+
onEdit: () => {
|
386
433
|
const message = useMessage.getState().message;
|
387
434
|
if (message.role !== "user")
|
388
435
|
throw new Error("Editing is only supported for user messages");
|
389
436
|
if (message.content[0]?.type !== "text")
|
390
437
|
throw new Error("Editing is only supported for text-only messages");
|
391
|
-
return
|
392
|
-
isEditing: true,
|
393
|
-
value: message.content[0].text
|
394
|
-
});
|
438
|
+
return message.content[0].text;
|
395
439
|
},
|
396
|
-
|
397
|
-
send: () => {
|
440
|
+
onSend: (text) => {
|
398
441
|
const message = useMessage.getState().message;
|
399
|
-
|
400
|
-
throw new Error("Editing is only supported for user messages");
|
401
|
-
useThread.getState().append({
|
442
|
+
return useThread.getState().append({
|
402
443
|
parentId: message.parentId,
|
403
|
-
content: [{ type: "text", text
|
444
|
+
content: [{ type: "text", text }]
|
404
445
|
});
|
405
|
-
|
406
|
-
|
407
|
-
value: "",
|
408
|
-
setValue: (value) => set({ value })
|
409
|
-
}));
|
446
|
+
}
|
447
|
+
});
|
410
448
|
return { useMessage, useComposer };
|
411
449
|
});
|
412
450
|
return context;
|
@@ -571,11 +609,10 @@ import {
|
|
571
609
|
} from "@radix-ui/react-primitive";
|
572
610
|
import { forwardRef as forwardRef4 } from "react";
|
573
611
|
var ThreadScrollToBottom = forwardRef4(({ onClick, ...rest }, ref) => {
|
574
|
-
const {
|
575
|
-
const isAtBottom =
|
612
|
+
const { useViewport } = useAssistantContext();
|
613
|
+
const isAtBottom = useViewport((s) => s.isAtBottom);
|
576
614
|
const handleScrollToBottom = () => {
|
577
|
-
|
578
|
-
thread.scrollToBottom();
|
615
|
+
useViewport.getState().scrollToBottom();
|
579
616
|
};
|
580
617
|
return /* @__PURE__ */ React.createElement(
|
581
618
|
Primitive4.button,
|
@@ -658,8 +695,8 @@ var ComposerInput = forwardRef6(
|
|
658
695
|
useComposer.getState().cancel();
|
659
696
|
}
|
660
697
|
if (e.key === "Enter" && e.shiftKey === false) {
|
661
|
-
const
|
662
|
-
if (!
|
698
|
+
const isRunning = useThread.getState().isRunning;
|
699
|
+
if (!isRunning) {
|
663
700
|
e.preventDefault();
|
664
701
|
composer.send();
|
665
702
|
}
|
@@ -790,7 +827,7 @@ var useGoToNextBranch = () => {
|
|
790
827
|
const { useComposer, useMessage } = useMessageContext();
|
791
828
|
const disabled = useCombinedStore(
|
792
829
|
[useThread, useComposer, useMessage],
|
793
|
-
(t, c, m) => t.
|
830
|
+
(t, c, m) => t.isRunning || c.isEditing || m.message.branchId + 1 >= m.message.branchCount
|
794
831
|
);
|
795
832
|
if (disabled)
|
796
833
|
return null;
|
@@ -833,7 +870,7 @@ var useGoToPreviousBranch = () => {
|
|
833
870
|
const { useComposer, useMessage } = useMessageContext();
|
834
871
|
const disabled = useCombinedStore(
|
835
872
|
[useThread, useComposer, useMessage],
|
836
|
-
(t, c, m) => t.
|
873
|
+
(t, c, m) => t.isRunning || c.isEditing || m.message.branchId <= 0
|
837
874
|
);
|
838
875
|
if (disabled)
|
839
876
|
return null;
|
@@ -883,13 +920,13 @@ import {
|
|
883
920
|
Primitive as Primitive10
|
884
921
|
} from "@radix-ui/react-primitive";
|
885
922
|
import { forwardRef as forwardRef11 } from "react";
|
886
|
-
var ActionBarRoot = forwardRef11(({
|
923
|
+
var ActionBarRoot = forwardRef11(({ hideWhenRunning, autohide, autohideFloat, ...rest }, ref) => {
|
887
924
|
const { useThread } = useAssistantContext();
|
888
925
|
const { useMessage } = useMessageContext();
|
889
926
|
const hideAndfloatStatus = useCombinedStore(
|
890
927
|
[useThread, useMessage],
|
891
928
|
(t, m) => {
|
892
|
-
if (
|
929
|
+
if (hideWhenRunning && t.isRunning)
|
893
930
|
return "hidden" /* Hidden */;
|
894
931
|
const autohideEnabled = autohide === "always" || autohide === "not-last" && !m.isLast;
|
895
932
|
if (!autohideEnabled)
|
@@ -938,7 +975,7 @@ var useReloadMessage = () => {
|
|
938
975
|
const { useMessage } = useMessageContext();
|
939
976
|
const disabled = useCombinedStore(
|
940
977
|
[useThread, useMessage],
|
941
|
-
(t, m) => t.
|
978
|
+
(t, m) => t.isRunning || m.message.role !== "assistant"
|
942
979
|
);
|
943
980
|
if (disabled)
|
944
981
|
return null;
|
@@ -946,7 +983,7 @@ var useReloadMessage = () => {
|
|
946
983
|
const message = useMessage.getState().message;
|
947
984
|
if (message.role !== "assistant")
|
948
985
|
throw new Error("Reloading is only supported on assistant messages");
|
949
|
-
useThread.getState().
|
986
|
+
useThread.getState().startRun(message.parentId);
|
950
987
|
};
|
951
988
|
};
|
952
989
|
|
@@ -976,60 +1013,61 @@ import { useCallback as useCallback3, useMemo as useMemo4 } from "react";
|
|
976
1013
|
|
977
1014
|
// src/adapters/vercel/useDummyAIAssistantContext.tsx
|
978
1015
|
import { useState as useState2 } from "react";
|
979
|
-
import { create as
|
1016
|
+
import { create as create4 } from "zustand";
|
1017
|
+
|
1018
|
+
// src/utils/context/stores/ViewportStore.tsx
|
1019
|
+
import { create as create3 } from "zustand";
|
1020
|
+
var makeViewportStore = () => {
|
1021
|
+
const scrollToBottomListeners = /* @__PURE__ */ new Set();
|
1022
|
+
return create3(() => ({
|
1023
|
+
isAtBottom: true,
|
1024
|
+
scrollToBottom: () => {
|
1025
|
+
for (const listener of scrollToBottomListeners) {
|
1026
|
+
listener();
|
1027
|
+
}
|
1028
|
+
},
|
1029
|
+
onScrollToBottom: (callback) => {
|
1030
|
+
scrollToBottomListeners.add(callback);
|
1031
|
+
return () => {
|
1032
|
+
scrollToBottomListeners.delete(callback);
|
1033
|
+
};
|
1034
|
+
}
|
1035
|
+
}));
|
1036
|
+
};
|
1037
|
+
|
1038
|
+
// src/adapters/vercel/useDummyAIAssistantContext.tsx
|
980
1039
|
var useDummyAIAssistantContext = () => {
|
981
1040
|
const [context] = useState2(() => {
|
982
|
-
const
|
983
|
-
|
1041
|
+
const useThread = create4()(() => ({
|
1042
|
+
id: "",
|
984
1043
|
messages: [],
|
985
|
-
|
1044
|
+
isRunning: false,
|
986
1045
|
append: async () => {
|
987
1046
|
throw new Error("Not implemented");
|
988
1047
|
},
|
989
|
-
|
1048
|
+
cancelRun: () => {
|
990
1049
|
throw new Error("Not implemented");
|
991
1050
|
},
|
992
1051
|
switchToBranch: () => {
|
993
1052
|
throw new Error("Not implemented");
|
994
1053
|
},
|
995
|
-
|
1054
|
+
startRun: async () => {
|
996
1055
|
throw new Error("Not implemented");
|
997
|
-
},
|
998
|
-
isAtBottom: true,
|
999
|
-
scrollToBottom: () => {
|
1000
|
-
for (const listener of scrollToBottomListeners) {
|
1001
|
-
listener();
|
1002
|
-
}
|
1003
|
-
},
|
1004
|
-
onScrollToBottom: (callback) => {
|
1005
|
-
scrollToBottomListeners.add(callback);
|
1006
|
-
return () => {
|
1007
|
-
scrollToBottomListeners.delete(callback);
|
1008
|
-
};
|
1009
1056
|
}
|
1010
1057
|
}));
|
1011
|
-
const
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
setValue: (value) => {
|
1016
|
-
useComposer.setState({ value });
|
1017
|
-
},
|
1018
|
-
edit: () => {
|
1019
|
-
throw new Error("Not implemented");
|
1020
|
-
},
|
1021
|
-
send: () => {
|
1022
|
-
useThread.getState().append({
|
1058
|
+
const useViewport = makeViewportStore();
|
1059
|
+
const useComposer = makeThreadComposerStore({
|
1060
|
+
onSend: async (text) => {
|
1061
|
+
await useThread.getState().append({
|
1023
1062
|
parentId: useThread.getState().messages.at(-1)?.id ?? ROOT_PARENT_ID,
|
1024
|
-
content: [{ type: "text", text
|
1063
|
+
content: [{ type: "text", text }]
|
1025
1064
|
});
|
1026
|
-
useComposer.getState().setValue("");
|
1027
1065
|
},
|
1028
|
-
|
1029
|
-
useThread.getState().
|
1066
|
+
onCancel: () => {
|
1067
|
+
useThread.getState().cancelRun();
|
1030
1068
|
}
|
1031
|
-
})
|
1032
|
-
return { useThread, useComposer };
|
1069
|
+
});
|
1070
|
+
return { useThread, useViewport, useComposer };
|
1033
1071
|
});
|
1034
1072
|
return context;
|
1035
1073
|
};
|
@@ -1079,31 +1117,31 @@ var VercelAIAssistantProvider = ({
|
|
1079
1117
|
branches.getBranchState
|
1080
1118
|
);
|
1081
1119
|
}, [vercel.messages, branches.getBranchState]);
|
1082
|
-
const
|
1120
|
+
const cancelRun = useCallback3(() => {
|
1083
1121
|
const lastMessage = vercel.messages.at(-1);
|
1084
1122
|
vercel.stop();
|
1085
1123
|
if (lastMessage?.role === "user") {
|
1086
1124
|
vercel.setInput(lastMessage.content);
|
1087
1125
|
}
|
1088
1126
|
}, [vercel.messages, vercel.stop, vercel.setInput]);
|
1089
|
-
const
|
1127
|
+
const isRunning = "isLoading" in vercel ? vercel.isLoading : vercel.status === "in_progress";
|
1090
1128
|
useMemo4(() => {
|
1091
1129
|
context.useThread.setState({
|
1092
1130
|
messages,
|
1093
|
-
|
1094
|
-
|
1131
|
+
isRunning,
|
1132
|
+
cancelRun,
|
1095
1133
|
switchToBranch: branches.switchToBranch,
|
1096
1134
|
append: branches.append,
|
1097
|
-
|
1135
|
+
startRun: branches.startRun
|
1098
1136
|
});
|
1099
|
-
}, [context, messages,
|
1137
|
+
}, [context, messages, isRunning, cancelRun, branches]);
|
1100
1138
|
useMemo4(() => {
|
1101
1139
|
context.useComposer.setState({
|
1102
|
-
canCancel:
|
1140
|
+
canCancel: isRunning,
|
1103
1141
|
value: vercel.input,
|
1104
1142
|
setValue: vercel.setInput
|
1105
1143
|
});
|
1106
|
-
}, [context,
|
1144
|
+
}, [context, isRunning, vercel.input, vercel.setInput]);
|
1107
1145
|
return /* @__PURE__ */ React.createElement(AssistantContext.Provider, { value: context }, children);
|
1108
1146
|
};
|
1109
1147
|
|
@@ -1153,7 +1191,7 @@ var VercelRSCAssistantProvider = ({
|
|
1153
1191
|
if (message.content[0]?.type !== "text") {
|
1154
1192
|
throw new Error("Only text content is currently supported");
|
1155
1193
|
}
|
1156
|
-
context.
|
1194
|
+
context.useViewport.getState().scrollToBottom();
|
1157
1195
|
await vercelAppend(message);
|
1158
1196
|
},
|
1159
1197
|
[context, vercelAppend]
|