@assistant-ui/react 0.0.9 → 0.0.11
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 +29 -27
- package/dist/index.d.ts +29 -27
- package/dist/index.js +536 -384
- package/dist/index.mjs +514 -360
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
@@ -16,13 +16,14 @@ __export(thread_exports, {
|
|
16
16
|
});
|
17
17
|
|
18
18
|
// src/primitives/thread/ThreadRoot.tsx
|
19
|
-
import { forwardRef } from "react";
|
20
19
|
import {
|
21
20
|
Primitive
|
22
21
|
} from "@radix-ui/react-primitive";
|
22
|
+
import { forwardRef } from "react";
|
23
|
+
import { jsx } from "react/jsx-runtime";
|
23
24
|
var ThreadRoot = forwardRef(
|
24
25
|
(props, ref) => {
|
25
|
-
return /* @__PURE__ */
|
26
|
+
return /* @__PURE__ */ jsx(Primitive.div, { ...props, ref });
|
26
27
|
}
|
27
28
|
);
|
28
29
|
|
@@ -46,9 +47,9 @@ var useThreadIf = (props) => {
|
|
46
47
|
return false;
|
47
48
|
if (props.empty === false && thread.messages.length === 0)
|
48
49
|
return false;
|
49
|
-
if (props.
|
50
|
+
if (props.running === true && !thread.isRunning)
|
50
51
|
return false;
|
51
|
-
if (props.
|
52
|
+
if (props.running === false && thread.isRunning)
|
52
53
|
return false;
|
53
54
|
return true;
|
54
55
|
});
|
@@ -59,8 +60,9 @@ var ThreadIf = ({ children, ...query }) => {
|
|
59
60
|
};
|
60
61
|
|
61
62
|
// src/primitives/thread/ThreadEmpty.tsx
|
63
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
62
64
|
var ThreadEmpty = ({ children }) => {
|
63
|
-
return /* @__PURE__ */
|
65
|
+
return /* @__PURE__ */ jsx2(ThreadIf, { empty: true, children });
|
64
66
|
};
|
65
67
|
|
66
68
|
// src/primitives/thread/ThreadViewport.tsx
|
@@ -115,20 +117,21 @@ import { useEffect, useRef as useRef2 } from "react";
|
|
115
117
|
var useOnScrollToBottom = (callback) => {
|
116
118
|
const callbackRef = useRef2(callback);
|
117
119
|
callbackRef.current = callback;
|
118
|
-
const {
|
120
|
+
const { useViewport } = useAssistantContext();
|
119
121
|
useEffect(() => {
|
120
|
-
return
|
122
|
+
return useViewport.getState().onScrollToBottom(() => {
|
121
123
|
callbackRef.current();
|
122
124
|
});
|
123
|
-
}, [
|
125
|
+
}, [useViewport]);
|
124
126
|
};
|
125
127
|
|
126
128
|
// src/primitives/thread/ThreadViewport.tsx
|
129
|
+
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
127
130
|
var ThreadViewport = forwardRef2(({ autoScroll = true, onScroll, children, ...rest }, forwardedRef) => {
|
128
131
|
const messagesEndRef = useRef3(null);
|
129
132
|
const divRef = useRef3(null);
|
130
133
|
const ref = useComposedRefs(forwardedRef, divRef);
|
131
|
-
const {
|
134
|
+
const { useViewport } = useAssistantContext();
|
132
135
|
const firstRenderRef = useRef3(true);
|
133
136
|
const lastScrollTop = useRef3(0);
|
134
137
|
const scrollToBottom = () => {
|
@@ -137,11 +140,11 @@ var ThreadViewport = forwardRef2(({ autoScroll = true, onScroll, children, ...re
|
|
137
140
|
return;
|
138
141
|
const behavior = firstRenderRef.current ? "instant" : "auto";
|
139
142
|
firstRenderRef.current = false;
|
140
|
-
|
143
|
+
useViewport.setState({ isAtBottom: true });
|
141
144
|
div.scrollIntoView({ behavior });
|
142
145
|
};
|
143
146
|
useOnResizeContent(divRef, () => {
|
144
|
-
if (!
|
147
|
+
if (!useViewport.getState().isAtBottom)
|
145
148
|
return;
|
146
149
|
scrollToBottom();
|
147
150
|
});
|
@@ -152,164 +155,27 @@ var ThreadViewport = forwardRef2(({ autoScroll = true, onScroll, children, ...re
|
|
152
155
|
const div = divRef.current;
|
153
156
|
if (!div)
|
154
157
|
return;
|
155
|
-
const isAtBottom =
|
158
|
+
const isAtBottom = useViewport.getState().isAtBottom;
|
156
159
|
const newIsAtBottom = div.scrollHeight - div.scrollTop <= div.clientHeight;
|
157
160
|
if (!newIsAtBottom && lastScrollTop.current < div.scrollTop) {
|
158
161
|
} else if (newIsAtBottom !== isAtBottom) {
|
159
|
-
|
162
|
+
useViewport.setState({ isAtBottom: newIsAtBottom });
|
160
163
|
}
|
161
164
|
lastScrollTop.current = div.scrollTop;
|
162
165
|
};
|
163
|
-
return /* @__PURE__ */
|
166
|
+
return /* @__PURE__ */ jsxs(
|
164
167
|
Primitive2.div,
|
165
168
|
{
|
166
169
|
...rest,
|
167
170
|
onScroll: composeEventHandlers(onScroll, handleScroll),
|
168
|
-
ref
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
});
|
174
|
-
|
175
|
-
// src/adapters/vercel/useVercelAIBranches.tsx
|
176
|
-
import { useCallback, useMemo, useRef as useRef4 } from "react";
|
177
|
-
|
178
|
-
// src/utils/context/stores/AssistantTypes.ts
|
179
|
-
var ROOT_PARENT_ID = "__ROOT_ID__";
|
180
|
-
|
181
|
-
// src/adapters/vercel/useVercelAIBranches.tsx
|
182
|
-
var UPCOMING_MESSAGE_ID = "__UPCOMING_MESSAGE_ID__";
|
183
|
-
var updateBranchData = (data, messages) => {
|
184
|
-
for (let i = 0; i < messages.length; i++) {
|
185
|
-
const child = messages[i];
|
186
|
-
const childId = child.id;
|
187
|
-
const parentId = messages[i - 1]?.id ?? ROOT_PARENT_ID;
|
188
|
-
data.parentMap.set(childId, parentId);
|
189
|
-
const parentArray = data.branchMap.get(parentId);
|
190
|
-
if (!parentArray) {
|
191
|
-
data.branchMap.set(parentId, [childId]);
|
192
|
-
} else if (!parentArray.includes(childId)) {
|
193
|
-
parentArray.push(childId);
|
171
|
+
ref,
|
172
|
+
children: [
|
173
|
+
children,
|
174
|
+
/* @__PURE__ */ jsx3("div", { ref: messagesEndRef })
|
175
|
+
]
|
194
176
|
}
|
195
|
-
data.snapshots.set(childId, messages);
|
196
|
-
}
|
197
|
-
};
|
198
|
-
var getParentId = (data, messages, messageId) => {
|
199
|
-
if (messageId === UPCOMING_MESSAGE_ID) {
|
200
|
-
const parent = messages.at(-1);
|
201
|
-
if (!parent)
|
202
|
-
return ROOT_PARENT_ID;
|
203
|
-
return parent.id;
|
204
|
-
}
|
205
|
-
const parentId = data.parentMap.get(messageId);
|
206
|
-
if (!parentId)
|
207
|
-
throw new Error("Unexpected: Message parent not found");
|
208
|
-
return parentId;
|
209
|
-
};
|
210
|
-
var getBranchStateImpl = (data, messages, messageId) => {
|
211
|
-
const parentId = getParentId(data, messages, messageId);
|
212
|
-
const branches = data.branchMap.get(parentId) ?? [];
|
213
|
-
const branchId = messageId === UPCOMING_MESSAGE_ID ? branches.length : branches.indexOf(messageId);
|
214
|
-
if (branchId === -1)
|
215
|
-
throw new Error("Unexpected: Message not found in parent children");
|
216
|
-
const upcomingOffset = messageId === UPCOMING_MESSAGE_ID ? 1 : 0;
|
217
|
-
return {
|
218
|
-
branchId,
|
219
|
-
branchCount: branches.length + upcomingOffset
|
220
|
-
};
|
221
|
-
};
|
222
|
-
var switchToBranchImpl = (data, messages, messageId, branchId) => {
|
223
|
-
const parentId = getParentId(data, messages, messageId);
|
224
|
-
const branches = data.branchMap.get(parentId);
|
225
|
-
if (!branches)
|
226
|
-
throw new Error("Unexpected: Parent children not found");
|
227
|
-
const newMessageId = branches[branchId];
|
228
|
-
if (!newMessageId)
|
229
|
-
throw new Error("Unexpected: Requested branch not found");
|
230
|
-
if (branchId < 0 || branchId >= branches.length)
|
231
|
-
throw new Error("Switch to branch called with a branch index out of range");
|
232
|
-
if (newMessageId === messageId)
|
233
|
-
return messages;
|
234
|
-
const snapshot = data.snapshots.get(newMessageId);
|
235
|
-
if (!snapshot)
|
236
|
-
throw new Error("Unexpected: Branch snapshot not found");
|
237
|
-
return snapshot;
|
238
|
-
};
|
239
|
-
var sliceMessagesUntil = (messages, messageId) => {
|
240
|
-
if (messageId === ROOT_PARENT_ID)
|
241
|
-
return [];
|
242
|
-
if (messageId === UPCOMING_MESSAGE_ID)
|
243
|
-
return messages;
|
244
|
-
const messageIdx = messages.findIndex((m) => m.id === messageId);
|
245
|
-
if (messageIdx === -1)
|
246
|
-
throw new Error("Unexpected: Message not found");
|
247
|
-
return messages.slice(0, messageIdx + 1);
|
248
|
-
};
|
249
|
-
var useVercelAIBranches = (chat, context) => {
|
250
|
-
const data = useRef4({
|
251
|
-
parentMap: /* @__PURE__ */ new Map(),
|
252
|
-
branchMap: /* @__PURE__ */ new Map(),
|
253
|
-
snapshots: /* @__PURE__ */ new Map()
|
254
|
-
}).current;
|
255
|
-
updateBranchData(data, chat.messages);
|
256
|
-
const getBranchState = useCallback(
|
257
|
-
(messageId) => {
|
258
|
-
return getBranchStateImpl(data, chat.messages, messageId);
|
259
|
-
},
|
260
|
-
[data, chat.messages]
|
261
|
-
);
|
262
|
-
const switchToBranch = useCallback(
|
263
|
-
(messageId, branchId) => {
|
264
|
-
const newMessages = switchToBranchImpl(
|
265
|
-
data,
|
266
|
-
chat.messages,
|
267
|
-
messageId,
|
268
|
-
branchId
|
269
|
-
);
|
270
|
-
chat.setMessages(newMessages);
|
271
|
-
},
|
272
|
-
[data, chat.messages, chat.setMessages]
|
273
|
-
);
|
274
|
-
const reloadMaybe = "reload" in chat ? chat.reload : void 0;
|
275
|
-
const reload = useCallback(
|
276
|
-
async (messageId) => {
|
277
|
-
if (!reloadMaybe)
|
278
|
-
throw new Error("Reload not supported by Vercel AI SDK's useAssistant");
|
279
|
-
const newMessages = sliceMessagesUntil(chat.messages, messageId);
|
280
|
-
chat.setMessages(newMessages);
|
281
|
-
context.useThread.getState().scrollToBottom();
|
282
|
-
await reloadMaybe();
|
283
|
-
},
|
284
|
-
[context, chat.messages, chat.setMessages, reloadMaybe]
|
285
|
-
);
|
286
|
-
const append = useCallback(
|
287
|
-
async (message) => {
|
288
|
-
const newMessages = sliceMessagesUntil(chat.messages, message.parentId);
|
289
|
-
chat.setMessages(newMessages);
|
290
|
-
if (message.content.length !== 1 || message.content[0]?.type !== "text")
|
291
|
-
throw new Error("Only text content is currently supported");
|
292
|
-
context.useThread.getState().scrollToBottom();
|
293
|
-
await chat.append({
|
294
|
-
role: "user",
|
295
|
-
content: message.content[0].text
|
296
|
-
});
|
297
|
-
},
|
298
|
-
[context, chat.messages, chat.setMessages, chat.append]
|
299
|
-
);
|
300
|
-
return useMemo(
|
301
|
-
() => ({
|
302
|
-
getBranchState,
|
303
|
-
switchToBranch,
|
304
|
-
append,
|
305
|
-
reload
|
306
|
-
}),
|
307
|
-
[getBranchState, switchToBranch, append, reload]
|
308
177
|
);
|
309
|
-
};
|
310
|
-
var hasUpcomingMessage = (thread) => {
|
311
|
-
return thread.isLoading && thread.messages[thread.messages.length - 1]?.role !== "assistant";
|
312
|
-
};
|
178
|
+
});
|
313
179
|
|
314
180
|
// src/utils/context/useComposerContext.ts
|
315
181
|
import { useContext as useContext3 } from "react";
|
@@ -360,17 +226,74 @@ __export(message_exports, {
|
|
360
226
|
});
|
361
227
|
|
362
228
|
// src/primitives/message/MessageProvider.tsx
|
363
|
-
import { useMemo
|
364
|
-
import { create } from "zustand";
|
229
|
+
import { useMemo, useState } from "react";
|
230
|
+
import { create as create2 } from "zustand";
|
231
|
+
|
232
|
+
// src/utils/context/stores/ComposerStore.ts
|
233
|
+
import {
|
234
|
+
create
|
235
|
+
} from "zustand";
|
236
|
+
var makeBaseComposer = (set) => ({
|
237
|
+
value: "",
|
238
|
+
setValue: (value) => {
|
239
|
+
set({ value });
|
240
|
+
}
|
241
|
+
});
|
242
|
+
var makeMessageComposerStore = ({
|
243
|
+
onEdit,
|
244
|
+
onSend
|
245
|
+
}) => create()((set, get, store) => ({
|
246
|
+
...makeBaseComposer(set, get, store),
|
247
|
+
isEditing: false,
|
248
|
+
edit: () => {
|
249
|
+
const value = onEdit();
|
250
|
+
set({ isEditing: true, value });
|
251
|
+
},
|
252
|
+
send: () => {
|
253
|
+
const value = get().value;
|
254
|
+
set({ isEditing: false });
|
255
|
+
onSend(value);
|
256
|
+
},
|
257
|
+
cancel: () => {
|
258
|
+
if (!get().isEditing)
|
259
|
+
return false;
|
260
|
+
set({ isEditing: false });
|
261
|
+
return true;
|
262
|
+
}
|
263
|
+
}));
|
264
|
+
var makeThreadComposerStore = (useThread) => create()((set, get, store) => {
|
265
|
+
return {
|
266
|
+
...makeBaseComposer(set, get, store),
|
267
|
+
isEditing: true,
|
268
|
+
send: () => {
|
269
|
+
const { value } = get();
|
270
|
+
set({ value: "" });
|
271
|
+
useThread.getState().append({
|
272
|
+
parentId: useThread.getState().messages.at(-1)?.id ?? null,
|
273
|
+
content: [{ type: "text", text: value }]
|
274
|
+
});
|
275
|
+
},
|
276
|
+
cancel: () => {
|
277
|
+
const thread = useThread.getState();
|
278
|
+
if (!thread.isRunning)
|
279
|
+
return false;
|
280
|
+
useThread.getState().cancelRun();
|
281
|
+
return true;
|
282
|
+
}
|
283
|
+
};
|
284
|
+
});
|
285
|
+
|
286
|
+
// src/primitives/message/MessageProvider.tsx
|
287
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
365
288
|
var getIsLast = (thread, message) => {
|
366
|
-
|
367
|
-
return hasUpcoming ? message.id === UPCOMING_MESSAGE_ID : thread.messages[thread.messages.length - 1]?.id === message.id;
|
289
|
+
return thread.messages[thread.messages.length - 1]?.id === message.id;
|
368
290
|
};
|
369
291
|
var useMessageContext2 = () => {
|
292
|
+
const { useThread } = useAssistantContext();
|
370
293
|
const [context] = useState(() => {
|
371
|
-
const
|
372
|
-
const useMessage = create(() => ({
|
294
|
+
const useMessage = create2(() => ({
|
373
295
|
message: null,
|
296
|
+
branches: [],
|
374
297
|
isLast: false,
|
375
298
|
isCopied: false,
|
376
299
|
isHovering: false,
|
@@ -379,34 +302,23 @@ var useMessageContext2 = () => {
|
|
379
302
|
setIsHovering: () => {
|
380
303
|
}
|
381
304
|
}));
|
382
|
-
const useComposer =
|
383
|
-
|
384
|
-
canCancel: true,
|
385
|
-
edit: () => {
|
305
|
+
const useComposer = makeMessageComposerStore({
|
306
|
+
onEdit: () => {
|
386
307
|
const message = useMessage.getState().message;
|
387
308
|
if (message.role !== "user")
|
388
309
|
throw new Error("Editing is only supported for user messages");
|
389
310
|
if (message.content[0]?.type !== "text")
|
390
311
|
throw new Error("Editing is only supported for text-only messages");
|
391
|
-
return
|
392
|
-
isEditing: true,
|
393
|
-
value: message.content[0].text
|
394
|
-
});
|
312
|
+
return message.content[0].text;
|
395
313
|
},
|
396
|
-
|
397
|
-
send: () => {
|
314
|
+
onSend: (text) => {
|
398
315
|
const message = useMessage.getState().message;
|
399
|
-
if (message.role !== "user")
|
400
|
-
throw new Error("Editing is only supported for user messages");
|
401
316
|
useThread.getState().append({
|
402
317
|
parentId: message.parentId,
|
403
|
-
content: [{ type: "text", text
|
318
|
+
content: [{ type: "text", text }]
|
404
319
|
});
|
405
|
-
|
406
|
-
|
407
|
-
value: "",
|
408
|
-
setValue: (value) => set({ value })
|
409
|
-
}));
|
320
|
+
}
|
321
|
+
});
|
410
322
|
return { useMessage, useComposer };
|
411
323
|
});
|
412
324
|
return context;
|
@@ -418,12 +330,14 @@ var MessageProvider = ({
|
|
418
330
|
const { useThread } = useAssistantContext();
|
419
331
|
const context = useMessageContext2();
|
420
332
|
const isLast = useThread((thread) => getIsLast(thread, message));
|
333
|
+
const branches = useThread((thread) => thread.getBranches(message.id));
|
421
334
|
const [isCopied, setIsCopied] = useState(false);
|
422
335
|
const [isHovering, setIsHovering] = useState(false);
|
423
|
-
|
336
|
+
useMemo(() => {
|
424
337
|
context.useMessage.setState(
|
425
338
|
{
|
426
339
|
message,
|
340
|
+
branches,
|
427
341
|
isLast,
|
428
342
|
isCopied,
|
429
343
|
isHovering,
|
@@ -432,8 +346,8 @@ var MessageProvider = ({
|
|
432
346
|
},
|
433
347
|
true
|
434
348
|
);
|
435
|
-
}, [context, message, isLast, isCopied, isHovering]);
|
436
|
-
return /* @__PURE__ */
|
349
|
+
}, [context, message, branches, isLast, isCopied, isHovering]);
|
350
|
+
return /* @__PURE__ */ jsx4(MessageContext.Provider, { value: context, children });
|
437
351
|
};
|
438
352
|
|
439
353
|
// src/primitives/message/MessageRoot.tsx
|
@@ -442,6 +356,7 @@ import {
|
|
442
356
|
Primitive as Primitive3
|
443
357
|
} from "@radix-ui/react-primitive";
|
444
358
|
import { forwardRef as forwardRef3 } from "react";
|
359
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
445
360
|
var MessageRoot = forwardRef3(
|
446
361
|
({ onMouseEnter, onMouseLeave, ...rest }, ref) => {
|
447
362
|
const { useMessage } = useMessageContext();
|
@@ -452,7 +367,7 @@ var MessageRoot = forwardRef3(
|
|
452
367
|
const handleMouseLeave = () => {
|
453
368
|
setIsHovering(false);
|
454
369
|
};
|
455
|
-
return /* @__PURE__ */
|
370
|
+
return /* @__PURE__ */ jsx5(
|
456
371
|
Primitive3.div,
|
457
372
|
{
|
458
373
|
...rest,
|
@@ -467,8 +382,8 @@ var MessageRoot = forwardRef3(
|
|
467
382
|
// src/primitives/message/MessageIf.tsx
|
468
383
|
var useMessageIf = (props) => {
|
469
384
|
const { useMessage } = useMessageContext();
|
470
|
-
return useMessage(({ message, isLast, isCopied, isHovering }) => {
|
471
|
-
if (props.hasBranches === true &&
|
385
|
+
return useMessage(({ message, branches, isLast, isCopied, isHovering }) => {
|
386
|
+
if (props.hasBranches === true && branches.length < 2)
|
472
387
|
return false;
|
473
388
|
if (props.user && message.role !== "user")
|
474
389
|
return false;
|
@@ -489,8 +404,9 @@ var MessageIf = ({ children, ...query }) => {
|
|
489
404
|
};
|
490
405
|
|
491
406
|
// src/primitives/message/MessageContent.tsx
|
407
|
+
import { Fragment, jsx as jsx6 } from "react/jsx-runtime";
|
492
408
|
var defaultComponents = {
|
493
|
-
Text: ({ part }) => /* @__PURE__ */
|
409
|
+
Text: ({ part }) => /* @__PURE__ */ jsx6(Fragment, { children: part.text }),
|
494
410
|
Image: () => null,
|
495
411
|
UI: ({ part }) => part.display,
|
496
412
|
tools: {
|
@@ -507,26 +423,27 @@ var MessageContent = ({
|
|
507
423
|
}) => {
|
508
424
|
const { useMessage } = useMessageContext();
|
509
425
|
const content = useMessage((s) => s.message.content);
|
510
|
-
return /* @__PURE__ */
|
426
|
+
return /* @__PURE__ */ jsx6(Fragment, { children: content.map((part, i) => {
|
511
427
|
const key = i;
|
512
428
|
switch (part.type) {
|
513
429
|
case "text":
|
514
|
-
return /* @__PURE__ */
|
430
|
+
return /* @__PURE__ */ jsx6(Text, { part }, key);
|
515
431
|
case "image":
|
516
|
-
return /* @__PURE__ */
|
432
|
+
return /* @__PURE__ */ jsx6(Image, { part }, key);
|
517
433
|
case "ui":
|
518
|
-
return /* @__PURE__ */
|
434
|
+
return /* @__PURE__ */ jsx6(UI, { part }, key);
|
519
435
|
case "tool-call": {
|
520
436
|
const Tool = by_name[part.name] || Fallback;
|
521
|
-
return /* @__PURE__ */
|
437
|
+
return /* @__PURE__ */ jsx6(Tool, { part }, key);
|
522
438
|
}
|
523
439
|
default:
|
524
440
|
return null;
|
525
441
|
}
|
526
|
-
}));
|
442
|
+
}) });
|
527
443
|
};
|
528
444
|
|
529
445
|
// src/primitives/thread/ThreadMessages.tsx
|
446
|
+
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
|
530
447
|
var getComponents = (components) => {
|
531
448
|
return {
|
532
449
|
EditComposer: components.EditComposer ?? components.UserMessage ?? components.Message,
|
@@ -541,27 +458,18 @@ var ThreadMessages = ({ components }) => {
|
|
541
458
|
const { UserMessage, EditComposer, AssistantMessage } = getComponents(components);
|
542
459
|
if (messages.length === 0)
|
543
460
|
return null;
|
544
|
-
return /* @__PURE__ */
|
461
|
+
return /* @__PURE__ */ jsx7(Fragment2, { children: messages.map((message, idx) => {
|
545
462
|
return (
|
546
463
|
// biome-ignore lint/suspicious/noArrayIndexKey: fixes a11y issues with branch navigation
|
547
|
-
/* @__PURE__ */
|
464
|
+
/* @__PURE__ */ jsxs2(MessageProvider, { message, children: [
|
465
|
+
/* @__PURE__ */ jsxs2(MessageIf, { user: true, children: [
|
466
|
+
/* @__PURE__ */ jsx7(ComposerIf, { editing: false, children: /* @__PURE__ */ jsx7(UserMessage, {}) }),
|
467
|
+
/* @__PURE__ */ jsx7(ComposerIf, { editing: true, children: /* @__PURE__ */ jsx7(EditComposer, {}) })
|
468
|
+
] }),
|
469
|
+
/* @__PURE__ */ jsx7(MessageIf, { assistant: true, children: /* @__PURE__ */ jsx7(AssistantMessage, {}) })
|
470
|
+
] }, idx)
|
548
471
|
);
|
549
|
-
})
|
550
|
-
MessageProvider,
|
551
|
-
{
|
552
|
-
message: {
|
553
|
-
id: UPCOMING_MESSAGE_ID,
|
554
|
-
role: "assistant",
|
555
|
-
content: [{ type: "text", text: "..." }],
|
556
|
-
parentId: messages.at(-1)?.id ?? ROOT_PARENT_ID,
|
557
|
-
// TODO fix these (move upcoming message to AssistantContext)
|
558
|
-
branchId: 0,
|
559
|
-
branchCount: 1,
|
560
|
-
createdAt: /* @__PURE__ */ new Date()
|
561
|
-
}
|
562
|
-
},
|
563
|
-
/* @__PURE__ */ React.createElement(AssistantMessage, null)
|
564
|
-
));
|
472
|
+
}) });
|
565
473
|
};
|
566
474
|
|
567
475
|
// src/primitives/thread/ThreadScrollToBottom.tsx
|
@@ -570,14 +478,14 @@ import {
|
|
570
478
|
Primitive as Primitive4
|
571
479
|
} from "@radix-ui/react-primitive";
|
572
480
|
import { forwardRef as forwardRef4 } from "react";
|
481
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
573
482
|
var ThreadScrollToBottom = forwardRef4(({ onClick, ...rest }, ref) => {
|
574
|
-
const {
|
575
|
-
const isAtBottom =
|
483
|
+
const { useViewport } = useAssistantContext();
|
484
|
+
const isAtBottom = useViewport((s) => s.isAtBottom);
|
576
485
|
const handleScrollToBottom = () => {
|
577
|
-
|
578
|
-
thread.scrollToBottom();
|
486
|
+
useViewport.getState().scrollToBottom();
|
579
487
|
};
|
580
|
-
return /* @__PURE__ */
|
488
|
+
return /* @__PURE__ */ jsx8(
|
581
489
|
Primitive4.button,
|
582
490
|
{
|
583
491
|
...rest,
|
@@ -604,11 +512,13 @@ import { useComposedRefs as useComposedRefs2 } from "@radix-ui/react-compose-ref
|
|
604
512
|
import {
|
605
513
|
Primitive as Primitive5
|
606
514
|
} from "@radix-ui/react-primitive";
|
607
|
-
import { forwardRef as forwardRef5, useRef as
|
515
|
+
import { forwardRef as forwardRef5, useRef as useRef4 } from "react";
|
516
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
608
517
|
var ComposerRoot = forwardRef5(
|
609
518
|
({ onSubmit, ...rest }, forwardedRef) => {
|
519
|
+
const { useViewport } = useAssistantContext();
|
610
520
|
const { useComposer } = useComposerContext();
|
611
|
-
const formRef =
|
521
|
+
const formRef = useRef4(null);
|
612
522
|
const ref = useComposedRefs2(forwardedRef, formRef);
|
613
523
|
const handleSubmit = (e) => {
|
614
524
|
const composerState = useComposer.getState();
|
@@ -616,8 +526,9 @@ var ComposerRoot = forwardRef5(
|
|
616
526
|
return;
|
617
527
|
e.preventDefault();
|
618
528
|
composerState.send();
|
529
|
+
useViewport.getState().scrollToBottom();
|
619
530
|
};
|
620
|
-
return /* @__PURE__ */
|
531
|
+
return /* @__PURE__ */ jsx9(
|
621
532
|
Primitive5.form,
|
622
533
|
{
|
623
534
|
...rest,
|
@@ -634,14 +545,15 @@ import { useComposedRefs as useComposedRefs3 } from "@radix-ui/react-compose-ref
|
|
634
545
|
import { Slot } from "@radix-ui/react-slot";
|
635
546
|
import {
|
636
547
|
forwardRef as forwardRef6,
|
637
|
-
useCallback
|
548
|
+
useCallback,
|
638
549
|
useEffect as useEffect2,
|
639
|
-
useRef as
|
550
|
+
useRef as useRef5
|
640
551
|
} from "react";
|
641
552
|
import TextareaAutosize from "react-textarea-autosize";
|
553
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
642
554
|
var ComposerInput = forwardRef6(
|
643
555
|
({ autoFocus, asChild, disabled, onChange, onKeyDown, ...rest }, forwardedRef) => {
|
644
|
-
const { useThread } = useAssistantContext();
|
556
|
+
const { useThread, useViewport } = useAssistantContext();
|
645
557
|
const { useComposer, type } = useComposerContext();
|
646
558
|
const value = useComposer((c) => {
|
647
559
|
if (!c.isEditing)
|
@@ -653,22 +565,23 @@ var ComposerInput = forwardRef6(
|
|
653
565
|
if (disabled)
|
654
566
|
return;
|
655
567
|
const composer = useComposer.getState();
|
656
|
-
if (e.key === "Escape"
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
if (e.key === "Enter" && e.shiftKey === false) {
|
661
|
-
const
|
662
|
-
if (!
|
568
|
+
if (e.key === "Escape") {
|
569
|
+
if (useComposer.getState().cancel()) {
|
570
|
+
e.preventDefault();
|
571
|
+
}
|
572
|
+
} else if (e.key === "Enter" && e.shiftKey === false) {
|
573
|
+
const isRunning = useThread.getState().isRunning;
|
574
|
+
if (!isRunning) {
|
663
575
|
e.preventDefault();
|
664
576
|
composer.send();
|
577
|
+
useViewport.getState().scrollToBottom();
|
665
578
|
}
|
666
579
|
}
|
667
580
|
};
|
668
|
-
const textareaRef =
|
581
|
+
const textareaRef = useRef5(null);
|
669
582
|
const ref = useComposedRefs3(forwardedRef, textareaRef);
|
670
583
|
const autoFocusEnabled = autoFocus !== false && !disabled;
|
671
|
-
const focus =
|
584
|
+
const focus = useCallback(() => {
|
672
585
|
const textarea = textareaRef.current;
|
673
586
|
if (!textarea || !autoFocusEnabled)
|
674
587
|
return;
|
@@ -684,7 +597,7 @@ var ComposerInput = forwardRef6(
|
|
684
597
|
focus();
|
685
598
|
}
|
686
599
|
});
|
687
|
-
return /* @__PURE__ */
|
600
|
+
return /* @__PURE__ */ jsx10(
|
688
601
|
Component,
|
689
602
|
{
|
690
603
|
value,
|
@@ -708,11 +621,12 @@ import {
|
|
708
621
|
Primitive as Primitive6
|
709
622
|
} from "@radix-ui/react-primitive";
|
710
623
|
import { forwardRef as forwardRef7 } from "react";
|
624
|
+
import { jsx as jsx11 } from "react/jsx-runtime";
|
711
625
|
var ComposerSend = forwardRef7(
|
712
626
|
({ disabled, ...rest }, ref) => {
|
713
627
|
const { useComposer } = useComposerContext();
|
714
628
|
const hasValue = useComposer((c) => c.isEditing && c.value.length > 0);
|
715
|
-
return /* @__PURE__ */
|
629
|
+
return /* @__PURE__ */ jsx11(
|
716
630
|
Primitive6.button,
|
717
631
|
{
|
718
632
|
type: "submit",
|
@@ -730,20 +644,19 @@ import {
|
|
730
644
|
Primitive as Primitive7
|
731
645
|
} from "@radix-ui/react-primitive";
|
732
646
|
import { forwardRef as forwardRef8 } from "react";
|
733
|
-
|
647
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
648
|
+
var ComposerCancel = forwardRef8(({ onClick, ...rest }, ref) => {
|
734
649
|
const { useComposer } = useComposerContext();
|
735
|
-
const
|
736
|
-
const handleClose = () => {
|
650
|
+
const handleCancel = () => {
|
737
651
|
useComposer.getState().cancel();
|
738
652
|
};
|
739
|
-
return /* @__PURE__ */
|
653
|
+
return /* @__PURE__ */ jsx12(
|
740
654
|
Primitive7.button,
|
741
655
|
{
|
742
656
|
type: "button",
|
743
657
|
...rest,
|
744
658
|
ref,
|
745
|
-
onClick: composeEventHandlers6(onClick,
|
746
|
-
disabled: disabled || !hasValue
|
659
|
+
onClick: composeEventHandlers6(onClick, handleCancel)
|
747
660
|
}
|
748
661
|
);
|
749
662
|
});
|
@@ -759,7 +672,7 @@ __export(branchPicker_exports, {
|
|
759
672
|
});
|
760
673
|
|
761
674
|
// src/utils/context/combined/useCombinedStore.ts
|
762
|
-
import { useMemo as
|
675
|
+
import { useMemo as useMemo2 } from "react";
|
763
676
|
|
764
677
|
// src/utils/context/combined/createCombinedStore.ts
|
765
678
|
import { useSyncExternalStore } from "react";
|
@@ -780,37 +693,38 @@ var createCombinedStore = (stores) => {
|
|
780
693
|
|
781
694
|
// src/utils/context/combined/useCombinedStore.ts
|
782
695
|
var useCombinedStore = (stores, selector) => {
|
783
|
-
const useCombined =
|
696
|
+
const useCombined = useMemo2(() => createCombinedStore(stores), stores);
|
784
697
|
return useCombined(selector);
|
785
698
|
};
|
786
699
|
|
787
700
|
// src/actions/useGoToNextBranch.tsx
|
788
701
|
var useGoToNextBranch = () => {
|
789
702
|
const { useThread } = useAssistantContext();
|
790
|
-
const {
|
703
|
+
const { useMessage, useComposer } = useMessageContext();
|
791
704
|
const disabled = useCombinedStore(
|
792
|
-
[
|
793
|
-
(
|
705
|
+
[useMessage, useComposer],
|
706
|
+
(m, c) => c.isEditing || m.branches.indexOf(m.message.id) + 1 >= m.branches.length
|
794
707
|
);
|
795
708
|
if (disabled)
|
796
709
|
return null;
|
797
710
|
return () => {
|
798
|
-
const { message } = useMessage.getState();
|
799
|
-
useThread.getState().switchToBranch(message.id
|
711
|
+
const { message, branches } = useMessage.getState();
|
712
|
+
useThread.getState().switchToBranch(branches[branches.indexOf(message.id) + 1]);
|
800
713
|
};
|
801
714
|
};
|
802
715
|
|
803
716
|
// src/utils/createActionButton.tsx
|
804
|
-
import {
|
717
|
+
import { composeEventHandlers as composeEventHandlers7 } from "@radix-ui/primitive";
|
805
718
|
import {
|
806
719
|
Primitive as Primitive8
|
807
720
|
} from "@radix-ui/react-primitive";
|
808
|
-
import {
|
721
|
+
import { forwardRef as forwardRef9 } from "react";
|
722
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
809
723
|
var createActionButton = (useActionButton) => {
|
810
724
|
return forwardRef9(
|
811
725
|
(props, forwardedRef) => {
|
812
726
|
const onClick = useActionButton(props);
|
813
|
-
return /* @__PURE__ */
|
727
|
+
return /* @__PURE__ */ jsx13(
|
814
728
|
Primitive8.button,
|
815
729
|
{
|
816
730
|
type: "button",
|
@@ -830,16 +744,19 @@ var BranchPickerNext = createActionButton(useGoToNextBranch);
|
|
830
744
|
// src/actions/useGoToPreviousBranch.tsx
|
831
745
|
var useGoToPreviousBranch = () => {
|
832
746
|
const { useThread } = useAssistantContext();
|
833
|
-
const {
|
747
|
+
const { useMessage, useComposer } = useMessageContext();
|
834
748
|
const disabled = useCombinedStore(
|
835
|
-
[
|
836
|
-
(
|
749
|
+
[useMessage, useComposer],
|
750
|
+
(m, c) => c.isEditing || m.branches.indexOf(m.message.id) <= 0
|
837
751
|
);
|
838
752
|
if (disabled)
|
839
753
|
return null;
|
840
754
|
return () => {
|
841
|
-
const { message } = useMessage.getState();
|
842
|
-
useThread.getState().switchToBranch(
|
755
|
+
const { message, branches } = useMessage.getState();
|
756
|
+
useThread.getState().switchToBranch(
|
757
|
+
branches[branches.indexOf(message.id) - 1]
|
758
|
+
// TODO probably there's a more elegant way to do this
|
759
|
+
);
|
843
760
|
};
|
844
761
|
};
|
845
762
|
|
@@ -847,17 +764,19 @@ var useGoToPreviousBranch = () => {
|
|
847
764
|
var BranchPickerPrevious = createActionButton(useGoToPreviousBranch);
|
848
765
|
|
849
766
|
// src/primitives/branchPicker/BranchPickerCount.tsx
|
767
|
+
import { Fragment as Fragment3, jsx as jsx14 } from "react/jsx-runtime";
|
850
768
|
var BranchPickerCount = () => {
|
851
769
|
const { useMessage } = useMessageContext();
|
852
|
-
const branchCount = useMessage((s) => s.
|
853
|
-
return /* @__PURE__ */
|
770
|
+
const branchCount = useMessage((s) => s.branches.length);
|
771
|
+
return /* @__PURE__ */ jsx14(Fragment3, { children: branchCount });
|
854
772
|
};
|
855
773
|
|
856
774
|
// src/primitives/branchPicker/BranchPickerNumber.tsx
|
775
|
+
import { Fragment as Fragment4, jsx as jsx15 } from "react/jsx-runtime";
|
857
776
|
var BranchPickerNumber = () => {
|
858
777
|
const { useMessage } = useMessageContext();
|
859
|
-
const
|
860
|
-
return /* @__PURE__ */
|
778
|
+
const branchIdx = useMessage((s) => s.branches.indexOf(s.message.id));
|
779
|
+
return /* @__PURE__ */ jsx15(Fragment4, { children: branchIdx + 1 });
|
861
780
|
};
|
862
781
|
|
863
782
|
// src/primitives/branchPicker/BranchPickerRoot.tsx
|
@@ -865,8 +784,9 @@ import {
|
|
865
784
|
Primitive as Primitive9
|
866
785
|
} from "@radix-ui/react-primitive";
|
867
786
|
import { forwardRef as forwardRef10 } from "react";
|
787
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
868
788
|
var BranchPickerRoot = forwardRef10(({ hideWhenSingleBranch, ...rest }, ref) => {
|
869
|
-
return /* @__PURE__ */
|
789
|
+
return /* @__PURE__ */ jsx16(MessageIf, { hasBranches: hideWhenSingleBranch ? true : void 0, children: /* @__PURE__ */ jsx16(Primitive9.div, { ...rest, ref }) });
|
870
790
|
});
|
871
791
|
|
872
792
|
// src/primitives/actionBar/index.ts
|
@@ -883,27 +803,28 @@ import {
|
|
883
803
|
Primitive as Primitive10
|
884
804
|
} from "@radix-ui/react-primitive";
|
885
805
|
import { forwardRef as forwardRef11 } from "react";
|
886
|
-
|
806
|
+
import { jsx as jsx17 } from "react/jsx-runtime";
|
807
|
+
var ActionBarRoot = forwardRef11(({ hideWhenRunning, autohide, autohideFloat, ...rest }, ref) => {
|
887
808
|
const { useThread } = useAssistantContext();
|
888
809
|
const { useMessage } = useMessageContext();
|
889
810
|
const hideAndfloatStatus = useCombinedStore(
|
890
811
|
[useThread, useMessage],
|
891
812
|
(t, m) => {
|
892
|
-
if (
|
813
|
+
if (hideWhenRunning && t.isRunning)
|
893
814
|
return "hidden" /* Hidden */;
|
894
815
|
const autohideEnabled = autohide === "always" || autohide === "not-last" && !m.isLast;
|
895
816
|
if (!autohideEnabled)
|
896
817
|
return "normal" /* Normal */;
|
897
818
|
if (!m.isHovering)
|
898
819
|
return "hidden" /* Hidden */;
|
899
|
-
if (autohideFloat === "always" || autohideFloat === "single-branch" && m.
|
820
|
+
if (autohideFloat === "always" || autohideFloat === "single-branch" && m.branches.length <= 1)
|
900
821
|
return "floating" /* Floating */;
|
901
822
|
return "normal" /* Normal */;
|
902
823
|
}
|
903
824
|
);
|
904
825
|
if (hideAndfloatStatus === "hidden" /* Hidden */)
|
905
826
|
return null;
|
906
|
-
return /* @__PURE__ */
|
827
|
+
return /* @__PURE__ */ jsx17(
|
907
828
|
Primitive10.div,
|
908
829
|
{
|
909
830
|
"data-floating": hideAndfloatStatus === "floating" /* Floating */,
|
@@ -934,11 +855,11 @@ var ActionBarCopy = createActionButton(useCopyMessage);
|
|
934
855
|
|
935
856
|
// src/actions/useReloadMessage.tsx
|
936
857
|
var useReloadMessage = () => {
|
937
|
-
const { useThread } = useAssistantContext();
|
858
|
+
const { useThread, useViewport } = useAssistantContext();
|
938
859
|
const { useMessage } = useMessageContext();
|
939
860
|
const disabled = useCombinedStore(
|
940
861
|
[useThread, useMessage],
|
941
|
-
(t, m) => t.
|
862
|
+
(t, m) => t.isRunning || m.message.role !== "assistant"
|
942
863
|
);
|
943
864
|
if (disabled)
|
944
865
|
return null;
|
@@ -946,7 +867,8 @@ var useReloadMessage = () => {
|
|
946
867
|
const message = useMessage.getState().message;
|
947
868
|
if (message.role !== "assistant")
|
948
869
|
throw new Error("Reloading is only supported on assistant messages");
|
949
|
-
useThread.getState().
|
870
|
+
useThread.getState().startRun(message.parentId);
|
871
|
+
useViewport.getState().scrollToBottom();
|
950
872
|
};
|
951
873
|
};
|
952
874
|
|
@@ -976,67 +898,308 @@ import { useCallback as useCallback3, useMemo as useMemo4 } from "react";
|
|
976
898
|
|
977
899
|
// src/adapters/vercel/useDummyAIAssistantContext.tsx
|
978
900
|
import { useState as useState2 } from "react";
|
979
|
-
import { create as
|
901
|
+
import { create as create4 } from "zustand";
|
902
|
+
|
903
|
+
// src/utils/context/stores/ViewportStore.tsx
|
904
|
+
import { create as create3 } from "zustand";
|
905
|
+
var makeViewportStore = () => {
|
906
|
+
const scrollToBottomListeners = /* @__PURE__ */ new Set();
|
907
|
+
return create3(() => ({
|
908
|
+
isAtBottom: true,
|
909
|
+
scrollToBottom: () => {
|
910
|
+
for (const listener of scrollToBottomListeners) {
|
911
|
+
listener();
|
912
|
+
}
|
913
|
+
},
|
914
|
+
onScrollToBottom: (callback) => {
|
915
|
+
scrollToBottomListeners.add(callback);
|
916
|
+
return () => {
|
917
|
+
scrollToBottomListeners.delete(callback);
|
918
|
+
};
|
919
|
+
}
|
920
|
+
}));
|
921
|
+
};
|
922
|
+
|
923
|
+
// src/adapters/vercel/useDummyAIAssistantContext.tsx
|
924
|
+
var makeDummyThreadStore = () => {
|
925
|
+
return create4(() => ({
|
926
|
+
messages: [],
|
927
|
+
isRunning: false,
|
928
|
+
getBranches: () => {
|
929
|
+
return [];
|
930
|
+
},
|
931
|
+
switchToBranch: () => {
|
932
|
+
throw new Error("Not implemented");
|
933
|
+
},
|
934
|
+
append: async () => {
|
935
|
+
throw new Error("Not implemented");
|
936
|
+
},
|
937
|
+
cancelRun: () => {
|
938
|
+
throw new Error("Not implemented");
|
939
|
+
},
|
940
|
+
startRun: async () => {
|
941
|
+
throw new Error("Not implemented");
|
942
|
+
}
|
943
|
+
}));
|
944
|
+
};
|
980
945
|
var useDummyAIAssistantContext = () => {
|
981
946
|
const [context] = useState2(() => {
|
982
|
-
const
|
983
|
-
const
|
984
|
-
|
985
|
-
|
986
|
-
append: async () => {
|
987
|
-
throw new Error("Not implemented");
|
988
|
-
},
|
989
|
-
stop: () => {
|
990
|
-
throw new Error("Not implemented");
|
991
|
-
},
|
992
|
-
switchToBranch: () => {
|
993
|
-
throw new Error("Not implemented");
|
994
|
-
},
|
995
|
-
reload: async () => {
|
996
|
-
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
|
-
}
|
1010
|
-
}));
|
1011
|
-
const useComposer = create2()(() => ({
|
1012
|
-
isEditing: true,
|
1013
|
-
canCancel: false,
|
1014
|
-
value: "",
|
1015
|
-
setValue: (value) => {
|
1016
|
-
useComposer.setState({ value });
|
1017
|
-
},
|
1018
|
-
edit: () => {
|
1019
|
-
throw new Error("Not implemented");
|
1020
|
-
},
|
1021
|
-
send: () => {
|
1022
|
-
useThread.getState().append({
|
1023
|
-
parentId: useThread.getState().messages.at(-1)?.id ?? ROOT_PARENT_ID,
|
1024
|
-
content: [{ type: "text", text: useComposer.getState().value }]
|
1025
|
-
});
|
1026
|
-
useComposer.getState().setValue("");
|
1027
|
-
},
|
1028
|
-
cancel: () => {
|
1029
|
-
useThread.getState().stop();
|
1030
|
-
}
|
1031
|
-
}));
|
1032
|
-
return { useThread, useComposer };
|
947
|
+
const useThread = makeDummyThreadStore();
|
948
|
+
const useViewport = makeViewportStore();
|
949
|
+
const useComposer = makeThreadComposerStore(useThread);
|
950
|
+
return { useThread, useViewport, useComposer };
|
1033
951
|
});
|
1034
952
|
return context;
|
1035
953
|
};
|
1036
954
|
|
955
|
+
// src/adapters/vercel/useVercelAIBranches.tsx
|
956
|
+
import { useCallback as useCallback2, useMemo as useMemo3, useRef as useRef6, useState as useState3 } from "react";
|
957
|
+
|
958
|
+
// ../../node_modules/nanoid/non-secure/index.js
|
959
|
+
var customAlphabet = (alphabet, defaultSize = 21) => {
|
960
|
+
return (size = defaultSize) => {
|
961
|
+
let id = "";
|
962
|
+
let i = size;
|
963
|
+
while (i--) {
|
964
|
+
id += alphabet[Math.random() * alphabet.length | 0];
|
965
|
+
}
|
966
|
+
return id;
|
967
|
+
};
|
968
|
+
};
|
969
|
+
|
970
|
+
// src/adapters/MessageRepository.tsx
|
971
|
+
var generateId = customAlphabet(
|
972
|
+
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
|
973
|
+
7
|
974
|
+
);
|
975
|
+
var optimisticPrefix = "__optimistic__";
|
976
|
+
var generateOptimisticId = () => `${optimisticPrefix}${generateId()}`;
|
977
|
+
var isOptimisticId = (id) => id.startsWith(optimisticPrefix);
|
978
|
+
var findHead = (message) => {
|
979
|
+
if (message.next)
|
980
|
+
return findHead(message.next);
|
981
|
+
return message;
|
982
|
+
};
|
983
|
+
var MessageRepository = class {
|
984
|
+
messages = /* @__PURE__ */ new Map();
|
985
|
+
// message_id -> item
|
986
|
+
head = null;
|
987
|
+
rootChildren = [];
|
988
|
+
getMessages() {
|
989
|
+
const messages = new Array(this.head?.level ?? 0);
|
990
|
+
for (let current = this.head; current; current = current.prev) {
|
991
|
+
messages[current.level] = current.current;
|
992
|
+
}
|
993
|
+
return messages;
|
994
|
+
}
|
995
|
+
addOrUpdateMessage(message) {
|
996
|
+
const item = this.messages.get(message.id);
|
997
|
+
if (item) {
|
998
|
+
if (item.current.parentId !== message.parentId) {
|
999
|
+
this.deleteMessage(message.id);
|
1000
|
+
} else {
|
1001
|
+
item.current = message;
|
1002
|
+
return;
|
1003
|
+
}
|
1004
|
+
}
|
1005
|
+
const prev = message.parentId ? this.messages.get(message.parentId) : null;
|
1006
|
+
if (prev === void 0)
|
1007
|
+
throw new Error("Unexpected: Parent message not found");
|
1008
|
+
const newItem = {
|
1009
|
+
prev,
|
1010
|
+
current: message,
|
1011
|
+
next: null,
|
1012
|
+
children: [],
|
1013
|
+
level: prev ? prev.level + 1 : 0
|
1014
|
+
};
|
1015
|
+
this.messages.set(message.id, newItem);
|
1016
|
+
if (prev) {
|
1017
|
+
prev.children = [...prev.children, message.id];
|
1018
|
+
prev.next = newItem;
|
1019
|
+
} else {
|
1020
|
+
this.rootChildren = [...this.rootChildren, message.id];
|
1021
|
+
}
|
1022
|
+
if (this.head === prev) {
|
1023
|
+
this.head = newItem;
|
1024
|
+
}
|
1025
|
+
}
|
1026
|
+
deleteMessage(messageId) {
|
1027
|
+
const message = this.messages.get(messageId);
|
1028
|
+
if (!message)
|
1029
|
+
throw new Error("Unexpected: Message not found");
|
1030
|
+
if (message.children.length > 0) {
|
1031
|
+
for (const child of message.children) {
|
1032
|
+
this.deleteMessage(child);
|
1033
|
+
}
|
1034
|
+
}
|
1035
|
+
this.messages.delete(messageId);
|
1036
|
+
if (message.prev) {
|
1037
|
+
message.prev.children = message.prev.children.filter(
|
1038
|
+
(m) => m !== messageId
|
1039
|
+
);
|
1040
|
+
if (message.prev.next === message) {
|
1041
|
+
const childId = message.prev.children.at(-1);
|
1042
|
+
const child = childId ? this.messages.get(childId) : null;
|
1043
|
+
if (child === void 0)
|
1044
|
+
throw new Error("Unexpected: Child message not found");
|
1045
|
+
message.prev.next = child;
|
1046
|
+
}
|
1047
|
+
} else {
|
1048
|
+
this.rootChildren = this.rootChildren.filter((m) => m !== messageId);
|
1049
|
+
}
|
1050
|
+
if (this.head === message) {
|
1051
|
+
this.head = message.prev ? findHead(message.prev) : null;
|
1052
|
+
}
|
1053
|
+
}
|
1054
|
+
getOptimisticId = () => {
|
1055
|
+
let optimisticId;
|
1056
|
+
do {
|
1057
|
+
optimisticId = generateOptimisticId();
|
1058
|
+
} while (this.messages.has(optimisticId));
|
1059
|
+
return optimisticId;
|
1060
|
+
};
|
1061
|
+
commitOptimisticRun(parentId) {
|
1062
|
+
const optimisticId = this.getOptimisticId();
|
1063
|
+
this.addOrUpdateMessage({
|
1064
|
+
id: optimisticId,
|
1065
|
+
role: "assistant",
|
1066
|
+
content: [
|
1067
|
+
{
|
1068
|
+
type: "text",
|
1069
|
+
text: ""
|
1070
|
+
}
|
1071
|
+
],
|
1072
|
+
parentId,
|
1073
|
+
createdAt: /* @__PURE__ */ new Date()
|
1074
|
+
});
|
1075
|
+
return optimisticId;
|
1076
|
+
}
|
1077
|
+
getBranches(messageId) {
|
1078
|
+
const message = this.messages.get(messageId);
|
1079
|
+
if (!message)
|
1080
|
+
throw new Error("Unexpected: Message not found");
|
1081
|
+
if (message.prev) {
|
1082
|
+
return message.prev.children;
|
1083
|
+
}
|
1084
|
+
return this.rootChildren;
|
1085
|
+
}
|
1086
|
+
switchToBranch(messageId) {
|
1087
|
+
const message = this.messages.get(messageId);
|
1088
|
+
if (!message)
|
1089
|
+
throw new Error("Unexpected: Branch not found");
|
1090
|
+
if (message.prev) {
|
1091
|
+
message.prev.next = message;
|
1092
|
+
}
|
1093
|
+
this.head = findHead(message);
|
1094
|
+
}
|
1095
|
+
resetHead(messageId) {
|
1096
|
+
if (messageId) {
|
1097
|
+
const message = this.messages.get(messageId);
|
1098
|
+
if (!message)
|
1099
|
+
throw new Error("Unexpected: Branch not found");
|
1100
|
+
this.head = message;
|
1101
|
+
for (let current = message; current; current = current.prev) {
|
1102
|
+
if (current.prev) {
|
1103
|
+
current.prev.next = current;
|
1104
|
+
}
|
1105
|
+
}
|
1106
|
+
} else {
|
1107
|
+
this.head = null;
|
1108
|
+
}
|
1109
|
+
}
|
1110
|
+
};
|
1111
|
+
|
1112
|
+
// src/adapters/vercel/useVercelAIBranches.tsx
|
1113
|
+
var sliceMessagesUntil = (messages, messageId) => {
|
1114
|
+
if (messageId == null)
|
1115
|
+
return [];
|
1116
|
+
if (isOptimisticId(messageId))
|
1117
|
+
return messages;
|
1118
|
+
const messageIdx = messages.findIndex((m) => m.id === messageId);
|
1119
|
+
if (messageIdx === -1)
|
1120
|
+
throw new Error("Unexpected: Message not found");
|
1121
|
+
return messages.slice(0, messageIdx + 1);
|
1122
|
+
};
|
1123
|
+
var hasUpcomingMessage = (isRunning, messages) => {
|
1124
|
+
return isRunning && messages[messages.length - 1]?.role !== "assistant";
|
1125
|
+
};
|
1126
|
+
var useVercelAIBranches = (chat, messages) => {
|
1127
|
+
const [data] = useState3(() => new MessageRepository());
|
1128
|
+
const isRunning = "isLoading" in chat ? chat.isLoading : chat.status === "in_progress";
|
1129
|
+
const assistantOptimisticIdRef = useRef6(null);
|
1130
|
+
const messagesEx = useMemo3(() => {
|
1131
|
+
for (const message of messages) {
|
1132
|
+
data.addOrUpdateMessage(message);
|
1133
|
+
}
|
1134
|
+
if (assistantOptimisticIdRef.current) {
|
1135
|
+
data.deleteMessage(assistantOptimisticIdRef.current);
|
1136
|
+
assistantOptimisticIdRef.current = null;
|
1137
|
+
}
|
1138
|
+
if (hasUpcomingMessage(isRunning, messages)) {
|
1139
|
+
assistantOptimisticIdRef.current = data.commitOptimisticRun(
|
1140
|
+
messages.at(-1)?.id ?? null
|
1141
|
+
);
|
1142
|
+
}
|
1143
|
+
data.resetHead(
|
1144
|
+
assistantOptimisticIdRef.current ?? messages.at(-1)?.id ?? null
|
1145
|
+
);
|
1146
|
+
return data.getMessages();
|
1147
|
+
}, [data, isRunning, messages]);
|
1148
|
+
const getBranches = useCallback2(
|
1149
|
+
(messageId) => {
|
1150
|
+
return data.getBranches(messageId);
|
1151
|
+
},
|
1152
|
+
[data]
|
1153
|
+
);
|
1154
|
+
const switchToBranch = useCallback2(
|
1155
|
+
(messageId) => {
|
1156
|
+
data.switchToBranch(messageId);
|
1157
|
+
chat.setMessages(
|
1158
|
+
data.getMessages().filter((m) => !isOptimisticId(m.id)).map((m) => m.innerMessage)
|
1159
|
+
);
|
1160
|
+
},
|
1161
|
+
[data, chat.setMessages]
|
1162
|
+
);
|
1163
|
+
const reloadMaybe = "reload" in chat ? chat.reload : void 0;
|
1164
|
+
const startRun = useCallback2(
|
1165
|
+
async (parentId) => {
|
1166
|
+
if (!reloadMaybe)
|
1167
|
+
throw new Error("Reload not supported by Vercel AI SDK's useAssistant");
|
1168
|
+
const newMessages = sliceMessagesUntil(chat.messages, parentId);
|
1169
|
+
chat.setMessages(newMessages);
|
1170
|
+
await reloadMaybe();
|
1171
|
+
},
|
1172
|
+
[chat.messages, chat.setMessages, reloadMaybe]
|
1173
|
+
);
|
1174
|
+
const append = useCallback2(
|
1175
|
+
async (message) => {
|
1176
|
+
if (message.content.length !== 1 || message.content[0]?.type !== "text")
|
1177
|
+
throw new Error("Only text content is supported by Vercel AI SDK");
|
1178
|
+
const newMessages = sliceMessagesUntil(chat.messages, message.parentId);
|
1179
|
+
chat.setMessages(newMessages);
|
1180
|
+
await chat.append({
|
1181
|
+
role: "user",
|
1182
|
+
content: message.content[0].text
|
1183
|
+
});
|
1184
|
+
},
|
1185
|
+
[chat.messages, chat.setMessages, chat.append]
|
1186
|
+
);
|
1187
|
+
return useMemo3(
|
1188
|
+
() => ({
|
1189
|
+
messages: messagesEx,
|
1190
|
+
getBranches,
|
1191
|
+
switchToBranch,
|
1192
|
+
append,
|
1193
|
+
startRun
|
1194
|
+
}),
|
1195
|
+
[messagesEx, getBranches, switchToBranch, append, startRun]
|
1196
|
+
);
|
1197
|
+
};
|
1198
|
+
|
1037
1199
|
// src/adapters/vercel/VercelAIAssistantProvider.tsx
|
1200
|
+
import { jsx as jsx18 } from "react/jsx-runtime";
|
1038
1201
|
var ThreadMessageCache = /* @__PURE__ */ new WeakMap();
|
1039
|
-
var vercelToThreadMessage = (message, parentId
|
1202
|
+
var vercelToThreadMessage = (message, parentId) => {
|
1040
1203
|
if (message.role !== "user" && message.role !== "assistant")
|
1041
1204
|
throw new Error("Unsupported role");
|
1042
1205
|
return {
|
@@ -1044,24 +1207,17 @@ var vercelToThreadMessage = (message, parentId, branchId, branchCount) => {
|
|
1044
1207
|
id: message.id,
|
1045
1208
|
role: message.role,
|
1046
1209
|
content: [{ type: "text", text: message.content }],
|
1047
|
-
|
1048
|
-
|
1049
|
-
createdAt: message.createdAt ?? /* @__PURE__ */ new Date()
|
1210
|
+
createdAt: message.createdAt ?? /* @__PURE__ */ new Date(),
|
1211
|
+
innerMessage: message
|
1050
1212
|
};
|
1051
1213
|
};
|
1052
|
-
var vercelToCachedThreadMessages = (messages
|
1214
|
+
var vercelToCachedThreadMessages = (messages) => {
|
1053
1215
|
return messages.map((m, idx) => {
|
1054
1216
|
const cached = ThreadMessageCache.get(m);
|
1055
|
-
const parentId = messages[idx - 1]?.id ??
|
1056
|
-
|
1057
|
-
if (cached && cached.parentId === parentId && cached.branchId === branchId && cached.branchCount === branchCount)
|
1217
|
+
const parentId = messages[idx - 1]?.id ?? null;
|
1218
|
+
if (cached && cached.parentId === parentId)
|
1058
1219
|
return cached;
|
1059
|
-
const newMessage = vercelToThreadMessage(
|
1060
|
-
m,
|
1061
|
-
parentId,
|
1062
|
-
branchId,
|
1063
|
-
branchCount
|
1064
|
-
);
|
1220
|
+
const newMessage = vercelToThreadMessage(m, parentId);
|
1065
1221
|
ThreadMessageCache.set(m, newMessage);
|
1066
1222
|
return newMessage;
|
1067
1223
|
});
|
@@ -1072,39 +1228,39 @@ var VercelAIAssistantProvider = ({
|
|
1072
1228
|
}) => {
|
1073
1229
|
const context = useDummyAIAssistantContext();
|
1074
1230
|
const vercel = "chat" in rest ? rest.chat : rest.assistant;
|
1075
|
-
const branches = useVercelAIBranches(vercel, context);
|
1076
1231
|
const messages = useMemo4(() => {
|
1077
|
-
return vercelToCachedThreadMessages(
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
}, [vercel.messages, branches.getBranchState]);
|
1082
|
-
const stop = useCallback3(() => {
|
1232
|
+
return vercelToCachedThreadMessages(vercel.messages);
|
1233
|
+
}, [vercel.messages]);
|
1234
|
+
const branches = useVercelAIBranches(vercel, messages);
|
1235
|
+
const cancelRun = useCallback3(() => {
|
1083
1236
|
const lastMessage = vercel.messages.at(-1);
|
1084
1237
|
vercel.stop();
|
1085
1238
|
if (lastMessage?.role === "user") {
|
1086
1239
|
vercel.setInput(lastMessage.content);
|
1087
1240
|
}
|
1088
1241
|
}, [vercel.messages, vercel.stop, vercel.setInput]);
|
1089
|
-
const
|
1242
|
+
const isRunning = "isLoading" in vercel ? vercel.isLoading : vercel.status === "in_progress";
|
1090
1243
|
useMemo4(() => {
|
1091
|
-
context.useThread.setState(
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1244
|
+
context.useThread.setState(
|
1245
|
+
{
|
1246
|
+
messages: branches.messages,
|
1247
|
+
isRunning,
|
1248
|
+
getBranches: branches.getBranches,
|
1249
|
+
switchToBranch: branches.switchToBranch,
|
1250
|
+
append: branches.append,
|
1251
|
+
startRun: branches.startRun,
|
1252
|
+
cancelRun
|
1253
|
+
},
|
1254
|
+
true
|
1255
|
+
);
|
1256
|
+
}, [context, isRunning, cancelRun, branches]);
|
1100
1257
|
useMemo4(() => {
|
1101
1258
|
context.useComposer.setState({
|
1102
|
-
canCancel: isLoading,
|
1103
1259
|
value: vercel.input,
|
1104
1260
|
setValue: vercel.setInput
|
1105
1261
|
});
|
1106
|
-
}, [context,
|
1107
|
-
return /* @__PURE__ */
|
1262
|
+
}, [context, vercel.input, vercel.setInput]);
|
1263
|
+
return /* @__PURE__ */ jsx18(AssistantContext.Provider, { value: context, children });
|
1108
1264
|
};
|
1109
1265
|
|
1110
1266
|
// src/adapters/vercel/VercelRSCAssistantProvider.tsx
|
@@ -1112,6 +1268,7 @@ import {
|
|
1112
1268
|
useCallback as useCallback4,
|
1113
1269
|
useMemo as useMemo5
|
1114
1270
|
} from "react";
|
1271
|
+
import { jsx as jsx19 } from "react/jsx-runtime";
|
1115
1272
|
var ThreadMessageCache2 = /* @__PURE__ */ new WeakMap();
|
1116
1273
|
var vercelToThreadMessage2 = (parentId, message) => {
|
1117
1274
|
if (message.role !== "user" && message.role !== "assistant")
|
@@ -1121,15 +1278,13 @@ var vercelToThreadMessage2 = (parentId, message) => {
|
|
1121
1278
|
id: message.id,
|
1122
1279
|
role: message.role,
|
1123
1280
|
content: [{ type: "ui", display: message.display }],
|
1124
|
-
createdAt: message.createdAt ?? /* @__PURE__ */ new Date()
|
1125
|
-
branchId: 0,
|
1126
|
-
branchCount: 1
|
1281
|
+
createdAt: message.createdAt ?? /* @__PURE__ */ new Date()
|
1127
1282
|
};
|
1128
1283
|
};
|
1129
1284
|
var vercelToCachedThreadMessages2 = (messages) => {
|
1130
1285
|
return messages.map((m, idx) => {
|
1131
1286
|
const cached = ThreadMessageCache2.get(m);
|
1132
|
-
const parentId = messages[idx - 1]?.id ??
|
1287
|
+
const parentId = messages[idx - 1]?.id ?? null;
|
1133
1288
|
if (cached && cached.parentId === parentId)
|
1134
1289
|
return cached;
|
1135
1290
|
const newMessage = vercelToThreadMessage2(parentId, m);
|
@@ -1148,12 +1303,11 @@ var VercelRSCAssistantProvider = ({
|
|
1148
1303
|
}, [vercelMessages]);
|
1149
1304
|
const append = useCallback4(
|
1150
1305
|
async (message) => {
|
1151
|
-
if (message.parentId !== (context.useThread.getState().messages.at(-1)?.id ??
|
1306
|
+
if (message.parentId !== (context.useThread.getState().messages.at(-1)?.id ?? null))
|
1152
1307
|
throw new Error("Unexpected: Message editing is not supported");
|
1153
1308
|
if (message.content[0]?.type !== "text") {
|
1154
1309
|
throw new Error("Only text content is currently supported");
|
1155
1310
|
}
|
1156
|
-
context.useThread.getState().scrollToBottom();
|
1157
1311
|
await vercelAppend(message);
|
1158
1312
|
},
|
1159
1313
|
[context, vercelAppend]
|
@@ -1164,7 +1318,7 @@ var VercelRSCAssistantProvider = ({
|
|
1164
1318
|
append
|
1165
1319
|
});
|
1166
1320
|
}, [context, messages, append]);
|
1167
|
-
return /* @__PURE__ */
|
1321
|
+
return /* @__PURE__ */ jsx19(AssistantContext.Provider, { value: context, children });
|
1168
1322
|
};
|
1169
1323
|
export {
|
1170
1324
|
actionBar_exports as ActionBarPrimitive,
|