@assistant-ui/react 0.1.11 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -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,