@assistant-ui/react 0.1.11 → 0.2.0

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
@@ -1,19 +1,24 @@
1
- import {
2
- AssistantContext,
3
- ContentPartContext,
4
- MessageContext,
5
- ThreadContext,
6
- __export,
7
- useAssistantContext,
8
- useComposerContext,
9
- useContentPartContext,
10
- useMessageContext,
11
- useThreadContext
12
- } from "./chunk-KUACYNLE.mjs";
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
13
6
 
14
7
  // src/primitive-hooks/actionBar/useActionBarCopy.tsx
15
8
  import { useCallback } from "react";
16
9
 
10
+ // src/context/react/MessageContext.ts
11
+ import { createContext, useContext } from "react";
12
+ var MessageContext = createContext(null);
13
+ var useMessageContext = () => {
14
+ const context = useContext(MessageContext);
15
+ if (!context)
16
+ throw new Error(
17
+ "This component can only be used inside a component passed to <ThreadPrimitive.Messages components={...} />."
18
+ );
19
+ return context;
20
+ };
21
+
17
22
  // src/utils/combined/useCombinedStore.ts
18
23
  import { useMemo } from "react";
19
24
 
@@ -90,8 +95,22 @@ var useActionBarEdit = () => {
90
95
 
91
96
  // src/primitive-hooks/actionBar/useActionBarReload.tsx
92
97
  import { useCallback as useCallback3 } from "react";
98
+
99
+ // src/context/react/ThreadContext.ts
100
+ import { createContext as createContext2, useContext as useContext2 } from "react";
101
+ var ThreadContext = createContext2(null);
102
+ var useThreadContext = () => {
103
+ const context = useContext2(ThreadContext);
104
+ if (!context)
105
+ throw new Error(
106
+ "This component must be used within an AssistantRuntimeProvider."
107
+ );
108
+ return context;
109
+ };
110
+
111
+ // src/primitive-hooks/actionBar/useActionBarReload.tsx
93
112
  var useActionBarReload = () => {
94
- const { useThread, useThreadActions, useViewport } = useThreadContext();
113
+ const { useThread, useThreadActions, useComposer, useViewport } = useThreadContext();
95
114
  const { useMessage } = useMessageContext();
96
115
  const disabled = useCombinedStore(
97
116
  [useThread, useMessage],
@@ -101,7 +120,8 @@ var useActionBarReload = () => {
101
120
  const { parentId } = useMessage.getState();
102
121
  useThreadActions.getState().startRun(parentId);
103
122
  useViewport.getState().scrollToBottom();
104
- }, [useThreadActions, useMessage, useViewport]);
123
+ useComposer.getState().focus();
124
+ }, [useThreadActions, useComposer, useViewport, useMessage]);
105
125
  if (disabled) return null;
106
126
  return callback;
107
127
  };
@@ -156,6 +176,309 @@ var useBranchPickerPrevious = () => {
156
176
 
157
177
  // src/primitive-hooks/composer/useComposerCancel.tsx
158
178
  import { useCallback as useCallback6 } from "react";
179
+
180
+ // src/context/providers/AssistantRuntimeProvider.tsx
181
+ import { memo } from "react";
182
+
183
+ // src/context/providers/AssistantProvider.tsx
184
+ import { useEffect as useEffect2, useInsertionEffect as useInsertionEffect2, useRef as useRef2, useState as useState2 } from "react";
185
+
186
+ // src/context/react/AssistantContext.ts
187
+ import { createContext as createContext3, useContext as useContext3 } from "react";
188
+ var AssistantContext = createContext3(
189
+ null
190
+ );
191
+ var useAssistantContext = () => {
192
+ const context = useContext3(AssistantContext);
193
+ if (!context)
194
+ throw new Error(
195
+ "This component must be used within an AssistantRuntimeProvider."
196
+ );
197
+ return context;
198
+ };
199
+
200
+ // src/context/stores/AssistantModelConfig.ts
201
+ import { create } from "zustand";
202
+
203
+ // src/types/ModelConfigTypes.ts
204
+ var mergeModelConfigs = (configSet) => {
205
+ const configs = Array.from(configSet).map((c) => c()).sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
206
+ return configs.reduce((acc, config) => {
207
+ if (config.system) {
208
+ if (acc.system) {
209
+ acc.system += `
210
+
211
+ ${config.system}`;
212
+ } else {
213
+ acc.system = config.system;
214
+ }
215
+ }
216
+ if (config.tools) {
217
+ for (const [name, tool] of Object.entries(config.tools)) {
218
+ if (acc.tools?.[name]) {
219
+ throw new Error(
220
+ `You tried to define a tool with the name ${name}, but it already exists.`
221
+ );
222
+ }
223
+ if (!acc.tools) acc.tools = {};
224
+ acc.tools[name] = tool;
225
+ }
226
+ }
227
+ return acc;
228
+ }, {});
229
+ };
230
+
231
+ // src/utils/ProxyConfigProvider.ts
232
+ var ProxyConfigProvider = class {
233
+ _providers = /* @__PURE__ */ new Set();
234
+ getModelConfig() {
235
+ return mergeModelConfigs(this._providers);
236
+ }
237
+ registerModelConfigProvider(provider) {
238
+ this._providers.add(provider);
239
+ return () => {
240
+ this._providers.delete(provider);
241
+ };
242
+ }
243
+ };
244
+
245
+ // src/context/stores/AssistantModelConfig.ts
246
+ var makeAssistantModelConfigStore = () => create(() => {
247
+ const proxy = new ProxyConfigProvider();
248
+ return Object.freeze({
249
+ getModelConfig: () => {
250
+ return proxy.getModelConfig();
251
+ },
252
+ registerModelConfigProvider: (provider) => {
253
+ return proxy.registerModelConfigProvider(provider);
254
+ }
255
+ });
256
+ });
257
+
258
+ // src/context/stores/AssistantToolUIs.ts
259
+ import { create as create2 } from "zustand";
260
+ var makeAssistantToolUIsStore = () => create2((set) => {
261
+ const renderers = /* @__PURE__ */ new Map();
262
+ return Object.freeze({
263
+ getToolUI: (name) => {
264
+ const arr = renderers.get(name);
265
+ const last = arr?.at(-1);
266
+ if (last) return last;
267
+ return null;
268
+ },
269
+ setToolUI: (name, render) => {
270
+ let arr = renderers.get(name);
271
+ if (!arr) {
272
+ arr = [];
273
+ renderers.set(name, arr);
274
+ }
275
+ arr.push(render);
276
+ set({});
277
+ return () => {
278
+ const index = arr.indexOf(render);
279
+ if (index !== -1) {
280
+ arr.splice(index, 1);
281
+ }
282
+ if (index === arr.length) {
283
+ set({});
284
+ }
285
+ };
286
+ }
287
+ });
288
+ });
289
+
290
+ // src/context/providers/ThreadProvider.tsx
291
+ import { useEffect, useInsertionEffect, useRef, useState } from "react";
292
+
293
+ // src/context/stores/Composer.ts
294
+ import { create as create3 } from "zustand";
295
+
296
+ // src/context/stores/BaseComposer.ts
297
+ var makeBaseComposer = (set) => ({
298
+ value: "",
299
+ setValue: (value) => {
300
+ set({ value });
301
+ }
302
+ });
303
+
304
+ // src/context/stores/Composer.ts
305
+ var makeComposerStore = (useThread, useThreadActions) => {
306
+ const focusListeners = /* @__PURE__ */ new Set();
307
+ return create3()((set, get, store) => {
308
+ return {
309
+ ...makeBaseComposer(set, get, store),
310
+ isEditing: true,
311
+ send: () => {
312
+ const { setValue, value } = get();
313
+ setValue("");
314
+ useThreadActions.getState().append({
315
+ parentId: useThread.getState().messages.at(-1)?.id ?? null,
316
+ role: "user",
317
+ content: [{ type: "text", text: value }]
318
+ });
319
+ },
320
+ cancel: () => {
321
+ const thread = useThread.getState();
322
+ if (!thread.isRunning) return false;
323
+ useThreadActions.getState().cancelRun();
324
+ return true;
325
+ },
326
+ focus: () => {
327
+ for (const listener of focusListeners) {
328
+ listener();
329
+ }
330
+ },
331
+ onFocus: (listener) => {
332
+ focusListeners.add(listener);
333
+ return () => {
334
+ focusListeners.delete(listener);
335
+ };
336
+ }
337
+ };
338
+ });
339
+ };
340
+
341
+ // src/context/stores/Thread.ts
342
+ import { create as create4 } from "zustand";
343
+ var makeThreadStore = (runtimeRef) => {
344
+ return create4(() => ({
345
+ messages: runtimeRef.current.messages,
346
+ isRunning: runtimeRef.current.isRunning
347
+ }));
348
+ };
349
+
350
+ // src/context/stores/ThreadViewport.tsx
351
+ import { create as create5 } from "zustand";
352
+ var makeThreadViewportStore = () => {
353
+ const scrollToBottomListeners = /* @__PURE__ */ new Set();
354
+ return create5(() => ({
355
+ isAtBottom: true,
356
+ scrollToBottom: () => {
357
+ for (const listener of scrollToBottomListeners) {
358
+ listener();
359
+ }
360
+ },
361
+ onScrollToBottom: (callback) => {
362
+ scrollToBottomListeners.add(callback);
363
+ return () => {
364
+ scrollToBottomListeners.delete(callback);
365
+ };
366
+ }
367
+ }));
368
+ };
369
+
370
+ // src/context/stores/ThreadActions.ts
371
+ import { create as create6 } from "zustand";
372
+ var makeThreadActionStore = (runtimeRef) => {
373
+ return create6(
374
+ () => Object.freeze({
375
+ getBranches: (messageId) => runtimeRef.current.getBranches(messageId),
376
+ switchToBranch: (branchId) => runtimeRef.current.switchToBranch(branchId),
377
+ startRun: (parentId) => runtimeRef.current.startRun(parentId),
378
+ append: (message) => runtimeRef.current.append(message),
379
+ cancelRun: () => runtimeRef.current.cancelRun(),
380
+ addToolResult: (toolCallId, result) => runtimeRef.current.addToolResult(toolCallId, result)
381
+ })
382
+ );
383
+ };
384
+
385
+ // src/context/providers/ThreadProvider.tsx
386
+ import { jsx, jsxs } from "react/jsx-runtime";
387
+ var ThreadProvider = ({
388
+ children,
389
+ runtime
390
+ }) => {
391
+ const runtimeRef = useRef(runtime);
392
+ useInsertionEffect(() => {
393
+ runtimeRef.current = runtime;
394
+ });
395
+ const [context] = useState(() => {
396
+ const useThread = makeThreadStore(runtimeRef);
397
+ const useThreadActions = makeThreadActionStore(runtimeRef);
398
+ const useViewport = makeThreadViewportStore();
399
+ const useComposer = makeComposerStore(useThread, useThreadActions);
400
+ return {
401
+ useThread,
402
+ useThreadActions,
403
+ useComposer,
404
+ useViewport
405
+ };
406
+ });
407
+ useEffect(() => {
408
+ const onRuntimeUpdate = () => {
409
+ context.useThread.setState(
410
+ Object.freeze({
411
+ messages: runtimeRef.current.messages,
412
+ isRunning: runtimeRef.current.isRunning
413
+ }),
414
+ true
415
+ );
416
+ };
417
+ onRuntimeUpdate();
418
+ return runtime.subscribe(onRuntimeUpdate);
419
+ }, [context, runtime]);
420
+ const RuntimeSynchronizer = runtime.unstable_synchronizer;
421
+ return /* @__PURE__ */ jsxs(ThreadContext.Provider, { value: context, children: [
422
+ RuntimeSynchronizer && /* @__PURE__ */ jsx(RuntimeSynchronizer, {}),
423
+ children
424
+ ] });
425
+ };
426
+
427
+ // src/context/providers/AssistantProvider.tsx
428
+ import { jsx as jsx2 } from "react/jsx-runtime";
429
+ var AssistantProvider = ({ children, runtime }) => {
430
+ const runtimeRef = useRef2(runtime);
431
+ useInsertionEffect2(() => {
432
+ runtimeRef.current = runtime;
433
+ });
434
+ const [context] = useState2(() => {
435
+ const useModelConfig = makeAssistantModelConfigStore();
436
+ const useToolUIs = makeAssistantToolUIsStore();
437
+ return { useModelConfig, useToolUIs };
438
+ });
439
+ const getModelCOnfig = context.useModelConfig((c) => c.getModelConfig);
440
+ useEffect2(() => {
441
+ return runtime.registerModelConfigProvider(getModelCOnfig);
442
+ }, [runtime, getModelCOnfig]);
443
+ return /* @__PURE__ */ jsx2(AssistantContext.Provider, { value: context, children: /* @__PURE__ */ jsx2(ThreadProvider, { runtime, children }) });
444
+ };
445
+
446
+ // src/context/providers/AssistantRuntimeProvider.tsx
447
+ import { jsx as jsx3 } from "react/jsx-runtime";
448
+ var AssistantRuntimeProviderImpl = ({ children, runtime }) => {
449
+ return /* @__PURE__ */ jsx3(AssistantProvider, { runtime, children });
450
+ };
451
+ var AssistantRuntimeProvider = memo(AssistantRuntimeProviderImpl);
452
+
453
+ // src/context/react/ComposerContext.ts
454
+ import { useContext as useContext4, useMemo as useMemo2 } from "react";
455
+ var useComposerContext = () => {
456
+ const { useComposer } = useThreadContext();
457
+ const { useEditComposer } = useContext4(MessageContext) ?? {};
458
+ return useMemo2(
459
+ () => ({
460
+ useComposer: useEditComposer ?? useComposer,
461
+ type: useEditComposer ? "edit" : "new"
462
+ }),
463
+ [useEditComposer, useComposer]
464
+ );
465
+ };
466
+
467
+ // src/context/react/ContentPartContext.ts
468
+ import { createContext as createContext4, useContext as useContext5 } from "react";
469
+ var ContentPartContext = createContext4(
470
+ null
471
+ );
472
+ var useContentPartContext = () => {
473
+ const context = useContext5(ContentPartContext);
474
+ if (!context)
475
+ throw new Error(
476
+ "This component can only be used inside a component passed to <MessagePrimitive.Content components={...} >."
477
+ );
478
+ return context;
479
+ };
480
+
481
+ // src/primitive-hooks/composer/useComposerCancel.tsx
159
482
  var useComposerCancel = () => {
160
483
  const { useComposer } = useComposerContext();
161
484
  const disabled = useComposer((c) => !c.isEditing);
@@ -180,7 +503,7 @@ var useComposerIf = (props) => {
180
503
  // src/primitive-hooks/composer/useComposerSend.tsx
181
504
  import { useCallback as useCallback7 } from "react";
182
505
  var useComposerSend = () => {
183
- const { useViewport } = useThreadContext();
506
+ const { useViewport, useComposer: useNewComposer } = useThreadContext();
184
507
  const { useComposer } = useComposerContext();
185
508
  const disabled = useComposer((c) => !c.isEditing || c.value.length === 0);
186
509
  const callback = useCallback7(() => {
@@ -188,7 +511,8 @@ var useComposerSend = () => {
188
511
  if (!composerState.isEditing) return;
189
512
  composerState.send();
190
513
  useViewport.getState().scrollToBottom();
191
- }, [useComposer, useViewport]);
514
+ useNewComposer.getState().focus();
515
+ }, [useNewComposer, useComposer, useViewport]);
192
516
  if (disabled) return null;
193
517
  return callback;
194
518
  };
@@ -280,12 +604,12 @@ var useThreadEmpty = () => {
280
604
  // src/primitive-hooks/thread/useThreadScrollToBottom.tsx
281
605
  import { useCallback as useCallback8 } from "react";
282
606
  var useThreadScrollToBottom = () => {
283
- const { useViewport } = useThreadContext();
607
+ const { useComposer, useViewport } = useThreadContext();
284
608
  const isAtBottom = useViewport((s) => s.isAtBottom);
285
609
  const handleScrollToBottom = useCallback8(() => {
286
- const { scrollToBottom } = useViewport.getState();
287
- scrollToBottom();
288
- }, [useViewport]);
610
+ useViewport.getState().scrollToBottom();
611
+ useComposer.getState().focus();
612
+ }, [useViewport, useComposer]);
289
613
  if (isAtBottom) return null;
290
614
  return handleScrollToBottom;
291
615
  };
@@ -310,416 +634,449 @@ var useThreadSuggestion = ({
310
634
  return callback;
311
635
  };
312
636
 
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
637
+ // src/primitives/actionBar/index.ts
638
+ var actionBar_exports = {};
639
+ __export(actionBar_exports, {
640
+ Copy: () => ActionBarCopy,
641
+ Edit: () => ActionBarEdit,
642
+ Reload: () => ActionBarReload,
643
+ Root: () => ActionBarRoot
323
644
  });
324
645
 
325
- // src/primitives/thread/ThreadRoot.tsx
646
+ // src/primitives/actionBar/ActionBarRoot.tsx
326
647
  import { Primitive } from "@radix-ui/react-primitive";
327
648
  import { forwardRef } from "react";
328
- 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;
649
+ import { jsx as jsx4 } from "react/jsx-runtime";
650
+ var useActionBarFloatStatus = ({
651
+ hideWhenRunning,
652
+ autohide,
653
+ autohideFloat
654
+ }) => {
655
+ const { useThread } = useThreadContext();
656
+ const { useMessage, useMessageUtils } = useMessageContext();
657
+ return useCombinedStore(
658
+ [useThread, useMessage, useMessageUtils],
659
+ (t, m, mu) => {
660
+ if (hideWhenRunning && t.isRunning) return "hidden" /* Hidden */;
661
+ const autohideEnabled = autohide === "always" || autohide === "not-last" && !m.isLast;
662
+ if (!autohideEnabled) return "normal" /* Normal */;
663
+ if (!mu.isHovering) return "hidden" /* Hidden */;
664
+ if (autohideFloat === "always" || autohideFloat === "single-branch" && m.branches.length <= 1)
665
+ return "floating" /* Floating */;
666
+ return "normal" /* Normal */;
667
+ }
668
+ );
346
669
  };
670
+ var ActionBarRoot = forwardRef(({ hideWhenRunning, autohide, autohideFloat, ...rest }, ref) => {
671
+ const hideAndfloatStatus = useActionBarFloatStatus({
672
+ hideWhenRunning,
673
+ autohide,
674
+ autohideFloat
675
+ });
676
+ if (hideAndfloatStatus === "hidden" /* Hidden */) return null;
677
+ return /* @__PURE__ */ jsx4(
678
+ Primitive.div,
679
+ {
680
+ ...hideAndfloatStatus === "floating" /* Floating */ ? { "data-floating": "true" } : null,
681
+ ...rest,
682
+ ref
683
+ }
684
+ );
685
+ });
686
+ ActionBarRoot.displayName = "ActionBarRoot";
347
687
 
348
- // src/primitives/thread/ThreadViewport.tsx
349
- import { useComposedRefs as useComposedRefs2 } from "@radix-ui/react-compose-refs";
688
+ // src/utils/createActionButton.tsx
689
+ import { composeEventHandlers } from "@radix-ui/primitive";
350
690
  import { Primitive as Primitive2 } from "@radix-ui/react-primitive";
351
691
  import { forwardRef as forwardRef2 } from "react";
692
+ import { jsx as jsx5 } from "react/jsx-runtime";
693
+ var createActionButton = (displayName, useActionButton) => {
694
+ const ActionButton = forwardRef2((props, forwardedRef) => {
695
+ const callback = useActionButton(props);
696
+ return /* @__PURE__ */ jsx5(
697
+ Primitive2.button,
698
+ {
699
+ type: "button",
700
+ disabled: !callback,
701
+ ...props,
702
+ ref: forwardedRef,
703
+ onClick: composeEventHandlers(props.onClick, () => {
704
+ callback?.();
705
+ })
706
+ }
707
+ );
708
+ });
709
+ ActionButton.displayName = displayName;
710
+ return ActionButton;
711
+ };
352
712
 
353
- // src/primitive-hooks/thread/useThreadViewportAutoScroll.tsx
354
- import { useComposedRefs } from "@radix-ui/react-compose-refs";
355
- import { useRef as useRef2 } from "react";
713
+ // src/primitives/actionBar/ActionBarCopy.tsx
714
+ var ActionBarCopy = createActionButton(
715
+ "ActionBarCopy",
716
+ useActionBarCopy
717
+ );
356
718
 
357
- // src/utils/hooks/useOnResizeContent.tsx
358
- import { useCallbackRef } from "@radix-ui/react-use-callback-ref";
359
- import { useCallback as useCallback11 } from "react";
719
+ // src/primitives/actionBar/ActionBarReload.tsx
720
+ var ActionBarReload = createActionButton(
721
+ "ActionBarReload",
722
+ useActionBarReload
723
+ );
360
724
 
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
- };
725
+ // src/primitives/actionBar/ActionBarEdit.tsx
726
+ var ActionBarEdit = createActionButton(
727
+ "ActionBarEdit",
728
+ useActionBarEdit
729
+ );
378
730
 
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
- };
731
+ // src/primitives/assistantModal/index.ts
732
+ var assistantModal_exports = {};
733
+ __export(assistantModal_exports, {
734
+ Content: () => AssistantModalContent,
735
+ Root: () => AssistantModalRoot,
736
+ Trigger: () => AssistantModalTrigger
737
+ });
416
738
 
417
- // src/utils/hooks/useOnScrollToBottom.tsx
418
- import { useCallbackRef as useCallbackRef2 } from "@radix-ui/react-use-callback-ref";
419
- import { useEffect } from "react";
420
- var useOnScrollToBottom = (callback) => {
421
- const callbackRef = useCallbackRef2(callback);
422
- const { useViewport } = useThreadContext();
423
- useEffect(() => {
424
- return useViewport.getState().onScrollToBottom(() => {
739
+ // src/primitives/assistantModal/AssistantModalRoot.tsx
740
+ import { useState as useState3 } from "react";
741
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
742
+ import { composeEventHandlers as composeEventHandlers2 } from "@radix-ui/primitive";
743
+
744
+ // src/utils/hooks/useOnComposerFocus.tsx
745
+ import { useCallbackRef } from "@radix-ui/react-use-callback-ref";
746
+ import { useEffect as useEffect3 } from "react";
747
+ var useOnComposerFocus = (callback) => {
748
+ const callbackRef = useCallbackRef(callback);
749
+ const { useComposer } = useThreadContext();
750
+ useEffect3(() => {
751
+ return useComposer.getState().onFocus(() => {
425
752
  callbackRef();
426
753
  });
427
- }, [useViewport, callbackRef]);
754
+ }, [useComposer, callbackRef]);
428
755
  };
429
756
 
430
- // src/primitive-hooks/thread/useThreadViewportAutoScroll.tsx
431
- var useThreadViewportAutoScroll = ({
432
- autoScroll = true
757
+ // src/primitives/assistantModal/AssistantModalRoot.tsx
758
+ import { jsx as jsx6 } from "react/jsx-runtime";
759
+ var usePopoverScope = PopoverPrimitive.createPopoverScope();
760
+ var useAssistantModalOpenState = (defaultOpen = false) => {
761
+ const state = useState3(defaultOpen);
762
+ const [, setOpen] = state;
763
+ useOnComposerFocus(() => {
764
+ setOpen(true);
765
+ });
766
+ return state;
767
+ };
768
+ var AssistantModalRoot = ({
769
+ __scopeAssistantModal,
770
+ defaultOpen,
771
+ open,
772
+ onOpenChange,
773
+ ...rest
433
774
  }) => {
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();
775
+ const scope = usePopoverScope(__scopeAssistantModal);
776
+ const [modalOpen, setOpen] = useAssistantModalOpenState(defaultOpen);
777
+ return /* @__PURE__ */ jsx6(
778
+ PopoverPrimitive.Root,
779
+ {
780
+ ...scope,
781
+ open: open === void 0 ? modalOpen : open,
782
+ onOpenChange: composeEventHandlers2(onOpenChange, setOpen),
783
+ ...rest
468
784
  }
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;
785
+ );
481
786
  };
787
+ AssistantModalRoot.displayName = "AssistantModalRoot";
482
788
 
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";
789
+ // src/primitives/assistantModal/AssistantModalTrigger.tsx
790
+ import { forwardRef as forwardRef3 } from "react";
791
+ import * as PopoverPrimitive2 from "@radix-ui/react-popover";
792
+ import { jsx as jsx7 } from "react/jsx-runtime";
793
+ var AssistantModalTrigger = forwardRef3(
794
+ ({ __scopeAssistantModal, ...rest }, ref) => {
795
+ const scope = usePopoverScope(__scopeAssistantModal);
796
+ return /* @__PURE__ */ jsx7(PopoverPrimitive2.Trigger, { ...scope, ...rest, ref });
797
+ }
798
+ );
799
+ AssistantModalTrigger.displayName = "AssistantModalTrigger";
503
800
 
504
- // src/context/stores/BaseComposer.ts
505
- var makeBaseComposer = (set) => ({
506
- value: "",
507
- setValue: (value) => {
508
- set({ value });
801
+ // src/primitives/assistantModal/AssistantModalContent.tsx
802
+ import { forwardRef as forwardRef4 } from "react";
803
+ import * as PopoverPrimitive3 from "@radix-ui/react-popover";
804
+ import { composeEventHandlers as composeEventHandlers3 } from "@radix-ui/primitive";
805
+ import { jsx as jsx8 } from "react/jsx-runtime";
806
+ var AssistantModalContent = forwardRef4(
807
+ ({
808
+ __scopeAssistantModal,
809
+ side,
810
+ align,
811
+ onInteractOutside,
812
+ dissmissOnInteractOutside = false,
813
+ ...props
814
+ }, forwardedRef) => {
815
+ const scope = usePopoverScope(__scopeAssistantModal);
816
+ return /* @__PURE__ */ jsx8(PopoverPrimitive3.Portal, { ...scope, children: /* @__PURE__ */ jsx8(
817
+ PopoverPrimitive3.Content,
818
+ {
819
+ ...scope,
820
+ ...props,
821
+ ref: forwardedRef,
822
+ side: side ?? "top",
823
+ align: align ?? "end",
824
+ onInteractOutside: composeEventHandlers3(
825
+ onInteractOutside,
826
+ dissmissOnInteractOutside ? void 0 : (e) => e.preventDefault()
827
+ )
828
+ }
829
+ ) });
509
830
  }
510
- });
831
+ );
832
+ AssistantModalContent.displayName = "AssistantModalContent";
511
833
 
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
- }));
834
+ // src/primitives/branchPicker/index.ts
835
+ var branchPicker_exports = {};
836
+ __export(branchPicker_exports, {
837
+ Count: () => BranchPickerCount,
838
+ Next: () => BranchPickerNext,
839
+ Number: () => BranchPickerNumber,
840
+ Previous: () => BranchPickerPrevious,
841
+ Root: () => BranchPickerRoot
842
+ });
534
843
 
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
- }));
844
+ // src/primitives/branchPicker/BranchPickerNext.tsx
845
+ var BranchPickerNext = createActionButton(
846
+ "BranchPickerNext",
847
+ useBranchPickerNext
848
+ );
551
849
 
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;
850
+ // src/primitives/branchPicker/BranchPickerPrevious.tsx
851
+ var BranchPickerPrevious = createActionButton(
852
+ "BranchPickerPrevious",
853
+ useBranchPickerPrevious
854
+ );
855
+
856
+ // src/primitives/branchPicker/BranchPickerCount.tsx
857
+ import { Fragment, jsx as jsx9 } from "react/jsx-runtime";
858
+ var BranchPickerCount = () => {
859
+ const branchCount = useBranchPickerCount();
860
+ return /* @__PURE__ */ jsx9(Fragment, { children: branchCount });
556
861
  };
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
- });
862
+
863
+ // src/primitives/branchPicker/BranchPickerNumber.tsx
864
+ import { Fragment as Fragment2, jsx as jsx10 } from "react/jsx-runtime";
865
+ var BranchPickerNumber = () => {
866
+ const branchNumber = useBranchPickerNumber();
867
+ return /* @__PURE__ */ jsx10(Fragment2, { children: branchNumber });
572
868
  };
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
- });
869
+
870
+ // src/primitives/branchPicker/BranchPickerRoot.tsx
871
+ import { Primitive as Primitive6 } from "@radix-ui/react-primitive";
872
+ import { forwardRef as forwardRef8 } from "react";
873
+
874
+ // src/primitives/message/index.ts
875
+ var message_exports = {};
876
+ __export(message_exports, {
877
+ Content: () => MessageContent,
878
+ If: () => MessageIf,
879
+ InProgress: () => MessageInProgress,
880
+ Root: () => MessageRoot
881
+ });
882
+
883
+ // src/primitives/message/MessageRoot.tsx
884
+ import { composeEventHandlers as composeEventHandlers4 } from "@radix-ui/primitive";
885
+ import { Primitive as Primitive3 } from "@radix-ui/react-primitive";
886
+ import { forwardRef as forwardRef5 } from "react";
887
+ import { jsx as jsx11 } from "react/jsx-runtime";
888
+ var MessageRoot = forwardRef5(
889
+ ({ onMouseEnter, onMouseLeave, ...rest }, ref) => {
890
+ const { useMessageUtils } = useMessageContext();
891
+ const setIsHovering = useMessageUtils((s) => s.setIsHovering);
892
+ const handleMouseEnter = () => {
893
+ setIsHovering(true);
894
+ };
895
+ const handleMouseLeave = () => {
896
+ setIsHovering(false);
897
+ };
898
+ return /* @__PURE__ */ jsx11(
899
+ Primitive3.div,
900
+ {
901
+ ...rest,
902
+ ref,
903
+ onMouseEnter: composeEventHandlers4(onMouseEnter, handleMouseEnter),
904
+ onMouseLeave: composeEventHandlers4(onMouseLeave, handleMouseLeave)
602
905
  }
603
- });
604
- syncMessage(
605
- useThread.getState(),
606
- useThreadActions.getState().getBranches,
607
- useMessage,
608
- messageIndex
609
906
  );
610
- return { useMessage, useMessageUtils, useEditComposer };
907
+ }
908
+ );
909
+ MessageRoot.displayName = "MessageRoot";
910
+
911
+ // src/primitives/message/MessageIf.tsx
912
+ var MessageIf = ({ children, ...query }) => {
913
+ const result = useMessageIf(query);
914
+ return result ? children : null;
915
+ };
916
+
917
+ // src/primitives/message/MessageContent.tsx
918
+ import { memo as memo2 } from "react";
919
+
920
+ // src/context/providers/ContentPartProvider.tsx
921
+ import { useEffect as useEffect4, useState as useState4 } from "react";
922
+ import { create as create7 } from "zustand";
923
+ import { jsx as jsx12 } from "react/jsx-runtime";
924
+ var syncContentPart = ({ message }, useContentPart, partIndex) => {
925
+ const part = message.content[partIndex];
926
+ if (!part) return;
927
+ const messageStatus = message.role === "assistant" ? message.status : "done";
928
+ const status = partIndex === message.content.length - 1 ? messageStatus : "done";
929
+ const currentState = useContentPart.getState();
930
+ if (currentState.part === part && currentState.status === status) return;
931
+ useContentPart.setState(
932
+ Object.freeze({
933
+ part,
934
+ status
935
+ })
936
+ );
937
+ };
938
+ var useContentPartContext2 = (partIndex) => {
939
+ const { useMessage } = useMessageContext();
940
+ const [context] = useState4(() => {
941
+ const useContentPart = create7(
942
+ () => ({})
943
+ );
944
+ syncContentPart(useMessage.getState(), useContentPart, partIndex);
945
+ return { useContentPart };
611
946
  });
612
- useEffect2(() => {
613
- return useThread.subscribe((thread) => {
614
- syncMessage(
615
- thread,
616
- useThreadActions.getState().getBranches,
617
- context.useMessage,
618
- messageIndex
619
- );
947
+ useEffect4(() => {
948
+ syncContentPart(useMessage.getState(), context.useContentPart, partIndex);
949
+ return useMessage.subscribe((message) => {
950
+ syncContentPart(message, context.useContentPart, partIndex);
620
951
  });
621
- }, [useThread, useThreadActions, context, messageIndex]);
952
+ }, [context, useMessage, partIndex]);
622
953
  return context;
623
954
  };
624
- var MessageProvider = ({
625
- messageIndex,
955
+ var ContentPartProvider = ({
956
+ partIndex,
626
957
  children
627
958
  }) => {
628
- const context = useMessageContext2(messageIndex);
629
- return /* @__PURE__ */ jsx3(MessageContext.Provider, { value: context, children });
959
+ const context = useContentPartContext2(partIndex);
960
+ return /* @__PURE__ */ jsx12(ContentPartContext.Provider, { value: context, children });
630
961
  };
631
962
 
632
- // src/primitives/composer/ComposerIf.tsx
633
- var ComposerIf = ({ children, ...query }) => {
634
- const result = useComposerIf(query);
635
- return result ? children : null;
963
+ // src/primitives/contentPart/ContentPartDisplay.tsx
964
+ var ContentPartDisplay = () => {
965
+ const display = useContentPartDisplay();
966
+ return display ?? null;
636
967
  };
637
968
 
638
- // src/primitives/message/MessageIf.tsx
639
- var MessageIf = ({ children, ...query }) => {
640
- const result = useMessageIf(query);
641
- return result ? children : null;
969
+ // src/primitives/contentPart/ContentPartInProgressIndicator.tsx
970
+ var ContentPartInProgressIndicator = () => {
971
+ const indicator = useContentPartInProgressIndicator();
972
+ return indicator;
642
973
  };
643
974
 
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
- };
975
+ // src/primitives/contentPart/ContentPartText.tsx
976
+ import { Primitive as Primitive4 } from "@radix-ui/react-primitive";
977
+ import { forwardRef as forwardRef6 } from "react";
978
+ import { jsx as jsx13 } from "react/jsx-runtime";
979
+ var ContentPartText = forwardRef6((props, forwardedRef) => {
980
+ const text = useContentPartText();
981
+ return /* @__PURE__ */ jsx13(Primitive4.p, { ...props, ref: forwardedRef, children: text });
982
+ });
983
+ ContentPartText.displayName = "ContentPartText";
984
+
985
+ // src/primitives/message/MessageContent.tsx
986
+ import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs2 } from "react/jsx-runtime";
987
+ var defaultComponents = {
988
+ Text: () => /* @__PURE__ */ jsxs2(Fragment3, { children: [
989
+ /* @__PURE__ */ jsx14(ContentPartText, { style: { whiteSpace: "pre-line" } }),
990
+ /* @__PURE__ */ jsx14(ContentPartInProgressIndicator, {})
991
+ ] }),
992
+ Image: () => null,
993
+ UI: () => /* @__PURE__ */ jsx14(ContentPartDisplay, {}),
994
+ tools: {
995
+ Fallback: (props) => {
996
+ const { useToolUIs } = useAssistantContext();
997
+ const Render = useToolUIs((s) => s.getToolUI(props.part.toolName));
998
+ if (!Render) return null;
999
+ return /* @__PURE__ */ jsx14(Render, { ...props });
1000
+ }
1001
+ }
652
1002
  };
653
- var ThreadMessageImpl = ({
654
- messageIndex,
655
- components
1003
+ var MessageContentPartComponent = ({
1004
+ components: {
1005
+ Text = defaultComponents.Text,
1006
+ Image = defaultComponents.Image,
1007
+ UI = defaultComponents.UI,
1008
+ tools: { by_name = {}, Fallback = defaultComponents.tools.Fallback } = {}
1009
+ } = {}
656
1010
  }) => {
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
- ] });
665
- };
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
1011
+ const { useThreadActions } = useThreadContext();
1012
+ const addToolResult = useThreadActions((t) => t.addToolResult);
1013
+ const { useContentPart } = useContentPartContext();
1014
+ const { part, status } = useContentPart();
1015
+ const type = part.type;
1016
+ switch (type) {
1017
+ case "text":
1018
+ return /* @__PURE__ */ jsx14(Text, { part, status });
1019
+ case "image":
1020
+ return /* @__PURE__ */ jsx14(Image, { part, status });
1021
+ case "ui":
1022
+ return /* @__PURE__ */ jsx14(UI, { part, status });
1023
+ case "tool-call": {
1024
+ const Tool = by_name[part.toolName] || Fallback;
1025
+ const addResult = (result) => addToolResult(part.toolCallId, result);
1026
+ return /* @__PURE__ */ jsx14(Tool, { part, status, addResult });
1027
+ }
1028
+ default:
1029
+ throw new Error(`Unknown content part type: ${type}`);
1030
+ }
1031
+ };
1032
+ var MessageContentPartImpl = ({
1033
+ partIndex,
1034
+ components
1035
+ }) => {
1036
+ return /* @__PURE__ */ jsx14(ContentPartProvider, { partIndex, children: /* @__PURE__ */ jsx14(MessageContentPartComponent, { components }) });
1037
+ };
1038
+ var MessageContentPart = memo2(
1039
+ MessageContentPartImpl,
1040
+ (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
1041
  );
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,
1042
+ var MessageContent = ({ components }) => {
1043
+ const { useMessage } = useMessageContext();
1044
+ const contentLength = useMessage((s) => s.message.content.length);
1045
+ return new Array(contentLength).fill(null).map((_, idx) => {
1046
+ const partIndex = idx;
1047
+ return /* @__PURE__ */ jsx14(
1048
+ MessageContentPart,
678
1049
  {
679
- messageIndex,
1050
+ partIndex,
680
1051
  components
681
1052
  },
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
- }
1053
+ partIndex
706
1054
  );
707
1055
  });
708
- ActionButton.displayName = displayName;
709
- return ActionButton;
710
1056
  };
711
1057
 
712
- // src/primitives/thread/ThreadScrollToBottom.tsx
713
- var ThreadScrollToBottom = createActionButton(
714
- "ThreadScrollToBottom",
715
- useThreadScrollToBottom
716
- );
1058
+ // src/primitives/message/MessageInProgress.tsx
1059
+ import { Primitive as Primitive5 } from "@radix-ui/react-primitive";
1060
+ import {
1061
+ forwardRef as forwardRef7,
1062
+ useMemo as useMemo3
1063
+ } from "react";
1064
+ import { jsx as jsx15 } from "react/jsx-runtime";
1065
+ var MessageInProgress = forwardRef7((props, ref) => {
1066
+ const { useMessageUtils } = useMessageContext();
1067
+ useMemo3(() => {
1068
+ useMessageUtils.getState().setInProgressIndicator(/* @__PURE__ */ jsx15(Primitive5.span, { ...props, ref }));
1069
+ }, [useMessageUtils, props, ref]);
1070
+ return null;
1071
+ });
1072
+ MessageInProgress.displayName = "MessageInProgress";
717
1073
 
718
- // src/primitives/thread/ThreadSuggestion.tsx
719
- var ThreadSuggestion = createActionButton(
720
- "ThreadSuggestion",
721
- useThreadSuggestion
722
- );
1074
+ // src/primitives/branchPicker/BranchPickerRoot.tsx
1075
+ import { jsx as jsx16 } from "react/jsx-runtime";
1076
+ var BranchPickerRoot = forwardRef8(({ hideWhenSingleBranch, ...rest }, ref) => {
1077
+ return /* @__PURE__ */ jsx16(MessageIf, { hasBranches: hideWhenSingleBranch ? true : void 0, children: /* @__PURE__ */ jsx16(Primitive6.div, { ...rest, ref }) });
1078
+ });
1079
+ BranchPickerRoot.displayName = "BranchPickerRoot";
723
1080
 
724
1081
  // src/primitives/composer/index.ts
725
1082
  var composer_exports = {};
@@ -732,13 +1089,13 @@ __export(composer_exports, {
732
1089
  });
733
1090
 
734
1091
  // src/primitives/composer/ComposerRoot.tsx
735
- import { composeEventHandlers as composeEventHandlers2 } from "@radix-ui/primitive";
736
- import { Primitive as Primitive4 } from "@radix-ui/react-primitive";
1092
+ import { composeEventHandlers as composeEventHandlers5 } from "@radix-ui/primitive";
1093
+ import { Primitive as Primitive7 } from "@radix-ui/react-primitive";
737
1094
  import {
738
- forwardRef as forwardRef4
1095
+ forwardRef as forwardRef9
739
1096
  } from "react";
740
- import { jsx as jsx6 } from "react/jsx-runtime";
741
- var ComposerRoot = forwardRef4(
1097
+ import { jsx as jsx17 } from "react/jsx-runtime";
1098
+ var ComposerRoot = forwardRef9(
742
1099
  ({ onSubmit, ...rest }, forwardedRef) => {
743
1100
  const send = useComposerSend();
744
1101
  const handleSubmit = (e) => {
@@ -746,12 +1103,12 @@ var ComposerRoot = forwardRef4(
746
1103
  e.preventDefault();
747
1104
  send();
748
1105
  };
749
- return /* @__PURE__ */ jsx6(
750
- Primitive4.form,
1106
+ return /* @__PURE__ */ jsx17(
1107
+ Primitive7.form,
751
1108
  {
752
1109
  ...rest,
753
1110
  ref: forwardedRef,
754
- onSubmit: composeEventHandlers2(onSubmit, handleSubmit)
1111
+ onSubmit: composeEventHandlers5(onSubmit, handleSubmit)
755
1112
  }
756
1113
  );
757
1114
  }
@@ -759,19 +1116,19 @@ var ComposerRoot = forwardRef4(
759
1116
  ComposerRoot.displayName = "ComposerRoot";
760
1117
 
761
1118
  // 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";
1119
+ import { composeEventHandlers as composeEventHandlers6 } from "@radix-ui/primitive";
1120
+ import { useComposedRefs } from "@radix-ui/react-compose-refs";
764
1121
  import { Slot } from "@radix-ui/react-slot";
765
1122
  import {
766
- forwardRef as forwardRef5,
767
- useCallback as useCallback12,
768
- useEffect as useEffect3,
1123
+ forwardRef as forwardRef10,
1124
+ useCallback as useCallback10,
1125
+ useEffect as useEffect5,
769
1126
  useRef as useRef3
770
1127
  } from "react";
771
1128
  import TextareaAutosize from "react-textarea-autosize";
772
1129
  import { useEscapeKeydown } from "@radix-ui/react-use-escape-keydown";
773
- import { jsx as jsx7 } from "react/jsx-runtime";
774
- var ComposerInput = forwardRef5(
1130
+ import { jsx as jsx18 } from "react/jsx-runtime";
1131
+ var ComposerInput = forwardRef10(
775
1132
  ({ autoFocus = false, asChild, disabled, onChange, onKeyDown, ...rest }, forwardedRef) => {
776
1133
  const { useThread } = useThreadContext();
777
1134
  const { useComposer, type } = useComposerContext();
@@ -781,7 +1138,7 @@ var ComposerInput = forwardRef5(
781
1138
  });
782
1139
  const Component = asChild ? Slot : TextareaAutosize;
783
1140
  const textareaRef = useRef3(null);
784
- const ref = useComposedRefs3(forwardedRef, textareaRef);
1141
+ const ref = useComposedRefs(forwardedRef, textareaRef);
785
1142
  useEscapeKeydown((e) => {
786
1143
  const composer = useComposer.getState();
787
1144
  if (composer.cancel()) {
@@ -799,35 +1156,34 @@ var ComposerInput = forwardRef5(
799
1156
  }
800
1157
  };
801
1158
  const autoFocusEnabled = autoFocus && !disabled;
802
- const focus = useCallback12(() => {
1159
+ const focus = useCallback10(() => {
803
1160
  const textarea = textareaRef.current;
804
1161
  if (!textarea || !autoFocusEnabled) return;
805
- textarea.focus();
1162
+ textarea.focus({ preventScroll: true });
806
1163
  textarea.setSelectionRange(
807
1164
  textareaRef.current.value.length,
808
1165
  textareaRef.current.value.length
809
1166
  );
810
1167
  }, [autoFocusEnabled]);
811
- useEffect3(() => focus(), [focus]);
812
- useOnScrollToBottom(() => {
1168
+ useEffect5(() => focus(), [focus]);
1169
+ useOnComposerFocus(() => {
813
1170
  if (type === "new") {
814
1171
  focus();
815
1172
  }
816
1173
  });
817
- return /* @__PURE__ */ jsx7(
1174
+ return /* @__PURE__ */ jsx18(
818
1175
  Component,
819
1176
  {
820
1177
  value,
821
1178
  ...rest,
822
1179
  ref,
823
- autoFocus,
824
1180
  disabled,
825
- onChange: composeEventHandlers3(onChange, (e) => {
1181
+ onChange: composeEventHandlers6(onChange, (e) => {
826
1182
  const composerState = useComposer.getState();
827
1183
  if (!composerState.isEditing) return;
828
1184
  return composerState.setValue(e.target.value);
829
1185
  }),
830
- onKeyDown: composeEventHandlers3(onKeyDown, handleKeyPress)
1186
+ onKeyDown: composeEventHandlers6(onKeyDown, handleKeyPress)
831
1187
  }
832
1188
  );
833
1189
  }
@@ -835,15 +1191,15 @@ var ComposerInput = forwardRef5(
835
1191
  ComposerInput.displayName = "ComposerInput";
836
1192
 
837
1193
  // 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(
1194
+ import { forwardRef as forwardRef11 } from "react";
1195
+ import { Primitive as Primitive8 } from "@radix-ui/react-primitive";
1196
+ import { jsx as jsx19 } from "react/jsx-runtime";
1197
+ var ComposerSend = forwardRef11(
842
1198
  ({ disabled, ...rest }, ref) => {
843
1199
  const { useComposer } = useComposerContext();
844
1200
  const hasValue = useComposer((c) => c.isEditing && c.value.length > 0);
845
- return /* @__PURE__ */ jsx8(
846
- Primitive5.button,
1201
+ return /* @__PURE__ */ jsx19(
1202
+ Primitive8.button,
847
1203
  {
848
1204
  type: "submit",
849
1205
  ...rest,
@@ -861,363 +1217,397 @@ var ComposerCancel = createActionButton(
861
1217
  useComposerCancel
862
1218
  );
863
1219
 
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
1220
+ // src/primitives/composer/ComposerIf.tsx
1221
+ var ComposerIf = ({ children, ...query }) => {
1222
+ const result = useComposerIf(query);
1223
+ return result ? children : null;
1224
+ };
1225
+
1226
+ // src/primitives/contentPart/index.ts
1227
+ var contentPart_exports = {};
1228
+ __export(contentPart_exports, {
1229
+ Display: () => ContentPartDisplay,
1230
+ Image: () => ContentPartImage,
1231
+ InProgressIndicator: () => ContentPartInProgressIndicator,
1232
+ Text: () => ContentPartText
871
1233
  });
872
1234
 
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
- );
1235
+ // src/primitives/contentPart/ContentPartImage.tsx
1236
+ import { Primitive as Primitive9 } from "@radix-ui/react-primitive";
1237
+ import { forwardRef as forwardRef12 } from "react";
1238
+ import { jsx as jsx20 } from "react/jsx-runtime";
1239
+ var ContentPartImage = forwardRef12((props, forwardedRef) => {
1240
+ const image = useContentPartImage();
1241
+ return /* @__PURE__ */ jsx20(Primitive9.img, { src: image, ...props, ref: forwardedRef });
1242
+ });
1243
+ ContentPartImage.displayName = "ContentPartImage";
1244
+
1245
+ // src/primitives/thread/index.ts
1246
+ var thread_exports = {};
1247
+ __export(thread_exports, {
1248
+ Empty: () => ThreadEmpty,
1249
+ If: () => ThreadIf,
1250
+ Messages: () => ThreadMessages,
1251
+ Root: () => ThreadRoot,
1252
+ ScrollToBottom: () => ThreadScrollToBottom,
1253
+ Suggestion: () => ThreadSuggestion,
1254
+ Viewport: () => ThreadViewport
1255
+ });
1256
+
1257
+ // src/primitives/thread/ThreadRoot.tsx
1258
+ import { Primitive as Primitive10 } from "@radix-ui/react-primitive";
1259
+ import { forwardRef as forwardRef13 } from "react";
1260
+ import { jsx as jsx21 } from "react/jsx-runtime";
1261
+ var ThreadRoot = forwardRef13(
1262
+ (props, ref) => {
1263
+ return /* @__PURE__ */ jsx21(Primitive10.div, { ...props, ref });
897
1264
  }
898
1265
  );
899
- MessageRoot.displayName = "MessageRoot";
900
-
901
- // src/primitives/message/MessageContent.tsx
902
- import { memo as memo2 } from "react";
1266
+ ThreadRoot.displayName = "ThreadRoot";
903
1267
 
904
- // src/context/providers/ContentPartProvider.tsx
905
- import { useEffect as useEffect4, useState as useState2 } from "react";
906
- 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
- );
921
- };
922
- var useContentPartContext2 = (partIndex) => {
923
- const { useMessage } = useMessageContext();
924
- const [context] = useState2(() => {
925
- const useContentPart = create4(
926
- () => ({})
927
- );
928
- syncContentPart(useMessage.getState(), useContentPart, partIndex);
929
- return { useContentPart };
930
- });
931
- useEffect4(() => {
932
- syncContentPart(useMessage.getState(), context.useContentPart, partIndex);
933
- return useMessage.subscribe((message) => {
934
- syncContentPart(message, context.useContentPart, partIndex);
935
- });
936
- }, [context, useMessage, partIndex]);
937
- return context;
938
- };
939
- var ContentPartProvider = ({
940
- partIndex,
941
- children
942
- }) => {
943
- const context = useContentPartContext2(partIndex);
944
- return /* @__PURE__ */ jsx10(ContentPartContext.Provider, { value: context, children });
1268
+ // src/primitives/thread/ThreadEmpty.tsx
1269
+ var ThreadEmpty = ({ children }) => {
1270
+ const empty = useThreadEmpty();
1271
+ return empty ? children : null;
945
1272
  };
946
1273
 
947
- // src/primitives/contentPart/ContentPartDisplay.tsx
948
- var ContentPartDisplay = () => {
949
- const display = useContentPartDisplay();
950
- return display ?? null;
1274
+ // src/primitives/thread/ThreadIf.tsx
1275
+ var ThreadIf = ({ children, ...query }) => {
1276
+ const result = useThreadIf(query);
1277
+ return result ? children : null;
951
1278
  };
952
1279
 
953
- // src/primitives/contentPart/ContentPartInProgressIndicator.tsx
954
- var ContentPartInProgressIndicator = () => {
955
- const indicator = useContentPartInProgressIndicator();
956
- return indicator;
957
- };
1280
+ // src/primitives/thread/ThreadViewport.tsx
1281
+ import { useComposedRefs as useComposedRefs3 } from "@radix-ui/react-compose-refs";
1282
+ import { Primitive as Primitive11 } from "@radix-ui/react-primitive";
1283
+ import { forwardRef as forwardRef14 } from "react";
958
1284
 
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";
1285
+ // src/primitive-hooks/thread/useThreadViewportAutoScroll.tsx
1286
+ import { useComposedRefs as useComposedRefs2 } from "@radix-ui/react-compose-refs";
1287
+ import { useRef as useRef5 } from "react";
968
1288
 
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
- }
1289
+ // src/utils/hooks/useOnResizeContent.tsx
1290
+ import { useCallbackRef as useCallbackRef2 } from "@radix-ui/react-use-callback-ref";
1291
+ import { useCallback as useCallback12 } from "react";
1292
+
1293
+ // src/utils/hooks/useManagedRef.ts
1294
+ import { useCallback as useCallback11, useRef as useRef4 } from "react";
1295
+ var useManagedRef = (callback) => {
1296
+ const cleanupRef = useRef4();
1297
+ const ref = useCallback11(
1298
+ (el) => {
1299
+ if (cleanupRef.current) {
1300
+ cleanupRef.current();
1301
+ }
1302
+ if (el) {
1303
+ cleanupRef.current = callback(el);
1304
+ }
1305
+ },
1306
+ [callback]
1307
+ );
1308
+ return ref;
1015
1309
  };
1016
- var MessageContentPartImpl = ({
1017
- partIndex,
1018
- components
1019
- }) => {
1020
- return /* @__PURE__ */ jsx12(ContentPartProvider, { partIndex, children: /* @__PURE__ */ jsx12(MessageContentPartComponent, { components }) });
1310
+
1311
+ // src/utils/hooks/useOnResizeContent.tsx
1312
+ var useOnResizeContent = (callback) => {
1313
+ const callbackRef = useCallbackRef2(callback);
1314
+ const refCallback = useCallback12(
1315
+ (el) => {
1316
+ const resizeObserver = new ResizeObserver(() => {
1317
+ callbackRef();
1318
+ });
1319
+ const mutationObserver = new MutationObserver((mutations) => {
1320
+ for (const mutation of mutations) {
1321
+ for (const node of mutation.addedNodes) {
1322
+ if (node instanceof Element) {
1323
+ resizeObserver.observe(node);
1324
+ }
1325
+ }
1326
+ for (const node of mutation.removedNodes) {
1327
+ if (node instanceof Element) {
1328
+ resizeObserver.unobserve(node);
1329
+ }
1330
+ }
1331
+ }
1332
+ callbackRef();
1333
+ });
1334
+ resizeObserver.observe(el);
1335
+ mutationObserver.observe(el, { childList: true });
1336
+ for (const child of el.children) {
1337
+ resizeObserver.observe(child);
1338
+ }
1339
+ return () => {
1340
+ resizeObserver.disconnect();
1341
+ mutationObserver.disconnect();
1342
+ };
1343
+ },
1344
+ [callbackRef]
1345
+ );
1346
+ return useManagedRef(refCallback);
1021
1347
  };
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
1025
- );
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,
1033
- {
1034
- partIndex,
1035
- components
1036
- },
1037
- partIndex
1038
- );
1348
+
1349
+ // src/utils/hooks/useOnScrollToBottom.tsx
1350
+ import { useCallbackRef as useCallbackRef3 } from "@radix-ui/react-use-callback-ref";
1351
+ import { useEffect as useEffect6 } from "react";
1352
+ var useOnScrollToBottom = (callback) => {
1353
+ const callbackRef = useCallbackRef3(callback);
1354
+ const { useViewport } = useThreadContext();
1355
+ useEffect6(() => {
1356
+ return useViewport.getState().onScrollToBottom(() => {
1357
+ callbackRef();
1358
+ });
1359
+ }, [useViewport, callbackRef]);
1360
+ };
1361
+
1362
+ // src/primitive-hooks/thread/useThreadViewportAutoScroll.tsx
1363
+ var useThreadViewportAutoScroll = ({
1364
+ autoScroll = true
1365
+ }) => {
1366
+ const divRef = useRef5(null);
1367
+ const { useViewport } = useThreadContext();
1368
+ const firstRenderRef = useRef5(true);
1369
+ const lastScrollTop = useRef5(0);
1370
+ const isScrollingToBottomRef = useRef5(false);
1371
+ const scrollToBottom = () => {
1372
+ const div = divRef.current;
1373
+ if (!div || !autoScroll) return;
1374
+ const behavior = firstRenderRef.current ? "instant" : "auto";
1375
+ firstRenderRef.current = false;
1376
+ isScrollingToBottomRef.current = true;
1377
+ div.scrollTo({ top: div.scrollHeight, behavior });
1378
+ };
1379
+ const handleScroll = () => {
1380
+ const div = divRef.current;
1381
+ if (!div) return;
1382
+ const isAtBottom = useViewport.getState().isAtBottom;
1383
+ const newIsAtBottom = div.scrollHeight - div.scrollTop <= div.clientHeight;
1384
+ if (!newIsAtBottom && lastScrollTop.current < div.scrollTop) {
1385
+ } else {
1386
+ isScrollingToBottomRef.current = newIsAtBottom;
1387
+ if (newIsAtBottom !== isAtBottom) {
1388
+ useViewport.setState({
1389
+ isAtBottom: newIsAtBottom
1390
+ });
1391
+ }
1392
+ }
1393
+ lastScrollTop.current = div.scrollTop;
1394
+ };
1395
+ const resizeRef = useOnResizeContent(() => {
1396
+ if (!isScrollingToBottomRef.current && !useViewport.getState().isAtBottom && !firstRenderRef.current) {
1397
+ handleScroll();
1398
+ } else {
1399
+ scrollToBottom();
1400
+ }
1401
+ });
1402
+ const scrollRef = useManagedRef((el) => {
1403
+ el.addEventListener("scroll", handleScroll);
1404
+ return () => {
1405
+ el.removeEventListener("scroll", handleScroll);
1406
+ };
1407
+ });
1408
+ const autoScrollRef = useComposedRefs2(resizeRef, scrollRef, divRef);
1409
+ useOnScrollToBottom(() => {
1410
+ scrollToBottom();
1039
1411
  });
1412
+ return autoScrollRef;
1040
1413
  };
1041
1414
 
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;
1415
+ // src/primitives/thread/ThreadViewport.tsx
1416
+ import { jsx as jsx22 } from "react/jsx-runtime";
1417
+ var ThreadViewport = forwardRef14(({ autoScroll, onScroll, children, ...rest }, forwardedRef) => {
1418
+ const autoScrollRef = useThreadViewportAutoScroll({
1419
+ autoScroll
1420
+ });
1421
+ const ref = useComposedRefs3(forwardedRef, autoScrollRef);
1422
+ return /* @__PURE__ */ jsx22(Primitive11.div, { ...rest, ref, children });
1055
1423
  });
1056
- MessageInProgress.displayName = "MessageInProgress";
1424
+ ThreadViewport.displayName = "ThreadViewport";
1057
1425
 
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
- });
1426
+ // src/primitives/thread/ThreadMessages.tsx
1427
+ import { memo as memo3 } from "react";
1067
1428
 
1068
- // src/primitives/branchPicker/BranchPickerNext.tsx
1069
- var BranchPickerNext = createActionButton(
1070
- "BranchPickerNext",
1071
- useBranchPickerNext
1072
- );
1429
+ // src/context/providers/MessageProvider.tsx
1430
+ import { useEffect as useEffect7, useState as useState5 } from "react";
1431
+ import { create as create10 } from "zustand";
1073
1432
 
1074
- // src/primitives/branchPicker/BranchPickerPrevious.tsx
1075
- var BranchPickerPrevious = createActionButton(
1076
- "BranchPickerPrevious",
1077
- useBranchPickerPrevious
1078
- );
1433
+ // src/context/stores/EditComposer.ts
1434
+ import { create as create8 } from "zustand";
1435
+ var makeEditComposerStore = ({
1436
+ onEdit,
1437
+ onSend
1438
+ }) => create8()((set, get, store) => ({
1439
+ ...makeBaseComposer(set, get, store),
1440
+ isEditing: false,
1441
+ edit: () => {
1442
+ const value = onEdit();
1443
+ set({ isEditing: true, value });
1444
+ },
1445
+ send: () => {
1446
+ const value = get().value;
1447
+ set({ isEditing: false });
1448
+ onSend(value);
1449
+ },
1450
+ cancel: () => {
1451
+ if (!get().isEditing) return false;
1452
+ set({ isEditing: false });
1453
+ return true;
1454
+ }
1455
+ }));
1079
1456
 
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
- };
1457
+ // src/context/stores/MessageUtils.ts
1458
+ import { create as create9 } from "zustand";
1459
+ var makeMessageUtilsStore = () => create9((set) => ({
1460
+ inProgressIndicator: null,
1461
+ setInProgressIndicator: (value) => {
1462
+ set({ inProgressIndicator: value });
1463
+ },
1464
+ isCopied: false,
1465
+ setIsCopied: (value) => {
1466
+ set({ isCopied: value });
1467
+ },
1468
+ isHovering: false,
1469
+ setIsHovering: (value) => {
1470
+ set({ isHovering: value });
1471
+ }
1472
+ }));
1086
1473
 
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 });
1474
+ // src/context/providers/MessageProvider.tsx
1475
+ import { jsx as jsx23 } from "react/jsx-runtime";
1476
+ var getIsLast = (thread, message) => {
1477
+ return thread.messages[thread.messages.length - 1]?.id === message.id;
1092
1478
  };
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
- );
1479
+ var syncMessage = (thread, getBranches, useMessage, messageIndex) => {
1480
+ const parentId = thread.messages[messageIndex - 1]?.id ?? null;
1481
+ const message = thread.messages[messageIndex];
1482
+ if (!message) return;
1483
+ const isLast = getIsLast(thread, message);
1484
+ const branches = getBranches(message.id);
1485
+ const currentState = useMessage.getState();
1486
+ if (currentState.message === message && currentState.parentId === parentId && currentState.branches === branches && currentState.isLast === isLast)
1487
+ return;
1488
+ useMessage.setState({
1489
+ message,
1490
+ parentId,
1491
+ branches,
1492
+ isLast
1493
+ });
1135
1494
  };
1136
- var ActionBarRoot = forwardRef11(({ hideWhenRunning, autohide, autohideFloat, ...rest }, ref) => {
1137
- const hideAndfloatStatus = useActionBarFloatStatus({
1138
- hideWhenRunning,
1139
- autohide,
1140
- autohideFloat
1495
+ var useMessageContext2 = (messageIndex) => {
1496
+ const { useThread, useThreadActions } = useThreadContext();
1497
+ const [context] = useState5(() => {
1498
+ const useMessage = create10(() => ({}));
1499
+ const useMessageUtils = makeMessageUtilsStore();
1500
+ const useEditComposer = makeEditComposerStore({
1501
+ onEdit: () => {
1502
+ const message = useMessage.getState().message;
1503
+ if (message.role !== "user")
1504
+ throw new Error(
1505
+ "Tried to edit a non-user message. Editing is only supported for user messages. This is likely an internal bug in assistant-ui."
1506
+ );
1507
+ const text = getMessageText(message);
1508
+ return text;
1509
+ },
1510
+ onSend: (text) => {
1511
+ const { message, parentId } = useMessage.getState();
1512
+ if (message.role !== "user")
1513
+ throw new Error(
1514
+ "Tried to edit a non-user message. Editing is only supported for user messages. This is likely an internal bug in assistant-ui."
1515
+ );
1516
+ const nonTextParts = message.content.filter(
1517
+ (part) => part.type !== "text" && part.type !== "ui"
1518
+ );
1519
+ useThreadActions.getState().append({
1520
+ parentId,
1521
+ role: "user",
1522
+ content: [{ type: "text", text }, ...nonTextParts]
1523
+ });
1524
+ }
1525
+ });
1526
+ syncMessage(
1527
+ useThread.getState(),
1528
+ useThreadActions.getState().getBranches,
1529
+ useMessage,
1530
+ messageIndex
1531
+ );
1532
+ return { useMessage, useMessageUtils, useEditComposer };
1141
1533
  });
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";
1534
+ useEffect7(() => {
1535
+ return useThread.subscribe((thread) => {
1536
+ syncMessage(
1537
+ thread,
1538
+ useThreadActions.getState().getBranches,
1539
+ context.useMessage,
1540
+ messageIndex
1541
+ );
1542
+ });
1543
+ }, [useThread, useThreadActions, context, messageIndex]);
1544
+ return context;
1545
+ };
1546
+ var MessageProvider = ({
1547
+ messageIndex,
1548
+ children
1549
+ }) => {
1550
+ const context = useMessageContext2(messageIndex);
1551
+ return /* @__PURE__ */ jsx23(MessageContext.Provider, { value: context, children });
1552
+ };
1153
1553
 
1154
- // src/primitives/actionBar/ActionBarCopy.tsx
1155
- var ActionBarCopy = createActionButton(
1156
- "ActionBarCopy",
1157
- useActionBarCopy
1554
+ // src/primitives/thread/ThreadMessages.tsx
1555
+ import { jsx as jsx24, jsxs as jsxs3 } from "react/jsx-runtime";
1556
+ var getComponents = (components) => {
1557
+ return {
1558
+ EditComposer: components.EditComposer ?? components.UserMessage ?? components.Message,
1559
+ UserMessage: components.UserMessage ?? components.Message,
1560
+ AssistantMessage: components.AssistantMessage ?? components.Message
1561
+ };
1562
+ };
1563
+ var ThreadMessageImpl = ({
1564
+ messageIndex,
1565
+ components
1566
+ }) => {
1567
+ const { UserMessage, EditComposer, AssistantMessage } = getComponents(components);
1568
+ return /* @__PURE__ */ jsxs3(MessageProvider, { messageIndex, children: [
1569
+ /* @__PURE__ */ jsxs3(MessageIf, { user: true, children: [
1570
+ /* @__PURE__ */ jsx24(ComposerIf, { editing: false, children: /* @__PURE__ */ jsx24(UserMessage, {}) }),
1571
+ /* @__PURE__ */ jsx24(ComposerIf, { editing: true, children: /* @__PURE__ */ jsx24(EditComposer, {}) })
1572
+ ] }),
1573
+ /* @__PURE__ */ jsx24(MessageIf, { assistant: true, children: /* @__PURE__ */ jsx24(AssistantMessage, {}) })
1574
+ ] });
1575
+ };
1576
+ var ThreadMessage = memo3(
1577
+ ThreadMessageImpl,
1578
+ (prev, next) => prev.messageIndex === next.messageIndex && prev.components.UserMessage === next.components.UserMessage && prev.components.EditComposer === next.components.EditComposer && prev.components.AssistantMessage === next.components.AssistantMessage
1158
1579
  );
1580
+ var ThreadMessages = ({ components }) => {
1581
+ const { useThread } = useThreadContext();
1582
+ const messagesLength = useThread((t) => t.messages.length);
1583
+ if (messagesLength === 0) return null;
1584
+ return new Array(messagesLength).fill(null).map((_, idx) => {
1585
+ const messageIndex = idx;
1586
+ return /* @__PURE__ */ jsx24(
1587
+ ThreadMessage,
1588
+ {
1589
+ messageIndex,
1590
+ components
1591
+ },
1592
+ messageIndex
1593
+ );
1594
+ });
1595
+ };
1159
1596
 
1160
- // src/primitives/actionBar/ActionBarReload.tsx
1161
- var ActionBarReload = createActionButton(
1162
- "ActionBarReload",
1163
- useActionBarReload
1597
+ // src/primitives/thread/ThreadScrollToBottom.tsx
1598
+ var ThreadScrollToBottom = createActionButton(
1599
+ "ThreadScrollToBottom",
1600
+ useThreadScrollToBottom
1164
1601
  );
1165
1602
 
1166
- // src/primitives/actionBar/ActionBarEdit.tsx
1167
- var ActionBarEdit = createActionButton(
1168
- "ActionBarEdit",
1169
- useActionBarEdit
1603
+ // src/primitives/thread/ThreadSuggestion.tsx
1604
+ var ThreadSuggestion = createActionButton(
1605
+ "ThreadSuggestion",
1606
+ useThreadSuggestion
1170
1607
  );
1171
1608
 
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
1609
  // src/runtime/local/useLocalRuntime.tsx
1192
- import { useInsertionEffect, useState as useState3 } from "react";
1193
-
1194
- // src/utils/ModelConfigTypes.ts
1195
- var mergeModelConfigs = (configSet) => {
1196
- const configs = Array.from(configSet).map((c) => c()).sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
1197
- return configs.reduce((acc, config) => {
1198
- if (config.system) {
1199
- if (acc.system) {
1200
- acc.system += `
1201
-
1202
- ${config.system}`;
1203
- } else {
1204
- acc.system = config.system;
1205
- }
1206
- }
1207
- if (config.tools) {
1208
- for (const [name, tool] of Object.entries(config.tools)) {
1209
- if (acc.tools?.[name]) {
1210
- throw new Error(
1211
- `You tried to define a tool with the name ${name}, but it already exists.`
1212
- );
1213
- }
1214
- if (!acc.tools) acc.tools = {};
1215
- acc.tools[name] = tool;
1216
- }
1217
- }
1218
- return acc;
1219
- }, {});
1220
- };
1610
+ import { useInsertionEffect as useInsertionEffect3, useState as useState6 } from "react";
1221
1611
 
1222
1612
  // src/runtime/utils/idUtils.tsx
1223
1613
  import { customAlphabet } from "nanoid/non-secure";
@@ -1475,219 +1865,81 @@ var LocalRuntime = class {
1475
1865
 
1476
1866
  // src/runtime/local/useLocalRuntime.tsx
1477
1867
  var useLocalRuntime = (adapter) => {
1478
- const [runtime] = useState3(() => new LocalRuntime(adapter));
1479
- useInsertionEffect(() => {
1868
+ const [runtime] = useState6(() => new LocalRuntime(adapter));
1869
+ useInsertionEffect3(() => {
1480
1870
  runtime.adapter = adapter;
1481
1871
  });
1482
1872
  return runtime;
1483
1873
  };
1484
1874
 
1485
- // src/context/providers/AssistantRuntimeProvider.tsx
1486
- import { memo as memo3 } from "react";
1487
-
1488
- // src/context/providers/AssistantProvider.tsx
1489
- import { useEffect as useEffect6, useInsertionEffect as useInsertionEffect3, useRef as useRef5, useState as useState5 } from "react";
1490
-
1491
- // src/context/stores/AssistantModelConfig.ts
1492
- import { create as create5 } from "zustand";
1493
-
1494
- // src/utils/ProxyConfigProvider.ts
1495
- var ProxyConfigProvider = class {
1496
- _providers = /* @__PURE__ */ new Set();
1497
- getModelConfig() {
1498
- return mergeModelConfigs(this._providers);
1499
- }
1500
- registerModelConfigProvider(provider) {
1501
- this._providers.add(provider);
1875
+ // src/model-config/useAssistantTool.tsx
1876
+ import { useEffect as useEffect8 } from "react";
1877
+ var useAssistantTool = (tool) => {
1878
+ const { useModelConfig, useToolUIs } = useAssistantContext();
1879
+ const registerModelConfigProvider = useModelConfig(
1880
+ (s) => s.registerModelConfigProvider
1881
+ );
1882
+ const setToolUI = useToolUIs((s) => s.setToolUI);
1883
+ useEffect8(() => {
1884
+ const { toolName, render, ...rest } = tool;
1885
+ const config = {
1886
+ tools: {
1887
+ [tool.toolName]: rest
1888
+ }
1889
+ };
1890
+ const unsub1 = registerModelConfigProvider(() => config);
1891
+ const unsub2 = render ? setToolUI(toolName, render) : void 0;
1502
1892
  return () => {
1503
- this._providers.delete(provider);
1893
+ unsub1();
1894
+ unsub2?.();
1504
1895
  };
1505
- }
1896
+ }, [registerModelConfigProvider, setToolUI, tool]);
1506
1897
  };
1507
1898
 
1508
- // src/context/stores/AssistantModelConfig.ts
1509
- var makeAssistantModelConfigStore = () => create5(() => {
1510
- const proxy = new ProxyConfigProvider();
1511
- return Object.freeze({
1512
- getModelConfig: () => {
1513
- return proxy.getModelConfig();
1514
- },
1515
- registerModelConfigProvider: (provider) => {
1516
- return proxy.registerModelConfigProvider(provider);
1517
- }
1518
- });
1519
- });
1520
-
1521
- // src/context/stores/AssistantToolUIs.ts
1522
- import { create as create6 } from "zustand";
1523
- var makeAssistantToolUIsStore = () => create6((set) => {
1524
- const renderers = /* @__PURE__ */ new Map();
1525
- return Object.freeze({
1526
- getToolUI: (name) => {
1527
- const arr = renderers.get(name);
1528
- const last = arr?.at(-1);
1529
- if (last) return last;
1530
- return null;
1531
- },
1532
- setToolUI: (name, render) => {
1533
- let arr = renderers.get(name);
1534
- if (!arr) {
1535
- arr = [];
1536
- renderers.set(name, arr);
1537
- }
1538
- arr.push(render);
1539
- set({});
1540
- return () => {
1541
- const index = arr.indexOf(render);
1542
- if (index !== -1) {
1543
- arr.splice(index, 1);
1544
- }
1545
- if (index === arr.length) {
1546
- set({});
1547
- }
1548
- };
1549
- }
1550
- });
1551
- });
1552
-
1553
- // src/context/providers/ThreadProvider.tsx
1554
- import { useEffect as useEffect5, useInsertionEffect as useInsertionEffect2, useRef as useRef4, useState as useState4 } from "react";
1555
-
1556
- // src/context/stores/Composer.ts
1557
- 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
- }
1899
+ // src/model-config/makeAssistantTool.tsx
1900
+ var makeAssistantTool = (tool) => {
1901
+ const Tool = () => {
1902
+ useAssistantTool(tool);
1903
+ return null;
1577
1904
  };
1578
- });
1579
-
1580
- // src/context/stores/Thread.ts
1581
- import { create as create8 } from "zustand";
1582
- var makeThreadStore = (runtimeRef) => {
1583
- return create8(() => ({
1584
- messages: runtimeRef.current.messages,
1585
- isRunning: runtimeRef.current.isRunning
1586
- }));
1905
+ return Tool;
1587
1906
  };
1588
1907
 
1589
- // src/context/stores/ThreadViewport.tsx
1590
- import { create as create9 } from "zustand";
1591
- var makeThreadViewportStore = () => {
1592
- const scrollToBottomListeners = /* @__PURE__ */ new Set();
1593
- return create9(() => ({
1594
- isAtBottom: true,
1595
- scrollToBottom: () => {
1596
- for (const listener of scrollToBottomListeners) {
1597
- listener();
1598
- }
1599
- },
1600
- onScrollToBottom: (callback) => {
1601
- scrollToBottomListeners.add(callback);
1602
- return () => {
1603
- scrollToBottomListeners.delete(callback);
1604
- };
1605
- }
1606
- }));
1908
+ // src/model-config/useAssistantToolUI.tsx
1909
+ import { useEffect as useEffect9 } from "react";
1910
+ var useAssistantToolUI = (tool) => {
1911
+ const { useToolUIs } = useAssistantContext();
1912
+ const setToolUI = useToolUIs((s) => s.setToolUI);
1913
+ useEffect9(() => {
1914
+ if (!tool) return;
1915
+ const { toolName, render } = tool;
1916
+ return setToolUI(toolName, render);
1917
+ }, [setToolUI, tool]);
1607
1918
  };
1608
1919
 
1609
- // src/context/stores/ThreadActions.ts
1610
- import { create as create10 } from "zustand";
1611
- var makeThreadActionStore = (runtimeRef) => {
1612
- return create10(
1613
- () => Object.freeze({
1614
- getBranches: (messageId) => runtimeRef.current.getBranches(messageId),
1615
- switchToBranch: (branchId) => runtimeRef.current.switchToBranch(branchId),
1616
- startRun: (parentId) => runtimeRef.current.startRun(parentId),
1617
- append: (message) => runtimeRef.current.append(message),
1618
- cancelRun: () => runtimeRef.current.cancelRun(),
1619
- addToolResult: (toolCallId, result) => runtimeRef.current.addToolResult(toolCallId, result)
1620
- })
1621
- );
1920
+ // src/model-config/makeAssistantToolUI.tsx
1921
+ var makeAssistantToolUI = (tool) => {
1922
+ const ToolUI = () => {
1923
+ useAssistantToolUI(tool);
1924
+ return null;
1925
+ };
1926
+ return ToolUI;
1622
1927
  };
1623
1928
 
1624
- // src/context/providers/ThreadProvider.tsx
1625
- import { jsx as jsx19, jsxs as jsxs3 } from "react/jsx-runtime";
1626
- var ThreadProvider = ({
1627
- children,
1628
- runtime
1629
- }) => {
1630
- const runtimeRef = useRef4(runtime);
1631
- useInsertionEffect2(() => {
1632
- runtimeRef.current = runtime;
1633
- });
1634
- const [context] = useState4(() => {
1635
- const useThread = makeThreadStore(runtimeRef);
1636
- const useThreadActions = makeThreadActionStore(runtimeRef);
1637
- const useViewport = makeThreadViewportStore();
1638
- const useComposer = makeComposerStore(useThread, useThreadActions);
1639
- return {
1640
- useThread,
1641
- useThreadActions,
1642
- useComposer,
1643
- useViewport
1644
- };
1645
- });
1646
- useEffect5(() => {
1647
- const onRuntimeUpdate = () => {
1648
- context.useThread.setState(
1649
- Object.freeze({
1650
- messages: runtimeRef.current.messages,
1651
- isRunning: runtimeRef.current.isRunning
1652
- }),
1653
- true
1654
- );
1929
+ // src/model-config/useAssistantInstructions.tsx
1930
+ import { useEffect as useEffect10 } from "react";
1931
+ var useAssistantInstructions = (instruction) => {
1932
+ const { useModelConfig } = useAssistantContext();
1933
+ const registerModelConfigProvider = useModelConfig(
1934
+ (s) => s.registerModelConfigProvider
1935
+ );
1936
+ useEffect10(() => {
1937
+ const config = {
1938
+ system: instruction
1655
1939
  };
1656
- onRuntimeUpdate();
1657
- return runtime.subscribe(onRuntimeUpdate);
1658
- }, [context, runtime]);
1659
- const RuntimeSynchronizer = runtime.unstable_synchronizer;
1660
- return /* @__PURE__ */ jsxs3(ThreadContext.Provider, { value: context, children: [
1661
- RuntimeSynchronizer && /* @__PURE__ */ jsx19(RuntimeSynchronizer, {}),
1662
- children
1663
- ] });
1664
- };
1665
-
1666
- // src/context/providers/AssistantProvider.tsx
1667
- import { jsx as jsx20 } from "react/jsx-runtime";
1668
- var AssistantProvider = ({ children, runtime }) => {
1669
- const runtimeRef = useRef5(runtime);
1670
- useInsertionEffect3(() => {
1671
- runtimeRef.current = runtime;
1672
- });
1673
- const [context] = useState5(() => {
1674
- const useModelConfig = makeAssistantModelConfigStore();
1675
- const useToolUIs = makeAssistantToolUIsStore();
1676
- return { useModelConfig, useToolUIs };
1677
- });
1678
- const getModelCOnfig = context.useModelConfig((c) => c.getModelConfig);
1679
- useEffect6(() => {
1680
- return runtime.registerModelConfigProvider(getModelCOnfig);
1681
- }, [runtime, getModelCOnfig]);
1682
- return /* @__PURE__ */ jsx20(AssistantContext.Provider, { value: context, children: /* @__PURE__ */ jsx20(ThreadProvider, { runtime, children }) });
1683
- };
1684
-
1685
- // src/context/providers/AssistantRuntimeProvider.tsx
1686
- import { jsx as jsx21 } from "react/jsx-runtime";
1687
- var AssistantRuntimeProviderImpl = ({ children, runtime }) => {
1688
- return /* @__PURE__ */ jsx21(AssistantProvider, { runtime, children });
1940
+ return registerModelConfigProvider(() => config);
1941
+ }, [registerModelConfigProvider, instruction]);
1689
1942
  };
1690
- var AssistantRuntimeProvider = memo3(AssistantRuntimeProviderImpl);
1691
1943
 
1692
1944
  // src/internal.ts
1693
1945
  var internal_exports = {};
@@ -1697,6 +1949,7 @@ __export(internal_exports, {
1697
1949
  });
1698
1950
  export {
1699
1951
  actionBar_exports as ActionBarPrimitive,
1952
+ assistantModal_exports as AssistantModalPrimitive,
1700
1953
  AssistantRuntimeProvider,
1701
1954
  branchPicker_exports as BranchPickerPrimitive,
1702
1955
  composer_exports as ComposerPrimitive,
@@ -1704,22 +1957,32 @@ export {
1704
1957
  internal_exports as INTERNAL,
1705
1958
  message_exports as MessagePrimitive,
1706
1959
  thread_exports as ThreadPrimitive,
1960
+ makeAssistantTool,
1961
+ makeAssistantToolUI,
1707
1962
  useActionBarCopy,
1708
1963
  useActionBarEdit,
1709
1964
  useActionBarReload,
1965
+ useAssistantContext,
1966
+ useAssistantInstructions,
1967
+ useAssistantTool,
1968
+ useAssistantToolUI,
1710
1969
  useBranchPickerCount,
1711
1970
  useBranchPickerNext,
1712
1971
  useBranchPickerNumber,
1713
1972
  useBranchPickerPrevious,
1714
1973
  useComposerCancel,
1974
+ useComposerContext,
1715
1975
  useComposerIf,
1716
1976
  useComposerSend,
1977
+ useContentPartContext,
1717
1978
  useContentPartDisplay,
1718
1979
  useContentPartImage,
1719
1980
  useContentPartInProgressIndicator,
1720
1981
  useContentPartText,
1721
1982
  useLocalRuntime,
1983
+ useMessageContext,
1722
1984
  useMessageIf,
1985
+ useThreadContext,
1723
1986
  useThreadEmpty,
1724
1987
  useThreadIf,
1725
1988
  useThreadScrollToBottom,