@assistant-ui/react 0.5.77 → 0.5.78

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
@@ -412,15 +412,11 @@ function useComposerRuntime(options) {
412
412
  import { useCallback } from "react";
413
413
  var useAppendMessage = () => {
414
414
  const threadRuntime = useThreadRuntime();
415
- const threadViewportStore = useThreadViewportStore();
416
- const threadComposerStore = useThreadComposerStore();
417
415
  const append = useCallback(
418
416
  (message) => {
419
417
  threadRuntime.append(message);
420
- threadViewportStore.getState().scrollToBottom();
421
- threadComposerStore.getState().focus();
422
418
  },
423
- [threadRuntime, threadViewportStore, threadComposerStore]
419
+ [threadRuntime]
424
420
  );
425
421
  return append;
426
422
  };
@@ -429,11 +425,9 @@ var useAppendMessage = () => {
429
425
  import { useCallback as useCallback2 } from "react";
430
426
  var useSwitchToNewThread = () => {
431
427
  const assistantRuntime = useAssistantRuntime();
432
- const threadComposerStore = useThreadComposerStore();
433
428
  const switchToNewThread = useCallback2(() => {
434
429
  assistantRuntime.switchToNewThread();
435
- threadComposerStore.getState().focus();
436
- }, [assistantRuntime, threadComposerStore]);
430
+ }, [assistantRuntime]);
437
431
  return switchToNewThread;
438
432
  };
439
433
 
@@ -546,25 +540,25 @@ var getThreadMessageText = (message) => {
546
540
  var useActionBarCopy = ({
547
541
  copiedDuration = 3e3
548
542
  } = {}) => {
549
- const messageStore = useMessageStore();
543
+ const messageRuntime = useMessageRuntime();
544
+ const composerRuntime = useComposerRuntime();
550
545
  const messageUtilsStore = useMessageUtilsStore();
551
- const editComposerStore = useEditComposerStore();
552
546
  const hasCopyableContent = useCombinedStore(
553
- [messageStore, editComposerStore],
547
+ [messageRuntime, composerRuntime],
554
548
  (message, c) => {
555
549
  return !c.isEditing && (message.role !== "assistant" || message.status.type !== "running") && message.content.some((c2) => c2.type === "text" && c2.text.length > 0);
556
550
  }
557
551
  );
558
552
  const callback = useCallback3(() => {
559
- const message = messageStore.getState();
553
+ const message = messageRuntime.getState();
560
554
  const { setIsCopied } = messageUtilsStore.getState();
561
- const { isEditing, text: composerValue } = editComposerStore.getState();
555
+ const { isEditing, text: composerValue } = composerRuntime.getState();
562
556
  const valueToCopy = isEditing ? composerValue : getThreadMessageText(message);
563
557
  navigator.clipboard.writeText(valueToCopy).then(() => {
564
558
  setIsCopied(true);
565
559
  setTimeout(() => setIsCopied(false), copiedDuration);
566
560
  });
567
- }, [messageStore, messageUtilsStore, editComposerStore, copiedDuration]);
561
+ }, [messageRuntime, messageUtilsStore, composerRuntime, copiedDuration]);
568
562
  if (!hasCopyableContent) return null;
569
563
  return callback;
570
564
  };
@@ -584,20 +578,15 @@ var useActionBarEdit = () => {
584
578
  // src/primitive-hooks/actionBar/useActionBarReload.tsx
585
579
  import { useCallback as useCallback5 } from "react";
586
580
  var useActionBarReload = () => {
587
- const messageStore = useMessageStore();
588
- const threadStore = useThreadStore();
589
581
  const messageRuntime = useMessageRuntime();
590
- const threadComposerStore = useThreadComposerStore();
591
- const threadViewportStore = useThreadViewportStore();
582
+ const threadRuntime = useThreadRuntime();
592
583
  const disabled = useCombinedStore(
593
- [threadStore, messageStore],
584
+ [threadRuntime, messageRuntime],
594
585
  (t, m) => t.isRunning || t.isDisabled || m.role !== "assistant"
595
586
  );
596
587
  const callback = useCallback5(() => {
597
588
  messageRuntime.reload();
598
- threadViewportStore.getState().scrollToBottom();
599
- threadComposerStore.getState().focus();
600
- }, [messageRuntime, threadComposerStore, threadViewportStore]);
589
+ }, [messageRuntime]);
601
590
  if (disabled) return null;
602
591
  return callback;
603
592
  };
@@ -687,12 +676,11 @@ var useBranchPickerPrevious = () => {
687
676
  // src/primitive-hooks/composer/useComposerCancel.tsx
688
677
  import { useCallback as useCallback12 } from "react";
689
678
  var useComposerCancel = () => {
690
- const composerStore = useComposerStore();
679
+ const composerRuntime = useComposerRuntime();
691
680
  const disabled = useComposer((c) => !c.canCancel);
692
681
  const callback = useCallback12(() => {
693
- const { cancel } = composerStore.getState();
694
- cancel();
695
- }, [composerStore]);
682
+ composerRuntime.cancel();
683
+ }, [composerRuntime]);
696
684
  if (disabled) return null;
697
685
  return callback;
698
686
  };
@@ -709,21 +697,16 @@ var useComposerIf = (props) => {
709
697
  // src/primitive-hooks/composer/useComposerSend.tsx
710
698
  import { useCallback as useCallback13 } from "react";
711
699
  var useComposerSend = () => {
712
- const threadStore = useThreadStore();
713
- const threadViewportStore = useThreadViewportStore();
714
- const composerStore = useComposerStore();
715
- const threadComposerStore = useThreadComposerStore();
700
+ const composerRuntime = useComposerRuntime();
701
+ const threadRuntime = useThreadRuntime();
716
702
  const disabled = useCombinedStore(
717
- [threadStore, composerStore],
703
+ [threadRuntime, composerRuntime],
718
704
  (t, c) => t.isRunning || !c.isEditing || c.isEmpty
719
705
  );
720
706
  const callback = useCallback13(() => {
721
- const composerState = composerStore.getState();
722
- if (!composerState.isEditing) return;
723
- composerState.send();
724
- threadViewportStore.getState().scrollToBottom();
725
- threadComposerStore.getState().focus();
726
- }, [threadComposerStore, composerStore, threadViewportStore]);
707
+ if (!composerRuntime.getState().isEditing) return;
708
+ composerRuntime.send();
709
+ }, [threadRuntime]);
727
710
  if (disabled) return null;
728
711
  return callback;
729
712
  };
@@ -789,10 +772,10 @@ var useContentPartText = () => {
789
772
 
790
773
  // src/primitive-hooks/message/useMessageIf.tsx
791
774
  var useMessageIf = (props) => {
792
- const messageStore = useMessageStore();
775
+ const messageRuntime = useMessageRuntime();
793
776
  const messageUtilsStore = useMessageUtilsStore();
794
777
  return useCombinedStore(
795
- [messageStore, messageUtilsStore],
778
+ [messageRuntime, messageUtilsStore],
796
779
  ({
797
780
  role,
798
781
  attachments,
@@ -847,11 +830,9 @@ import { useCallback as useCallback15 } from "react";
847
830
  var useThreadScrollToBottom = () => {
848
831
  const isAtBottom = useThreadViewport((s) => s.isAtBottom);
849
832
  const threadViewportStore = useThreadViewportStore();
850
- const threadComposerStore = useThreadComposerStore();
851
833
  const handleScrollToBottom = useCallback15(() => {
852
834
  threadViewportStore.getState().scrollToBottom();
853
- threadComposerStore.getState().focus();
854
- }, [threadViewportStore, threadComposerStore]);
835
+ }, [threadViewportStore]);
855
836
  if (isAtBottom) return null;
856
837
  return handleScrollToBottom;
857
838
  };
@@ -862,20 +843,16 @@ var useThreadSuggestion = ({
862
843
  prompt,
863
844
  autoSend
864
845
  }) => {
865
- const threadStore = useThreadStore();
866
- const composerStore = useThreadComposerStore();
867
- const append = useAppendMessage();
846
+ const threadRuntime = useThreadRuntime();
868
847
  const disabled = useThread((t) => t.isDisabled);
869
848
  const callback = useCallback16(() => {
870
- const thread = threadStore.getState();
871
- const composer = composerStore.getState();
872
- if (autoSend && !thread.isRunning) {
873
- append(prompt);
874
- composer.setText("");
849
+ if (autoSend && !threadRuntime.getState().isRunning) {
850
+ threadRuntime.append(prompt);
851
+ threadRuntime.composer.setText("");
875
852
  } else {
876
- composer.setText(prompt);
853
+ threadRuntime.composer.setText(prompt);
877
854
  }
878
- }, [threadStore, composerStore, autoSend, append, prompt]);
855
+ }, [threadRuntime, autoSend, prompt]);
879
856
  if (disabled) return null;
880
857
  return callback;
881
858
  };
@@ -903,11 +880,11 @@ var useActionBarFloatStatus = ({
903
880
  autohide,
904
881
  autohideFloat
905
882
  }) => {
906
- const threadStore = useThreadStore();
907
- const messageStore = useMessageStore();
883
+ const threadRuntime = useThreadRuntime();
884
+ const messageRuntime = useMessageRuntime();
908
885
  const messageUtilsStore = useMessageUtilsStore();
909
886
  return useCombinedStore(
910
- [threadStore, messageStore, messageUtilsStore],
887
+ [threadRuntime, messageRuntime, messageUtilsStore],
911
888
  (t, m, mu) => {
912
889
  if (hideWhenRunning && t.isRunning) return "hidden" /* Hidden */;
913
890
  const autohideEnabled = autohide === "always" || autohide === "not-last" && !m.isLast;
@@ -1107,46 +1084,44 @@ __export(assistantModal_exports, {
1107
1084
  });
1108
1085
 
1109
1086
  // src/primitives/assistantModal/AssistantModalRoot.tsx
1110
- import { useState as useState4 } from "react";
1087
+ import { useEffect as useEffect7, useState as useState4 } from "react";
1111
1088
  import * as PopoverPrimitive2 from "@radix-ui/react-popover";
1112
1089
  import { composeEventHandlers as composeEventHandlers6 } from "@radix-ui/primitive";
1113
1090
 
1114
- // src/utils/hooks/useOnComposerFocus.tsx
1115
- import { useCallbackRef } from "@radix-ui/react-use-callback-ref";
1116
- import { useEffect as useEffect7 } from "react";
1117
- var useOnComposerFocus = (callback) => {
1118
- const callbackRef = useCallbackRef(callback);
1119
- const threadComposerStore = useThreadComposerStore();
1120
- useEffect7(() => {
1121
- return threadComposerStore.getState().onFocus(() => {
1122
- callbackRef();
1123
- });
1124
- }, [threadComposerStore, callbackRef]);
1125
- };
1126
-
1127
1091
  // src/primitives/assistantModal/scope.tsx
1128
1092
  import * as PopoverPrimitive from "@radix-ui/react-popover";
1129
1093
  var usePopoverScope = PopoverPrimitive.createPopoverScope();
1130
1094
 
1131
1095
  // src/primitives/assistantModal/AssistantModalRoot.tsx
1132
1096
  import { jsx as jsx10 } from "react/jsx-runtime";
1133
- var useAssistantModalOpenState = (defaultOpen = false) => {
1097
+ var useAssistantModalOpenState = ({
1098
+ defaultOpen = false,
1099
+ unstable_openOnRunStart = true
1100
+ }) => {
1134
1101
  const state = useState4(defaultOpen);
1135
1102
  const [, setOpen] = state;
1136
- useOnComposerFocus(() => {
1137
- setOpen(true);
1138
- });
1103
+ const threadRuntime = useThreadRuntime();
1104
+ useEffect7(() => {
1105
+ if (!unstable_openOnRunStart) return void 0;
1106
+ return threadRuntime.unstable_on("run-start", () => {
1107
+ setOpen(true);
1108
+ });
1109
+ }, [unstable_openOnRunStart]);
1139
1110
  return state;
1140
1111
  };
1141
1112
  var AssistantModalPrimitiveRoot = ({
1142
1113
  __scopeAssistantModal,
1143
1114
  defaultOpen,
1115
+ unstable_openOnRunStart,
1144
1116
  open,
1145
1117
  onOpenChange,
1146
1118
  ...rest
1147
1119
  }) => {
1148
1120
  const scope = usePopoverScope(__scopeAssistantModal);
1149
- const [modalOpen, setOpen] = useAssistantModalOpenState(defaultOpen);
1121
+ const [modalOpen, setOpen] = useAssistantModalOpenState({
1122
+ defaultOpen,
1123
+ unstable_openOnRunStart
1124
+ });
1150
1125
  return /* @__PURE__ */ jsx10(
1151
1126
  PopoverPrimitive2.Root,
1152
1127
  {
@@ -1497,7 +1472,7 @@ import {
1497
1472
 
1498
1473
  // src/utils/smooth/useSmooth.tsx
1499
1474
  import { useEffect as useEffect9, useMemo as useMemo5, useRef as useRef2, useState as useState7 } from "react";
1500
- import { useCallbackRef as useCallbackRef2 } from "@radix-ui/react-use-callback-ref";
1475
+ import { useCallbackRef } from "@radix-ui/react-use-callback-ref";
1501
1476
 
1502
1477
  // src/utils/smooth/SmoothContext.tsx
1503
1478
  import {
@@ -1599,7 +1574,7 @@ var useSmooth = (state, smooth = false) => {
1599
1574
  const idRef = useRef2(id);
1600
1575
  const [displayedText, setDisplayedText] = useState7(text);
1601
1576
  const smoothStatusStore = useSmoothStatusStore({ optional: true });
1602
- const setText = useCallbackRef2((text2) => {
1577
+ const setText = useCallbackRef((text2) => {
1603
1578
  setDisplayedText(text2);
1604
1579
  if (smoothStatusStore) {
1605
1580
  writableStore(smoothStatusStore).setState(
@@ -1837,7 +1812,7 @@ var METHOD_NOT_SUPPORTED = () => {
1837
1812
  throw new Error("Composer is not available");
1838
1813
  };
1839
1814
  var EMPTY_ARRAY = Object.freeze([]);
1840
- var getThreadComposerState = (runtime, focus, onFocus) => {
1815
+ var getThreadComposerState = (runtime) => {
1841
1816
  return Object.freeze({
1842
1817
  type: "thread",
1843
1818
  isEditing: runtime?.isEditing ?? false,
@@ -1851,8 +1826,6 @@ var getThreadComposerState = (runtime, focus, onFocus) => {
1851
1826
  // edit: beginEdit,
1852
1827
  send: runtime?.send.bind(runtime) ?? METHOD_NOT_SUPPORTED,
1853
1828
  cancel: runtime?.cancel.bind(runtime) ?? METHOD_NOT_SUPPORTED,
1854
- focus,
1855
- onFocus,
1856
1829
  reset: runtime?.reset.bind(runtime) ?? METHOD_NOT_SUPPORTED,
1857
1830
  addAttachment: runtime?.addAttachment.bind(runtime) ?? METHOD_NOT_SUPPORTED,
1858
1831
  removeAttachment: runtime?.removeAttachment.bind(runtime) ?? METHOD_NOT_SUPPORTED
@@ -1969,11 +1942,7 @@ var ThreadComposerRuntimeImpl = class extends ComposerRuntimeImpl {
1969
1942
  _getState;
1970
1943
  constructor(core) {
1971
1944
  const stateBinding = new LazyMemoizeSubject({
1972
- getState: () => getThreadComposerState(
1973
- core.getState(),
1974
- this.focus.bind(this),
1975
- this.onFocus.bind(this)
1976
- ),
1945
+ getState: () => getThreadComposerState(core.getState()),
1977
1946
  subscribe: (callback) => core.subscribe(callback)
1978
1947
  });
1979
1948
  super({
@@ -1988,21 +1957,6 @@ var ThreadComposerRuntimeImpl = class extends ComposerRuntimeImpl {
1988
1957
  getState() {
1989
1958
  return this._getState();
1990
1959
  }
1991
- // TODO replace with events
1992
- _focusListeners = /* @__PURE__ */ new Set();
1993
- /**
1994
- * @deprecated This feature is being removed in 0.6.0. Submit feedback if you need it.
1995
- */
1996
- focus() {
1997
- this._focusListeners.forEach((callback) => callback());
1998
- }
1999
- /**
2000
- * @deprecated This feature is being removed in 0.6.0. Submit feedback if you need it.
2001
- */
2002
- onFocus(callback) {
2003
- this._focusListeners.add(callback);
2004
- return () => this._focusListeners.delete(callback);
2005
- }
2006
1960
  getAttachmentByIndex(idx) {
2007
1961
  return new ThreadComposerAttachmentRuntimeImpl(
2008
1962
  new ShallowMemoizeSubject({
@@ -2080,6 +2034,9 @@ var NestedSubscriptionSubject = class extends BaseSubject {
2080
2034
  getState() {
2081
2035
  return this.binding.getState();
2082
2036
  }
2037
+ outerSubscribe(callback) {
2038
+ return this.binding.subscribe(callback);
2039
+ }
2083
2040
  _connect() {
2084
2041
  const callback = () => {
2085
2042
  this.notifySubscribers();
@@ -2094,7 +2051,7 @@ var NestedSubscriptionSubject = class extends BaseSubject {
2094
2051
  innerUnsubscribe = this.binding.getState()?.subscribe(callback);
2095
2052
  callback();
2096
2053
  };
2097
- const outerUnsubscribe = this.binding.subscribe(onRuntimeUpdate);
2054
+ const outerUnsubscribe = this.outerSubscribe(onRuntimeUpdate);
2098
2055
  return () => {
2099
2056
  outerUnsubscribe?.();
2100
2057
  innerUnsubscribe?.();
@@ -2265,8 +2222,7 @@ var MessageContentPartComponent = ({
2265
2222
  tools: { by_name = {}, Fallback: Fallback2 = void 0 } = {}
2266
2223
  } = {}
2267
2224
  }) => {
2268
- const messageStore = useMessageStore();
2269
- const threadRuntime = useThreadRuntime();
2225
+ const contentPartRuntime = useContentPartRuntime();
2270
2226
  const part = useContentPart();
2271
2227
  const type = part.type;
2272
2228
  switch (type) {
@@ -2287,12 +2243,7 @@ var MessageContentPartComponent = ({
2287
2243
  return /* @__PURE__ */ jsx23(UI, { ...part, part });
2288
2244
  case "tool-call": {
2289
2245
  const Tool = by_name[part.toolName] || Fallback2;
2290
- const addResult = (result) => threadRuntime.addToolResult({
2291
- messageId: messageStore.getState().id,
2292
- toolName: part.toolName,
2293
- toolCallId: part.toolCallId,
2294
- result
2295
- });
2246
+ const addResult = (result) => contentPartRuntime.addToolResult(result);
2296
2247
  return /* @__PURE__ */ jsx23(ToolUIDisplay, { ...part, part, UI: Tool, addResult });
2297
2248
  }
2298
2249
  default:
@@ -2475,11 +2426,24 @@ import { Slot } from "@radix-ui/react-slot";
2475
2426
  import {
2476
2427
  forwardRef as forwardRef18,
2477
2428
  useCallback as useCallback20,
2478
- useEffect as useEffect11,
2429
+ useEffect as useEffect12,
2479
2430
  useRef as useRef3
2480
2431
  } from "react";
2481
2432
  import TextareaAutosize from "react-textarea-autosize";
2482
2433
  import { useEscapeKeydown as useEscapeKeydown2 } from "@radix-ui/react-use-escape-keydown";
2434
+
2435
+ // src/utils/hooks/useOnScrollToBottom.tsx
2436
+ import { useCallbackRef as useCallbackRef2 } from "@radix-ui/react-use-callback-ref";
2437
+ import { useEffect as useEffect11 } from "react";
2438
+ var useOnScrollToBottom = (callback) => {
2439
+ const callbackRef = useCallbackRef2(callback);
2440
+ const onScrollToBottom = useThreadViewport((vp) => vp.onScrollToBottom);
2441
+ useEffect11(() => {
2442
+ return onScrollToBottom(callbackRef);
2443
+ }, [onScrollToBottom, callbackRef]);
2444
+ };
2445
+
2446
+ // src/primitives/composer/ComposerInput.tsx
2483
2447
  import { jsx as jsx28 } from "react/jsx-runtime";
2484
2448
  var ComposerPrimitiveInput = forwardRef18(
2485
2449
  ({
@@ -2490,10 +2454,13 @@ var ComposerPrimitiveInput = forwardRef18(
2490
2454
  onKeyDown,
2491
2455
  submitOnEnter = true,
2492
2456
  cancelOnEscape = true,
2457
+ unstable_focusOnRunStart = true,
2458
+ unstable_focusOnScrollToBottom = true,
2459
+ unstable_focusOnThreadSwitched = true,
2493
2460
  ...rest
2494
2461
  }, forwardedRef) => {
2495
- const threadStore = useThreadStore();
2496
- const composerStore = useComposerStore();
2462
+ const threadRuntime = useThreadRuntime();
2463
+ const composerRuntime = useComposerRuntime();
2497
2464
  const value = useComposer((c) => {
2498
2465
  if (!c.isEditing) return "";
2499
2466
  return c.text;
@@ -2504,9 +2471,8 @@ var ComposerPrimitiveInput = forwardRef18(
2504
2471
  const ref = useComposedRefs2(forwardedRef, textareaRef);
2505
2472
  useEscapeKeydown2((e) => {
2506
2473
  if (!cancelOnEscape) return;
2507
- const composer = composerStore.getState();
2508
- if (composer.canCancel) {
2509
- composer.cancel();
2474
+ if (composerRuntime.getState().canCancel) {
2475
+ composerRuntime.cancel();
2510
2476
  e.preventDefault();
2511
2477
  }
2512
2478
  });
@@ -2514,7 +2480,7 @@ var ComposerPrimitiveInput = forwardRef18(
2514
2480
  if (isDisabled || !submitOnEnter) return;
2515
2481
  if (e.nativeEvent.isComposing) return;
2516
2482
  if (e.key === "Enter" && e.shiftKey === false) {
2517
- const { isRunning } = threadStore.getState();
2483
+ const { isRunning } = threadRuntime.getState();
2518
2484
  if (!isRunning) {
2519
2485
  e.preventDefault();
2520
2486
  textareaRef.current?.closest("form")?.requestSubmit();
@@ -2522,7 +2488,7 @@ var ComposerPrimitiveInput = forwardRef18(
2522
2488
  }
2523
2489
  };
2524
2490
  const autoFocusEnabled = autoFocus && !isDisabled;
2525
- const focus = useCallback20(() => {
2491
+ const focus2 = useCallback20(() => {
2526
2492
  const textarea = textareaRef.current;
2527
2493
  if (!textarea || !autoFocusEnabled) return;
2528
2494
  textarea.focus({ preventScroll: true });
@@ -2531,12 +2497,22 @@ var ComposerPrimitiveInput = forwardRef18(
2531
2497
  textareaRef.current.value.length
2532
2498
  );
2533
2499
  }, [autoFocusEnabled]);
2534
- useEffect11(() => focus(), [focus]);
2535
- useOnComposerFocus(() => {
2536
- if (composerStore.getState().type === "thread") {
2537
- focus();
2500
+ useEffect12(() => focus2(), [focus2]);
2501
+ useOnScrollToBottom(() => {
2502
+ if (composerRuntime.type === "thread" && unstable_focusOnScrollToBottom) {
2503
+ focus2();
2538
2504
  }
2539
2505
  });
2506
+ useEffect12(() => {
2507
+ if (composerRuntime.type !== "thread" || !unstable_focusOnRunStart)
2508
+ return void 0;
2509
+ return threadRuntime.unstable_on("run-start", focus2);
2510
+ }, [unstable_focusOnRunStart]);
2511
+ useEffect12(() => {
2512
+ if (composerRuntime.type !== "thread" || !unstable_focusOnThreadSwitched)
2513
+ return void 0;
2514
+ return threadRuntime.unstable_on("switched-to", focus2);
2515
+ }, [unstable_focusOnThreadSwitched]);
2540
2516
  return /* @__PURE__ */ jsx28(
2541
2517
  Component,
2542
2518
  {
@@ -2546,9 +2522,8 @@ var ComposerPrimitiveInput = forwardRef18(
2546
2522
  ref,
2547
2523
  disabled: isDisabled,
2548
2524
  onChange: composeEventHandlers9(onChange, (e) => {
2549
- const composerState = composerStore.getState();
2550
- if (!composerState.isEditing) return;
2551
- return composerState.setText(e.target.value);
2525
+ if (!composerRuntime.getState().isEditing) return;
2526
+ return composerRuntime.setText(e.target.value);
2552
2527
  }),
2553
2528
  onKeyDown: composeEventHandlers9(onKeyDown, handleKeyPress)
2554
2529
  }
@@ -2690,7 +2665,7 @@ import { forwardRef as forwardRef20 } from "react";
2690
2665
 
2691
2666
  // src/primitive-hooks/thread/useThreadViewportAutoScroll.tsx
2692
2667
  import { useComposedRefs as useComposedRefs3 } from "@radix-ui/react-compose-refs";
2693
- import { useRef as useRef4 } from "react";
2668
+ import { useEffect as useEffect13, useRef as useRef4 } from "react";
2694
2669
 
2695
2670
  // src/utils/hooks/useOnResizeContent.tsx
2696
2671
  import { useCallbackRef as useCallbackRef3 } from "@radix-ui/react-use-callback-ref";
@@ -2732,22 +2707,10 @@ var useOnResizeContent = (callback) => {
2732
2707
  return useManagedRef(refCallback);
2733
2708
  };
2734
2709
 
2735
- // src/utils/hooks/useOnScrollToBottom.tsx
2736
- import { useCallbackRef as useCallbackRef4 } from "@radix-ui/react-use-callback-ref";
2737
- import { useEffect as useEffect12 } from "react";
2738
- var useOnScrollToBottom = (callback) => {
2739
- const callbackRef = useCallbackRef4(callback);
2740
- const threadViewportStore = useThreadViewportStore();
2741
- useEffect12(() => {
2742
- return threadViewportStore.getState().onScrollToBottom(() => {
2743
- callbackRef();
2744
- });
2745
- }, [threadViewportStore, callbackRef]);
2746
- };
2747
-
2748
2710
  // src/primitive-hooks/thread/useThreadViewportAutoScroll.tsx
2749
2711
  var useThreadViewportAutoScroll = ({
2750
- autoScroll = true
2712
+ autoScroll = true,
2713
+ unstable_scrollToBottomOnRunStart = true
2751
2714
  }) => {
2752
2715
  const divRef = useRef4(null);
2753
2716
  const threadViewportStore = useThreadViewportStore();
@@ -2793,6 +2756,11 @@ var useThreadViewportAutoScroll = ({
2793
2756
  useOnScrollToBottom(() => {
2794
2757
  scrollToBottom("auto");
2795
2758
  });
2759
+ const threadRuntime = useThreadRuntime();
2760
+ useEffect13(() => {
2761
+ if (!unstable_scrollToBottomOnRunStart) return void 0;
2762
+ return threadRuntime.unstable_on("run-start", focus);
2763
+ }, [unstable_scrollToBottomOnRunStart]);
2796
2764
  return autoScrollRef;
2797
2765
  };
2798
2766
 
@@ -2811,7 +2779,7 @@ ThreadPrimitiveViewport.displayName = "ThreadPrimitive.Viewport";
2811
2779
  import { memo as memo5, useMemo as useMemo10 } from "react";
2812
2780
 
2813
2781
  // src/context/providers/MessageRuntimeProvider.tsx
2814
- import { useEffect as useEffect13, useState as useState9 } from "react";
2782
+ import { useEffect as useEffect14, useState as useState9 } from "react";
2815
2783
  import { create as create10 } from "zustand";
2816
2784
 
2817
2785
  // src/context/stores/MessageUtils.ts
@@ -2833,14 +2801,14 @@ var makeMessageUtilsStore = () => create9((set) => {
2833
2801
  import { jsx as jsx32 } from "react/jsx-runtime";
2834
2802
  var useMessageRuntimeStore = (runtime) => {
2835
2803
  const [store] = useState9(() => create10(() => runtime));
2836
- useEffect13(() => {
2804
+ useEffect14(() => {
2837
2805
  writableStore(store).setState(runtime, true);
2838
2806
  }, [runtime, store]);
2839
2807
  return store;
2840
2808
  };
2841
2809
  var useMessageStore2 = (runtime) => {
2842
2810
  const [store] = useState9(() => create10(() => runtime.getState()));
2843
- useEffect13(() => {
2811
+ useEffect14(() => {
2844
2812
  const updateState = () => writableStore(store).setState(runtime.getState(), true);
2845
2813
  updateState();
2846
2814
  return runtime.subscribe(updateState);
@@ -2854,7 +2822,7 @@ var useMessageUtilsStore2 = () => {
2854
2822
  var useEditComposerStore2 = (useMessageRuntime2) => {
2855
2823
  const runtime = useMessageRuntime2.getState().composer;
2856
2824
  const [store] = useState9(() => create10(() => runtime.getState()));
2857
- useEffect13(() => {
2825
+ useEffect14(() => {
2858
2826
  const updateState = () => writableStore(store).setState(runtime.getState());
2859
2827
  updateState();
2860
2828
  return runtime.subscribe(updateState);
@@ -3588,6 +3556,7 @@ var ThreadRuntimeImpl = class {
3588
3556
  this._threadBinding = {
3589
3557
  getState: () => threadBinding.getState(),
3590
3558
  getStateState: () => stateBinding.getState(),
3559
+ outerSubscribe: (callback) => threadBinding.outerSubscribe(callback),
3591
3560
  subscribe: (callback) => threadBinding.subscribe(callback)
3592
3561
  };
3593
3562
  }
@@ -3700,6 +3669,20 @@ var ThreadRuntimeImpl = class {
3700
3669
  this._threadBinding
3701
3670
  );
3702
3671
  }
3672
+ _eventListenerNestedSubscriptions = /* @__PURE__ */ new Map();
3673
+ unstable_on(event, callback) {
3674
+ let subject = this._eventListenerNestedSubscriptions.get(event);
3675
+ if (!subject) {
3676
+ subject = new NestedSubscriptionSubject({
3677
+ getState: () => ({
3678
+ subscribe: (callback2) => this._threadBinding.getState().unstable_on(event, callback2)
3679
+ }),
3680
+ subscribe: (callback2) => this._threadBinding.outerSubscribe(callback2)
3681
+ });
3682
+ this._eventListenerNestedSubscriptions.set(event, subject);
3683
+ }
3684
+ return subject.subscribe(callback);
3685
+ }
3703
3686
  };
3704
3687
 
3705
3688
  // src/api/AssistantRuntime.ts
@@ -4163,18 +4146,23 @@ var BaseThreadRuntimeCore = class {
4163
4146
  this.repository.getMessage(messageId)
4164
4147
  )
4165
4148
  );
4166
- this.notifySubscribers();
4149
+ this._notifySubscribers();
4167
4150
  }
4168
4151
  getBranches(messageId) {
4169
4152
  return this.repository.getBranches(messageId);
4170
4153
  }
4171
4154
  switchToBranch(branchId) {
4172
4155
  this.repository.switchToBranch(branchId);
4173
- this.notifySubscribers();
4156
+ this._notifySubscribers();
4174
4157
  }
4175
- notifySubscribers() {
4158
+ _notifySubscribers() {
4176
4159
  for (const callback of this._subscriptions) callback();
4177
4160
  }
4161
+ _notifyEventSubscribers(event) {
4162
+ const subscribers = this._eventSubscribers.get(event);
4163
+ if (!subscribers) return;
4164
+ for (const callback of subscribers) callback();
4165
+ }
4178
4166
  subscribe(callback) {
4179
4167
  this._subscriptions.add(callback);
4180
4168
  return () => this._subscriptions.delete(callback);
@@ -4189,7 +4177,7 @@ var BaseThreadRuntimeCore = class {
4189
4177
  const { message } = this.repository.getMessage(messageId);
4190
4178
  adapter.submit({ message, type });
4191
4179
  this._submittedFeedback[messageId] = { type };
4192
- this.notifySubscribers();
4180
+ this._notifySubscribers();
4193
4181
  }
4194
4182
  _stopSpeaking;
4195
4183
  speech;
@@ -4206,10 +4194,10 @@ var BaseThreadRuntimeCore = class {
4206
4194
  } else {
4207
4195
  this.speech = { messageId, status: utterance.status };
4208
4196
  }
4209
- this.notifySubscribers();
4197
+ this._notifySubscribers();
4210
4198
  });
4211
4199
  this.speech = { messageId, status: utterance.status };
4212
- this.notifySubscribers();
4200
+ this._notifySubscribers();
4213
4201
  this._stopSpeaking = () => {
4214
4202
  utterance.cancel();
4215
4203
  unsub();
@@ -4220,14 +4208,27 @@ var BaseThreadRuntimeCore = class {
4220
4208
  stopSpeaking() {
4221
4209
  if (!this._stopSpeaking) throw new Error("No message is being spoken");
4222
4210
  this._stopSpeaking();
4223
- this.notifySubscribers();
4211
+ this._notifySubscribers();
4224
4212
  }
4225
4213
  export() {
4226
4214
  return this.repository.export();
4227
4215
  }
4228
4216
  import(data) {
4229
4217
  this.repository.import(data);
4230
- this.notifySubscribers();
4218
+ this._notifySubscribers();
4219
+ }
4220
+ _eventSubscribers = /* @__PURE__ */ new Map();
4221
+ unstable_on(event, callback) {
4222
+ const subscribers = this._eventSubscribers.get(event);
4223
+ if (!subscribers) {
4224
+ this._eventSubscribers.set(event, /* @__PURE__ */ new Set([callback]));
4225
+ } else {
4226
+ subscribers.add(callback);
4227
+ }
4228
+ return () => {
4229
+ const subscribers2 = this._eventSubscribers.get(event);
4230
+ subscribers2.delete(callback);
4231
+ };
4231
4232
  }
4232
4233
  };
4233
4234
 
@@ -4289,7 +4290,7 @@ var LocalThreadRuntimeCore = class extends BaseThreadRuntimeCore {
4289
4290
  this.capabilities.feedback = canFeedback;
4290
4291
  hasUpdates = true;
4291
4292
  }
4292
- if (hasUpdates) this.notifySubscribers();
4293
+ if (hasUpdates) this._notifySubscribers();
4293
4294
  }
4294
4295
  async append(message) {
4295
4296
  const newMessage = fromCoreMessage(message, {
@@ -4300,7 +4301,7 @@ var LocalThreadRuntimeCore = class extends BaseThreadRuntimeCore {
4300
4301
  await this.startRun(newMessage.id);
4301
4302
  } else {
4302
4303
  this.repository.resetHead(newMessage.id);
4303
- this.notifySubscribers();
4304
+ this._notifySubscribers();
4304
4305
  }
4305
4306
  }
4306
4307
  async startRun(parentId) {
@@ -4313,6 +4314,7 @@ var LocalThreadRuntimeCore = class extends BaseThreadRuntimeCore {
4313
4314
  content: [],
4314
4315
  createdAt: /* @__PURE__ */ new Date()
4315
4316
  };
4317
+ this._notifyEventSubscribers("run-start");
4316
4318
  do {
4317
4319
  message = await this.performRoundtrip(parentId, message);
4318
4320
  } while (shouldContinue(message));
@@ -4344,7 +4346,7 @@ var LocalThreadRuntimeCore = class extends BaseThreadRuntimeCore {
4344
4346
  } : void 0
4345
4347
  };
4346
4348
  this.repository.addOrUpdateMessage(parentId, message);
4347
- this.notifySubscribers();
4349
+ this._notifySubscribers();
4348
4350
  };
4349
4351
  const maxSteps = this.options.maxSteps ? this.options.maxSteps : (this.options.maxToolRoundtrips ?? 1) + 1;
4350
4352
  const steps = message.metadata?.steps?.length ?? 0;
@@ -4455,6 +4457,7 @@ var LocalRuntimeCore = class extends BaseAssistantRuntimeCore {
4455
4457
  this.thread.adapter,
4456
4458
  options
4457
4459
  );
4460
+ this.thread._notifyEventSubscribers("switched-to");
4458
4461
  }
4459
4462
  switchToThread(threadId) {
4460
4463
  if (threadId !== null) {
@@ -4503,7 +4506,7 @@ var useLocalRuntime = (adapter, options = {}) => {
4503
4506
  };
4504
4507
 
4505
4508
  // src/runtimes/external-store/useExternalStoreRuntime.tsx
4506
- import { useEffect as useEffect14, useMemo as useMemo12, useState as useState12 } from "react";
4509
+ import { useEffect as useEffect15, useMemo as useMemo12, useState as useState12 } from "react";
4507
4510
 
4508
4511
  // src/runtimes/external-store/getExternalStoreMessage.tsx
4509
4512
  var symbolInnerMessage = Symbol("innerMessage");
@@ -4676,7 +4679,7 @@ var ExternalStoreThreadRuntimeCore = class extends BaseThreadRuntimeCore {
4676
4679
  if (oldStore.convertMessage !== store.convertMessage) {
4677
4680
  this._converter = new ThreadMessageConverter();
4678
4681
  } else if (oldStore.isRunning === store.isRunning && oldStore.messages === store.messages) {
4679
- this.notifySubscribers();
4682
+ this._notifySubscribers();
4680
4683
  return;
4681
4684
  }
4682
4685
  }
@@ -4716,7 +4719,7 @@ var ExternalStoreThreadRuntimeCore = class extends BaseThreadRuntimeCore {
4716
4719
  this.assistantOptimisticId ?? messages2.at(-1)?.id ?? null
4717
4720
  );
4718
4721
  this._messages = this.repository.getMessages();
4719
- this.notifySubscribers();
4722
+ this._notifySubscribers();
4720
4723
  }
4721
4724
  switchToBranch(branchId) {
4722
4725
  if (!this._store.setMessages)
@@ -4755,7 +4758,7 @@ var ExternalStoreThreadRuntimeCore = class extends BaseThreadRuntimeCore {
4755
4758
  }
4756
4759
  messages2 = this.repository.getMessages();
4757
4760
  } else {
4758
- this.notifySubscribers();
4761
+ this._notifySubscribers();
4759
4762
  }
4760
4763
  setTimeout(() => {
4761
4764
  this.updateMessages(messages2);
@@ -4798,6 +4801,7 @@ var ExternalStoreRuntimeCore = class extends BaseAssistantRuntimeCore {
4798
4801
  }
4799
4802
  );
4800
4803
  await this.thread.store.onSwitchToNewThread();
4804
+ this.thread._notifyEventSubscribers("switched-to");
4801
4805
  }
4802
4806
  async switchToThread(threadId) {
4803
4807
  if (threadId !== null) {
@@ -4811,7 +4815,8 @@ var ExternalStoreRuntimeCore = class extends BaseAssistantRuntimeCore {
4811
4815
  // ignore messages until rerender
4812
4816
  }
4813
4817
  );
4814
- this.thread.store.onSwitchToThread(threadId);
4818
+ await this.thread.store.onSwitchToThread(threadId);
4819
+ this.thread._notifyEventSubscribers("switched-to");
4815
4820
  } else {
4816
4821
  this.switchToNewThread();
4817
4822
  }
@@ -4821,7 +4826,7 @@ var ExternalStoreRuntimeCore = class extends BaseAssistantRuntimeCore {
4821
4826
  // src/runtimes/external-store/useExternalStoreRuntime.tsx
4822
4827
  var useExternalStoreRuntime = (store) => {
4823
4828
  const [runtime] = useState12(() => new ExternalStoreRuntimeCore(store));
4824
- useEffect14(() => {
4829
+ useEffect15(() => {
4825
4830
  runtime.thread.store = store;
4826
4831
  });
4827
4832
  return useMemo12(
@@ -5593,7 +5598,7 @@ CircleStopIcon.displayName = "CircleStopIcon";
5593
5598
  // src/ui/attachment.tsx
5594
5599
  import {
5595
5600
  forwardRef as forwardRef28,
5596
- useEffect as useEffect15,
5601
+ useEffect as useEffect16,
5597
5602
  useState as useState14
5598
5603
  } from "react";
5599
5604
  import { CircleXIcon, FileIcon } from "lucide-react";
@@ -5638,7 +5643,7 @@ var AttachmentRoot = withDefaults(attachment_exports.Root, {
5638
5643
  AttachmentRoot.displayName = "AttachmentRoot";
5639
5644
  var useFileSrc = (file) => {
5640
5645
  const [src, setSrc] = useState14(void 0);
5641
- useEffect15(() => {
5646
+ useEffect16(() => {
5642
5647
  if (!file) {
5643
5648
  setSrc(void 0);
5644
5649
  return;