@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.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,