@assistant-ui/react 0.0.9 → 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
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__ */ React.createElement(Primitive.div, { ...props, ref });
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.busy === true && !thread.isLoading)
50
+ if (props.running === true && !thread.isRunning)
50
51
  return false;
51
- if (props.busy === false && thread.isLoading)
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__ */ React.createElement(ThreadIf, { empty: true }, children);
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 { useThread } = useAssistantContext();
120
+ const { useViewport } = useAssistantContext();
119
121
  useEffect(() => {
120
- return useThread.getState().onScrollToBottom(() => {
122
+ return useViewport.getState().onScrollToBottom(() => {
121
123
  callbackRef.current();
122
124
  });
123
- }, [useThread]);
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 { useThread } = useAssistantContext();
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
- useThread.setState({ isAtBottom: true });
143
+ useViewport.setState({ isAtBottom: true });
141
144
  div.scrollIntoView({ behavior });
142
145
  };
143
146
  useOnResizeContent(divRef, () => {
144
- if (!useThread.getState().isAtBottom)
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 = useThread.getState().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
- useThread.setState({ isAtBottom: newIsAtBottom });
162
+ useViewport.setState({ isAtBottom: newIsAtBottom });
160
163
  }
161
164
  lastScrollTop.current = div.scrollTop;
162
165
  };
163
- return /* @__PURE__ */ React.createElement(
166
+ return /* @__PURE__ */ jsxs(
164
167
  Primitive2.div,
165
168
  {
166
169
  ...rest,
167
170
  onScroll: composeEventHandlers(onScroll, handleScroll),
168
- ref
169
- },
170
- children,
171
- /* @__PURE__ */ React.createElement("div", { ref: messagesEndRef })
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 as useMemo2, useState } from "react";
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
- const hasUpcoming = hasUpcomingMessage(thread);
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 { useThread } = useAssistantContext();
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 = create((set, get) => ({
383
- isEditing: false,
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 set({
392
- isEditing: true,
393
- value: message.content[0].text
394
- });
312
+ return message.content[0].text;
395
313
  },
396
- cancel: () => set({ isEditing: false }),
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: get().value }]
318
+ content: [{ type: "text", text }]
404
319
  });
405
- set({ isEditing: false });
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
- useMemo2(() => {
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__ */ React.createElement(MessageContext.Provider, { value: context }, children);
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__ */ React.createElement(
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 && message.branchCount < 2)
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__ */ React.createElement(React.Fragment, null, part.text),
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__ */ React.createElement(React.Fragment, null, content.map((part, i) => {
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__ */ React.createElement(Text, { key, part });
430
+ return /* @__PURE__ */ jsx6(Text, { part }, key);
515
431
  case "image":
516
- return /* @__PURE__ */ React.createElement(Image, { key, part });
432
+ return /* @__PURE__ */ jsx6(Image, { part }, key);
517
433
  case "ui":
518
- return /* @__PURE__ */ React.createElement(UI, { key, part });
434
+ return /* @__PURE__ */ jsx6(UI, { part }, key);
519
435
  case "tool-call": {
520
436
  const Tool = by_name[part.name] || Fallback;
521
- return /* @__PURE__ */ React.createElement(Tool, { key, part });
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__ */ React.createElement(React.Fragment, null, messages.map((message, idx) => {
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__ */ React.createElement(MessageProvider, { key: idx, message }, /* @__PURE__ */ React.createElement(MessageIf, { user: true }, /* @__PURE__ */ React.createElement(ComposerIf, { editing: false }, /* @__PURE__ */ React.createElement(UserMessage, null)), /* @__PURE__ */ React.createElement(ComposerIf, { editing: true }, /* @__PURE__ */ React.createElement(EditComposer, null))), /* @__PURE__ */ React.createElement(MessageIf, { assistant: true }, /* @__PURE__ */ React.createElement(AssistantMessage, null)))
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
- }), hasUpcomingMessage(thread) && /* @__PURE__ */ React.createElement(
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 { useThread } = useAssistantContext();
575
- const isAtBottom = useThread((s) => s.isAtBottom);
483
+ const { useViewport } = useAssistantContext();
484
+ const isAtBottom = useViewport((s) => s.isAtBottom);
576
485
  const handleScrollToBottom = () => {
577
- const thread = useThread.getState();
578
- thread.scrollToBottom();
486
+ useViewport.getState().scrollToBottom();
579
487
  };
580
- return /* @__PURE__ */ React.createElement(
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 useRef5 } from "react";
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 = useRef5(null);
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__ */ React.createElement(
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 as useCallback2,
548
+ useCallback,
638
549
  useEffect as useEffect2,
639
- useRef as useRef6
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" && composer.canCancel) {
657
- e.preventDefault();
658
- useComposer.getState().cancel();
659
- }
660
- if (e.key === "Enter" && e.shiftKey === false) {
661
- const isLoading = useThread.getState().isLoading;
662
- if (!isLoading) {
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 = useRef6(null);
581
+ const textareaRef = useRef5(null);
669
582
  const ref = useComposedRefs3(forwardedRef, textareaRef);
670
583
  const autoFocusEnabled = autoFocus !== false && !disabled;
671
- const focus = useCallback2(() => {
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__ */ React.createElement(
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__ */ React.createElement(
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
- var ComposerCancel = forwardRef8(({ disabled, onClick, ...rest }, ref) => {
647
+ import { jsx as jsx12 } from "react/jsx-runtime";
648
+ var ComposerCancel = forwardRef8(({ onClick, ...rest }, ref) => {
734
649
  const { useComposer } = useComposerContext();
735
- const hasValue = useComposer((c) => c.canCancel);
736
- const handleClose = () => {
650
+ const handleCancel = () => {
737
651
  useComposer.getState().cancel();
738
652
  };
739
- return /* @__PURE__ */ React.createElement(
653
+ return /* @__PURE__ */ jsx12(
740
654
  Primitive7.button,
741
655
  {
742
656
  type: "button",
743
657
  ...rest,
744
658
  ref,
745
- onClick: composeEventHandlers6(onClick, handleClose),
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 useMemo3 } from "react";
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 = useMemo3(() => createCombinedStore(stores), stores);
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 { useComposer, useMessage } = useMessageContext();
703
+ const { useMessage, useComposer } = useMessageContext();
791
704
  const disabled = useCombinedStore(
792
- [useThread, useComposer, useMessage],
793
- (t, c, m) => t.isLoading || c.isEditing || m.message.branchId + 1 >= m.message.branchCount
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, message.branchId + 1);
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 { forwardRef as forwardRef9 } from "react";
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 { composeEventHandlers as composeEventHandlers7 } from "@radix-ui/primitive";
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__ */ React.createElement(
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 { useComposer, useMessage } = useMessageContext();
747
+ const { useMessage, useComposer } = useMessageContext();
834
748
  const disabled = useCombinedStore(
835
- [useThread, useComposer, useMessage],
836
- (t, c, m) => t.isLoading || c.isEditing || m.message.branchId <= 0
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(message.id, message.branchId - 1);
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.message.branchCount);
853
- return /* @__PURE__ */ React.createElement(React.Fragment, null, branchCount);
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 branchId = useMessage((s) => s.message.branchId);
860
- return /* @__PURE__ */ React.createElement(React.Fragment, null, branchId + 1);
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__ */ React.createElement(MessageIf, { hasBranches: hideWhenSingleBranch ? true : void 0 }, /* @__PURE__ */ React.createElement(Primitive9.div, { ...rest, ref }));
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
- var ActionBarRoot = forwardRef11(({ hideWhenBusy, autohide, autohideFloat, ...rest }, ref) => {
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 (hideWhenBusy && t.isLoading)
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.message.branchCount <= 1)
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__ */ React.createElement(
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.isLoading || m.message.role !== "assistant"
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().reload(message.id);
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 create2 } from "zustand";
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 scrollToBottomListeners = /* @__PURE__ */ new Set();
983
- const useThread = create2()(() => ({
984
- messages: [],
985
- isLoading: false,
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, branchId, branchCount) => {
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
- branchId,
1048
- branchCount,
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, getBranchState) => {
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 ?? ROOT_PARENT_ID;
1056
- const { branchId, branchCount } = getBranchState(m.id);
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
- vercel.messages,
1079
- branches.getBranchState
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 isLoading = "isLoading" in vercel ? vercel.isLoading : vercel.status === "in_progress";
1242
+ const isRunning = "isLoading" in vercel ? vercel.isLoading : vercel.status === "in_progress";
1090
1243
  useMemo4(() => {
1091
- context.useThread.setState({
1092
- messages,
1093
- isLoading,
1094
- stop,
1095
- switchToBranch: branches.switchToBranch,
1096
- append: branches.append,
1097
- reload: branches.reload
1098
- });
1099
- }, [context, messages, isLoading, stop, branches]);
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, isLoading, vercel.input, vercel.setInput]);
1107
- return /* @__PURE__ */ React.createElement(AssistantContext.Provider, { value: context }, children);
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 ?? ROOT_PARENT_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 ?? ROOT_PARENT_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__ */ React.createElement(AssistantContext.Provider, { value: context }, children);
1321
+ return /* @__PURE__ */ jsx19(AssistantContext.Provider, { value: context, children });
1168
1322
  };
1169
1323
  export {
1170
1324
  actionBar_exports as ActionBarPrimitive,