@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.js CHANGED
@@ -58,11 +58,12 @@ __export(thread_exports, {
58
58
  });
59
59
 
60
60
  // src/primitives/thread/ThreadRoot.tsx
61
- var import_react = require("react");
62
61
  var import_react_primitive = require("@radix-ui/react-primitive");
62
+ var import_react = require("react");
63
+ var import_jsx_runtime = require("react/jsx-runtime");
63
64
  var ThreadRoot = (0, import_react.forwardRef)(
64
65
  (props, ref) => {
65
- return /* @__PURE__ */ React.createElement(import_react_primitive.Primitive.div, { ...props, ref });
66
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_primitive.Primitive.div, { ...props, ref });
66
67
  }
67
68
  );
68
69
 
@@ -86,9 +87,9 @@ var useThreadIf = (props) => {
86
87
  return false;
87
88
  if (props.empty === false && thread.messages.length === 0)
88
89
  return false;
89
- if (props.busy === true && !thread.isLoading)
90
+ if (props.running === true && !thread.isRunning)
90
91
  return false;
91
- if (props.busy === false && thread.isLoading)
92
+ if (props.running === false && thread.isRunning)
92
93
  return false;
93
94
  return true;
94
95
  });
@@ -99,8 +100,9 @@ var ThreadIf = ({ children, ...query }) => {
99
100
  };
100
101
 
101
102
  // src/primitives/thread/ThreadEmpty.tsx
103
+ var import_jsx_runtime2 = require("react/jsx-runtime");
102
104
  var ThreadEmpty = ({ children }) => {
103
- return /* @__PURE__ */ React.createElement(ThreadIf, { empty: true }, children);
105
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ThreadIf, { empty: true, children });
104
106
  };
105
107
 
106
108
  // src/primitives/thread/ThreadViewport.tsx
@@ -153,20 +155,21 @@ var import_react4 = require("react");
153
155
  var useOnScrollToBottom = (callback) => {
154
156
  const callbackRef = (0, import_react4.useRef)(callback);
155
157
  callbackRef.current = callback;
156
- const { useThread } = useAssistantContext();
158
+ const { useViewport } = useAssistantContext();
157
159
  (0, import_react4.useEffect)(() => {
158
- return useThread.getState().onScrollToBottom(() => {
160
+ return useViewport.getState().onScrollToBottom(() => {
159
161
  callbackRef.current();
160
162
  });
161
- }, [useThread]);
163
+ }, [useViewport]);
162
164
  };
163
165
 
164
166
  // src/primitives/thread/ThreadViewport.tsx
167
+ var import_jsx_runtime3 = require("react/jsx-runtime");
165
168
  var ThreadViewport = (0, import_react5.forwardRef)(({ autoScroll = true, onScroll, children, ...rest }, forwardedRef) => {
166
169
  const messagesEndRef = (0, import_react5.useRef)(null);
167
170
  const divRef = (0, import_react5.useRef)(null);
168
171
  const ref = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, divRef);
169
- const { useThread } = useAssistantContext();
172
+ const { useViewport } = useAssistantContext();
170
173
  const firstRenderRef = (0, import_react5.useRef)(true);
171
174
  const lastScrollTop = (0, import_react5.useRef)(0);
172
175
  const scrollToBottom = () => {
@@ -175,11 +178,11 @@ var ThreadViewport = (0, import_react5.forwardRef)(({ autoScroll = true, onScrol
175
178
  return;
176
179
  const behavior = firstRenderRef.current ? "instant" : "auto";
177
180
  firstRenderRef.current = false;
178
- useThread.setState({ isAtBottom: true });
181
+ useViewport.setState({ isAtBottom: true });
179
182
  div.scrollIntoView({ behavior });
180
183
  };
181
184
  useOnResizeContent(divRef, () => {
182
- if (!useThread.getState().isAtBottom)
185
+ if (!useViewport.getState().isAtBottom)
183
186
  return;
184
187
  scrollToBottom();
185
188
  });
@@ -190,173 +193,36 @@ var ThreadViewport = (0, import_react5.forwardRef)(({ autoScroll = true, onScrol
190
193
  const div = divRef.current;
191
194
  if (!div)
192
195
  return;
193
- const isAtBottom = useThread.getState().isAtBottom;
196
+ const isAtBottom = useViewport.getState().isAtBottom;
194
197
  const newIsAtBottom = div.scrollHeight - div.scrollTop <= div.clientHeight;
195
198
  if (!newIsAtBottom && lastScrollTop.current < div.scrollTop) {
196
199
  } else if (newIsAtBottom !== isAtBottom) {
197
- useThread.setState({ isAtBottom: newIsAtBottom });
200
+ useViewport.setState({ isAtBottom: newIsAtBottom });
198
201
  }
199
202
  lastScrollTop.current = div.scrollTop;
200
203
  };
201
- return /* @__PURE__ */ React.createElement(
204
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
202
205
  import_react_primitive2.Primitive.div,
203
206
  {
204
207
  ...rest,
205
208
  onScroll: (0, import_primitive.composeEventHandlers)(onScroll, handleScroll),
206
- ref
207
- },
208
- children,
209
- /* @__PURE__ */ React.createElement("div", { ref: messagesEndRef })
210
- );
211
- });
212
-
213
- // src/adapters/vercel/useVercelAIBranches.tsx
214
- var import_react6 = require("react");
215
-
216
- // src/utils/context/stores/AssistantTypes.ts
217
- var ROOT_PARENT_ID = "__ROOT_ID__";
218
-
219
- // src/adapters/vercel/useVercelAIBranches.tsx
220
- var UPCOMING_MESSAGE_ID = "__UPCOMING_MESSAGE_ID__";
221
- var updateBranchData = (data, messages) => {
222
- for (let i = 0; i < messages.length; i++) {
223
- const child = messages[i];
224
- const childId = child.id;
225
- const parentId = messages[i - 1]?.id ?? ROOT_PARENT_ID;
226
- data.parentMap.set(childId, parentId);
227
- const parentArray = data.branchMap.get(parentId);
228
- if (!parentArray) {
229
- data.branchMap.set(parentId, [childId]);
230
- } else if (!parentArray.includes(childId)) {
231
- parentArray.push(childId);
209
+ ref,
210
+ children: [
211
+ children,
212
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { ref: messagesEndRef })
213
+ ]
232
214
  }
233
- data.snapshots.set(childId, messages);
234
- }
235
- };
236
- var getParentId = (data, messages, messageId) => {
237
- if (messageId === UPCOMING_MESSAGE_ID) {
238
- const parent = messages.at(-1);
239
- if (!parent)
240
- return ROOT_PARENT_ID;
241
- return parent.id;
242
- }
243
- const parentId = data.parentMap.get(messageId);
244
- if (!parentId)
245
- throw new Error("Unexpected: Message parent not found");
246
- return parentId;
247
- };
248
- var getBranchStateImpl = (data, messages, messageId) => {
249
- const parentId = getParentId(data, messages, messageId);
250
- const branches = data.branchMap.get(parentId) ?? [];
251
- const branchId = messageId === UPCOMING_MESSAGE_ID ? branches.length : branches.indexOf(messageId);
252
- if (branchId === -1)
253
- throw new Error("Unexpected: Message not found in parent children");
254
- const upcomingOffset = messageId === UPCOMING_MESSAGE_ID ? 1 : 0;
255
- return {
256
- branchId,
257
- branchCount: branches.length + upcomingOffset
258
- };
259
- };
260
- var switchToBranchImpl = (data, messages, messageId, branchId) => {
261
- const parentId = getParentId(data, messages, messageId);
262
- const branches = data.branchMap.get(parentId);
263
- if (!branches)
264
- throw new Error("Unexpected: Parent children not found");
265
- const newMessageId = branches[branchId];
266
- if (!newMessageId)
267
- throw new Error("Unexpected: Requested branch not found");
268
- if (branchId < 0 || branchId >= branches.length)
269
- throw new Error("Switch to branch called with a branch index out of range");
270
- if (newMessageId === messageId)
271
- return messages;
272
- const snapshot = data.snapshots.get(newMessageId);
273
- if (!snapshot)
274
- throw new Error("Unexpected: Branch snapshot not found");
275
- return snapshot;
276
- };
277
- var sliceMessagesUntil = (messages, messageId) => {
278
- if (messageId === ROOT_PARENT_ID)
279
- return [];
280
- if (messageId === UPCOMING_MESSAGE_ID)
281
- return messages;
282
- const messageIdx = messages.findIndex((m) => m.id === messageId);
283
- if (messageIdx === -1)
284
- throw new Error("Unexpected: Message not found");
285
- return messages.slice(0, messageIdx + 1);
286
- };
287
- var useVercelAIBranches = (chat, context) => {
288
- const data = (0, import_react6.useRef)({
289
- parentMap: /* @__PURE__ */ new Map(),
290
- branchMap: /* @__PURE__ */ new Map(),
291
- snapshots: /* @__PURE__ */ new Map()
292
- }).current;
293
- updateBranchData(data, chat.messages);
294
- const getBranchState = (0, import_react6.useCallback)(
295
- (messageId) => {
296
- return getBranchStateImpl(data, chat.messages, messageId);
297
- },
298
- [data, chat.messages]
299
215
  );
300
- const switchToBranch = (0, import_react6.useCallback)(
301
- (messageId, branchId) => {
302
- const newMessages = switchToBranchImpl(
303
- data,
304
- chat.messages,
305
- messageId,
306
- branchId
307
- );
308
- chat.setMessages(newMessages);
309
- },
310
- [data, chat.messages, chat.setMessages]
311
- );
312
- const reloadMaybe = "reload" in chat ? chat.reload : void 0;
313
- const reload = (0, import_react6.useCallback)(
314
- async (messageId) => {
315
- if (!reloadMaybe)
316
- throw new Error("Reload not supported by Vercel AI SDK's useAssistant");
317
- const newMessages = sliceMessagesUntil(chat.messages, messageId);
318
- chat.setMessages(newMessages);
319
- context.useThread.getState().scrollToBottom();
320
- await reloadMaybe();
321
- },
322
- [context, chat.messages, chat.setMessages, reloadMaybe]
323
- );
324
- const append = (0, import_react6.useCallback)(
325
- async (message) => {
326
- const newMessages = sliceMessagesUntil(chat.messages, message.parentId);
327
- chat.setMessages(newMessages);
328
- if (message.content.length !== 1 || message.content[0]?.type !== "text")
329
- throw new Error("Only text content is currently supported");
330
- context.useThread.getState().scrollToBottom();
331
- await chat.append({
332
- role: "user",
333
- content: message.content[0].text
334
- });
335
- },
336
- [context, chat.messages, chat.setMessages, chat.append]
337
- );
338
- return (0, import_react6.useMemo)(
339
- () => ({
340
- getBranchState,
341
- switchToBranch,
342
- append,
343
- reload
344
- }),
345
- [getBranchState, switchToBranch, append, reload]
346
- );
347
- };
348
- var hasUpcomingMessage = (thread) => {
349
- return thread.isLoading && thread.messages[thread.messages.length - 1]?.role !== "assistant";
350
- };
216
+ });
351
217
 
352
218
  // src/utils/context/useComposerContext.ts
353
- var import_react8 = require("react");
219
+ var import_react7 = require("react");
354
220
 
355
221
  // src/utils/context/useMessageContext.ts
356
- var import_react7 = require("react");
357
- var MessageContext = (0, import_react7.createContext)(null);
222
+ var import_react6 = require("react");
223
+ var MessageContext = (0, import_react6.createContext)(null);
358
224
  var useMessageContext = () => {
359
- const context = (0, import_react7.useContext)(MessageContext);
225
+ const context = (0, import_react6.useContext)(MessageContext);
360
226
  if (!context)
361
227
  throw new Error("useMessageContext must be used within a MessageProvider");
362
228
  return context;
@@ -365,7 +231,7 @@ var useMessageContext = () => {
365
231
  // src/utils/context/useComposerContext.ts
366
232
  var useComposerContext = () => {
367
233
  const { useComposer: useAssisstantComposer } = useAssistantContext();
368
- const { useComposer: useMessageComposer } = (0, import_react8.useContext)(MessageContext) ?? {};
234
+ const { useComposer: useMessageComposer } = (0, import_react7.useContext)(MessageContext) ?? {};
369
235
  return {
370
236
  useComposer: useMessageComposer ?? useAssisstantComposer,
371
237
  type: useMessageComposer ? "message" : "assistant"
@@ -398,17 +264,72 @@ __export(message_exports, {
398
264
  });
399
265
 
400
266
  // src/primitives/message/MessageProvider.tsx
401
- var import_react9 = require("react");
267
+ var import_react8 = require("react");
268
+ var import_zustand2 = require("zustand");
269
+
270
+ // src/utils/context/stores/ComposerStore.ts
402
271
  var import_zustand = require("zustand");
272
+ var makeBaseComposer = (set) => ({
273
+ value: "",
274
+ setValue: (value) => {
275
+ set({ value });
276
+ }
277
+ });
278
+ var makeMessageComposerStore = ({
279
+ onEdit,
280
+ onSend
281
+ }) => (0, import_zustand.create)()((set, get, store) => ({
282
+ ...makeBaseComposer(set, get, store),
283
+ isEditing: false,
284
+ edit: () => {
285
+ const value = onEdit();
286
+ set({ isEditing: true, value });
287
+ },
288
+ send: () => {
289
+ const value = get().value;
290
+ set({ isEditing: false });
291
+ onSend(value);
292
+ },
293
+ cancel: () => {
294
+ if (!get().isEditing)
295
+ return false;
296
+ set({ isEditing: false });
297
+ return true;
298
+ }
299
+ }));
300
+ var makeThreadComposerStore = (useThread) => (0, import_zustand.create)()((set, get, store) => {
301
+ return {
302
+ ...makeBaseComposer(set, get, store),
303
+ isEditing: true,
304
+ send: () => {
305
+ const { value } = get();
306
+ set({ value: "" });
307
+ useThread.getState().append({
308
+ parentId: useThread.getState().messages.at(-1)?.id ?? null,
309
+ content: [{ type: "text", text: value }]
310
+ });
311
+ },
312
+ cancel: () => {
313
+ const thread = useThread.getState();
314
+ if (!thread.isRunning)
315
+ return false;
316
+ useThread.getState().cancelRun();
317
+ return true;
318
+ }
319
+ };
320
+ });
321
+
322
+ // src/primitives/message/MessageProvider.tsx
323
+ var import_jsx_runtime4 = require("react/jsx-runtime");
403
324
  var getIsLast = (thread, message) => {
404
- const hasUpcoming = hasUpcomingMessage(thread);
405
- return hasUpcoming ? message.id === UPCOMING_MESSAGE_ID : thread.messages[thread.messages.length - 1]?.id === message.id;
325
+ return thread.messages[thread.messages.length - 1]?.id === message.id;
406
326
  };
407
327
  var useMessageContext2 = () => {
408
- const [context] = (0, import_react9.useState)(() => {
409
- const { useThread } = useAssistantContext();
410
- const useMessage = (0, import_zustand.create)(() => ({
328
+ const { useThread } = useAssistantContext();
329
+ const [context] = (0, import_react8.useState)(() => {
330
+ const useMessage = (0, import_zustand2.create)(() => ({
411
331
  message: null,
332
+ branches: [],
412
333
  isLast: false,
413
334
  isCopied: false,
414
335
  isHovering: false,
@@ -417,34 +338,23 @@ var useMessageContext2 = () => {
417
338
  setIsHovering: () => {
418
339
  }
419
340
  }));
420
- const useComposer = (0, import_zustand.create)((set, get) => ({
421
- isEditing: false,
422
- canCancel: true,
423
- edit: () => {
341
+ const useComposer = makeMessageComposerStore({
342
+ onEdit: () => {
424
343
  const message = useMessage.getState().message;
425
344
  if (message.role !== "user")
426
345
  throw new Error("Editing is only supported for user messages");
427
346
  if (message.content[0]?.type !== "text")
428
347
  throw new Error("Editing is only supported for text-only messages");
429
- return set({
430
- isEditing: true,
431
- value: message.content[0].text
432
- });
348
+ return message.content[0].text;
433
349
  },
434
- cancel: () => set({ isEditing: false }),
435
- send: () => {
350
+ onSend: (text) => {
436
351
  const message = useMessage.getState().message;
437
- if (message.role !== "user")
438
- throw new Error("Editing is only supported for user messages");
439
352
  useThread.getState().append({
440
353
  parentId: message.parentId,
441
- content: [{ type: "text", text: get().value }]
354
+ content: [{ type: "text", text }]
442
355
  });
443
- set({ isEditing: false });
444
- },
445
- value: "",
446
- setValue: (value) => set({ value })
447
- }));
356
+ }
357
+ });
448
358
  return { useMessage, useComposer };
449
359
  });
450
360
  return context;
@@ -456,12 +366,14 @@ var MessageProvider = ({
456
366
  const { useThread } = useAssistantContext();
457
367
  const context = useMessageContext2();
458
368
  const isLast = useThread((thread) => getIsLast(thread, message));
459
- const [isCopied, setIsCopied] = (0, import_react9.useState)(false);
460
- const [isHovering, setIsHovering] = (0, import_react9.useState)(false);
461
- (0, import_react9.useMemo)(() => {
369
+ const branches = useThread((thread) => thread.getBranches(message.id));
370
+ const [isCopied, setIsCopied] = (0, import_react8.useState)(false);
371
+ const [isHovering, setIsHovering] = (0, import_react8.useState)(false);
372
+ (0, import_react8.useMemo)(() => {
462
373
  context.useMessage.setState(
463
374
  {
464
375
  message,
376
+ branches,
465
377
  isLast,
466
378
  isCopied,
467
379
  isHovering,
@@ -470,15 +382,16 @@ var MessageProvider = ({
470
382
  },
471
383
  true
472
384
  );
473
- }, [context, message, isLast, isCopied, isHovering]);
474
- return /* @__PURE__ */ React.createElement(MessageContext.Provider, { value: context }, children);
385
+ }, [context, message, branches, isLast, isCopied, isHovering]);
386
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MessageContext.Provider, { value: context, children });
475
387
  };
476
388
 
477
389
  // src/primitives/message/MessageRoot.tsx
478
390
  var import_primitive2 = require("@radix-ui/primitive");
479
391
  var import_react_primitive3 = require("@radix-ui/react-primitive");
480
- var import_react10 = require("react");
481
- var MessageRoot = (0, import_react10.forwardRef)(
392
+ var import_react9 = require("react");
393
+ var import_jsx_runtime5 = require("react/jsx-runtime");
394
+ var MessageRoot = (0, import_react9.forwardRef)(
482
395
  ({ onMouseEnter, onMouseLeave, ...rest }, ref) => {
483
396
  const { useMessage } = useMessageContext();
484
397
  const setIsHovering = useMessage((s) => s.setIsHovering);
@@ -488,7 +401,7 @@ var MessageRoot = (0, import_react10.forwardRef)(
488
401
  const handleMouseLeave = () => {
489
402
  setIsHovering(false);
490
403
  };
491
- return /* @__PURE__ */ React.createElement(
404
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
492
405
  import_react_primitive3.Primitive.div,
493
406
  {
494
407
  ...rest,
@@ -503,8 +416,8 @@ var MessageRoot = (0, import_react10.forwardRef)(
503
416
  // src/primitives/message/MessageIf.tsx
504
417
  var useMessageIf = (props) => {
505
418
  const { useMessage } = useMessageContext();
506
- return useMessage(({ message, isLast, isCopied, isHovering }) => {
507
- if (props.hasBranches === true && message.branchCount < 2)
419
+ return useMessage(({ message, branches, isLast, isCopied, isHovering }) => {
420
+ if (props.hasBranches === true && branches.length < 2)
508
421
  return false;
509
422
  if (props.user && message.role !== "user")
510
423
  return false;
@@ -525,8 +438,9 @@ var MessageIf = ({ children, ...query }) => {
525
438
  };
526
439
 
527
440
  // src/primitives/message/MessageContent.tsx
441
+ var import_jsx_runtime6 = require("react/jsx-runtime");
528
442
  var defaultComponents = {
529
- Text: ({ part }) => /* @__PURE__ */ React.createElement(React.Fragment, null, part.text),
443
+ Text: ({ part }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: part.text }),
530
444
  Image: () => null,
531
445
  UI: ({ part }) => part.display,
532
446
  tools: {
@@ -543,26 +457,27 @@ var MessageContent = ({
543
457
  }) => {
544
458
  const { useMessage } = useMessageContext();
545
459
  const content = useMessage((s) => s.message.content);
546
- return /* @__PURE__ */ React.createElement(React.Fragment, null, content.map((part, i) => {
460
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: content.map((part, i) => {
547
461
  const key = i;
548
462
  switch (part.type) {
549
463
  case "text":
550
- return /* @__PURE__ */ React.createElement(Text, { key, part });
464
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { part }, key);
551
465
  case "image":
552
- return /* @__PURE__ */ React.createElement(Image, { key, part });
466
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Image, { part }, key);
553
467
  case "ui":
554
- return /* @__PURE__ */ React.createElement(UI, { key, part });
468
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(UI, { part }, key);
555
469
  case "tool-call": {
556
470
  const Tool = by_name[part.name] || Fallback;
557
- return /* @__PURE__ */ React.createElement(Tool, { key, part });
471
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Tool, { part }, key);
558
472
  }
559
473
  default:
560
474
  return null;
561
475
  }
562
- }));
476
+ }) });
563
477
  };
564
478
 
565
479
  // src/primitives/thread/ThreadMessages.tsx
480
+ var import_jsx_runtime7 = require("react/jsx-runtime");
566
481
  var getComponents = (components) => {
567
482
  return {
568
483
  EditComposer: components.EditComposer ?? components.UserMessage ?? components.Message,
@@ -577,41 +492,32 @@ var ThreadMessages = ({ components }) => {
577
492
  const { UserMessage, EditComposer, AssistantMessage } = getComponents(components);
578
493
  if (messages.length === 0)
579
494
  return null;
580
- return /* @__PURE__ */ React.createElement(React.Fragment, null, messages.map((message, idx) => {
495
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: messages.map((message, idx) => {
581
496
  return (
582
497
  // biome-ignore lint/suspicious/noArrayIndexKey: fixes a11y issues with branch navigation
583
- /* @__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)))
498
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(MessageProvider, { message, children: [
499
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(MessageIf, { user: true, children: [
500
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ComposerIf, { editing: false, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(UserMessage, {}) }),
501
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ComposerIf, { editing: true, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(EditComposer, {}) })
502
+ ] }),
503
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(MessageIf, { assistant: true, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AssistantMessage, {}) })
504
+ ] }, idx)
584
505
  );
585
- }), hasUpcomingMessage(thread) && /* @__PURE__ */ React.createElement(
586
- MessageProvider,
587
- {
588
- message: {
589
- id: UPCOMING_MESSAGE_ID,
590
- role: "assistant",
591
- content: [{ type: "text", text: "..." }],
592
- parentId: messages.at(-1)?.id ?? ROOT_PARENT_ID,
593
- // TODO fix these (move upcoming message to AssistantContext)
594
- branchId: 0,
595
- branchCount: 1,
596
- createdAt: /* @__PURE__ */ new Date()
597
- }
598
- },
599
- /* @__PURE__ */ React.createElement(AssistantMessage, null)
600
- ));
506
+ }) });
601
507
  };
602
508
 
603
509
  // src/primitives/thread/ThreadScrollToBottom.tsx
604
510
  var import_primitive3 = require("@radix-ui/primitive");
605
511
  var import_react_primitive4 = require("@radix-ui/react-primitive");
606
- var import_react11 = require("react");
607
- var ThreadScrollToBottom = (0, import_react11.forwardRef)(({ onClick, ...rest }, ref) => {
608
- const { useThread } = useAssistantContext();
609
- const isAtBottom = useThread((s) => s.isAtBottom);
512
+ var import_react10 = require("react");
513
+ var import_jsx_runtime8 = require("react/jsx-runtime");
514
+ var ThreadScrollToBottom = (0, import_react10.forwardRef)(({ onClick, ...rest }, ref) => {
515
+ const { useViewport } = useAssistantContext();
516
+ const isAtBottom = useViewport((s) => s.isAtBottom);
610
517
  const handleScrollToBottom = () => {
611
- const thread = useThread.getState();
612
- thread.scrollToBottom();
518
+ useViewport.getState().scrollToBottom();
613
519
  };
614
- return /* @__PURE__ */ React.createElement(
520
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
615
521
  import_react_primitive4.Primitive.button,
616
522
  {
617
523
  ...rest,
@@ -636,11 +542,13 @@ __export(composer_exports, {
636
542
  var import_primitive4 = require("@radix-ui/primitive");
637
543
  var import_react_compose_refs2 = require("@radix-ui/react-compose-refs");
638
544
  var import_react_primitive5 = require("@radix-ui/react-primitive");
639
- var import_react12 = require("react");
640
- var ComposerRoot = (0, import_react12.forwardRef)(
545
+ var import_react11 = require("react");
546
+ var import_jsx_runtime9 = require("react/jsx-runtime");
547
+ var ComposerRoot = (0, import_react11.forwardRef)(
641
548
  ({ onSubmit, ...rest }, forwardedRef) => {
549
+ const { useViewport } = useAssistantContext();
642
550
  const { useComposer } = useComposerContext();
643
- const formRef = (0, import_react12.useRef)(null);
551
+ const formRef = (0, import_react11.useRef)(null);
644
552
  const ref = (0, import_react_compose_refs2.useComposedRefs)(forwardedRef, formRef);
645
553
  const handleSubmit = (e) => {
646
554
  const composerState = useComposer.getState();
@@ -648,8 +556,9 @@ var ComposerRoot = (0, import_react12.forwardRef)(
648
556
  return;
649
557
  e.preventDefault();
650
558
  composerState.send();
559
+ useViewport.getState().scrollToBottom();
651
560
  };
652
- return /* @__PURE__ */ React.createElement(
561
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
653
562
  import_react_primitive5.Primitive.form,
654
563
  {
655
564
  ...rest,
@@ -664,11 +573,12 @@ var ComposerRoot = (0, import_react12.forwardRef)(
664
573
  var import_primitive5 = require("@radix-ui/primitive");
665
574
  var import_react_compose_refs3 = require("@radix-ui/react-compose-refs");
666
575
  var import_react_slot = require("@radix-ui/react-slot");
667
- var import_react13 = require("react");
576
+ var import_react12 = require("react");
668
577
  var import_react_textarea_autosize = __toESM(require("react-textarea-autosize"));
669
- var ComposerInput = (0, import_react13.forwardRef)(
578
+ var import_jsx_runtime10 = require("react/jsx-runtime");
579
+ var ComposerInput = (0, import_react12.forwardRef)(
670
580
  ({ autoFocus, asChild, disabled, onChange, onKeyDown, ...rest }, forwardedRef) => {
671
- const { useThread } = useAssistantContext();
581
+ const { useThread, useViewport } = useAssistantContext();
672
582
  const { useComposer, type } = useComposerContext();
673
583
  const value = useComposer((c) => {
674
584
  if (!c.isEditing)
@@ -680,22 +590,23 @@ var ComposerInput = (0, import_react13.forwardRef)(
680
590
  if (disabled)
681
591
  return;
682
592
  const composer = useComposer.getState();
683
- if (e.key === "Escape" && composer.canCancel) {
684
- e.preventDefault();
685
- useComposer.getState().cancel();
686
- }
687
- if (e.key === "Enter" && e.shiftKey === false) {
688
- const isLoading = useThread.getState().isLoading;
689
- if (!isLoading) {
593
+ if (e.key === "Escape") {
594
+ if (useComposer.getState().cancel()) {
595
+ e.preventDefault();
596
+ }
597
+ } else if (e.key === "Enter" && e.shiftKey === false) {
598
+ const isRunning = useThread.getState().isRunning;
599
+ if (!isRunning) {
690
600
  e.preventDefault();
691
601
  composer.send();
602
+ useViewport.getState().scrollToBottom();
692
603
  }
693
604
  }
694
605
  };
695
- const textareaRef = (0, import_react13.useRef)(null);
606
+ const textareaRef = (0, import_react12.useRef)(null);
696
607
  const ref = (0, import_react_compose_refs3.useComposedRefs)(forwardedRef, textareaRef);
697
608
  const autoFocusEnabled = autoFocus !== false && !disabled;
698
- const focus = (0, import_react13.useCallback)(() => {
609
+ const focus = (0, import_react12.useCallback)(() => {
699
610
  const textarea = textareaRef.current;
700
611
  if (!textarea || !autoFocusEnabled)
701
612
  return;
@@ -705,13 +616,13 @@ var ComposerInput = (0, import_react13.forwardRef)(
705
616
  textareaRef.current.value.length
706
617
  );
707
618
  }, [autoFocusEnabled]);
708
- (0, import_react13.useEffect)(() => focus(), [focus]);
619
+ (0, import_react12.useEffect)(() => focus(), [focus]);
709
620
  useOnScrollToBottom(() => {
710
621
  if (type === "assistant") {
711
622
  focus();
712
623
  }
713
624
  });
714
- return /* @__PURE__ */ React.createElement(
625
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
715
626
  Component,
716
627
  {
717
628
  value,
@@ -732,12 +643,13 @@ var ComposerInput = (0, import_react13.forwardRef)(
732
643
 
733
644
  // src/primitives/composer/ComposerSend.tsx
734
645
  var import_react_primitive6 = require("@radix-ui/react-primitive");
735
- var import_react14 = require("react");
736
- var ComposerSend = (0, import_react14.forwardRef)(
646
+ var import_react13 = require("react");
647
+ var import_jsx_runtime11 = require("react/jsx-runtime");
648
+ var ComposerSend = (0, import_react13.forwardRef)(
737
649
  ({ disabled, ...rest }, ref) => {
738
650
  const { useComposer } = useComposerContext();
739
651
  const hasValue = useComposer((c) => c.isEditing && c.value.length > 0);
740
- return /* @__PURE__ */ React.createElement(
652
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
741
653
  import_react_primitive6.Primitive.button,
742
654
  {
743
655
  type: "submit",
@@ -752,21 +664,20 @@ var ComposerSend = (0, import_react14.forwardRef)(
752
664
  // src/primitives/composer/ComposerCancel.tsx
753
665
  var import_primitive6 = require("@radix-ui/primitive");
754
666
  var import_react_primitive7 = require("@radix-ui/react-primitive");
755
- var import_react15 = require("react");
756
- var ComposerCancel = (0, import_react15.forwardRef)(({ disabled, onClick, ...rest }, ref) => {
667
+ var import_react14 = require("react");
668
+ var import_jsx_runtime12 = require("react/jsx-runtime");
669
+ var ComposerCancel = (0, import_react14.forwardRef)(({ onClick, ...rest }, ref) => {
757
670
  const { useComposer } = useComposerContext();
758
- const hasValue = useComposer((c) => c.canCancel);
759
- const handleClose = () => {
671
+ const handleCancel = () => {
760
672
  useComposer.getState().cancel();
761
673
  };
762
- return /* @__PURE__ */ React.createElement(
674
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
763
675
  import_react_primitive7.Primitive.button,
764
676
  {
765
677
  type: "button",
766
678
  ...rest,
767
679
  ref,
768
- onClick: (0, import_primitive6.composeEventHandlers)(onClick, handleClose),
769
- disabled: disabled || !hasValue
680
+ onClick: (0, import_primitive6.composeEventHandlers)(onClick, handleCancel)
770
681
  }
771
682
  );
772
683
  });
@@ -782,10 +693,10 @@ __export(branchPicker_exports, {
782
693
  });
783
694
 
784
695
  // src/utils/context/combined/useCombinedStore.ts
785
- var import_react17 = require("react");
696
+ var import_react16 = require("react");
786
697
 
787
698
  // src/utils/context/combined/createCombinedStore.ts
788
- var import_react16 = require("react");
699
+ var import_react15 = require("react");
789
700
  var createCombinedStore = (stores) => {
790
701
  const subscribe = (callback) => {
791
702
  const unsubscribes = stores.map((store) => store.subscribe(callback));
@@ -797,41 +708,42 @@ var createCombinedStore = (stores) => {
797
708
  };
798
709
  return (selector) => {
799
710
  const getSnapshot = () => selector(...stores.map((store) => store.getState()));
800
- return (0, import_react16.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
711
+ return (0, import_react15.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
801
712
  };
802
713
  };
803
714
 
804
715
  // src/utils/context/combined/useCombinedStore.ts
805
716
  var useCombinedStore = (stores, selector) => {
806
- const useCombined = (0, import_react17.useMemo)(() => createCombinedStore(stores), stores);
717
+ const useCombined = (0, import_react16.useMemo)(() => createCombinedStore(stores), stores);
807
718
  return useCombined(selector);
808
719
  };
809
720
 
810
721
  // src/actions/useGoToNextBranch.tsx
811
722
  var useGoToNextBranch = () => {
812
723
  const { useThread } = useAssistantContext();
813
- const { useComposer, useMessage } = useMessageContext();
724
+ const { useMessage, useComposer } = useMessageContext();
814
725
  const disabled = useCombinedStore(
815
- [useThread, useComposer, useMessage],
816
- (t, c, m) => t.isLoading || c.isEditing || m.message.branchId + 1 >= m.message.branchCount
726
+ [useMessage, useComposer],
727
+ (m, c) => c.isEditing || m.branches.indexOf(m.message.id) + 1 >= m.branches.length
817
728
  );
818
729
  if (disabled)
819
730
  return null;
820
731
  return () => {
821
- const { message } = useMessage.getState();
822
- useThread.getState().switchToBranch(message.id, message.branchId + 1);
732
+ const { message, branches } = useMessage.getState();
733
+ useThread.getState().switchToBranch(branches[branches.indexOf(message.id) + 1]);
823
734
  };
824
735
  };
825
736
 
826
737
  // src/utils/createActionButton.tsx
827
- var import_react18 = require("react");
828
- var import_react_primitive8 = require("@radix-ui/react-primitive");
829
738
  var import_primitive7 = require("@radix-ui/primitive");
739
+ var import_react_primitive8 = require("@radix-ui/react-primitive");
740
+ var import_react17 = require("react");
741
+ var import_jsx_runtime13 = require("react/jsx-runtime");
830
742
  var createActionButton = (useActionButton) => {
831
- return (0, import_react18.forwardRef)(
743
+ return (0, import_react17.forwardRef)(
832
744
  (props, forwardedRef) => {
833
745
  const onClick = useActionButton(props);
834
- return /* @__PURE__ */ React.createElement(
746
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
835
747
  import_react_primitive8.Primitive.button,
836
748
  {
837
749
  type: "button",
@@ -851,16 +763,19 @@ var BranchPickerNext = createActionButton(useGoToNextBranch);
851
763
  // src/actions/useGoToPreviousBranch.tsx
852
764
  var useGoToPreviousBranch = () => {
853
765
  const { useThread } = useAssistantContext();
854
- const { useComposer, useMessage } = useMessageContext();
766
+ const { useMessage, useComposer } = useMessageContext();
855
767
  const disabled = useCombinedStore(
856
- [useThread, useComposer, useMessage],
857
- (t, c, m) => t.isLoading || c.isEditing || m.message.branchId <= 0
768
+ [useMessage, useComposer],
769
+ (m, c) => c.isEditing || m.branches.indexOf(m.message.id) <= 0
858
770
  );
859
771
  if (disabled)
860
772
  return null;
861
773
  return () => {
862
- const { message } = useMessage.getState();
863
- useThread.getState().switchToBranch(message.id, message.branchId - 1);
774
+ const { message, branches } = useMessage.getState();
775
+ useThread.getState().switchToBranch(
776
+ branches[branches.indexOf(message.id) - 1]
777
+ // TODO probably there's a more elegant way to do this
778
+ );
864
779
  };
865
780
  };
866
781
 
@@ -868,24 +783,27 @@ var useGoToPreviousBranch = () => {
868
783
  var BranchPickerPrevious = createActionButton(useGoToPreviousBranch);
869
784
 
870
785
  // src/primitives/branchPicker/BranchPickerCount.tsx
786
+ var import_jsx_runtime14 = require("react/jsx-runtime");
871
787
  var BranchPickerCount = () => {
872
788
  const { useMessage } = useMessageContext();
873
- const branchCount = useMessage((s) => s.message.branchCount);
874
- return /* @__PURE__ */ React.createElement(React.Fragment, null, branchCount);
789
+ const branchCount = useMessage((s) => s.branches.length);
790
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_jsx_runtime14.Fragment, { children: branchCount });
875
791
  };
876
792
 
877
793
  // src/primitives/branchPicker/BranchPickerNumber.tsx
794
+ var import_jsx_runtime15 = require("react/jsx-runtime");
878
795
  var BranchPickerNumber = () => {
879
796
  const { useMessage } = useMessageContext();
880
- const branchId = useMessage((s) => s.message.branchId);
881
- return /* @__PURE__ */ React.createElement(React.Fragment, null, branchId + 1);
797
+ const branchIdx = useMessage((s) => s.branches.indexOf(s.message.id));
798
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_jsx_runtime15.Fragment, { children: branchIdx + 1 });
882
799
  };
883
800
 
884
801
  // src/primitives/branchPicker/BranchPickerRoot.tsx
885
802
  var import_react_primitive9 = require("@radix-ui/react-primitive");
886
- var import_react19 = require("react");
887
- var BranchPickerRoot = (0, import_react19.forwardRef)(({ hideWhenSingleBranch, ...rest }, ref) => {
888
- return /* @__PURE__ */ React.createElement(MessageIf, { hasBranches: hideWhenSingleBranch ? true : void 0 }, /* @__PURE__ */ React.createElement(import_react_primitive9.Primitive.div, { ...rest, ref }));
803
+ var import_react18 = require("react");
804
+ var import_jsx_runtime16 = require("react/jsx-runtime");
805
+ var BranchPickerRoot = (0, import_react18.forwardRef)(({ hideWhenSingleBranch, ...rest }, ref) => {
806
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(MessageIf, { hasBranches: hideWhenSingleBranch ? true : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_react_primitive9.Primitive.div, { ...rest, ref }) });
889
807
  });
890
808
 
891
809
  // src/primitives/actionBar/index.ts
@@ -899,28 +817,29 @@ __export(actionBar_exports, {
899
817
 
900
818
  // src/primitives/actionBar/ActionBarRoot.tsx
901
819
  var import_react_primitive10 = require("@radix-ui/react-primitive");
902
- var import_react20 = require("react");
903
- var ActionBarRoot = (0, import_react20.forwardRef)(({ hideWhenBusy, autohide, autohideFloat, ...rest }, ref) => {
820
+ var import_react19 = require("react");
821
+ var import_jsx_runtime17 = require("react/jsx-runtime");
822
+ var ActionBarRoot = (0, import_react19.forwardRef)(({ hideWhenRunning, autohide, autohideFloat, ...rest }, ref) => {
904
823
  const { useThread } = useAssistantContext();
905
824
  const { useMessage } = useMessageContext();
906
825
  const hideAndfloatStatus = useCombinedStore(
907
826
  [useThread, useMessage],
908
827
  (t, m) => {
909
- if (hideWhenBusy && t.isLoading)
828
+ if (hideWhenRunning && t.isRunning)
910
829
  return "hidden" /* Hidden */;
911
830
  const autohideEnabled = autohide === "always" || autohide === "not-last" && !m.isLast;
912
831
  if (!autohideEnabled)
913
832
  return "normal" /* Normal */;
914
833
  if (!m.isHovering)
915
834
  return "hidden" /* Hidden */;
916
- if (autohideFloat === "always" || autohideFloat === "single-branch" && m.message.branchCount <= 1)
835
+ if (autohideFloat === "always" || autohideFloat === "single-branch" && m.branches.length <= 1)
917
836
  return "floating" /* Floating */;
918
837
  return "normal" /* Normal */;
919
838
  }
920
839
  );
921
840
  if (hideAndfloatStatus === "hidden" /* Hidden */)
922
841
  return null;
923
- return /* @__PURE__ */ React.createElement(
842
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
924
843
  import_react_primitive10.Primitive.div,
925
844
  {
926
845
  "data-floating": hideAndfloatStatus === "floating" /* Floating */,
@@ -951,11 +870,11 @@ var ActionBarCopy = createActionButton(useCopyMessage);
951
870
 
952
871
  // src/actions/useReloadMessage.tsx
953
872
  var useReloadMessage = () => {
954
- const { useThread } = useAssistantContext();
873
+ const { useThread, useViewport } = useAssistantContext();
955
874
  const { useMessage } = useMessageContext();
956
875
  const disabled = useCombinedStore(
957
876
  [useThread, useMessage],
958
- (t, m) => t.isLoading || m.message.role !== "assistant"
877
+ (t, m) => t.isRunning || m.message.role !== "assistant"
959
878
  );
960
879
  if (disabled)
961
880
  return null;
@@ -963,7 +882,8 @@ var useReloadMessage = () => {
963
882
  const message = useMessage.getState().message;
964
883
  if (message.role !== "assistant")
965
884
  throw new Error("Reloading is only supported on assistant messages");
966
- useThread.getState().reload(message.id);
885
+ useThread.getState().startRun(message.parentId);
886
+ useViewport.getState().scrollToBottom();
967
887
  };
968
888
  };
969
889
 
@@ -992,68 +912,309 @@ var ActionBarEdit = createActionButton(useBeginMessageEdit);
992
912
  var import_react22 = require("react");
993
913
 
994
914
  // src/adapters/vercel/useDummyAIAssistantContext.tsx
995
- var import_react21 = require("react");
996
- var import_zustand2 = require("zustand");
997
- var useDummyAIAssistantContext = () => {
998
- const [context] = (0, import_react21.useState)(() => {
999
- const scrollToBottomListeners = /* @__PURE__ */ new Set();
1000
- const useThread = (0, import_zustand2.create)()(() => ({
1001
- messages: [],
1002
- isLoading: false,
1003
- append: async () => {
1004
- throw new Error("Not implemented");
1005
- },
1006
- stop: () => {
1007
- throw new Error("Not implemented");
1008
- },
1009
- switchToBranch: () => {
1010
- throw new Error("Not implemented");
1011
- },
1012
- reload: async () => {
1013
- throw new Error("Not implemented");
1014
- },
1015
- isAtBottom: true,
1016
- scrollToBottom: () => {
1017
- for (const listener of scrollToBottomListeners) {
1018
- listener();
1019
- }
1020
- },
1021
- onScrollToBottom: (callback) => {
1022
- scrollToBottomListeners.add(callback);
1023
- return () => {
1024
- scrollToBottomListeners.delete(callback);
1025
- };
1026
- }
1027
- }));
1028
- const useComposer = (0, import_zustand2.create)()(() => ({
1029
- isEditing: true,
1030
- canCancel: false,
1031
- value: "",
1032
- setValue: (value) => {
1033
- useComposer.setState({ value });
1034
- },
1035
- edit: () => {
1036
- throw new Error("Not implemented");
1037
- },
1038
- send: () => {
1039
- useThread.getState().append({
1040
- parentId: useThread.getState().messages.at(-1)?.id ?? ROOT_PARENT_ID,
1041
- content: [{ type: "text", text: useComposer.getState().value }]
1042
- });
1043
- useComposer.getState().setValue("");
1044
- },
1045
- cancel: () => {
1046
- useThread.getState().stop();
915
+ var import_react20 = require("react");
916
+ var import_zustand4 = require("zustand");
917
+
918
+ // src/utils/context/stores/ViewportStore.tsx
919
+ var import_zustand3 = require("zustand");
920
+ var makeViewportStore = () => {
921
+ const scrollToBottomListeners = /* @__PURE__ */ new Set();
922
+ return (0, import_zustand3.create)(() => ({
923
+ isAtBottom: true,
924
+ scrollToBottom: () => {
925
+ for (const listener of scrollToBottomListeners) {
926
+ listener();
1047
927
  }
1048
- }));
1049
- return { useThread, useComposer };
928
+ },
929
+ onScrollToBottom: (callback) => {
930
+ scrollToBottomListeners.add(callback);
931
+ return () => {
932
+ scrollToBottomListeners.delete(callback);
933
+ };
934
+ }
935
+ }));
936
+ };
937
+
938
+ // src/adapters/vercel/useDummyAIAssistantContext.tsx
939
+ var makeDummyThreadStore = () => {
940
+ return (0, import_zustand4.create)(() => ({
941
+ messages: [],
942
+ isRunning: false,
943
+ getBranches: () => {
944
+ return [];
945
+ },
946
+ switchToBranch: () => {
947
+ throw new Error("Not implemented");
948
+ },
949
+ append: async () => {
950
+ throw new Error("Not implemented");
951
+ },
952
+ cancelRun: () => {
953
+ throw new Error("Not implemented");
954
+ },
955
+ startRun: async () => {
956
+ throw new Error("Not implemented");
957
+ }
958
+ }));
959
+ };
960
+ var useDummyAIAssistantContext = () => {
961
+ const [context] = (0, import_react20.useState)(() => {
962
+ const useThread = makeDummyThreadStore();
963
+ const useViewport = makeViewportStore();
964
+ const useComposer = makeThreadComposerStore(useThread);
965
+ return { useThread, useViewport, useComposer };
1050
966
  });
1051
967
  return context;
1052
968
  };
1053
969
 
970
+ // src/adapters/vercel/useVercelAIBranches.tsx
971
+ var import_react21 = require("react");
972
+
973
+ // ../../node_modules/nanoid/non-secure/index.js
974
+ var customAlphabet = (alphabet, defaultSize = 21) => {
975
+ return (size = defaultSize) => {
976
+ let id = "";
977
+ let i = size;
978
+ while (i--) {
979
+ id += alphabet[Math.random() * alphabet.length | 0];
980
+ }
981
+ return id;
982
+ };
983
+ };
984
+
985
+ // src/adapters/MessageRepository.tsx
986
+ var generateId = customAlphabet(
987
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
988
+ 7
989
+ );
990
+ var optimisticPrefix = "__optimistic__";
991
+ var generateOptimisticId = () => `${optimisticPrefix}${generateId()}`;
992
+ var isOptimisticId = (id) => id.startsWith(optimisticPrefix);
993
+ var findHead = (message) => {
994
+ if (message.next)
995
+ return findHead(message.next);
996
+ return message;
997
+ };
998
+ var MessageRepository = class {
999
+ messages = /* @__PURE__ */ new Map();
1000
+ // message_id -> item
1001
+ head = null;
1002
+ rootChildren = [];
1003
+ getMessages() {
1004
+ const messages = new Array(this.head?.level ?? 0);
1005
+ for (let current = this.head; current; current = current.prev) {
1006
+ messages[current.level] = current.current;
1007
+ }
1008
+ return messages;
1009
+ }
1010
+ addOrUpdateMessage(message) {
1011
+ const item = this.messages.get(message.id);
1012
+ if (item) {
1013
+ if (item.current.parentId !== message.parentId) {
1014
+ this.deleteMessage(message.id);
1015
+ } else {
1016
+ item.current = message;
1017
+ return;
1018
+ }
1019
+ }
1020
+ const prev = message.parentId ? this.messages.get(message.parentId) : null;
1021
+ if (prev === void 0)
1022
+ throw new Error("Unexpected: Parent message not found");
1023
+ const newItem = {
1024
+ prev,
1025
+ current: message,
1026
+ next: null,
1027
+ children: [],
1028
+ level: prev ? prev.level + 1 : 0
1029
+ };
1030
+ this.messages.set(message.id, newItem);
1031
+ if (prev) {
1032
+ prev.children = [...prev.children, message.id];
1033
+ prev.next = newItem;
1034
+ } else {
1035
+ this.rootChildren = [...this.rootChildren, message.id];
1036
+ }
1037
+ if (this.head === prev) {
1038
+ this.head = newItem;
1039
+ }
1040
+ }
1041
+ deleteMessage(messageId) {
1042
+ const message = this.messages.get(messageId);
1043
+ if (!message)
1044
+ throw new Error("Unexpected: Message not found");
1045
+ if (message.children.length > 0) {
1046
+ for (const child of message.children) {
1047
+ this.deleteMessage(child);
1048
+ }
1049
+ }
1050
+ this.messages.delete(messageId);
1051
+ if (message.prev) {
1052
+ message.prev.children = message.prev.children.filter(
1053
+ (m) => m !== messageId
1054
+ );
1055
+ if (message.prev.next === message) {
1056
+ const childId = message.prev.children.at(-1);
1057
+ const child = childId ? this.messages.get(childId) : null;
1058
+ if (child === void 0)
1059
+ throw new Error("Unexpected: Child message not found");
1060
+ message.prev.next = child;
1061
+ }
1062
+ } else {
1063
+ this.rootChildren = this.rootChildren.filter((m) => m !== messageId);
1064
+ }
1065
+ if (this.head === message) {
1066
+ this.head = message.prev ? findHead(message.prev) : null;
1067
+ }
1068
+ }
1069
+ getOptimisticId = () => {
1070
+ let optimisticId;
1071
+ do {
1072
+ optimisticId = generateOptimisticId();
1073
+ } while (this.messages.has(optimisticId));
1074
+ return optimisticId;
1075
+ };
1076
+ commitOptimisticRun(parentId) {
1077
+ const optimisticId = this.getOptimisticId();
1078
+ this.addOrUpdateMessage({
1079
+ id: optimisticId,
1080
+ role: "assistant",
1081
+ content: [
1082
+ {
1083
+ type: "text",
1084
+ text: ""
1085
+ }
1086
+ ],
1087
+ parentId,
1088
+ createdAt: /* @__PURE__ */ new Date()
1089
+ });
1090
+ return optimisticId;
1091
+ }
1092
+ getBranches(messageId) {
1093
+ const message = this.messages.get(messageId);
1094
+ if (!message)
1095
+ throw new Error("Unexpected: Message not found");
1096
+ if (message.prev) {
1097
+ return message.prev.children;
1098
+ }
1099
+ return this.rootChildren;
1100
+ }
1101
+ switchToBranch(messageId) {
1102
+ const message = this.messages.get(messageId);
1103
+ if (!message)
1104
+ throw new Error("Unexpected: Branch not found");
1105
+ if (message.prev) {
1106
+ message.prev.next = message;
1107
+ }
1108
+ this.head = findHead(message);
1109
+ }
1110
+ resetHead(messageId) {
1111
+ if (messageId) {
1112
+ const message = this.messages.get(messageId);
1113
+ if (!message)
1114
+ throw new Error("Unexpected: Branch not found");
1115
+ this.head = message;
1116
+ for (let current = message; current; current = current.prev) {
1117
+ if (current.prev) {
1118
+ current.prev.next = current;
1119
+ }
1120
+ }
1121
+ } else {
1122
+ this.head = null;
1123
+ }
1124
+ }
1125
+ };
1126
+
1127
+ // src/adapters/vercel/useVercelAIBranches.tsx
1128
+ var sliceMessagesUntil = (messages, messageId) => {
1129
+ if (messageId == null)
1130
+ return [];
1131
+ if (isOptimisticId(messageId))
1132
+ return messages;
1133
+ const messageIdx = messages.findIndex((m) => m.id === messageId);
1134
+ if (messageIdx === -1)
1135
+ throw new Error("Unexpected: Message not found");
1136
+ return messages.slice(0, messageIdx + 1);
1137
+ };
1138
+ var hasUpcomingMessage = (isRunning, messages) => {
1139
+ return isRunning && messages[messages.length - 1]?.role !== "assistant";
1140
+ };
1141
+ var useVercelAIBranches = (chat, messages) => {
1142
+ const [data] = (0, import_react21.useState)(() => new MessageRepository());
1143
+ const isRunning = "isLoading" in chat ? chat.isLoading : chat.status === "in_progress";
1144
+ const assistantOptimisticIdRef = (0, import_react21.useRef)(null);
1145
+ const messagesEx = (0, import_react21.useMemo)(() => {
1146
+ for (const message of messages) {
1147
+ data.addOrUpdateMessage(message);
1148
+ }
1149
+ if (assistantOptimisticIdRef.current) {
1150
+ data.deleteMessage(assistantOptimisticIdRef.current);
1151
+ assistantOptimisticIdRef.current = null;
1152
+ }
1153
+ if (hasUpcomingMessage(isRunning, messages)) {
1154
+ assistantOptimisticIdRef.current = data.commitOptimisticRun(
1155
+ messages.at(-1)?.id ?? null
1156
+ );
1157
+ }
1158
+ data.resetHead(
1159
+ assistantOptimisticIdRef.current ?? messages.at(-1)?.id ?? null
1160
+ );
1161
+ return data.getMessages();
1162
+ }, [data, isRunning, messages]);
1163
+ const getBranches = (0, import_react21.useCallback)(
1164
+ (messageId) => {
1165
+ return data.getBranches(messageId);
1166
+ },
1167
+ [data]
1168
+ );
1169
+ const switchToBranch = (0, import_react21.useCallback)(
1170
+ (messageId) => {
1171
+ data.switchToBranch(messageId);
1172
+ chat.setMessages(
1173
+ data.getMessages().filter((m) => !isOptimisticId(m.id)).map((m) => m.innerMessage)
1174
+ );
1175
+ },
1176
+ [data, chat.setMessages]
1177
+ );
1178
+ const reloadMaybe = "reload" in chat ? chat.reload : void 0;
1179
+ const startRun = (0, import_react21.useCallback)(
1180
+ async (parentId) => {
1181
+ if (!reloadMaybe)
1182
+ throw new Error("Reload not supported by Vercel AI SDK's useAssistant");
1183
+ const newMessages = sliceMessagesUntil(chat.messages, parentId);
1184
+ chat.setMessages(newMessages);
1185
+ await reloadMaybe();
1186
+ },
1187
+ [chat.messages, chat.setMessages, reloadMaybe]
1188
+ );
1189
+ const append = (0, import_react21.useCallback)(
1190
+ async (message) => {
1191
+ if (message.content.length !== 1 || message.content[0]?.type !== "text")
1192
+ throw new Error("Only text content is supported by Vercel AI SDK");
1193
+ const newMessages = sliceMessagesUntil(chat.messages, message.parentId);
1194
+ chat.setMessages(newMessages);
1195
+ await chat.append({
1196
+ role: "user",
1197
+ content: message.content[0].text
1198
+ });
1199
+ },
1200
+ [chat.messages, chat.setMessages, chat.append]
1201
+ );
1202
+ return (0, import_react21.useMemo)(
1203
+ () => ({
1204
+ messages: messagesEx,
1205
+ getBranches,
1206
+ switchToBranch,
1207
+ append,
1208
+ startRun
1209
+ }),
1210
+ [messagesEx, getBranches, switchToBranch, append, startRun]
1211
+ );
1212
+ };
1213
+
1054
1214
  // src/adapters/vercel/VercelAIAssistantProvider.tsx
1215
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1055
1216
  var ThreadMessageCache = /* @__PURE__ */ new WeakMap();
1056
- var vercelToThreadMessage = (message, parentId, branchId, branchCount) => {
1217
+ var vercelToThreadMessage = (message, parentId) => {
1057
1218
  if (message.role !== "user" && message.role !== "assistant")
1058
1219
  throw new Error("Unsupported role");
1059
1220
  return {
@@ -1061,24 +1222,17 @@ var vercelToThreadMessage = (message, parentId, branchId, branchCount) => {
1061
1222
  id: message.id,
1062
1223
  role: message.role,
1063
1224
  content: [{ type: "text", text: message.content }],
1064
- branchId,
1065
- branchCount,
1066
- createdAt: message.createdAt ?? /* @__PURE__ */ new Date()
1225
+ createdAt: message.createdAt ?? /* @__PURE__ */ new Date(),
1226
+ innerMessage: message
1067
1227
  };
1068
1228
  };
1069
- var vercelToCachedThreadMessages = (messages, getBranchState) => {
1229
+ var vercelToCachedThreadMessages = (messages) => {
1070
1230
  return messages.map((m, idx) => {
1071
1231
  const cached = ThreadMessageCache.get(m);
1072
- const parentId = messages[idx - 1]?.id ?? ROOT_PARENT_ID;
1073
- const { branchId, branchCount } = getBranchState(m.id);
1074
- if (cached && cached.parentId === parentId && cached.branchId === branchId && cached.branchCount === branchCount)
1232
+ const parentId = messages[idx - 1]?.id ?? null;
1233
+ if (cached && cached.parentId === parentId)
1075
1234
  return cached;
1076
- const newMessage = vercelToThreadMessage(
1077
- m,
1078
- parentId,
1079
- branchId,
1080
- branchCount
1081
- );
1235
+ const newMessage = vercelToThreadMessage(m, parentId);
1082
1236
  ThreadMessageCache.set(m, newMessage);
1083
1237
  return newMessage;
1084
1238
  });
@@ -1089,43 +1243,44 @@ var VercelAIAssistantProvider = ({
1089
1243
  }) => {
1090
1244
  const context = useDummyAIAssistantContext();
1091
1245
  const vercel = "chat" in rest ? rest.chat : rest.assistant;
1092
- const branches = useVercelAIBranches(vercel, context);
1093
1246
  const messages = (0, import_react22.useMemo)(() => {
1094
- return vercelToCachedThreadMessages(
1095
- vercel.messages,
1096
- branches.getBranchState
1097
- );
1098
- }, [vercel.messages, branches.getBranchState]);
1099
- const stop = (0, import_react22.useCallback)(() => {
1247
+ return vercelToCachedThreadMessages(vercel.messages);
1248
+ }, [vercel.messages]);
1249
+ const branches = useVercelAIBranches(vercel, messages);
1250
+ const cancelRun = (0, import_react22.useCallback)(() => {
1100
1251
  const lastMessage = vercel.messages.at(-1);
1101
1252
  vercel.stop();
1102
1253
  if (lastMessage?.role === "user") {
1103
1254
  vercel.setInput(lastMessage.content);
1104
1255
  }
1105
1256
  }, [vercel.messages, vercel.stop, vercel.setInput]);
1106
- const isLoading = "isLoading" in vercel ? vercel.isLoading : vercel.status === "in_progress";
1257
+ const isRunning = "isLoading" in vercel ? vercel.isLoading : vercel.status === "in_progress";
1107
1258
  (0, import_react22.useMemo)(() => {
1108
- context.useThread.setState({
1109
- messages,
1110
- isLoading,
1111
- stop,
1112
- switchToBranch: branches.switchToBranch,
1113
- append: branches.append,
1114
- reload: branches.reload
1115
- });
1116
- }, [context, messages, isLoading, stop, branches]);
1259
+ context.useThread.setState(
1260
+ {
1261
+ messages: branches.messages,
1262
+ isRunning,
1263
+ getBranches: branches.getBranches,
1264
+ switchToBranch: branches.switchToBranch,
1265
+ append: branches.append,
1266
+ startRun: branches.startRun,
1267
+ cancelRun
1268
+ },
1269
+ true
1270
+ );
1271
+ }, [context, isRunning, cancelRun, branches]);
1117
1272
  (0, import_react22.useMemo)(() => {
1118
1273
  context.useComposer.setState({
1119
- canCancel: isLoading,
1120
1274
  value: vercel.input,
1121
1275
  setValue: vercel.setInput
1122
1276
  });
1123
- }, [context, isLoading, vercel.input, vercel.setInput]);
1124
- return /* @__PURE__ */ React.createElement(AssistantContext.Provider, { value: context }, children);
1277
+ }, [context, vercel.input, vercel.setInput]);
1278
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(AssistantContext.Provider, { value: context, children });
1125
1279
  };
1126
1280
 
1127
1281
  // src/adapters/vercel/VercelRSCAssistantProvider.tsx
1128
1282
  var import_react23 = require("react");
1283
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1129
1284
  var ThreadMessageCache2 = /* @__PURE__ */ new WeakMap();
1130
1285
  var vercelToThreadMessage2 = (parentId, message) => {
1131
1286
  if (message.role !== "user" && message.role !== "assistant")
@@ -1135,15 +1290,13 @@ var vercelToThreadMessage2 = (parentId, message) => {
1135
1290
  id: message.id,
1136
1291
  role: message.role,
1137
1292
  content: [{ type: "ui", display: message.display }],
1138
- createdAt: message.createdAt ?? /* @__PURE__ */ new Date(),
1139
- branchId: 0,
1140
- branchCount: 1
1293
+ createdAt: message.createdAt ?? /* @__PURE__ */ new Date()
1141
1294
  };
1142
1295
  };
1143
1296
  var vercelToCachedThreadMessages2 = (messages) => {
1144
1297
  return messages.map((m, idx) => {
1145
1298
  const cached = ThreadMessageCache2.get(m);
1146
- const parentId = messages[idx - 1]?.id ?? ROOT_PARENT_ID;
1299
+ const parentId = messages[idx - 1]?.id ?? null;
1147
1300
  if (cached && cached.parentId === parentId)
1148
1301
  return cached;
1149
1302
  const newMessage = vercelToThreadMessage2(parentId, m);
@@ -1162,12 +1315,11 @@ var VercelRSCAssistantProvider = ({
1162
1315
  }, [vercelMessages]);
1163
1316
  const append = (0, import_react23.useCallback)(
1164
1317
  async (message) => {
1165
- if (message.parentId !== (context.useThread.getState().messages.at(-1)?.id ?? ROOT_PARENT_ID))
1318
+ if (message.parentId !== (context.useThread.getState().messages.at(-1)?.id ?? null))
1166
1319
  throw new Error("Unexpected: Message editing is not supported");
1167
1320
  if (message.content[0]?.type !== "text") {
1168
1321
  throw new Error("Only text content is currently supported");
1169
1322
  }
1170
- context.useThread.getState().scrollToBottom();
1171
1323
  await vercelAppend(message);
1172
1324
  },
1173
1325
  [context, vercelAppend]
@@ -1178,7 +1330,7 @@ var VercelRSCAssistantProvider = ({
1178
1330
  append
1179
1331
  });
1180
1332
  }, [context, messages, append]);
1181
- return /* @__PURE__ */ React.createElement(AssistantContext.Provider, { value: context }, children);
1333
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(AssistantContext.Provider, { value: context, children });
1182
1334
  };
1183
1335
  // Annotate the CommonJS export names for ESM import in node:
1184
1336
  0 && (module.exports = {