@assistant-ui/react 0.1.11 → 0.1.12

Sign up to get free protection for your applications and to get access to all the features.
package/dist/index.mjs CHANGED
@@ -91,7 +91,7 @@ var useActionBarEdit = () => {
91
91
  // src/primitive-hooks/actionBar/useActionBarReload.tsx
92
92
  import { useCallback as useCallback3 } from "react";
93
93
  var useActionBarReload = () => {
94
- const { useThread, useThreadActions, useViewport } = useThreadContext();
94
+ const { useThread, useThreadActions, useComposer, useViewport } = useThreadContext();
95
95
  const { useMessage } = useMessageContext();
96
96
  const disabled = useCombinedStore(
97
97
  [useThread, useMessage],
@@ -101,7 +101,8 @@ var useActionBarReload = () => {
101
101
  const { parentId } = useMessage.getState();
102
102
  useThreadActions.getState().startRun(parentId);
103
103
  useViewport.getState().scrollToBottom();
104
- }, [useThreadActions, useMessage, useViewport]);
104
+ useComposer.getState().focus();
105
+ }, [useThreadActions, useComposer, useViewport, useMessage]);
105
106
  if (disabled) return null;
106
107
  return callback;
107
108
  };
@@ -180,7 +181,7 @@ var useComposerIf = (props) => {
180
181
  // src/primitive-hooks/composer/useComposerSend.tsx
181
182
  import { useCallback as useCallback7 } from "react";
182
183
  var useComposerSend = () => {
183
- const { useViewport } = useThreadContext();
184
+ const { useViewport, useComposer: useNewComposer } = useThreadContext();
184
185
  const { useComposer } = useComposerContext();
185
186
  const disabled = useComposer((c) => !c.isEditing || c.value.length === 0);
186
187
  const callback = useCallback7(() => {
@@ -188,7 +189,8 @@ var useComposerSend = () => {
188
189
  if (!composerState.isEditing) return;
189
190
  composerState.send();
190
191
  useViewport.getState().scrollToBottom();
191
- }, [useComposer, useViewport]);
192
+ useNewComposer.getState().focus();
193
+ }, [useNewComposer, useComposer, useViewport]);
192
194
  if (disabled) return null;
193
195
  return callback;
194
196
  };
@@ -280,12 +282,12 @@ var useThreadEmpty = () => {
280
282
  // src/primitive-hooks/thread/useThreadScrollToBottom.tsx
281
283
  import { useCallback as useCallback8 } from "react";
282
284
  var useThreadScrollToBottom = () => {
283
- const { useViewport } = useThreadContext();
285
+ const { useComposer, useViewport } = useThreadContext();
284
286
  const isAtBottom = useViewport((s) => s.isAtBottom);
285
287
  const handleScrollToBottom = useCallback8(() => {
286
- const { scrollToBottom } = useViewport.getState();
287
- scrollToBottom();
288
- }, [useViewport]);
288
+ useViewport.getState().scrollToBottom();
289
+ useComposer.getState().focus();
290
+ }, [useViewport, useComposer]);
289
291
  if (isAtBottom) return null;
290
292
  return handleScrollToBottom;
291
293
  };
@@ -310,416 +312,449 @@ var useThreadSuggestion = ({
310
312
  return callback;
311
313
  };
312
314
 
313
- // src/primitives/thread/index.ts
314
- var thread_exports = {};
315
- __export(thread_exports, {
316
- Empty: () => ThreadEmpty,
317
- If: () => ThreadIf,
318
- Messages: () => ThreadMessages,
319
- Root: () => ThreadRoot,
320
- ScrollToBottom: () => ThreadScrollToBottom,
321
- Suggestion: () => ThreadSuggestion,
322
- Viewport: () => ThreadViewport
315
+ // src/primitives/actionBar/index.ts
316
+ var actionBar_exports = {};
317
+ __export(actionBar_exports, {
318
+ Copy: () => ActionBarCopy,
319
+ Edit: () => ActionBarEdit,
320
+ Reload: () => ActionBarReload,
321
+ Root: () => ActionBarRoot
323
322
  });
324
323
 
325
- // src/primitives/thread/ThreadRoot.tsx
324
+ // src/primitives/actionBar/ActionBarRoot.tsx
326
325
  import { Primitive } from "@radix-ui/react-primitive";
327
326
  import { forwardRef } from "react";
328
327
  import { jsx } from "react/jsx-runtime";
329
- var ThreadRoot = forwardRef(
330
- (props, ref) => {
331
- return /* @__PURE__ */ jsx(Primitive.div, { ...props, ref });
332
- }
333
- );
334
- ThreadRoot.displayName = "ThreadRoot";
335
-
336
- // src/primitives/thread/ThreadEmpty.tsx
337
- var ThreadEmpty = ({ children }) => {
338
- const empty = useThreadEmpty();
339
- return empty ? children : null;
340
- };
341
-
342
- // src/primitives/thread/ThreadIf.tsx
343
- var ThreadIf = ({ children, ...query }) => {
344
- const result = useThreadIf(query);
345
- return result ? children : null;
328
+ var useActionBarFloatStatus = ({
329
+ hideWhenRunning,
330
+ autohide,
331
+ autohideFloat
332
+ }) => {
333
+ const { useThread } = useThreadContext();
334
+ const { useMessage, useMessageUtils } = useMessageContext();
335
+ return useCombinedStore(
336
+ [useThread, useMessage, useMessageUtils],
337
+ (t, m, mu) => {
338
+ if (hideWhenRunning && t.isRunning) return "hidden" /* Hidden */;
339
+ const autohideEnabled = autohide === "always" || autohide === "not-last" && !m.isLast;
340
+ if (!autohideEnabled) return "normal" /* Normal */;
341
+ if (!mu.isHovering) return "hidden" /* Hidden */;
342
+ if (autohideFloat === "always" || autohideFloat === "single-branch" && m.branches.length <= 1)
343
+ return "floating" /* Floating */;
344
+ return "normal" /* Normal */;
345
+ }
346
+ );
346
347
  };
348
+ var ActionBarRoot = forwardRef(({ hideWhenRunning, autohide, autohideFloat, ...rest }, ref) => {
349
+ const hideAndfloatStatus = useActionBarFloatStatus({
350
+ hideWhenRunning,
351
+ autohide,
352
+ autohideFloat
353
+ });
354
+ if (hideAndfloatStatus === "hidden" /* Hidden */) return null;
355
+ return /* @__PURE__ */ jsx(
356
+ Primitive.div,
357
+ {
358
+ ...hideAndfloatStatus === "floating" /* Floating */ ? { "data-floating": "true" } : null,
359
+ ...rest,
360
+ ref
361
+ }
362
+ );
363
+ });
364
+ ActionBarRoot.displayName = "ActionBarRoot";
347
365
 
348
- // src/primitives/thread/ThreadViewport.tsx
349
- import { useComposedRefs as useComposedRefs2 } from "@radix-ui/react-compose-refs";
366
+ // src/utils/createActionButton.tsx
367
+ import { composeEventHandlers } from "@radix-ui/primitive";
350
368
  import { Primitive as Primitive2 } from "@radix-ui/react-primitive";
351
369
  import { forwardRef as forwardRef2 } from "react";
370
+ import { jsx as jsx2 } from "react/jsx-runtime";
371
+ var createActionButton = (displayName, useActionButton) => {
372
+ const ActionButton = forwardRef2((props, forwardedRef) => {
373
+ const callback = useActionButton(props);
374
+ return /* @__PURE__ */ jsx2(
375
+ Primitive2.button,
376
+ {
377
+ type: "button",
378
+ disabled: !callback,
379
+ ...props,
380
+ ref: forwardedRef,
381
+ onClick: composeEventHandlers(props.onClick, () => {
382
+ callback?.();
383
+ })
384
+ }
385
+ );
386
+ });
387
+ ActionButton.displayName = displayName;
388
+ return ActionButton;
389
+ };
352
390
 
353
- // src/primitive-hooks/thread/useThreadViewportAutoScroll.tsx
354
- import { useComposedRefs } from "@radix-ui/react-compose-refs";
355
- import { useRef as useRef2 } from "react";
391
+ // src/primitives/actionBar/ActionBarCopy.tsx
392
+ var ActionBarCopy = createActionButton(
393
+ "ActionBarCopy",
394
+ useActionBarCopy
395
+ );
356
396
 
357
- // src/utils/hooks/useOnResizeContent.tsx
358
- import { useCallbackRef } from "@radix-ui/react-use-callback-ref";
359
- import { useCallback as useCallback11 } from "react";
397
+ // src/primitives/actionBar/ActionBarReload.tsx
398
+ var ActionBarReload = createActionButton(
399
+ "ActionBarReload",
400
+ useActionBarReload
401
+ );
360
402
 
361
- // src/utils/hooks/useManagedRef.ts
362
- import { useCallback as useCallback10, useRef } from "react";
363
- var useManagedRef = (callback) => {
364
- const cleanupRef = useRef();
365
- const ref = useCallback10(
366
- (el) => {
367
- if (cleanupRef.current) {
368
- cleanupRef.current();
369
- }
370
- if (el) {
371
- cleanupRef.current = callback(el);
372
- }
373
- },
374
- [callback]
375
- );
376
- return ref;
377
- };
403
+ // src/primitives/actionBar/ActionBarEdit.tsx
404
+ var ActionBarEdit = createActionButton(
405
+ "ActionBarEdit",
406
+ useActionBarEdit
407
+ );
378
408
 
379
- // src/utils/hooks/useOnResizeContent.tsx
380
- var useOnResizeContent = (callback) => {
381
- const callbackRef = useCallbackRef(callback);
382
- const refCallback = useCallback11(
383
- (el) => {
384
- const resizeObserver = new ResizeObserver(() => {
385
- callbackRef();
386
- });
387
- const mutationObserver = new MutationObserver((mutations) => {
388
- for (const mutation of mutations) {
389
- for (const node of mutation.addedNodes) {
390
- if (node instanceof Element) {
391
- resizeObserver.observe(node);
392
- }
393
- }
394
- for (const node of mutation.removedNodes) {
395
- if (node instanceof Element) {
396
- resizeObserver.unobserve(node);
397
- }
398
- }
399
- }
400
- callbackRef();
401
- });
402
- resizeObserver.observe(el);
403
- mutationObserver.observe(el, { childList: true });
404
- for (const child of el.children) {
405
- resizeObserver.observe(child);
406
- }
407
- return () => {
408
- resizeObserver.disconnect();
409
- mutationObserver.disconnect();
410
- };
411
- },
412
- [callbackRef]
413
- );
414
- return useManagedRef(refCallback);
415
- };
409
+ // src/primitives/assistantModal/index.ts
410
+ var assistantModal_exports = {};
411
+ __export(assistantModal_exports, {
412
+ Content: () => AssistantModalContent,
413
+ Root: () => AssistantModalRoot,
414
+ Trigger: () => AssistantModalTrigger
415
+ });
416
416
 
417
- // src/utils/hooks/useOnScrollToBottom.tsx
418
- import { useCallbackRef as useCallbackRef2 } from "@radix-ui/react-use-callback-ref";
417
+ // src/primitives/assistantModal/AssistantModalRoot.tsx
418
+ import { useState } from "react";
419
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
420
+ import { composeEventHandlers as composeEventHandlers2 } from "@radix-ui/primitive";
421
+
422
+ // src/utils/hooks/useOnComposerFocus.tsx
423
+ import { useCallbackRef } from "@radix-ui/react-use-callback-ref";
419
424
  import { useEffect } from "react";
420
- var useOnScrollToBottom = (callback) => {
421
- const callbackRef = useCallbackRef2(callback);
422
- const { useViewport } = useThreadContext();
425
+ var useOnComposerFocus = (callback) => {
426
+ const callbackRef = useCallbackRef(callback);
427
+ const { useComposer } = useThreadContext();
423
428
  useEffect(() => {
424
- return useViewport.getState().onScrollToBottom(() => {
429
+ return useComposer.getState().onFocus(() => {
425
430
  callbackRef();
426
431
  });
427
- }, [useViewport, callbackRef]);
432
+ }, [useComposer, callbackRef]);
428
433
  };
429
434
 
430
- // src/primitive-hooks/thread/useThreadViewportAutoScroll.tsx
431
- var useThreadViewportAutoScroll = ({
432
- autoScroll = true
435
+ // src/primitives/assistantModal/AssistantModalRoot.tsx
436
+ import { jsx as jsx3 } from "react/jsx-runtime";
437
+ var usePopoverScope = PopoverPrimitive.createPopoverScope();
438
+ var useAssistantModalOpenState = (defaultOpen = false) => {
439
+ const state = useState(defaultOpen);
440
+ const [, setOpen] = state;
441
+ useOnComposerFocus(() => {
442
+ setOpen(true);
443
+ });
444
+ return state;
445
+ };
446
+ var AssistantModalRoot = ({
447
+ __scopeAssistantModal,
448
+ defaultOpen,
449
+ open,
450
+ onOpenChange,
451
+ ...rest
433
452
  }) => {
434
- const divRef = useRef2(null);
435
- const { useViewport } = useThreadContext();
436
- const firstRenderRef = useRef2(true);
437
- const lastScrollTop = useRef2(0);
438
- const isScrollingToBottomRef = useRef2(false);
439
- const scrollToBottom = () => {
440
- const div = divRef.current;
441
- if (!div || !autoScroll) return;
442
- const behavior = firstRenderRef.current ? "instant" : "auto";
443
- firstRenderRef.current = false;
444
- isScrollingToBottomRef.current = true;
445
- div.scrollTo({ top: div.scrollHeight, behavior });
446
- };
447
- const handleScroll = () => {
448
- const div = divRef.current;
449
- if (!div) return;
450
- const isAtBottom = useViewport.getState().isAtBottom;
451
- const newIsAtBottom = div.scrollHeight - div.scrollTop <= div.clientHeight;
452
- if (!newIsAtBottom && lastScrollTop.current < div.scrollTop) {
453
- } else {
454
- isScrollingToBottomRef.current = newIsAtBottom;
455
- if (newIsAtBottom !== isAtBottom) {
456
- useViewport.setState({
457
- isAtBottom: newIsAtBottom
458
- });
459
- }
460
- }
461
- lastScrollTop.current = div.scrollTop;
462
- };
463
- const resizeRef = useOnResizeContent(() => {
464
- if (!isScrollingToBottomRef.current && !useViewport.getState().isAtBottom && !firstRenderRef.current) {
465
- handleScroll();
466
- } else {
467
- scrollToBottom();
453
+ const scope = usePopoverScope(__scopeAssistantModal);
454
+ const [modalOpen, setOpen] = useAssistantModalOpenState(defaultOpen);
455
+ return /* @__PURE__ */ jsx3(
456
+ PopoverPrimitive.Root,
457
+ {
458
+ ...scope,
459
+ open: open === void 0 ? modalOpen : open,
460
+ onOpenChange: composeEventHandlers2(onOpenChange, setOpen),
461
+ ...rest
468
462
  }
469
- });
470
- const scrollRef = useManagedRef((el) => {
471
- el.addEventListener("scroll", handleScroll);
472
- return () => {
473
- el.removeEventListener("scroll", handleScroll);
474
- };
475
- });
476
- const autoScrollRef = useComposedRefs(resizeRef, scrollRef, divRef);
477
- useOnScrollToBottom(() => {
478
- scrollToBottom();
479
- });
480
- return autoScrollRef;
463
+ );
481
464
  };
465
+ AssistantModalRoot.displayName = "AssistantModalRoot";
482
466
 
483
- // src/primitives/thread/ThreadViewport.tsx
484
- import { jsx as jsx2 } from "react/jsx-runtime";
485
- var ThreadViewport = forwardRef2(({ autoScroll, onScroll, children, ...rest }, forwardedRef) => {
486
- const autoScrollRef = useThreadViewportAutoScroll({
487
- autoScroll
488
- });
489
- const ref = useComposedRefs2(forwardedRef, autoScrollRef);
490
- return /* @__PURE__ */ jsx2(Primitive2.div, { ...rest, ref, children });
491
- });
492
- ThreadViewport.displayName = "ThreadViewport";
493
-
494
- // src/primitives/thread/ThreadMessages.tsx
495
- import { memo } from "react";
496
-
497
- // src/context/providers/MessageProvider.tsx
498
- import { useEffect as useEffect2, useState } from "react";
499
- import { create as create3 } from "zustand";
500
-
501
- // src/context/stores/EditComposer.ts
502
- import { create } from "zustand";
467
+ // src/primitives/assistantModal/AssistantModalTrigger.tsx
468
+ import { forwardRef as forwardRef3 } from "react";
469
+ import * as PopoverPrimitive2 from "@radix-ui/react-popover";
470
+ import { jsx as jsx4 } from "react/jsx-runtime";
471
+ var AssistantModalTrigger = forwardRef3(
472
+ ({ __scopeAssistantModal, ...rest }, ref) => {
473
+ const scope = usePopoverScope(__scopeAssistantModal);
474
+ return /* @__PURE__ */ jsx4(PopoverPrimitive2.Trigger, { ...scope, ...rest, ref });
475
+ }
476
+ );
477
+ AssistantModalTrigger.displayName = "AssistantModalTrigger";
503
478
 
504
- // src/context/stores/BaseComposer.ts
505
- var makeBaseComposer = (set) => ({
506
- value: "",
507
- setValue: (value) => {
508
- set({ value });
479
+ // src/primitives/assistantModal/AssistantModalContent.tsx
480
+ import { forwardRef as forwardRef4 } from "react";
481
+ import * as PopoverPrimitive3 from "@radix-ui/react-popover";
482
+ import { composeEventHandlers as composeEventHandlers3 } from "@radix-ui/primitive";
483
+ import { jsx as jsx5 } from "react/jsx-runtime";
484
+ var AssistantModalContent = forwardRef4(
485
+ ({
486
+ __scopeAssistantModal,
487
+ side,
488
+ align,
489
+ onInteractOutside,
490
+ dissmissOnInteractOutside = false,
491
+ ...props
492
+ }, forwardedRef) => {
493
+ const scope = usePopoverScope(__scopeAssistantModal);
494
+ return /* @__PURE__ */ jsx5(PopoverPrimitive3.Portal, { ...scope, children: /* @__PURE__ */ jsx5(
495
+ PopoverPrimitive3.Content,
496
+ {
497
+ ...scope,
498
+ ...props,
499
+ ref: forwardedRef,
500
+ side: side ?? "top",
501
+ align: align ?? "end",
502
+ onInteractOutside: composeEventHandlers3(
503
+ onInteractOutside,
504
+ dissmissOnInteractOutside ? void 0 : (e) => e.preventDefault()
505
+ )
506
+ }
507
+ ) });
509
508
  }
510
- });
509
+ );
510
+ AssistantModalContent.displayName = "AssistantModalContent";
511
511
 
512
- // src/context/stores/EditComposer.ts
513
- var makeEditComposerStore = ({
514
- onEdit,
515
- onSend
516
- }) => create()((set, get, store) => ({
517
- ...makeBaseComposer(set, get, store),
518
- isEditing: false,
519
- edit: () => {
520
- const value = onEdit();
521
- set({ isEditing: true, value });
522
- },
523
- send: () => {
524
- const value = get().value;
525
- set({ isEditing: false });
526
- onSend(value);
527
- },
528
- cancel: () => {
529
- if (!get().isEditing) return false;
530
- set({ isEditing: false });
531
- return true;
532
- }
533
- }));
512
+ // src/primitives/branchPicker/index.ts
513
+ var branchPicker_exports = {};
514
+ __export(branchPicker_exports, {
515
+ Count: () => BranchPickerCount,
516
+ Next: () => BranchPickerNext,
517
+ Number: () => BranchPickerNumber,
518
+ Previous: () => BranchPickerPrevious,
519
+ Root: () => BranchPickerRoot
520
+ });
534
521
 
535
- // src/context/stores/MessageUtils.ts
536
- import { create as create2 } from "zustand";
537
- var makeMessageUtilsStore = () => create2((set) => ({
538
- inProgressIndicator: null,
539
- setInProgressIndicator: (value) => {
540
- set({ inProgressIndicator: value });
541
- },
542
- isCopied: false,
543
- setIsCopied: (value) => {
544
- set({ isCopied: value });
545
- },
546
- isHovering: false,
547
- setIsHovering: (value) => {
548
- set({ isHovering: value });
549
- }
550
- }));
522
+ // src/primitives/branchPicker/BranchPickerNext.tsx
523
+ var BranchPickerNext = createActionButton(
524
+ "BranchPickerNext",
525
+ useBranchPickerNext
526
+ );
551
527
 
552
- // src/context/providers/MessageProvider.tsx
553
- import { jsx as jsx3 } from "react/jsx-runtime";
554
- var getIsLast = (thread, message) => {
555
- return thread.messages[thread.messages.length - 1]?.id === message.id;
528
+ // src/primitives/branchPicker/BranchPickerPrevious.tsx
529
+ var BranchPickerPrevious = createActionButton(
530
+ "BranchPickerPrevious",
531
+ useBranchPickerPrevious
532
+ );
533
+
534
+ // src/primitives/branchPicker/BranchPickerCount.tsx
535
+ import { Fragment, jsx as jsx6 } from "react/jsx-runtime";
536
+ var BranchPickerCount = () => {
537
+ const branchCount = useBranchPickerCount();
538
+ return /* @__PURE__ */ jsx6(Fragment, { children: branchCount });
556
539
  };
557
- var syncMessage = (thread, getBranches, useMessage, messageIndex) => {
558
- const parentId = thread.messages[messageIndex - 1]?.id ?? null;
559
- const message = thread.messages[messageIndex];
560
- if (!message) return;
561
- const isLast = getIsLast(thread, message);
562
- const branches = getBranches(message.id);
563
- const currentState = useMessage.getState();
564
- if (currentState.message === message && currentState.parentId === parentId && currentState.branches === branches && currentState.isLast === isLast)
565
- return;
566
- useMessage.setState({
567
- message,
568
- parentId,
569
- branches,
570
- isLast
571
- });
540
+
541
+ // src/primitives/branchPicker/BranchPickerNumber.tsx
542
+ import { Fragment as Fragment2, jsx as jsx7 } from "react/jsx-runtime";
543
+ var BranchPickerNumber = () => {
544
+ const branchNumber = useBranchPickerNumber();
545
+ return /* @__PURE__ */ jsx7(Fragment2, { children: branchNumber });
572
546
  };
573
- var useMessageContext2 = (messageIndex) => {
574
- const { useThread, useThreadActions } = useThreadContext();
575
- const [context] = useState(() => {
576
- const useMessage = create3(() => ({}));
577
- const useMessageUtils = makeMessageUtilsStore();
578
- const useEditComposer = makeEditComposerStore({
579
- onEdit: () => {
580
- const message = useMessage.getState().message;
581
- if (message.role !== "user")
582
- throw new Error(
583
- "Tried to edit a non-user message. Editing is only supported for user messages. This is likely an internal bug in assistant-ui."
584
- );
585
- const text = getMessageText(message);
586
- return text;
587
- },
588
- onSend: (text) => {
589
- const { message, parentId } = useMessage.getState();
590
- if (message.role !== "user")
591
- throw new Error(
592
- "Tried to edit a non-user message. Editing is only supported for user messages. This is likely an internal bug in assistant-ui."
593
- );
594
- const nonTextParts = message.content.filter(
595
- (part) => part.type !== "text" && part.type !== "ui"
596
- );
597
- useThreadActions.getState().append({
598
- parentId,
599
- role: "user",
600
- content: [{ type: "text", text }, ...nonTextParts]
601
- });
547
+
548
+ // src/primitives/branchPicker/BranchPickerRoot.tsx
549
+ import { Primitive as Primitive6 } from "@radix-ui/react-primitive";
550
+ import { forwardRef as forwardRef8 } from "react";
551
+
552
+ // src/primitives/message/index.ts
553
+ var message_exports = {};
554
+ __export(message_exports, {
555
+ Content: () => MessageContent,
556
+ If: () => MessageIf,
557
+ InProgress: () => MessageInProgress,
558
+ Root: () => MessageRoot
559
+ });
560
+
561
+ // src/primitives/message/MessageRoot.tsx
562
+ import { composeEventHandlers as composeEventHandlers4 } from "@radix-ui/primitive";
563
+ import { Primitive as Primitive3 } from "@radix-ui/react-primitive";
564
+ import { forwardRef as forwardRef5 } from "react";
565
+ import { jsx as jsx8 } from "react/jsx-runtime";
566
+ var MessageRoot = forwardRef5(
567
+ ({ onMouseEnter, onMouseLeave, ...rest }, ref) => {
568
+ const { useMessageUtils } = useMessageContext();
569
+ const setIsHovering = useMessageUtils((s) => s.setIsHovering);
570
+ const handleMouseEnter = () => {
571
+ setIsHovering(true);
572
+ };
573
+ const handleMouseLeave = () => {
574
+ setIsHovering(false);
575
+ };
576
+ return /* @__PURE__ */ jsx8(
577
+ Primitive3.div,
578
+ {
579
+ ...rest,
580
+ ref,
581
+ onMouseEnter: composeEventHandlers4(onMouseEnter, handleMouseEnter),
582
+ onMouseLeave: composeEventHandlers4(onMouseLeave, handleMouseLeave)
602
583
  }
603
- });
604
- syncMessage(
605
- useThread.getState(),
606
- useThreadActions.getState().getBranches,
607
- useMessage,
608
- messageIndex
609
584
  );
610
- return { useMessage, useMessageUtils, useEditComposer };
585
+ }
586
+ );
587
+ MessageRoot.displayName = "MessageRoot";
588
+
589
+ // src/primitives/message/MessageIf.tsx
590
+ var MessageIf = ({ children, ...query }) => {
591
+ const result = useMessageIf(query);
592
+ return result ? children : null;
593
+ };
594
+
595
+ // src/primitives/message/MessageContent.tsx
596
+ import { memo } from "react";
597
+
598
+ // src/context/providers/ContentPartProvider.tsx
599
+ import { useEffect as useEffect2, useState as useState2 } from "react";
600
+ import { create } from "zustand";
601
+ import { jsx as jsx9 } from "react/jsx-runtime";
602
+ var syncContentPart = ({ message }, useContentPart, partIndex) => {
603
+ const part = message.content[partIndex];
604
+ if (!part) return;
605
+ const messageStatus = message.role === "assistant" ? message.status : "done";
606
+ const status = partIndex === message.content.length - 1 ? messageStatus : "done";
607
+ const currentState = useContentPart.getState();
608
+ if (currentState.part === part && currentState.status === status) return;
609
+ useContentPart.setState(
610
+ Object.freeze({
611
+ part,
612
+ status
613
+ })
614
+ );
615
+ };
616
+ var useContentPartContext2 = (partIndex) => {
617
+ const { useMessage } = useMessageContext();
618
+ const [context] = useState2(() => {
619
+ const useContentPart = create(
620
+ () => ({})
621
+ );
622
+ syncContentPart(useMessage.getState(), useContentPart, partIndex);
623
+ return { useContentPart };
611
624
  });
612
625
  useEffect2(() => {
613
- return useThread.subscribe((thread) => {
614
- syncMessage(
615
- thread,
616
- useThreadActions.getState().getBranches,
617
- context.useMessage,
618
- messageIndex
619
- );
626
+ syncContentPart(useMessage.getState(), context.useContentPart, partIndex);
627
+ return useMessage.subscribe((message) => {
628
+ syncContentPart(message, context.useContentPart, partIndex);
620
629
  });
621
- }, [useThread, useThreadActions, context, messageIndex]);
630
+ }, [context, useMessage, partIndex]);
622
631
  return context;
623
632
  };
624
- var MessageProvider = ({
625
- messageIndex,
633
+ var ContentPartProvider = ({
634
+ partIndex,
626
635
  children
627
636
  }) => {
628
- const context = useMessageContext2(messageIndex);
629
- return /* @__PURE__ */ jsx3(MessageContext.Provider, { value: context, children });
637
+ const context = useContentPartContext2(partIndex);
638
+ return /* @__PURE__ */ jsx9(ContentPartContext.Provider, { value: context, children });
630
639
  };
631
640
 
632
- // src/primitives/composer/ComposerIf.tsx
633
- var ComposerIf = ({ children, ...query }) => {
634
- const result = useComposerIf(query);
635
- return result ? children : null;
641
+ // src/primitives/contentPart/ContentPartDisplay.tsx
642
+ var ContentPartDisplay = () => {
643
+ const display = useContentPartDisplay();
644
+ return display ?? null;
636
645
  };
637
646
 
638
- // src/primitives/message/MessageIf.tsx
639
- var MessageIf = ({ children, ...query }) => {
640
- const result = useMessageIf(query);
641
- return result ? children : null;
647
+ // src/primitives/contentPart/ContentPartInProgressIndicator.tsx
648
+ var ContentPartInProgressIndicator = () => {
649
+ const indicator = useContentPartInProgressIndicator();
650
+ return indicator;
642
651
  };
643
652
 
644
- // src/primitives/thread/ThreadMessages.tsx
645
- import { jsx as jsx4, jsxs } from "react/jsx-runtime";
646
- var getComponents = (components) => {
647
- return {
648
- EditComposer: components.EditComposer ?? components.UserMessage ?? components.Message,
649
- UserMessage: components.UserMessage ?? components.Message,
650
- AssistantMessage: components.AssistantMessage ?? components.Message
651
- };
653
+ // src/primitives/contentPart/ContentPartText.tsx
654
+ import { Primitive as Primitive4 } from "@radix-ui/react-primitive";
655
+ import { forwardRef as forwardRef6 } from "react";
656
+ import { jsx as jsx10 } from "react/jsx-runtime";
657
+ var ContentPartText = forwardRef6((props, forwardedRef) => {
658
+ const text = useContentPartText();
659
+ return /* @__PURE__ */ jsx10(Primitive4.span, { ...props, ref: forwardedRef, children: text });
660
+ });
661
+ ContentPartText.displayName = "ContentPartText";
662
+
663
+ // src/primitives/message/MessageContent.tsx
664
+ import { Fragment as Fragment3, jsx as jsx11, jsxs } from "react/jsx-runtime";
665
+ var defaultComponents = {
666
+ Text: () => /* @__PURE__ */ jsxs(Fragment3, { children: [
667
+ /* @__PURE__ */ jsx11(ContentPartText, {}),
668
+ /* @__PURE__ */ jsx11(ContentPartInProgressIndicator, {})
669
+ ] }),
670
+ Image: () => null,
671
+ UI: () => /* @__PURE__ */ jsx11(ContentPartDisplay, {}),
672
+ tools: {
673
+ Fallback: (props) => {
674
+ const { useToolUIs } = useAssistantContext();
675
+ const Render = useToolUIs((s) => s.getToolUI(props.part.toolName));
676
+ if (!Render) return null;
677
+ return /* @__PURE__ */ jsx11(Render, { ...props });
678
+ }
679
+ }
652
680
  };
653
- var ThreadMessageImpl = ({
654
- messageIndex,
655
- components
681
+ var MessageContentPartComponent = ({
682
+ components: {
683
+ Text = defaultComponents.Text,
684
+ Image = defaultComponents.Image,
685
+ UI = defaultComponents.UI,
686
+ tools: { by_name = {}, Fallback = defaultComponents.tools.Fallback } = {}
687
+ } = {}
656
688
  }) => {
657
- const { UserMessage, EditComposer, AssistantMessage } = getComponents(components);
658
- return /* @__PURE__ */ jsxs(MessageProvider, { messageIndex, children: [
659
- /* @__PURE__ */ jsxs(MessageIf, { user: true, children: [
660
- /* @__PURE__ */ jsx4(ComposerIf, { editing: false, children: /* @__PURE__ */ jsx4(UserMessage, {}) }),
661
- /* @__PURE__ */ jsx4(ComposerIf, { editing: true, children: /* @__PURE__ */ jsx4(EditComposer, {}) })
662
- ] }),
663
- /* @__PURE__ */ jsx4(MessageIf, { assistant: true, children: /* @__PURE__ */ jsx4(AssistantMessage, {}) })
664
- ] });
689
+ const { useThreadActions } = useThreadContext();
690
+ const addToolResult = useThreadActions((t) => t.addToolResult);
691
+ const { useContentPart } = useContentPartContext();
692
+ const { part, status } = useContentPart();
693
+ const type = part.type;
694
+ switch (type) {
695
+ case "text":
696
+ return /* @__PURE__ */ jsx11(Text, { part, status });
697
+ case "image":
698
+ return /* @__PURE__ */ jsx11(Image, { part, status });
699
+ case "ui":
700
+ return /* @__PURE__ */ jsx11(UI, { part, status });
701
+ case "tool-call": {
702
+ const Tool = by_name[part.toolName] || Fallback;
703
+ const addResult = (result) => addToolResult(part.toolCallId, result);
704
+ return /* @__PURE__ */ jsx11(Tool, { part, status, addResult });
705
+ }
706
+ default:
707
+ throw new Error(`Unknown content part type: ${type}`);
708
+ }
665
709
  };
666
- var ThreadMessage = memo(
667
- ThreadMessageImpl,
668
- (prev, next) => prev.messageIndex === next.messageIndex && prev.components.UserMessage === next.components.UserMessage && prev.components.EditComposer === next.components.EditComposer && prev.components.AssistantMessage === next.components.AssistantMessage
710
+ var MessageContentPartImpl = ({
711
+ partIndex,
712
+ components
713
+ }) => {
714
+ return /* @__PURE__ */ jsx11(ContentPartProvider, { partIndex, children: /* @__PURE__ */ jsx11(MessageContentPartComponent, { components }) });
715
+ };
716
+ var MessageContentPart = memo(
717
+ MessageContentPartImpl,
718
+ (prev, next) => prev.partIndex === next.partIndex && prev.components?.Text === next.components?.Text && prev.components?.Image === next.components?.Image && prev.components?.UI === next.components?.UI && prev.components?.tools === next.components?.tools
669
719
  );
670
- var ThreadMessages = ({ components }) => {
671
- const { useThread } = useThreadContext();
672
- const messagesLength = useThread((t) => t.messages.length);
673
- if (messagesLength === 0) return null;
674
- return new Array(messagesLength).fill(null).map((_, idx) => {
675
- const messageIndex = idx;
676
- return /* @__PURE__ */ jsx4(
677
- ThreadMessage,
720
+ var MessageContent = ({ components }) => {
721
+ const { useMessage } = useMessageContext();
722
+ const contentLength = useMessage((s) => s.message.content.length);
723
+ return new Array(contentLength).fill(null).map((_, idx) => {
724
+ const partIndex = idx;
725
+ return /* @__PURE__ */ jsx11(
726
+ MessageContentPart,
678
727
  {
679
- messageIndex,
728
+ partIndex,
680
729
  components
681
730
  },
682
- messageIndex
683
- );
684
- });
685
- };
686
-
687
- // src/utils/createActionButton.tsx
688
- import { composeEventHandlers } from "@radix-ui/primitive";
689
- import { Primitive as Primitive3 } from "@radix-ui/react-primitive";
690
- import { forwardRef as forwardRef3 } from "react";
691
- import { jsx as jsx5 } from "react/jsx-runtime";
692
- var createActionButton = (displayName, useActionButton) => {
693
- const ActionButton = forwardRef3((props, forwardedRef) => {
694
- const callback = useActionButton(props);
695
- return /* @__PURE__ */ jsx5(
696
- Primitive3.button,
697
- {
698
- type: "button",
699
- disabled: !callback,
700
- ...props,
701
- ref: forwardedRef,
702
- onClick: composeEventHandlers(props.onClick, () => {
703
- callback?.();
704
- })
705
- }
731
+ partIndex
706
732
  );
707
733
  });
708
- ActionButton.displayName = displayName;
709
- return ActionButton;
710
734
  };
711
735
 
712
- // src/primitives/thread/ThreadScrollToBottom.tsx
713
- var ThreadScrollToBottom = createActionButton(
714
- "ThreadScrollToBottom",
715
- useThreadScrollToBottom
716
- );
736
+ // src/primitives/message/MessageInProgress.tsx
737
+ import { Primitive as Primitive5 } from "@radix-ui/react-primitive";
738
+ import {
739
+ forwardRef as forwardRef7,
740
+ useMemo as useMemo2
741
+ } from "react";
742
+ import { jsx as jsx12 } from "react/jsx-runtime";
743
+ var MessageInProgress = forwardRef7((props, ref) => {
744
+ const { useMessageUtils } = useMessageContext();
745
+ useMemo2(() => {
746
+ useMessageUtils.getState().setInProgressIndicator(/* @__PURE__ */ jsx12(Primitive5.span, { ...props, ref }));
747
+ }, [useMessageUtils, props, ref]);
748
+ return null;
749
+ });
750
+ MessageInProgress.displayName = "MessageInProgress";
717
751
 
718
- // src/primitives/thread/ThreadSuggestion.tsx
719
- var ThreadSuggestion = createActionButton(
720
- "ThreadSuggestion",
721
- useThreadSuggestion
722
- );
752
+ // src/primitives/branchPicker/BranchPickerRoot.tsx
753
+ import { jsx as jsx13 } from "react/jsx-runtime";
754
+ var BranchPickerRoot = forwardRef8(({ hideWhenSingleBranch, ...rest }, ref) => {
755
+ return /* @__PURE__ */ jsx13(MessageIf, { hasBranches: hideWhenSingleBranch ? true : void 0, children: /* @__PURE__ */ jsx13(Primitive6.div, { ...rest, ref }) });
756
+ });
757
+ BranchPickerRoot.displayName = "BranchPickerRoot";
723
758
 
724
759
  // src/primitives/composer/index.ts
725
760
  var composer_exports = {};
@@ -732,13 +767,13 @@ __export(composer_exports, {
732
767
  });
733
768
 
734
769
  // src/primitives/composer/ComposerRoot.tsx
735
- import { composeEventHandlers as composeEventHandlers2 } from "@radix-ui/primitive";
736
- import { Primitive as Primitive4 } from "@radix-ui/react-primitive";
770
+ import { composeEventHandlers as composeEventHandlers5 } from "@radix-ui/primitive";
771
+ import { Primitive as Primitive7 } from "@radix-ui/react-primitive";
737
772
  import {
738
- forwardRef as forwardRef4
773
+ forwardRef as forwardRef9
739
774
  } from "react";
740
- import { jsx as jsx6 } from "react/jsx-runtime";
741
- var ComposerRoot = forwardRef4(
775
+ import { jsx as jsx14 } from "react/jsx-runtime";
776
+ var ComposerRoot = forwardRef9(
742
777
  ({ onSubmit, ...rest }, forwardedRef) => {
743
778
  const send = useComposerSend();
744
779
  const handleSubmit = (e) => {
@@ -746,12 +781,12 @@ var ComposerRoot = forwardRef4(
746
781
  e.preventDefault();
747
782
  send();
748
783
  };
749
- return /* @__PURE__ */ jsx6(
750
- Primitive4.form,
784
+ return /* @__PURE__ */ jsx14(
785
+ Primitive7.form,
751
786
  {
752
787
  ...rest,
753
788
  ref: forwardedRef,
754
- onSubmit: composeEventHandlers2(onSubmit, handleSubmit)
789
+ onSubmit: composeEventHandlers5(onSubmit, handleSubmit)
755
790
  }
756
791
  );
757
792
  }
@@ -759,19 +794,19 @@ var ComposerRoot = forwardRef4(
759
794
  ComposerRoot.displayName = "ComposerRoot";
760
795
 
761
796
  // src/primitives/composer/ComposerInput.tsx
762
- import { composeEventHandlers as composeEventHandlers3 } from "@radix-ui/primitive";
763
- import { useComposedRefs as useComposedRefs3 } from "@radix-ui/react-compose-refs";
797
+ import { composeEventHandlers as composeEventHandlers6 } from "@radix-ui/primitive";
798
+ import { useComposedRefs } from "@radix-ui/react-compose-refs";
764
799
  import { Slot } from "@radix-ui/react-slot";
765
800
  import {
766
- forwardRef as forwardRef5,
767
- useCallback as useCallback12,
801
+ forwardRef as forwardRef10,
802
+ useCallback as useCallback10,
768
803
  useEffect as useEffect3,
769
- useRef as useRef3
804
+ useRef
770
805
  } from "react";
771
806
  import TextareaAutosize from "react-textarea-autosize";
772
807
  import { useEscapeKeydown } from "@radix-ui/react-use-escape-keydown";
773
- import { jsx as jsx7 } from "react/jsx-runtime";
774
- var ComposerInput = forwardRef5(
808
+ import { jsx as jsx15 } from "react/jsx-runtime";
809
+ var ComposerInput = forwardRef10(
775
810
  ({ autoFocus = false, asChild, disabled, onChange, onKeyDown, ...rest }, forwardedRef) => {
776
811
  const { useThread } = useThreadContext();
777
812
  const { useComposer, type } = useComposerContext();
@@ -780,8 +815,8 @@ var ComposerInput = forwardRef5(
780
815
  return c.value;
781
816
  });
782
817
  const Component = asChild ? Slot : TextareaAutosize;
783
- const textareaRef = useRef3(null);
784
- const ref = useComposedRefs3(forwardedRef, textareaRef);
818
+ const textareaRef = useRef(null);
819
+ const ref = useComposedRefs(forwardedRef, textareaRef);
785
820
  useEscapeKeydown((e) => {
786
821
  const composer = useComposer.getState();
787
822
  if (composer.cancel()) {
@@ -799,35 +834,34 @@ var ComposerInput = forwardRef5(
799
834
  }
800
835
  };
801
836
  const autoFocusEnabled = autoFocus && !disabled;
802
- const focus = useCallback12(() => {
837
+ const focus = useCallback10(() => {
803
838
  const textarea = textareaRef.current;
804
839
  if (!textarea || !autoFocusEnabled) return;
805
- textarea.focus();
840
+ textarea.focus({ preventScroll: true });
806
841
  textarea.setSelectionRange(
807
842
  textareaRef.current.value.length,
808
843
  textareaRef.current.value.length
809
844
  );
810
845
  }, [autoFocusEnabled]);
811
846
  useEffect3(() => focus(), [focus]);
812
- useOnScrollToBottom(() => {
847
+ useOnComposerFocus(() => {
813
848
  if (type === "new") {
814
849
  focus();
815
850
  }
816
851
  });
817
- return /* @__PURE__ */ jsx7(
852
+ return /* @__PURE__ */ jsx15(
818
853
  Component,
819
854
  {
820
855
  value,
821
856
  ...rest,
822
857
  ref,
823
- autoFocus,
824
858
  disabled,
825
- onChange: composeEventHandlers3(onChange, (e) => {
859
+ onChange: composeEventHandlers6(onChange, (e) => {
826
860
  const composerState = useComposer.getState();
827
861
  if (!composerState.isEditing) return;
828
862
  return composerState.setValue(e.target.value);
829
863
  }),
830
- onKeyDown: composeEventHandlers3(onKeyDown, handleKeyPress)
864
+ onKeyDown: composeEventHandlers6(onKeyDown, handleKeyPress)
831
865
  }
832
866
  );
833
867
  }
@@ -835,15 +869,15 @@ var ComposerInput = forwardRef5(
835
869
  ComposerInput.displayName = "ComposerInput";
836
870
 
837
871
  // src/primitives/composer/ComposerSend.tsx
838
- import { forwardRef as forwardRef6 } from "react";
839
- import { Primitive as Primitive5 } from "@radix-ui/react-primitive";
840
- import { jsx as jsx8 } from "react/jsx-runtime";
841
- var ComposerSend = forwardRef6(
872
+ import { forwardRef as forwardRef11 } from "react";
873
+ import { Primitive as Primitive8 } from "@radix-ui/react-primitive";
874
+ import { jsx as jsx16 } from "react/jsx-runtime";
875
+ var ComposerSend = forwardRef11(
842
876
  ({ disabled, ...rest }, ref) => {
843
877
  const { useComposer } = useComposerContext();
844
878
  const hasValue = useComposer((c) => c.isEditing && c.value.length > 0);
845
- return /* @__PURE__ */ jsx8(
846
- Primitive5.button,
879
+ return /* @__PURE__ */ jsx16(
880
+ Primitive8.button,
847
881
  {
848
882
  type: "submit",
849
883
  ...rest,
@@ -861,335 +895,407 @@ var ComposerCancel = createActionButton(
861
895
  useComposerCancel
862
896
  );
863
897
 
864
- // src/primitives/message/index.ts
865
- var message_exports = {};
866
- __export(message_exports, {
867
- Content: () => MessageContent,
868
- If: () => MessageIf,
869
- InProgress: () => MessageInProgress,
870
- Root: () => MessageRoot
898
+ // src/primitives/composer/ComposerIf.tsx
899
+ var ComposerIf = ({ children, ...query }) => {
900
+ const result = useComposerIf(query);
901
+ return result ? children : null;
902
+ };
903
+
904
+ // src/primitives/contentPart/index.ts
905
+ var contentPart_exports = {};
906
+ __export(contentPart_exports, {
907
+ Display: () => ContentPartDisplay,
908
+ Image: () => ContentPartImage,
909
+ InProgressIndicator: () => ContentPartInProgressIndicator,
910
+ Text: () => ContentPartText
871
911
  });
872
912
 
873
- // src/primitives/message/MessageRoot.tsx
874
- import { composeEventHandlers as composeEventHandlers4 } from "@radix-ui/primitive";
875
- import { Primitive as Primitive6 } from "@radix-ui/react-primitive";
876
- import { forwardRef as forwardRef7 } from "react";
877
- import { jsx as jsx9 } from "react/jsx-runtime";
878
- var MessageRoot = forwardRef7(
879
- ({ onMouseEnter, onMouseLeave, ...rest }, ref) => {
880
- const { useMessageUtils } = useMessageContext();
881
- const setIsHovering = useMessageUtils((s) => s.setIsHovering);
882
- const handleMouseEnter = () => {
883
- setIsHovering(true);
884
- };
885
- const handleMouseLeave = () => {
886
- setIsHovering(false);
887
- };
888
- return /* @__PURE__ */ jsx9(
889
- Primitive6.div,
890
- {
891
- ...rest,
892
- ref,
893
- onMouseEnter: composeEventHandlers4(onMouseEnter, handleMouseEnter),
894
- onMouseLeave: composeEventHandlers4(onMouseLeave, handleMouseLeave)
895
- }
896
- );
913
+ // src/primitives/contentPart/ContentPartImage.tsx
914
+ import { Primitive as Primitive9 } from "@radix-ui/react-primitive";
915
+ import { forwardRef as forwardRef12 } from "react";
916
+ import { jsx as jsx17 } from "react/jsx-runtime";
917
+ var ContentPartImage = forwardRef12((props, forwardedRef) => {
918
+ const image = useContentPartImage();
919
+ return /* @__PURE__ */ jsx17(Primitive9.img, { src: image, ...props, ref: forwardedRef });
920
+ });
921
+ ContentPartImage.displayName = "ContentPartImage";
922
+
923
+ // src/primitives/thread/index.ts
924
+ var thread_exports = {};
925
+ __export(thread_exports, {
926
+ Empty: () => ThreadEmpty,
927
+ If: () => ThreadIf,
928
+ Messages: () => ThreadMessages,
929
+ Root: () => ThreadRoot,
930
+ ScrollToBottom: () => ThreadScrollToBottom,
931
+ Suggestion: () => ThreadSuggestion,
932
+ Viewport: () => ThreadViewport
933
+ });
934
+
935
+ // src/primitives/thread/ThreadRoot.tsx
936
+ import { Primitive as Primitive10 } from "@radix-ui/react-primitive";
937
+ import { forwardRef as forwardRef13 } from "react";
938
+ import { jsx as jsx18 } from "react/jsx-runtime";
939
+ var ThreadRoot = forwardRef13(
940
+ (props, ref) => {
941
+ return /* @__PURE__ */ jsx18(Primitive10.div, { ...props, ref });
897
942
  }
898
943
  );
899
- MessageRoot.displayName = "MessageRoot";
944
+ ThreadRoot.displayName = "ThreadRoot";
900
945
 
901
- // src/primitives/message/MessageContent.tsx
946
+ // src/primitives/thread/ThreadEmpty.tsx
947
+ var ThreadEmpty = ({ children }) => {
948
+ const empty = useThreadEmpty();
949
+ return empty ? children : null;
950
+ };
951
+
952
+ // src/primitives/thread/ThreadIf.tsx
953
+ var ThreadIf = ({ children, ...query }) => {
954
+ const result = useThreadIf(query);
955
+ return result ? children : null;
956
+ };
957
+
958
+ // src/primitives/thread/ThreadViewport.tsx
959
+ import { useComposedRefs as useComposedRefs3 } from "@radix-ui/react-compose-refs";
960
+ import { Primitive as Primitive11 } from "@radix-ui/react-primitive";
961
+ import { forwardRef as forwardRef14 } from "react";
962
+
963
+ // src/primitive-hooks/thread/useThreadViewportAutoScroll.tsx
964
+ import { useComposedRefs as useComposedRefs2 } from "@radix-ui/react-compose-refs";
965
+ import { useRef as useRef3 } from "react";
966
+
967
+ // src/utils/hooks/useOnResizeContent.tsx
968
+ import { useCallbackRef as useCallbackRef2 } from "@radix-ui/react-use-callback-ref";
969
+ import { useCallback as useCallback12 } from "react";
970
+
971
+ // src/utils/hooks/useManagedRef.ts
972
+ import { useCallback as useCallback11, useRef as useRef2 } from "react";
973
+ var useManagedRef = (callback) => {
974
+ const cleanupRef = useRef2();
975
+ const ref = useCallback11(
976
+ (el) => {
977
+ if (cleanupRef.current) {
978
+ cleanupRef.current();
979
+ }
980
+ if (el) {
981
+ cleanupRef.current = callback(el);
982
+ }
983
+ },
984
+ [callback]
985
+ );
986
+ return ref;
987
+ };
988
+
989
+ // src/utils/hooks/useOnResizeContent.tsx
990
+ var useOnResizeContent = (callback) => {
991
+ const callbackRef = useCallbackRef2(callback);
992
+ const refCallback = useCallback12(
993
+ (el) => {
994
+ const resizeObserver = new ResizeObserver(() => {
995
+ callbackRef();
996
+ });
997
+ const mutationObserver = new MutationObserver((mutations) => {
998
+ for (const mutation of mutations) {
999
+ for (const node of mutation.addedNodes) {
1000
+ if (node instanceof Element) {
1001
+ resizeObserver.observe(node);
1002
+ }
1003
+ }
1004
+ for (const node of mutation.removedNodes) {
1005
+ if (node instanceof Element) {
1006
+ resizeObserver.unobserve(node);
1007
+ }
1008
+ }
1009
+ }
1010
+ callbackRef();
1011
+ });
1012
+ resizeObserver.observe(el);
1013
+ mutationObserver.observe(el, { childList: true });
1014
+ for (const child of el.children) {
1015
+ resizeObserver.observe(child);
1016
+ }
1017
+ return () => {
1018
+ resizeObserver.disconnect();
1019
+ mutationObserver.disconnect();
1020
+ };
1021
+ },
1022
+ [callbackRef]
1023
+ );
1024
+ return useManagedRef(refCallback);
1025
+ };
1026
+
1027
+ // src/utils/hooks/useOnScrollToBottom.tsx
1028
+ import { useCallbackRef as useCallbackRef3 } from "@radix-ui/react-use-callback-ref";
1029
+ import { useEffect as useEffect4 } from "react";
1030
+ var useOnScrollToBottom = (callback) => {
1031
+ const callbackRef = useCallbackRef3(callback);
1032
+ const { useViewport } = useThreadContext();
1033
+ useEffect4(() => {
1034
+ return useViewport.getState().onScrollToBottom(() => {
1035
+ callbackRef();
1036
+ });
1037
+ }, [useViewport, callbackRef]);
1038
+ };
1039
+
1040
+ // src/primitive-hooks/thread/useThreadViewportAutoScroll.tsx
1041
+ var useThreadViewportAutoScroll = ({
1042
+ autoScroll = true
1043
+ }) => {
1044
+ const divRef = useRef3(null);
1045
+ const { useViewport } = useThreadContext();
1046
+ const firstRenderRef = useRef3(true);
1047
+ const lastScrollTop = useRef3(0);
1048
+ const isScrollingToBottomRef = useRef3(false);
1049
+ const scrollToBottom = () => {
1050
+ const div = divRef.current;
1051
+ if (!div || !autoScroll) return;
1052
+ const behavior = firstRenderRef.current ? "instant" : "auto";
1053
+ firstRenderRef.current = false;
1054
+ isScrollingToBottomRef.current = true;
1055
+ div.scrollTo({ top: div.scrollHeight, behavior });
1056
+ };
1057
+ const handleScroll = () => {
1058
+ const div = divRef.current;
1059
+ if (!div) return;
1060
+ const isAtBottom = useViewport.getState().isAtBottom;
1061
+ const newIsAtBottom = div.scrollHeight - div.scrollTop <= div.clientHeight;
1062
+ if (!newIsAtBottom && lastScrollTop.current < div.scrollTop) {
1063
+ } else {
1064
+ isScrollingToBottomRef.current = newIsAtBottom;
1065
+ if (newIsAtBottom !== isAtBottom) {
1066
+ useViewport.setState({
1067
+ isAtBottom: newIsAtBottom
1068
+ });
1069
+ }
1070
+ }
1071
+ lastScrollTop.current = div.scrollTop;
1072
+ };
1073
+ const resizeRef = useOnResizeContent(() => {
1074
+ if (!isScrollingToBottomRef.current && !useViewport.getState().isAtBottom && !firstRenderRef.current) {
1075
+ handleScroll();
1076
+ } else {
1077
+ scrollToBottom();
1078
+ }
1079
+ });
1080
+ const scrollRef = useManagedRef((el) => {
1081
+ el.addEventListener("scroll", handleScroll);
1082
+ return () => {
1083
+ el.removeEventListener("scroll", handleScroll);
1084
+ };
1085
+ });
1086
+ const autoScrollRef = useComposedRefs2(resizeRef, scrollRef, divRef);
1087
+ useOnScrollToBottom(() => {
1088
+ scrollToBottom();
1089
+ });
1090
+ return autoScrollRef;
1091
+ };
1092
+
1093
+ // src/primitives/thread/ThreadViewport.tsx
1094
+ import { jsx as jsx19 } from "react/jsx-runtime";
1095
+ var ThreadViewport = forwardRef14(({ autoScroll, onScroll, children, ...rest }, forwardedRef) => {
1096
+ const autoScrollRef = useThreadViewportAutoScroll({
1097
+ autoScroll
1098
+ });
1099
+ const ref = useComposedRefs3(forwardedRef, autoScrollRef);
1100
+ return /* @__PURE__ */ jsx19(Primitive11.div, { ...rest, ref, children });
1101
+ });
1102
+ ThreadViewport.displayName = "ThreadViewport";
1103
+
1104
+ // src/primitives/thread/ThreadMessages.tsx
902
1105
  import { memo as memo2 } from "react";
903
1106
 
904
- // src/context/providers/ContentPartProvider.tsx
905
- import { useEffect as useEffect4, useState as useState2 } from "react";
1107
+ // src/context/providers/MessageProvider.tsx
1108
+ import { useEffect as useEffect5, useState as useState3 } from "react";
906
1109
  import { create as create4 } from "zustand";
907
- import { jsx as jsx10 } from "react/jsx-runtime";
908
- var syncContentPart = ({ message }, useContentPart, partIndex) => {
909
- const part = message.content[partIndex];
910
- if (!part) return;
911
- const messageStatus = message.role === "assistant" ? message.status : "done";
912
- const status = partIndex === message.content.length - 1 ? messageStatus : "done";
913
- const currentState = useContentPart.getState();
914
- if (currentState.part === part && currentState.status === status) return;
915
- useContentPart.setState(
916
- Object.freeze({
917
- part,
918
- status
919
- })
920
- );
1110
+
1111
+ // src/context/stores/EditComposer.ts
1112
+ import { create as create2 } from "zustand";
1113
+
1114
+ // src/context/stores/BaseComposer.ts
1115
+ var makeBaseComposer = (set) => ({
1116
+ value: "",
1117
+ setValue: (value) => {
1118
+ set({ value });
1119
+ }
1120
+ });
1121
+
1122
+ // src/context/stores/EditComposer.ts
1123
+ var makeEditComposerStore = ({
1124
+ onEdit,
1125
+ onSend
1126
+ }) => create2()((set, get, store) => ({
1127
+ ...makeBaseComposer(set, get, store),
1128
+ isEditing: false,
1129
+ edit: () => {
1130
+ const value = onEdit();
1131
+ set({ isEditing: true, value });
1132
+ },
1133
+ send: () => {
1134
+ const value = get().value;
1135
+ set({ isEditing: false });
1136
+ onSend(value);
1137
+ },
1138
+ cancel: () => {
1139
+ if (!get().isEditing) return false;
1140
+ set({ isEditing: false });
1141
+ return true;
1142
+ }
1143
+ }));
1144
+
1145
+ // src/context/stores/MessageUtils.ts
1146
+ import { create as create3 } from "zustand";
1147
+ var makeMessageUtilsStore = () => create3((set) => ({
1148
+ inProgressIndicator: null,
1149
+ setInProgressIndicator: (value) => {
1150
+ set({ inProgressIndicator: value });
1151
+ },
1152
+ isCopied: false,
1153
+ setIsCopied: (value) => {
1154
+ set({ isCopied: value });
1155
+ },
1156
+ isHovering: false,
1157
+ setIsHovering: (value) => {
1158
+ set({ isHovering: value });
1159
+ }
1160
+ }));
1161
+
1162
+ // src/context/providers/MessageProvider.tsx
1163
+ import { jsx as jsx20 } from "react/jsx-runtime";
1164
+ var getIsLast = (thread, message) => {
1165
+ return thread.messages[thread.messages.length - 1]?.id === message.id;
921
1166
  };
922
- var useContentPartContext2 = (partIndex) => {
923
- const { useMessage } = useMessageContext();
924
- const [context] = useState2(() => {
925
- const useContentPart = create4(
926
- () => ({})
1167
+ var syncMessage = (thread, getBranches, useMessage, messageIndex) => {
1168
+ const parentId = thread.messages[messageIndex - 1]?.id ?? null;
1169
+ const message = thread.messages[messageIndex];
1170
+ if (!message) return;
1171
+ const isLast = getIsLast(thread, message);
1172
+ const branches = getBranches(message.id);
1173
+ const currentState = useMessage.getState();
1174
+ if (currentState.message === message && currentState.parentId === parentId && currentState.branches === branches && currentState.isLast === isLast)
1175
+ return;
1176
+ useMessage.setState({
1177
+ message,
1178
+ parentId,
1179
+ branches,
1180
+ isLast
1181
+ });
1182
+ };
1183
+ var useMessageContext2 = (messageIndex) => {
1184
+ const { useThread, useThreadActions } = useThreadContext();
1185
+ const [context] = useState3(() => {
1186
+ const useMessage = create4(() => ({}));
1187
+ const useMessageUtils = makeMessageUtilsStore();
1188
+ const useEditComposer = makeEditComposerStore({
1189
+ onEdit: () => {
1190
+ const message = useMessage.getState().message;
1191
+ if (message.role !== "user")
1192
+ throw new Error(
1193
+ "Tried to edit a non-user message. Editing is only supported for user messages. This is likely an internal bug in assistant-ui."
1194
+ );
1195
+ const text = getMessageText(message);
1196
+ return text;
1197
+ },
1198
+ onSend: (text) => {
1199
+ const { message, parentId } = useMessage.getState();
1200
+ if (message.role !== "user")
1201
+ throw new Error(
1202
+ "Tried to edit a non-user message. Editing is only supported for user messages. This is likely an internal bug in assistant-ui."
1203
+ );
1204
+ const nonTextParts = message.content.filter(
1205
+ (part) => part.type !== "text" && part.type !== "ui"
1206
+ );
1207
+ useThreadActions.getState().append({
1208
+ parentId,
1209
+ role: "user",
1210
+ content: [{ type: "text", text }, ...nonTextParts]
1211
+ });
1212
+ }
1213
+ });
1214
+ syncMessage(
1215
+ useThread.getState(),
1216
+ useThreadActions.getState().getBranches,
1217
+ useMessage,
1218
+ messageIndex
927
1219
  );
928
- syncContentPart(useMessage.getState(), useContentPart, partIndex);
929
- return { useContentPart };
1220
+ return { useMessage, useMessageUtils, useEditComposer };
930
1221
  });
931
- useEffect4(() => {
932
- syncContentPart(useMessage.getState(), context.useContentPart, partIndex);
933
- return useMessage.subscribe((message) => {
934
- syncContentPart(message, context.useContentPart, partIndex);
1222
+ useEffect5(() => {
1223
+ return useThread.subscribe((thread) => {
1224
+ syncMessage(
1225
+ thread,
1226
+ useThreadActions.getState().getBranches,
1227
+ context.useMessage,
1228
+ messageIndex
1229
+ );
935
1230
  });
936
- }, [context, useMessage, partIndex]);
1231
+ }, [useThread, useThreadActions, context, messageIndex]);
937
1232
  return context;
938
1233
  };
939
- var ContentPartProvider = ({
940
- partIndex,
1234
+ var MessageProvider = ({
1235
+ messageIndex,
941
1236
  children
942
1237
  }) => {
943
- const context = useContentPartContext2(partIndex);
944
- return /* @__PURE__ */ jsx10(ContentPartContext.Provider, { value: context, children });
945
- };
946
-
947
- // src/primitives/contentPart/ContentPartDisplay.tsx
948
- var ContentPartDisplay = () => {
949
- const display = useContentPartDisplay();
950
- return display ?? null;
951
- };
952
-
953
- // src/primitives/contentPart/ContentPartInProgressIndicator.tsx
954
- var ContentPartInProgressIndicator = () => {
955
- const indicator = useContentPartInProgressIndicator();
956
- return indicator;
1238
+ const context = useMessageContext2(messageIndex);
1239
+ return /* @__PURE__ */ jsx20(MessageContext.Provider, { value: context, children });
957
1240
  };
958
1241
 
959
- // src/primitives/contentPart/ContentPartText.tsx
960
- import { Primitive as Primitive7 } from "@radix-ui/react-primitive";
961
- import { forwardRef as forwardRef8 } from "react";
962
- import { jsx as jsx11 } from "react/jsx-runtime";
963
- var ContentPartText = forwardRef8((props, forwardedRef) => {
964
- const text = useContentPartText();
965
- return /* @__PURE__ */ jsx11(Primitive7.span, { ...props, ref: forwardedRef, children: text });
966
- });
967
- ContentPartText.displayName = "ContentPartText";
968
-
969
- // src/primitives/message/MessageContent.tsx
970
- import { Fragment, jsx as jsx12, jsxs as jsxs2 } from "react/jsx-runtime";
971
- var defaultComponents = {
972
- Text: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
973
- /* @__PURE__ */ jsx12(ContentPartText, {}),
974
- /* @__PURE__ */ jsx12(ContentPartInProgressIndicator, {})
975
- ] }),
976
- Image: () => null,
977
- UI: () => /* @__PURE__ */ jsx12(ContentPartDisplay, {}),
978
- tools: {
979
- Fallback: (props) => {
980
- const { useToolUIs } = useAssistantContext();
981
- const Render = useToolUIs((s) => s.getToolUI(props.part.toolName));
982
- if (!Render) return null;
983
- return /* @__PURE__ */ jsx12(Render, { ...props });
984
- }
985
- }
986
- };
987
- var MessageContentPartComponent = ({
988
- components: {
989
- Text = defaultComponents.Text,
990
- Image = defaultComponents.Image,
991
- UI = defaultComponents.UI,
992
- tools: { by_name = {}, Fallback = defaultComponents.tools.Fallback } = {}
993
- } = {}
994
- }) => {
995
- const { useThreadActions } = useThreadContext();
996
- const addToolResult = useThreadActions((t) => t.addToolResult);
997
- const { useContentPart } = useContentPartContext();
998
- const { part, status } = useContentPart();
999
- const type = part.type;
1000
- switch (type) {
1001
- case "text":
1002
- return /* @__PURE__ */ jsx12(Text, { part, status });
1003
- case "image":
1004
- return /* @__PURE__ */ jsx12(Image, { part, status });
1005
- case "ui":
1006
- return /* @__PURE__ */ jsx12(UI, { part, status });
1007
- case "tool-call": {
1008
- const Tool = by_name[part.toolName] || Fallback;
1009
- const addResult = (result) => addToolResult(part.toolCallId, result);
1010
- return /* @__PURE__ */ jsx12(Tool, { part, status, addResult });
1011
- }
1012
- default:
1013
- throw new Error(`Unknown content part type: ${type}`);
1014
- }
1242
+ // src/primitives/thread/ThreadMessages.tsx
1243
+ import { jsx as jsx21, jsxs as jsxs2 } from "react/jsx-runtime";
1244
+ var getComponents = (components) => {
1245
+ return {
1246
+ EditComposer: components.EditComposer ?? components.UserMessage ?? components.Message,
1247
+ UserMessage: components.UserMessage ?? components.Message,
1248
+ AssistantMessage: components.AssistantMessage ?? components.Message
1249
+ };
1015
1250
  };
1016
- var MessageContentPartImpl = ({
1017
- partIndex,
1251
+ var ThreadMessageImpl = ({
1252
+ messageIndex,
1018
1253
  components
1019
1254
  }) => {
1020
- return /* @__PURE__ */ jsx12(ContentPartProvider, { partIndex, children: /* @__PURE__ */ jsx12(MessageContentPartComponent, { components }) });
1255
+ const { UserMessage, EditComposer, AssistantMessage } = getComponents(components);
1256
+ return /* @__PURE__ */ jsxs2(MessageProvider, { messageIndex, children: [
1257
+ /* @__PURE__ */ jsxs2(MessageIf, { user: true, children: [
1258
+ /* @__PURE__ */ jsx21(ComposerIf, { editing: false, children: /* @__PURE__ */ jsx21(UserMessage, {}) }),
1259
+ /* @__PURE__ */ jsx21(ComposerIf, { editing: true, children: /* @__PURE__ */ jsx21(EditComposer, {}) })
1260
+ ] }),
1261
+ /* @__PURE__ */ jsx21(MessageIf, { assistant: true, children: /* @__PURE__ */ jsx21(AssistantMessage, {}) })
1262
+ ] });
1021
1263
  };
1022
- var MessageContentPart = memo2(
1023
- MessageContentPartImpl,
1024
- (prev, next) => prev.partIndex === next.partIndex && prev.components?.Text === next.components?.Text && prev.components?.Image === next.components?.Image && prev.components?.UI === next.components?.UI && prev.components?.tools === next.components?.tools
1264
+ var ThreadMessage = memo2(
1265
+ ThreadMessageImpl,
1266
+ (prev, next) => prev.messageIndex === next.messageIndex && prev.components.UserMessage === next.components.UserMessage && prev.components.EditComposer === next.components.EditComposer && prev.components.AssistantMessage === next.components.AssistantMessage
1025
1267
  );
1026
- var MessageContent = ({ components }) => {
1027
- const { useMessage } = useMessageContext();
1028
- const contentLength = useMessage((s) => s.message.content.length);
1029
- return new Array(contentLength).fill(null).map((_, idx) => {
1030
- const partIndex = idx;
1031
- return /* @__PURE__ */ jsx12(
1032
- MessageContentPart,
1268
+ var ThreadMessages = ({ components }) => {
1269
+ const { useThread } = useThreadContext();
1270
+ const messagesLength = useThread((t) => t.messages.length);
1271
+ if (messagesLength === 0) return null;
1272
+ return new Array(messagesLength).fill(null).map((_, idx) => {
1273
+ const messageIndex = idx;
1274
+ return /* @__PURE__ */ jsx21(
1275
+ ThreadMessage,
1033
1276
  {
1034
- partIndex,
1277
+ messageIndex,
1035
1278
  components
1036
1279
  },
1037
- partIndex
1280
+ messageIndex
1038
1281
  );
1039
1282
  });
1040
1283
  };
1041
1284
 
1042
- // src/primitives/message/MessageInProgress.tsx
1043
- import { Primitive as Primitive8 } from "@radix-ui/react-primitive";
1044
- import {
1045
- forwardRef as forwardRef9,
1046
- useMemo as useMemo2
1047
- } from "react";
1048
- import { jsx as jsx13 } from "react/jsx-runtime";
1049
- var MessageInProgress = forwardRef9((props, ref) => {
1050
- const { useMessageUtils } = useMessageContext();
1051
- useMemo2(() => {
1052
- useMessageUtils.getState().setInProgressIndicator(/* @__PURE__ */ jsx13(Primitive8.span, { ...props, ref }));
1053
- }, [useMessageUtils, props, ref]);
1054
- return null;
1055
- });
1056
- MessageInProgress.displayName = "MessageInProgress";
1057
-
1058
- // src/primitives/branchPicker/index.ts
1059
- var branchPicker_exports = {};
1060
- __export(branchPicker_exports, {
1061
- Count: () => BranchPickerCount,
1062
- Next: () => BranchPickerNext,
1063
- Number: () => BranchPickerNumber,
1064
- Previous: () => BranchPickerPrevious,
1065
- Root: () => BranchPickerRoot
1066
- });
1067
-
1068
- // src/primitives/branchPicker/BranchPickerNext.tsx
1069
- var BranchPickerNext = createActionButton(
1070
- "BranchPickerNext",
1071
- useBranchPickerNext
1072
- );
1073
-
1074
- // src/primitives/branchPicker/BranchPickerPrevious.tsx
1075
- var BranchPickerPrevious = createActionButton(
1076
- "BranchPickerPrevious",
1077
- useBranchPickerPrevious
1078
- );
1079
-
1080
- // src/primitives/branchPicker/BranchPickerCount.tsx
1081
- import { Fragment as Fragment2, jsx as jsx14 } from "react/jsx-runtime";
1082
- var BranchPickerCount = () => {
1083
- const branchCount = useBranchPickerCount();
1084
- return /* @__PURE__ */ jsx14(Fragment2, { children: branchCount });
1085
- };
1086
-
1087
- // src/primitives/branchPicker/BranchPickerNumber.tsx
1088
- import { Fragment as Fragment3, jsx as jsx15 } from "react/jsx-runtime";
1089
- var BranchPickerNumber = () => {
1090
- const branchNumber = useBranchPickerNumber();
1091
- return /* @__PURE__ */ jsx15(Fragment3, { children: branchNumber });
1092
- };
1093
-
1094
- // src/primitives/branchPicker/BranchPickerRoot.tsx
1095
- import { Primitive as Primitive9 } from "@radix-ui/react-primitive";
1096
- import { forwardRef as forwardRef10 } from "react";
1097
- import { jsx as jsx16 } from "react/jsx-runtime";
1098
- var BranchPickerRoot = forwardRef10(({ hideWhenSingleBranch, ...rest }, ref) => {
1099
- return /* @__PURE__ */ jsx16(MessageIf, { hasBranches: hideWhenSingleBranch ? true : void 0, children: /* @__PURE__ */ jsx16(Primitive9.div, { ...rest, ref }) });
1100
- });
1101
- BranchPickerRoot.displayName = "BranchPickerRoot";
1102
-
1103
- // src/primitives/actionBar/index.ts
1104
- var actionBar_exports = {};
1105
- __export(actionBar_exports, {
1106
- Copy: () => ActionBarCopy,
1107
- Edit: () => ActionBarEdit,
1108
- Reload: () => ActionBarReload,
1109
- Root: () => ActionBarRoot
1110
- });
1111
-
1112
- // src/primitives/actionBar/ActionBarRoot.tsx
1113
- import { Primitive as Primitive10 } from "@radix-ui/react-primitive";
1114
- import { forwardRef as forwardRef11 } from "react";
1115
- import { jsx as jsx17 } from "react/jsx-runtime";
1116
- var useActionBarFloatStatus = ({
1117
- hideWhenRunning,
1118
- autohide,
1119
- autohideFloat
1120
- }) => {
1121
- const { useThread } = useThreadContext();
1122
- const { useMessage, useMessageUtils } = useMessageContext();
1123
- return useCombinedStore(
1124
- [useThread, useMessage, useMessageUtils],
1125
- (t, m, mu) => {
1126
- if (hideWhenRunning && t.isRunning) return "hidden" /* Hidden */;
1127
- const autohideEnabled = autohide === "always" || autohide === "not-last" && !m.isLast;
1128
- if (!autohideEnabled) return "normal" /* Normal */;
1129
- if (!mu.isHovering) return "hidden" /* Hidden */;
1130
- if (autohideFloat === "always" || autohideFloat === "single-branch" && m.branches.length <= 1)
1131
- return "floating" /* Floating */;
1132
- return "normal" /* Normal */;
1133
- }
1134
- );
1135
- };
1136
- var ActionBarRoot = forwardRef11(({ hideWhenRunning, autohide, autohideFloat, ...rest }, ref) => {
1137
- const hideAndfloatStatus = useActionBarFloatStatus({
1138
- hideWhenRunning,
1139
- autohide,
1140
- autohideFloat
1141
- });
1142
- if (hideAndfloatStatus === "hidden" /* Hidden */) return null;
1143
- return /* @__PURE__ */ jsx17(
1144
- Primitive10.div,
1145
- {
1146
- ...hideAndfloatStatus === "floating" /* Floating */ ? { "data-floating": "true" } : null,
1147
- ...rest,
1148
- ref
1149
- }
1150
- );
1151
- });
1152
- ActionBarRoot.displayName = "ActionBarRoot";
1153
-
1154
- // src/primitives/actionBar/ActionBarCopy.tsx
1155
- var ActionBarCopy = createActionButton(
1156
- "ActionBarCopy",
1157
- useActionBarCopy
1158
- );
1159
-
1160
- // src/primitives/actionBar/ActionBarReload.tsx
1161
- var ActionBarReload = createActionButton(
1162
- "ActionBarReload",
1163
- useActionBarReload
1285
+ // src/primitives/thread/ThreadScrollToBottom.tsx
1286
+ var ThreadScrollToBottom = createActionButton(
1287
+ "ThreadScrollToBottom",
1288
+ useThreadScrollToBottom
1164
1289
  );
1165
1290
 
1166
- // src/primitives/actionBar/ActionBarEdit.tsx
1167
- var ActionBarEdit = createActionButton(
1168
- "ActionBarEdit",
1169
- useActionBarEdit
1291
+ // src/primitives/thread/ThreadSuggestion.tsx
1292
+ var ThreadSuggestion = createActionButton(
1293
+ "ThreadSuggestion",
1294
+ useThreadSuggestion
1170
1295
  );
1171
1296
 
1172
- // src/primitives/contentPart/index.ts
1173
- var contentPart_exports = {};
1174
- __export(contentPart_exports, {
1175
- Display: () => ContentPartDisplay,
1176
- Image: () => ContentPartImage,
1177
- InProgressIndicator: () => ContentPartInProgressIndicator,
1178
- Text: () => ContentPartText
1179
- });
1180
-
1181
- // src/primitives/contentPart/ContentPartImage.tsx
1182
- import { Primitive as Primitive11 } from "@radix-ui/react-primitive";
1183
- import { forwardRef as forwardRef12 } from "react";
1184
- import { jsx as jsx18 } from "react/jsx-runtime";
1185
- var ContentPartImage = forwardRef12((props, forwardedRef) => {
1186
- const image = useContentPartImage();
1187
- return /* @__PURE__ */ jsx18(Primitive11.img, { src: image, ...props, ref: forwardedRef });
1188
- });
1189
- ContentPartImage.displayName = "ContentPartImage";
1190
-
1191
1297
  // src/runtime/local/useLocalRuntime.tsx
1192
- import { useInsertionEffect, useState as useState3 } from "react";
1298
+ import { useInsertionEffect, useState as useState4 } from "react";
1193
1299
 
1194
1300
  // src/utils/ModelConfigTypes.ts
1195
1301
  var mergeModelConfigs = (configSet) => {
@@ -1475,7 +1581,7 @@ var LocalRuntime = class {
1475
1581
 
1476
1582
  // src/runtime/local/useLocalRuntime.tsx
1477
1583
  var useLocalRuntime = (adapter) => {
1478
- const [runtime] = useState3(() => new LocalRuntime(adapter));
1584
+ const [runtime] = useState4(() => new LocalRuntime(adapter));
1479
1585
  useInsertionEffect(() => {
1480
1586
  runtime.adapter = adapter;
1481
1587
  });
@@ -1486,7 +1592,7 @@ var useLocalRuntime = (adapter) => {
1486
1592
  import { memo as memo3 } from "react";
1487
1593
 
1488
1594
  // src/context/providers/AssistantProvider.tsx
1489
- import { useEffect as useEffect6, useInsertionEffect as useInsertionEffect3, useRef as useRef5, useState as useState5 } from "react";
1595
+ import { useEffect as useEffect7, useInsertionEffect as useInsertionEffect3, useRef as useRef5, useState as useState6 } from "react";
1490
1596
 
1491
1597
  // src/context/stores/AssistantModelConfig.ts
1492
1598
  import { create as create5 } from "zustand";
@@ -1551,31 +1657,45 @@ var makeAssistantToolUIsStore = () => create6((set) => {
1551
1657
  });
1552
1658
 
1553
1659
  // src/context/providers/ThreadProvider.tsx
1554
- import { useEffect as useEffect5, useInsertionEffect as useInsertionEffect2, useRef as useRef4, useState as useState4 } from "react";
1660
+ import { useEffect as useEffect6, useInsertionEffect as useInsertionEffect2, useRef as useRef4, useState as useState5 } from "react";
1555
1661
 
1556
1662
  // src/context/stores/Composer.ts
1557
1663
  import { create as create7 } from "zustand";
1558
- var makeComposerStore = (useThread, useThreadActions) => create7()((set, get, store) => {
1559
- return {
1560
- ...makeBaseComposer(set, get, store),
1561
- isEditing: true,
1562
- send: () => {
1563
- const { setValue, value } = get();
1564
- setValue("");
1565
- useThreadActions.getState().append({
1566
- parentId: useThread.getState().messages.at(-1)?.id ?? null,
1567
- role: "user",
1568
- content: [{ type: "text", text: value }]
1569
- });
1570
- },
1571
- cancel: () => {
1572
- const thread = useThread.getState();
1573
- if (!thread.isRunning) return false;
1574
- useThreadActions.getState().cancelRun();
1575
- return true;
1576
- }
1577
- };
1578
- });
1664
+ var makeComposerStore = (useThread, useThreadActions) => {
1665
+ const focusListeners = /* @__PURE__ */ new Set();
1666
+ return create7()((set, get, store) => {
1667
+ return {
1668
+ ...makeBaseComposer(set, get, store),
1669
+ isEditing: true,
1670
+ send: () => {
1671
+ const { setValue, value } = get();
1672
+ setValue("");
1673
+ useThreadActions.getState().append({
1674
+ parentId: useThread.getState().messages.at(-1)?.id ?? null,
1675
+ role: "user",
1676
+ content: [{ type: "text", text: value }]
1677
+ });
1678
+ },
1679
+ cancel: () => {
1680
+ const thread = useThread.getState();
1681
+ if (!thread.isRunning) return false;
1682
+ useThreadActions.getState().cancelRun();
1683
+ return true;
1684
+ },
1685
+ focus: () => {
1686
+ for (const listener of focusListeners) {
1687
+ listener();
1688
+ }
1689
+ },
1690
+ onFocus: (listener) => {
1691
+ focusListeners.add(listener);
1692
+ return () => {
1693
+ focusListeners.delete(listener);
1694
+ };
1695
+ }
1696
+ };
1697
+ });
1698
+ };
1579
1699
 
1580
1700
  // src/context/stores/Thread.ts
1581
1701
  import { create as create8 } from "zustand";
@@ -1622,7 +1742,7 @@ var makeThreadActionStore = (runtimeRef) => {
1622
1742
  };
1623
1743
 
1624
1744
  // src/context/providers/ThreadProvider.tsx
1625
- import { jsx as jsx19, jsxs as jsxs3 } from "react/jsx-runtime";
1745
+ import { jsx as jsx22, jsxs as jsxs3 } from "react/jsx-runtime";
1626
1746
  var ThreadProvider = ({
1627
1747
  children,
1628
1748
  runtime
@@ -1631,7 +1751,7 @@ var ThreadProvider = ({
1631
1751
  useInsertionEffect2(() => {
1632
1752
  runtimeRef.current = runtime;
1633
1753
  });
1634
- const [context] = useState4(() => {
1754
+ const [context] = useState5(() => {
1635
1755
  const useThread = makeThreadStore(runtimeRef);
1636
1756
  const useThreadActions = makeThreadActionStore(runtimeRef);
1637
1757
  const useViewport = makeThreadViewportStore();
@@ -1643,7 +1763,7 @@ var ThreadProvider = ({
1643
1763
  useViewport
1644
1764
  };
1645
1765
  });
1646
- useEffect5(() => {
1766
+ useEffect6(() => {
1647
1767
  const onRuntimeUpdate = () => {
1648
1768
  context.useThread.setState(
1649
1769
  Object.freeze({
@@ -1658,34 +1778,34 @@ var ThreadProvider = ({
1658
1778
  }, [context, runtime]);
1659
1779
  const RuntimeSynchronizer = runtime.unstable_synchronizer;
1660
1780
  return /* @__PURE__ */ jsxs3(ThreadContext.Provider, { value: context, children: [
1661
- RuntimeSynchronizer && /* @__PURE__ */ jsx19(RuntimeSynchronizer, {}),
1781
+ RuntimeSynchronizer && /* @__PURE__ */ jsx22(RuntimeSynchronizer, {}),
1662
1782
  children
1663
1783
  ] });
1664
1784
  };
1665
1785
 
1666
1786
  // src/context/providers/AssistantProvider.tsx
1667
- import { jsx as jsx20 } from "react/jsx-runtime";
1787
+ import { jsx as jsx23 } from "react/jsx-runtime";
1668
1788
  var AssistantProvider = ({ children, runtime }) => {
1669
1789
  const runtimeRef = useRef5(runtime);
1670
1790
  useInsertionEffect3(() => {
1671
1791
  runtimeRef.current = runtime;
1672
1792
  });
1673
- const [context] = useState5(() => {
1793
+ const [context] = useState6(() => {
1674
1794
  const useModelConfig = makeAssistantModelConfigStore();
1675
1795
  const useToolUIs = makeAssistantToolUIsStore();
1676
1796
  return { useModelConfig, useToolUIs };
1677
1797
  });
1678
1798
  const getModelCOnfig = context.useModelConfig((c) => c.getModelConfig);
1679
- useEffect6(() => {
1799
+ useEffect7(() => {
1680
1800
  return runtime.registerModelConfigProvider(getModelCOnfig);
1681
1801
  }, [runtime, getModelCOnfig]);
1682
- return /* @__PURE__ */ jsx20(AssistantContext.Provider, { value: context, children: /* @__PURE__ */ jsx20(ThreadProvider, { runtime, children }) });
1802
+ return /* @__PURE__ */ jsx23(AssistantContext.Provider, { value: context, children: /* @__PURE__ */ jsx23(ThreadProvider, { runtime, children }) });
1683
1803
  };
1684
1804
 
1685
1805
  // src/context/providers/AssistantRuntimeProvider.tsx
1686
- import { jsx as jsx21 } from "react/jsx-runtime";
1806
+ import { jsx as jsx24 } from "react/jsx-runtime";
1687
1807
  var AssistantRuntimeProviderImpl = ({ children, runtime }) => {
1688
- return /* @__PURE__ */ jsx21(AssistantProvider, { runtime, children });
1808
+ return /* @__PURE__ */ jsx24(AssistantProvider, { runtime, children });
1689
1809
  };
1690
1810
  var AssistantRuntimeProvider = memo3(AssistantRuntimeProviderImpl);
1691
1811
 
@@ -1697,6 +1817,7 @@ __export(internal_exports, {
1697
1817
  });
1698
1818
  export {
1699
1819
  actionBar_exports as ActionBarPrimitive,
1820
+ assistantModal_exports as AssistantModalPrimitive,
1700
1821
  AssistantRuntimeProvider,
1701
1822
  branchPicker_exports as BranchPickerPrimitive,
1702
1823
  composer_exports as ComposerPrimitive,