@assistant-ui/react 0.1.3 → 0.1.5

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 ADDED
@@ -0,0 +1,1572 @@
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-KIP3YFVM.mjs";
13
+
14
+ // src/actions/useCopyMessage.tsx
15
+ import { useCallback } from "react";
16
+
17
+ // src/utils/combined/useCombinedStore.ts
18
+ import { useMemo } from "react";
19
+
20
+ // src/utils/combined/createCombinedStore.ts
21
+ import { useSyncExternalStore } from "react";
22
+ var createCombinedStore = (stores) => {
23
+ const subscribe = (callback) => {
24
+ const unsubscribes = stores.map((store) => store.subscribe(callback));
25
+ return () => {
26
+ for (const unsub of unsubscribes) {
27
+ unsub();
28
+ }
29
+ };
30
+ };
31
+ return (selector) => {
32
+ const getSnapshot = () => selector(...stores.map((store) => store.getState()));
33
+ return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
34
+ };
35
+ };
36
+
37
+ // src/utils/combined/useCombinedStore.ts
38
+ var useCombinedStore = (stores, selector) => {
39
+ const useCombined = useMemo(() => createCombinedStore(stores), stores);
40
+ return useCombined(selector);
41
+ };
42
+
43
+ // src/utils/getMessageText.tsx
44
+ var getMessageText = (message) => {
45
+ const textParts = message.content.filter(
46
+ (part) => part.type === "text"
47
+ );
48
+ return textParts.map((part) => part.text).join("\n\n");
49
+ };
50
+
51
+ // src/actions/useCopyMessage.tsx
52
+ var useCopyMessage = ({ copiedDuration = 3e3 }) => {
53
+ const { useMessage, useComposer } = useMessageContext();
54
+ const hasCopyableContent = useCombinedStore(
55
+ [useMessage, useComposer],
56
+ (m, c) => {
57
+ return c.isEditing || m.message.content.some((c2) => c2.type === "text");
58
+ }
59
+ );
60
+ const callback = useCallback(() => {
61
+ const { isEditing, value: composerValue } = useComposer.getState();
62
+ const { message, setIsCopied } = useMessage.getState();
63
+ const valueToCopy = isEditing ? composerValue : getMessageText(message);
64
+ navigator.clipboard.writeText(valueToCopy);
65
+ setIsCopied(true);
66
+ setTimeout(() => setIsCopied(false), copiedDuration);
67
+ }, [useComposer, useMessage, copiedDuration]);
68
+ if (!hasCopyableContent) return null;
69
+ return callback;
70
+ };
71
+
72
+ // src/actions/useReloadMessage.tsx
73
+ import { useCallback as useCallback2 } from "react";
74
+ var useReloadMessage = () => {
75
+ const { useThread, useViewport } = useThreadContext();
76
+ const { useMessage } = useMessageContext();
77
+ const disabled = useCombinedStore(
78
+ [useThread, useMessage],
79
+ (t, m) => t.isRunning || m.message.role !== "assistant"
80
+ );
81
+ const callback = useCallback2(() => {
82
+ const { parentId } = useMessage.getState();
83
+ useThread.getState().startRun(parentId);
84
+ useViewport.getState().scrollToBottom();
85
+ }, [useMessage, useThread, useViewport]);
86
+ if (disabled) return null;
87
+ return callback;
88
+ };
89
+
90
+ // src/actions/useBeginMessageEdit.tsx
91
+ import { useCallback as useCallback3 } from "react";
92
+ var useBeginMessageEdit = () => {
93
+ const { useMessage, useComposer } = useMessageContext();
94
+ const disabled = useCombinedStore(
95
+ [useMessage, useComposer],
96
+ (m, c) => m.message.role !== "user" || c.isEditing
97
+ );
98
+ const callback = useCallback3(() => {
99
+ const { edit } = useComposer.getState();
100
+ edit();
101
+ }, [useComposer]);
102
+ if (disabled) return null;
103
+ return callback;
104
+ };
105
+
106
+ // src/actions/useGoToNextBranch.tsx
107
+ import { useCallback as useCallback4 } from "react";
108
+ var useGoToNextBranch = () => {
109
+ const { useThread } = useThreadContext();
110
+ const { useMessage, useComposer } = useMessageContext();
111
+ const disabled = useCombinedStore(
112
+ [useMessage, useComposer],
113
+ (m, c) => c.isEditing || m.branches.indexOf(m.message.id) + 1 >= m.branches.length
114
+ );
115
+ const callback = useCallback4(() => {
116
+ const { message, branches } = useMessage.getState();
117
+ useThread.getState().switchToBranch(branches[branches.indexOf(message.id) + 1]);
118
+ }, [useMessage, useThread]);
119
+ if (disabled) return null;
120
+ return callback;
121
+ };
122
+
123
+ // src/actions/useGoToPreviousBranch.tsx
124
+ import { useCallback as useCallback5 } from "react";
125
+ var useGoToPreviousBranch = () => {
126
+ const { useThread } = useThreadContext();
127
+ const { useMessage, useComposer } = useMessageContext();
128
+ const disabled = useCombinedStore(
129
+ [useMessage, useComposer],
130
+ (m, c) => c.isEditing || m.branches.indexOf(m.message.id) <= 0
131
+ );
132
+ const callback = useCallback5(() => {
133
+ const { message, branches } = useMessage.getState();
134
+ useThread.getState().switchToBranch(branches[branches.indexOf(message.id) - 1]);
135
+ }, [useMessage, useThread]);
136
+ if (disabled) return null;
137
+ return callback;
138
+ };
139
+
140
+ // src/primitives/thread/index.ts
141
+ var thread_exports = {};
142
+ __export(thread_exports, {
143
+ Empty: () => ThreadEmpty,
144
+ If: () => ThreadIf,
145
+ Messages: () => ThreadMessages,
146
+ Root: () => ThreadRoot,
147
+ ScrollToBottom: () => ThreadScrollToBottom,
148
+ Suggestion: () => ThreadSuggestion,
149
+ Viewport: () => ThreadViewport
150
+ });
151
+
152
+ // src/primitives/thread/ThreadRoot.tsx
153
+ import {
154
+ Primitive
155
+ } from "@radix-ui/react-primitive";
156
+ import { forwardRef } from "react";
157
+ import { jsx } from "react/jsx-runtime";
158
+ var ThreadRoot = forwardRef(
159
+ (props, ref) => {
160
+ return /* @__PURE__ */ jsx(Primitive.div, { ...props, ref });
161
+ }
162
+ );
163
+
164
+ // src/primitives/thread/ThreadIf.tsx
165
+ var useThreadIf = (props) => {
166
+ const { useThread } = useThreadContext();
167
+ return useThread((thread) => {
168
+ if (props.empty === true && thread.messages.length !== 0) return false;
169
+ if (props.empty === false && thread.messages.length === 0) return false;
170
+ if (props.running === true && !thread.isRunning) return false;
171
+ if (props.running === false && thread.isRunning) return false;
172
+ return true;
173
+ });
174
+ };
175
+ var ThreadIf = ({ children, ...query }) => {
176
+ const result = useThreadIf(query);
177
+ return result ? children : null;
178
+ };
179
+
180
+ // src/primitives/thread/ThreadEmpty.tsx
181
+ import { jsx as jsx2 } from "react/jsx-runtime";
182
+ var ThreadEmpty = ({ children }) => {
183
+ return /* @__PURE__ */ jsx2(ThreadIf, { empty: true, children });
184
+ };
185
+
186
+ // src/primitives/thread/ThreadViewport.tsx
187
+ import { composeEventHandlers } from "@radix-ui/primitive";
188
+ import { useComposedRefs } from "@radix-ui/react-compose-refs";
189
+ import {
190
+ Primitive as Primitive2
191
+ } from "@radix-ui/react-primitive";
192
+ import { forwardRef as forwardRef2, useRef } from "react";
193
+
194
+ // src/utils/hooks/useOnResizeContent.tsx
195
+ import { useCallbackRef } from "@radix-ui/react-use-callback-ref";
196
+ import { useEffect } from "react";
197
+ var useOnResizeContent = (ref, callback) => {
198
+ const callbackRef = useCallbackRef(callback);
199
+ useEffect(() => {
200
+ const el = ref.current;
201
+ if (!el) return;
202
+ const resizeObserver = new ResizeObserver(() => {
203
+ callbackRef();
204
+ });
205
+ const mutationObserver = new MutationObserver((mutations) => {
206
+ for (const mutation of mutations) {
207
+ for (const node of mutation.addedNodes) {
208
+ if (node instanceof Element) {
209
+ resizeObserver.observe(node);
210
+ }
211
+ }
212
+ for (const node of mutation.removedNodes) {
213
+ if (node instanceof Element) {
214
+ resizeObserver.unobserve(node);
215
+ }
216
+ }
217
+ }
218
+ callbackRef();
219
+ });
220
+ resizeObserver.observe(el);
221
+ mutationObserver.observe(el, { childList: true });
222
+ for (const child of el.children) {
223
+ resizeObserver.observe(child);
224
+ }
225
+ return () => {
226
+ resizeObserver.disconnect();
227
+ mutationObserver.disconnect();
228
+ };
229
+ }, [ref.current, callbackRef]);
230
+ };
231
+
232
+ // src/utils/hooks/useOnScrollToBottom.tsx
233
+ import { useCallbackRef as useCallbackRef2 } from "@radix-ui/react-use-callback-ref";
234
+ import { useEffect as useEffect2 } from "react";
235
+ var useOnScrollToBottom = (callback) => {
236
+ const callbackRef = useCallbackRef2(callback);
237
+ const { useViewport } = useThreadContext();
238
+ useEffect2(() => {
239
+ return useViewport.getState().onScrollToBottom(() => {
240
+ callbackRef();
241
+ });
242
+ }, [useViewport, callbackRef]);
243
+ };
244
+
245
+ // src/primitives/thread/ThreadViewport.tsx
246
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
247
+ var ThreadViewport = forwardRef2(({ autoScroll = true, onScroll, children, ...rest }, forwardedRef) => {
248
+ const messagesEndRef = useRef(null);
249
+ const divRef = useRef(null);
250
+ const ref = useComposedRefs(forwardedRef, divRef);
251
+ const { useViewport } = useThreadContext();
252
+ const firstRenderRef = useRef(true);
253
+ const isScrollingToBottomRef = useRef(false);
254
+ const lastScrollTop = useRef(0);
255
+ const scrollToBottom = () => {
256
+ const div = messagesEndRef.current;
257
+ if (!div || !autoScroll) return;
258
+ const behavior = firstRenderRef.current ? "instant" : "auto";
259
+ firstRenderRef.current = false;
260
+ isScrollingToBottomRef.current = true;
261
+ div.scrollIntoView({ behavior });
262
+ };
263
+ useOnResizeContent(divRef, () => {
264
+ if (!isScrollingToBottomRef.current && !useViewport.getState().isAtBottom) {
265
+ handleScroll();
266
+ } else {
267
+ scrollToBottom();
268
+ }
269
+ });
270
+ useOnScrollToBottom(() => {
271
+ scrollToBottom();
272
+ });
273
+ const handleScroll = () => {
274
+ const div = divRef.current;
275
+ if (!div) return;
276
+ const isAtBottom = useViewport.getState().isAtBottom;
277
+ const newIsAtBottom = div.scrollHeight - div.scrollTop <= div.clientHeight;
278
+ if (!newIsAtBottom && lastScrollTop.current < div.scrollTop) {
279
+ } else if (newIsAtBottom !== isAtBottom) {
280
+ isScrollingToBottomRef.current = false;
281
+ useViewport.setState({ isAtBottom: newIsAtBottom });
282
+ }
283
+ lastScrollTop.current = div.scrollTop;
284
+ };
285
+ return /* @__PURE__ */ jsxs(
286
+ Primitive2.div,
287
+ {
288
+ ...rest,
289
+ onScroll: composeEventHandlers(onScroll, handleScroll),
290
+ ref,
291
+ children: [
292
+ children,
293
+ /* @__PURE__ */ jsx3("div", { ref: messagesEndRef })
294
+ ]
295
+ }
296
+ );
297
+ });
298
+
299
+ // src/primitives/thread/ThreadMessages.tsx
300
+ import { memo } from "react";
301
+
302
+ // src/context/providers/MessageProvider.tsx
303
+ import { useEffect as useEffect3, useState } from "react";
304
+ import { create as create2 } from "zustand";
305
+
306
+ // src/context/stores/MessageComposer.ts
307
+ import { create } from "zustand";
308
+
309
+ // src/context/stores/BaseComposer.ts
310
+ var makeBaseComposer = (set) => ({
311
+ value: "",
312
+ setValue: (value) => {
313
+ set({ value });
314
+ }
315
+ });
316
+
317
+ // src/context/stores/MessageComposer.ts
318
+ var makeEditComposerStore = ({
319
+ onEdit,
320
+ onSend
321
+ }) => create()((set, get, store) => ({
322
+ ...makeBaseComposer(set, get, store),
323
+ isEditing: false,
324
+ edit: () => {
325
+ const value = onEdit();
326
+ set({ isEditing: true, value });
327
+ },
328
+ send: () => {
329
+ const value = get().value;
330
+ set({ isEditing: false });
331
+ onSend(value);
332
+ },
333
+ cancel: () => {
334
+ if (!get().isEditing) return false;
335
+ set({ isEditing: false });
336
+ return true;
337
+ }
338
+ }));
339
+
340
+ // src/context/providers/MessageProvider.tsx
341
+ import { jsx as jsx4 } from "react/jsx-runtime";
342
+ var getIsLast = (thread, message) => {
343
+ return thread.messages[thread.messages.length - 1]?.id === message.id;
344
+ };
345
+ var syncMessage = (thread, useMessage, messageIndex) => {
346
+ const parentId = thread.messages[messageIndex - 1]?.id ?? null;
347
+ const message = thread.messages[messageIndex];
348
+ if (!message) return;
349
+ const isLast = getIsLast(thread, message);
350
+ const branches = thread.getBranches(message.id);
351
+ const currentState = useMessage.getState();
352
+ if (currentState.message === message && currentState.parentId === parentId && currentState.branches === branches && currentState.isLast === isLast)
353
+ return;
354
+ useMessage.setState({
355
+ message,
356
+ parentId,
357
+ branches,
358
+ isLast
359
+ });
360
+ };
361
+ var useMessageContext2 = (messageIndex) => {
362
+ const { useThread } = useThreadContext();
363
+ const [context] = useState(() => {
364
+ const useMessage = create2((set) => ({
365
+ message: null,
366
+ parentId: null,
367
+ branches: [],
368
+ isLast: false,
369
+ inProgressIndicator: null,
370
+ isCopied: false,
371
+ isHovering: false,
372
+ setInProgressIndicator: (value) => {
373
+ set({ inProgressIndicator: value });
374
+ },
375
+ setIsCopied: (value) => {
376
+ set({ isCopied: value });
377
+ },
378
+ setIsHovering: (value) => {
379
+ set({ isHovering: value });
380
+ }
381
+ }));
382
+ const useComposer = makeEditComposerStore({
383
+ onEdit: () => {
384
+ const message = useMessage.getState().message;
385
+ if (message.role !== "user")
386
+ throw new Error(
387
+ "Tried to edit a non-user message. Editing is only supported for user messages. This is likely an internal bug in assistant-ui."
388
+ );
389
+ const text = getMessageText(message);
390
+ return text;
391
+ },
392
+ onSend: (text) => {
393
+ const { message, parentId } = useMessage.getState();
394
+ if (message.role !== "user")
395
+ throw new Error(
396
+ "Tried to edit a non-user message. Editing is only supported for user messages. This is likely an internal bug in assistant-ui."
397
+ );
398
+ const nonTextParts = message.content.filter(
399
+ (part) => part.type !== "text" && part.type !== "ui"
400
+ );
401
+ useThread.getState().append({
402
+ parentId,
403
+ content: [{ type: "text", text }, ...nonTextParts]
404
+ });
405
+ }
406
+ });
407
+ syncMessage(useThread.getState(), useMessage, messageIndex);
408
+ return { useMessage, useComposer };
409
+ });
410
+ useEffect3(() => {
411
+ return useThread.subscribe((thread) => {
412
+ syncMessage(thread, context.useMessage, messageIndex);
413
+ });
414
+ }, [context, useThread, messageIndex]);
415
+ return context;
416
+ };
417
+ var MessageProvider = ({
418
+ messageIndex,
419
+ children
420
+ }) => {
421
+ const context = useMessageContext2(messageIndex);
422
+ return /* @__PURE__ */ jsx4(MessageContext.Provider, { value: context, children });
423
+ };
424
+
425
+ // src/primitives/composer/ComposerIf.tsx
426
+ var useComposerIf = (props) => {
427
+ const { useComposer } = useComposerContext();
428
+ return useComposer((composer) => {
429
+ if (props.editing === true && !composer.isEditing) return false;
430
+ if (props.editing === false && composer.isEditing) return false;
431
+ return true;
432
+ });
433
+ };
434
+ var ComposerIf = ({ children, ...query }) => {
435
+ const result = useComposerIf(query);
436
+ return result ? children : null;
437
+ };
438
+
439
+ // src/primitives/message/MessageIf.tsx
440
+ var useMessageIf = (props) => {
441
+ const { useMessage } = useMessageContext();
442
+ return useMessage(({ message, branches, isLast, isCopied, isHovering }) => {
443
+ if (props.hasBranches === true && branches.length < 2) return false;
444
+ if (props.user && message.role !== "user") return false;
445
+ if (props.assistant && message.role !== "assistant") return false;
446
+ if (props.lastOrHover === true && !isHovering && !isLast) return false;
447
+ if (props.copied === true && !isCopied) return false;
448
+ if (props.copied === false && isCopied) return false;
449
+ return true;
450
+ });
451
+ };
452
+ var MessageIf = ({ children, ...query }) => {
453
+ const result = useMessageIf(query);
454
+ return result ? children : null;
455
+ };
456
+
457
+ // src/primitives/thread/ThreadMessages.tsx
458
+ import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
459
+ var getComponents = (components) => {
460
+ return {
461
+ EditComposer: components.EditComposer ?? components.UserMessage ?? components.Message,
462
+ UserMessage: components.UserMessage ?? components.Message,
463
+ AssistantMessage: components.AssistantMessage ?? components.Message
464
+ };
465
+ };
466
+ var ThreadMessageImpl = ({
467
+ messageIndex,
468
+ components
469
+ }) => {
470
+ const { UserMessage, EditComposer, AssistantMessage } = getComponents(components);
471
+ return /* @__PURE__ */ jsxs2(MessageProvider, { messageIndex, children: [
472
+ /* @__PURE__ */ jsxs2(MessageIf, { user: true, children: [
473
+ /* @__PURE__ */ jsx5(ComposerIf, { editing: false, children: /* @__PURE__ */ jsx5(UserMessage, {}) }),
474
+ /* @__PURE__ */ jsx5(ComposerIf, { editing: true, children: /* @__PURE__ */ jsx5(EditComposer, {}) })
475
+ ] }),
476
+ /* @__PURE__ */ jsx5(MessageIf, { assistant: true, children: /* @__PURE__ */ jsx5(AssistantMessage, {}) })
477
+ ] });
478
+ };
479
+ var ThreadMessage = memo(
480
+ ThreadMessageImpl,
481
+ (prev, next) => prev.messageIndex === next.messageIndex && prev.components.UserMessage === next.components.UserMessage && prev.components.EditComposer === next.components.EditComposer && prev.components.AssistantMessage === next.components.AssistantMessage
482
+ );
483
+ var ThreadMessages = ({ components }) => {
484
+ const { useThread } = useThreadContext();
485
+ const messagesLength = useThread((t) => t.messages.length);
486
+ if (messagesLength === 0) return null;
487
+ return new Array(messagesLength).fill(null).map((_, idx) => {
488
+ const messageIndex = idx;
489
+ return /* @__PURE__ */ jsx5(
490
+ ThreadMessage,
491
+ {
492
+ messageIndex,
493
+ components
494
+ },
495
+ messageIndex
496
+ );
497
+ });
498
+ };
499
+
500
+ // src/primitives/thread/ThreadScrollToBottom.tsx
501
+ import { composeEventHandlers as composeEventHandlers2 } from "@radix-ui/primitive";
502
+ import {
503
+ Primitive as Primitive3
504
+ } from "@radix-ui/react-primitive";
505
+ import { forwardRef as forwardRef3 } from "react";
506
+ import { jsx as jsx6 } from "react/jsx-runtime";
507
+ var ThreadScrollToBottom = forwardRef3(({ onClick, ...rest }, ref) => {
508
+ const { useViewport } = useThreadContext();
509
+ const isAtBottom = useViewport((s) => s.isAtBottom);
510
+ const handleScrollToBottom = () => {
511
+ useViewport.getState().scrollToBottom();
512
+ };
513
+ return /* @__PURE__ */ jsx6(
514
+ Primitive3.button,
515
+ {
516
+ ...rest,
517
+ disabled: isAtBottom,
518
+ ref,
519
+ onClick: composeEventHandlers2(onClick, handleScrollToBottom)
520
+ }
521
+ );
522
+ });
523
+
524
+ // src/primitives/thread/ThreadSuggestion.tsx
525
+ import { composeEventHandlers as composeEventHandlers3 } from "@radix-ui/primitive";
526
+ import {
527
+ Primitive as Primitive4
528
+ } from "@radix-ui/react-primitive";
529
+ import { forwardRef as forwardRef4 } from "react";
530
+ import { jsx as jsx7 } from "react/jsx-runtime";
531
+ var ThreadSuggestion = forwardRef4(({ onClick, prompt, method, autoSend: send, ...rest }, ref) => {
532
+ const { useThread, useComposer } = useThreadContext();
533
+ const isDisabled = useThread((t) => t.isRunning);
534
+ const handleApplySuggestion = () => {
535
+ const thread = useThread.getState();
536
+ const composer = useComposer.getState();
537
+ composer.setValue(prompt);
538
+ if (send && !thread.isRunning) {
539
+ composer.send();
540
+ }
541
+ };
542
+ return /* @__PURE__ */ jsx7(
543
+ Primitive4.button,
544
+ {
545
+ ...rest,
546
+ disabled: isDisabled,
547
+ ref,
548
+ onClick: composeEventHandlers3(onClick, handleApplySuggestion)
549
+ }
550
+ );
551
+ });
552
+
553
+ // src/primitives/composer/index.ts
554
+ var composer_exports = {};
555
+ __export(composer_exports, {
556
+ Cancel: () => ComposerCancel,
557
+ If: () => ComposerIf,
558
+ Input: () => ComposerInput,
559
+ Root: () => ComposerRoot,
560
+ Send: () => ComposerSend
561
+ });
562
+
563
+ // src/primitives/composer/ComposerRoot.tsx
564
+ import { composeEventHandlers as composeEventHandlers4 } from "@radix-ui/primitive";
565
+ import { useComposedRefs as useComposedRefs2 } from "@radix-ui/react-compose-refs";
566
+ import {
567
+ Primitive as Primitive5
568
+ } from "@radix-ui/react-primitive";
569
+ import { forwardRef as forwardRef5, useRef as useRef2 } from "react";
570
+ import { jsx as jsx8 } from "react/jsx-runtime";
571
+ var ComposerRoot = forwardRef5(
572
+ ({ onSubmit, ...rest }, forwardedRef) => {
573
+ const { useViewport } = useThreadContext();
574
+ const { useComposer } = useComposerContext();
575
+ const formRef = useRef2(null);
576
+ const ref = useComposedRefs2(forwardedRef, formRef);
577
+ const handleSubmit = (e) => {
578
+ const composerState = useComposer.getState();
579
+ if (!composerState.isEditing) return;
580
+ e.preventDefault();
581
+ composerState.send();
582
+ useViewport.getState().scrollToBottom();
583
+ };
584
+ return /* @__PURE__ */ jsx8(
585
+ Primitive5.form,
586
+ {
587
+ ...rest,
588
+ ref,
589
+ onSubmit: composeEventHandlers4(onSubmit, handleSubmit)
590
+ }
591
+ );
592
+ }
593
+ );
594
+
595
+ // src/primitives/composer/ComposerInput.tsx
596
+ import { composeEventHandlers as composeEventHandlers5 } from "@radix-ui/primitive";
597
+ import { useComposedRefs as useComposedRefs3 } from "@radix-ui/react-compose-refs";
598
+ import { Slot } from "@radix-ui/react-slot";
599
+ import {
600
+ forwardRef as forwardRef6,
601
+ useCallback as useCallback6,
602
+ useEffect as useEffect4,
603
+ useRef as useRef3
604
+ } from "react";
605
+ import TextareaAutosize from "react-textarea-autosize";
606
+ import { jsx as jsx9 } from "react/jsx-runtime";
607
+ var ComposerInput = forwardRef6(
608
+ ({ autoFocus = false, asChild, disabled, onChange, onKeyDown, ...rest }, forwardedRef) => {
609
+ const { useThread } = useThreadContext();
610
+ const { useComposer, type } = useComposerContext();
611
+ const value = useComposer((c) => {
612
+ if (!c.isEditing) return "";
613
+ return c.value;
614
+ });
615
+ const Component = asChild ? Slot : TextareaAutosize;
616
+ const textareaRef = useRef3(null);
617
+ const ref = useComposedRefs3(forwardedRef, textareaRef);
618
+ const handleKeyPress = (e) => {
619
+ if (disabled) return;
620
+ if (e.key === "Escape") {
621
+ const composer = useComposer.getState();
622
+ if (composer.cancel()) {
623
+ e.preventDefault();
624
+ }
625
+ } else if (e.key === "Enter" && e.shiftKey === false) {
626
+ const isRunning = useThread.getState().isRunning;
627
+ if (!isRunning) {
628
+ e.preventDefault();
629
+ textareaRef.current?.closest("form")?.requestSubmit();
630
+ }
631
+ }
632
+ };
633
+ const autoFocusEnabled = autoFocus && !disabled;
634
+ const focus = useCallback6(() => {
635
+ const textarea = textareaRef.current;
636
+ if (!textarea || !autoFocusEnabled) return;
637
+ textarea.focus();
638
+ textarea.setSelectionRange(
639
+ textareaRef.current.value.length,
640
+ textareaRef.current.value.length
641
+ );
642
+ }, [autoFocusEnabled]);
643
+ useEffect4(() => focus(), [focus]);
644
+ useOnScrollToBottom(() => {
645
+ if (type === "new") {
646
+ focus();
647
+ }
648
+ });
649
+ return /* @__PURE__ */ jsx9(
650
+ Component,
651
+ {
652
+ value,
653
+ ...rest,
654
+ ref,
655
+ disabled,
656
+ onChange: composeEventHandlers5(onChange, (e) => {
657
+ const composerState = useComposer.getState();
658
+ if (!composerState.isEditing) return;
659
+ return composerState.setValue(e.target.value);
660
+ }),
661
+ onKeyDown: composeEventHandlers5(onKeyDown, handleKeyPress)
662
+ }
663
+ );
664
+ }
665
+ );
666
+
667
+ // src/primitives/composer/ComposerSend.tsx
668
+ import {
669
+ Primitive as Primitive6
670
+ } from "@radix-ui/react-primitive";
671
+ import { forwardRef as forwardRef7 } from "react";
672
+ import { jsx as jsx10 } from "react/jsx-runtime";
673
+ var ComposerSend = forwardRef7(
674
+ ({ disabled, ...rest }, ref) => {
675
+ const { useComposer } = useComposerContext();
676
+ const hasValue = useComposer((c) => c.isEditing && c.value.length > 0);
677
+ return /* @__PURE__ */ jsx10(
678
+ Primitive6.button,
679
+ {
680
+ type: "submit",
681
+ ...rest,
682
+ ref,
683
+ disabled: disabled || !hasValue
684
+ }
685
+ );
686
+ }
687
+ );
688
+
689
+ // src/primitives/composer/ComposerCancel.tsx
690
+ import { composeEventHandlers as composeEventHandlers6 } from "@radix-ui/primitive";
691
+ import {
692
+ Primitive as Primitive7
693
+ } from "@radix-ui/react-primitive";
694
+ import { forwardRef as forwardRef8 } from "react";
695
+ import { jsx as jsx11 } from "react/jsx-runtime";
696
+ var ComposerCancel = forwardRef8(({ onClick, ...rest }, ref) => {
697
+ const { useComposer } = useComposerContext();
698
+ const handleCancel = () => {
699
+ useComposer.getState().cancel();
700
+ };
701
+ return /* @__PURE__ */ jsx11(
702
+ Primitive7.button,
703
+ {
704
+ type: "button",
705
+ ...rest,
706
+ ref,
707
+ onClick: composeEventHandlers6(onClick, handleCancel)
708
+ }
709
+ );
710
+ });
711
+
712
+ // src/primitives/message/index.ts
713
+ var message_exports = {};
714
+ __export(message_exports, {
715
+ Content: () => MessageContent,
716
+ If: () => MessageIf,
717
+ InProgress: () => MessageInProgress,
718
+ Root: () => MessageRoot
719
+ });
720
+
721
+ // src/primitives/message/MessageRoot.tsx
722
+ import { composeEventHandlers as composeEventHandlers7 } from "@radix-ui/primitive";
723
+ import {
724
+ Primitive as Primitive8
725
+ } from "@radix-ui/react-primitive";
726
+ import { forwardRef as forwardRef9 } from "react";
727
+ import { jsx as jsx12 } from "react/jsx-runtime";
728
+ var MessageRoot = forwardRef9(
729
+ ({ onMouseEnter, onMouseLeave, ...rest }, ref) => {
730
+ const { useMessage } = useMessageContext();
731
+ const setIsHovering = useMessage((s) => s.setIsHovering);
732
+ const handleMouseEnter = () => {
733
+ setIsHovering(true);
734
+ };
735
+ const handleMouseLeave = () => {
736
+ setIsHovering(false);
737
+ };
738
+ return /* @__PURE__ */ jsx12(
739
+ Primitive8.div,
740
+ {
741
+ ...rest,
742
+ ref,
743
+ onMouseEnter: composeEventHandlers7(onMouseEnter, handleMouseEnter),
744
+ onMouseLeave: composeEventHandlers7(onMouseLeave, handleMouseLeave)
745
+ }
746
+ );
747
+ }
748
+ );
749
+
750
+ // src/primitives/message/MessageContent.tsx
751
+ import { memo as memo2 } from "react";
752
+
753
+ // src/context/providers/ContentPartProvider.tsx
754
+ import { useEffect as useEffect5, useState as useState2 } from "react";
755
+ import { create as create3 } from "zustand";
756
+ import { jsx as jsx13 } from "react/jsx-runtime";
757
+ var syncContentPart = ({ message }, useContentPart, partIndex) => {
758
+ const part = message.content[partIndex];
759
+ if (!part) return;
760
+ const messageStatus = message.role === "assistant" ? message.status : "done";
761
+ const status = partIndex === message.content.length - 1 ? messageStatus : "done";
762
+ const currentState = useContentPart.getState();
763
+ if (currentState.part === part && currentState.status === status) return;
764
+ useContentPart.setState({ part, status });
765
+ };
766
+ var useContentPartContext2 = (partIndex) => {
767
+ const { useMessage } = useMessageContext();
768
+ const [context] = useState2(() => {
769
+ const useContentPart = create3(() => ({
770
+ part: { type: "text", text: "" },
771
+ status: "done"
772
+ }));
773
+ syncContentPart(useMessage.getState(), useContentPart, partIndex);
774
+ return { useContentPart };
775
+ });
776
+ useEffect5(() => {
777
+ return useMessage.subscribe((message) => {
778
+ syncContentPart(message, context.useContentPart, partIndex);
779
+ });
780
+ }, [context, useMessage, partIndex]);
781
+ return context;
782
+ };
783
+ var ContentPartProvider = ({
784
+ partIndex,
785
+ children
786
+ }) => {
787
+ const context = useContentPartContext2(partIndex);
788
+ return /* @__PURE__ */ jsx13(ContentPartContext.Provider, { value: context, children });
789
+ };
790
+
791
+ // src/primitives/contentPart/ContentPartDisplay.tsx
792
+ var ContentPartDisplay = () => {
793
+ const { useContentPart } = useContentPartContext();
794
+ const display = useContentPart((c) => {
795
+ if (c.part.type !== "ui")
796
+ throw new Error(
797
+ "ContentPartDisplay can only be used inside ui content parts."
798
+ );
799
+ return c.part.display;
800
+ });
801
+ return display ?? null;
802
+ };
803
+
804
+ // src/primitives/contentPart/ContentPartInProgressIndicator.tsx
805
+ var ContentPartInProgressIndicator = () => {
806
+ const { useMessage } = useMessageContext();
807
+ const { useContentPart } = useContentPartContext();
808
+ const indicator = useCombinedStore(
809
+ [useMessage, useContentPart],
810
+ (m, c) => c.status === "in_progress" ? m.inProgressIndicator : null
811
+ );
812
+ return indicator;
813
+ };
814
+
815
+ // src/primitives/contentPart/ContentPartText.tsx
816
+ import {
817
+ Primitive as Primitive9
818
+ } from "@radix-ui/react-primitive";
819
+ import { forwardRef as forwardRef10 } from "react";
820
+ import { jsx as jsx14 } from "react/jsx-runtime";
821
+ var ContentPartText = forwardRef10((props, forwardedRef) => {
822
+ const { useContentPart } = useContentPartContext();
823
+ const text = useContentPart((c) => {
824
+ if (c.part.type !== "text")
825
+ throw new Error(
826
+ "ContentPartText can only be used inside text content parts."
827
+ );
828
+ return c.part.text;
829
+ });
830
+ return /* @__PURE__ */ jsx14(Primitive9.span, { ...props, ref: forwardedRef, children: text });
831
+ });
832
+
833
+ // src/primitives/message/MessageContent.tsx
834
+ import { Fragment, jsx as jsx15, jsxs as jsxs3 } from "react/jsx-runtime";
835
+ var defaultComponents = {
836
+ Text: () => /* @__PURE__ */ jsxs3(Fragment, { children: [
837
+ /* @__PURE__ */ jsx15(ContentPartText, {}),
838
+ /* @__PURE__ */ jsx15(ContentPartInProgressIndicator, {})
839
+ ] }),
840
+ Image: () => null,
841
+ UI: () => /* @__PURE__ */ jsx15(ContentPartDisplay, {}),
842
+ tools: {
843
+ Fallback: (props) => {
844
+ const { useToolRenderers } = useAssistantContext();
845
+ const Render = useToolRenderers(
846
+ (s) => s.getToolRenderer(props.part.toolName)
847
+ );
848
+ if (!Render) return null;
849
+ return /* @__PURE__ */ jsx15(Render, { ...props });
850
+ }
851
+ }
852
+ };
853
+ var MessageContentPartComponent = ({
854
+ components: {
855
+ Text = defaultComponents.Text,
856
+ Image = defaultComponents.Image,
857
+ UI = defaultComponents.UI,
858
+ tools: { by_name = {}, Fallback = defaultComponents.tools.Fallback } = {}
859
+ } = {}
860
+ }) => {
861
+ const { useContentPart } = useContentPartContext();
862
+ const { part, status } = useContentPart();
863
+ const type = part.type;
864
+ switch (type) {
865
+ case "text":
866
+ return /* @__PURE__ */ jsx15(Text, { part, status });
867
+ case "image":
868
+ return /* @__PURE__ */ jsx15(Image, { part, status });
869
+ case "ui":
870
+ return /* @__PURE__ */ jsx15(UI, { part, status });
871
+ case "tool-call": {
872
+ const Tool = by_name[part.toolName] || Fallback;
873
+ return /* @__PURE__ */ jsx15(Tool, { part, status });
874
+ }
875
+ default:
876
+ throw new Error(`Unknown content part type: ${type}`);
877
+ }
878
+ };
879
+ var MessageContentPartImpl = ({
880
+ partIndex,
881
+ components
882
+ }) => {
883
+ return /* @__PURE__ */ jsx15(ContentPartProvider, { partIndex, children: /* @__PURE__ */ jsx15(MessageContentPartComponent, { components }) });
884
+ };
885
+ var MessageContentPart = memo2(
886
+ MessageContentPartImpl,
887
+ (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
888
+ );
889
+ var MessageContent = ({ components }) => {
890
+ const { useMessage } = useMessageContext();
891
+ const contentLength = useMessage((s) => s.message.content.length);
892
+ return new Array(contentLength).fill(null).map((_, idx) => {
893
+ const partIndex = idx;
894
+ return /* @__PURE__ */ jsx15(
895
+ MessageContentPart,
896
+ {
897
+ partIndex,
898
+ components
899
+ },
900
+ partIndex
901
+ );
902
+ });
903
+ };
904
+
905
+ // src/primitives/message/MessageInProgress.tsx
906
+ import {
907
+ Primitive as Primitive10
908
+ } from "@radix-ui/react-primitive";
909
+ import { forwardRef as forwardRef11, useMemo as useMemo2 } from "react";
910
+ import { jsx as jsx16 } from "react/jsx-runtime";
911
+ var MessageInProgress = forwardRef11((props, ref) => {
912
+ const { useMessage } = useMessageContext();
913
+ useMemo2(() => {
914
+ useMessage.getState().setInProgressIndicator(/* @__PURE__ */ jsx16(Primitive10.span, { ...props, ref }));
915
+ }, [useMessage, props, ref]);
916
+ return null;
917
+ });
918
+
919
+ // src/primitives/branchPicker/index.ts
920
+ var branchPicker_exports = {};
921
+ __export(branchPicker_exports, {
922
+ Count: () => BranchPickerCount,
923
+ Next: () => BranchPickerNext,
924
+ Number: () => BranchPickerNumber,
925
+ Previous: () => BranchPickerPrevious,
926
+ Root: () => BranchPickerRoot
927
+ });
928
+
929
+ // src/utils/createActionButton.tsx
930
+ import { composeEventHandlers as composeEventHandlers8 } from "@radix-ui/primitive";
931
+ import {
932
+ Primitive as Primitive11
933
+ } from "@radix-ui/react-primitive";
934
+ import { forwardRef as forwardRef12 } from "react";
935
+ import { jsx as jsx17 } from "react/jsx-runtime";
936
+ var createActionButton = (useActionButton) => {
937
+ return forwardRef12(
938
+ (props, forwardedRef) => {
939
+ const onClick = useActionButton(props);
940
+ return /* @__PURE__ */ jsx17(
941
+ Primitive11.button,
942
+ {
943
+ type: "button",
944
+ disabled: !onClick,
945
+ ...props,
946
+ ref: forwardedRef,
947
+ onClick: composeEventHandlers8(props.onClick, onClick ?? void 0)
948
+ }
949
+ );
950
+ }
951
+ );
952
+ };
953
+
954
+ // src/primitives/branchPicker/BranchPickerNext.tsx
955
+ var BranchPickerNext = createActionButton(useGoToNextBranch);
956
+
957
+ // src/primitives/branchPicker/BranchPickerPrevious.tsx
958
+ var BranchPickerPrevious = createActionButton(useGoToPreviousBranch);
959
+
960
+ // src/primitives/branchPicker/BranchPickerCount.tsx
961
+ import { Fragment as Fragment2, jsx as jsx18 } from "react/jsx-runtime";
962
+ var BranchPickerCount = () => {
963
+ const { useMessage } = useMessageContext();
964
+ const branchCount = useMessage((s) => s.branches.length);
965
+ return /* @__PURE__ */ jsx18(Fragment2, { children: branchCount });
966
+ };
967
+
968
+ // src/primitives/branchPicker/BranchPickerNumber.tsx
969
+ import { Fragment as Fragment3, jsx as jsx19 } from "react/jsx-runtime";
970
+ var BranchPickerNumber = () => {
971
+ const { useMessage } = useMessageContext();
972
+ const branchIdx = useMessage((s) => s.branches.indexOf(s.message.id));
973
+ return /* @__PURE__ */ jsx19(Fragment3, { children: branchIdx + 1 });
974
+ };
975
+
976
+ // src/primitives/branchPicker/BranchPickerRoot.tsx
977
+ import {
978
+ Primitive as Primitive12
979
+ } from "@radix-ui/react-primitive";
980
+ import { forwardRef as forwardRef13 } from "react";
981
+ import { jsx as jsx20 } from "react/jsx-runtime";
982
+ var BranchPickerRoot = forwardRef13(({ hideWhenSingleBranch, ...rest }, ref) => {
983
+ return /* @__PURE__ */ jsx20(MessageIf, { hasBranches: hideWhenSingleBranch ? true : void 0, children: /* @__PURE__ */ jsx20(Primitive12.div, { ...rest, ref }) });
984
+ });
985
+
986
+ // src/primitives/actionBar/index.ts
987
+ var actionBar_exports = {};
988
+ __export(actionBar_exports, {
989
+ Copy: () => ActionBarCopy,
990
+ Edit: () => ActionBarEdit,
991
+ Reload: () => ActionBarReload,
992
+ Root: () => ActionBarRoot
993
+ });
994
+
995
+ // src/primitives/actionBar/ActionBarRoot.tsx
996
+ import {
997
+ Primitive as Primitive13
998
+ } from "@radix-ui/react-primitive";
999
+ import { forwardRef as forwardRef14 } from "react";
1000
+ import { jsx as jsx21 } from "react/jsx-runtime";
1001
+ var ActionBarRoot = forwardRef14(({ hideWhenRunning, autohide, autohideFloat, ...rest }, ref) => {
1002
+ const { useThread } = useThreadContext();
1003
+ const { useMessage } = useMessageContext();
1004
+ const hideAndfloatStatus = useCombinedStore(
1005
+ [useThread, useMessage],
1006
+ (t, m) => {
1007
+ if (hideWhenRunning && t.isRunning) return "hidden" /* Hidden */;
1008
+ const autohideEnabled = autohide === "always" || autohide === "not-last" && !m.isLast;
1009
+ if (!autohideEnabled) return "normal" /* Normal */;
1010
+ if (!m.isHovering) return "hidden" /* Hidden */;
1011
+ if (autohideFloat === "always" || autohideFloat === "single-branch" && m.branches.length <= 1)
1012
+ return "floating" /* Floating */;
1013
+ return "normal" /* Normal */;
1014
+ }
1015
+ );
1016
+ if (hideAndfloatStatus === "hidden" /* Hidden */) return null;
1017
+ return /* @__PURE__ */ jsx21(
1018
+ Primitive13.div,
1019
+ {
1020
+ ...hideAndfloatStatus === "floating" /* Floating */ ? { "data-floating": "true" } : null,
1021
+ ...rest,
1022
+ ref
1023
+ }
1024
+ );
1025
+ });
1026
+
1027
+ // src/primitives/actionBar/ActionBarCopy.tsx
1028
+ var ActionBarCopy = createActionButton(useCopyMessage);
1029
+
1030
+ // src/primitives/actionBar/ActionBarReload.tsx
1031
+ var ActionBarReload = createActionButton(useReloadMessage);
1032
+
1033
+ // src/primitives/actionBar/ActionBarEdit.tsx
1034
+ var ActionBarEdit = createActionButton(useBeginMessageEdit);
1035
+
1036
+ // src/primitives/contentPart/index.ts
1037
+ var contentPart_exports = {};
1038
+ __export(contentPart_exports, {
1039
+ Display: () => ContentPartDisplay,
1040
+ Image: () => ContentPartImage,
1041
+ InProgressIndicator: () => ContentPartInProgressIndicator,
1042
+ Text: () => ContentPartText
1043
+ });
1044
+
1045
+ // src/primitives/contentPart/ContentPartImage.tsx
1046
+ import {
1047
+ Primitive as Primitive14
1048
+ } from "@radix-ui/react-primitive";
1049
+ import { forwardRef as forwardRef15 } from "react";
1050
+ import { jsx as jsx22 } from "react/jsx-runtime";
1051
+ var ContentPartImage = forwardRef15((props, forwardedRef) => {
1052
+ const { useContentPart } = useContentPartContext();
1053
+ const image = useContentPart((c) => {
1054
+ if (c.part.type !== "image")
1055
+ throw new Error(
1056
+ "ContentPartImage can only be used inside image content parts."
1057
+ );
1058
+ return c.part.image;
1059
+ });
1060
+ return /* @__PURE__ */ jsx22(Primitive14.img, { src: image, ...props, ref: forwardedRef });
1061
+ });
1062
+
1063
+ // src/runtime/local/useLocalRuntime.tsx
1064
+ import { useInsertionEffect, useState as useState3 } from "react";
1065
+
1066
+ // src/utils/ModelConfigTypes.ts
1067
+ var mergeModelConfigs = (configSet) => {
1068
+ const configs = Array.from(configSet).map((c) => c()).sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
1069
+ return configs.reduce((acc, config) => {
1070
+ if (config.system) {
1071
+ if (acc.system) {
1072
+ acc.system += `
1073
+
1074
+ ${config.system}`;
1075
+ } else {
1076
+ acc.system = config.system;
1077
+ }
1078
+ }
1079
+ if (config.tools) {
1080
+ for (const [name, tool] of Object.entries(config.tools)) {
1081
+ if (acc.tools?.[name]) {
1082
+ throw new Error(
1083
+ `You tried to define a tool with the name ${name}, but it already exists.`
1084
+ );
1085
+ }
1086
+ if (!acc.tools) acc.tools = {};
1087
+ acc.tools[name] = tool;
1088
+ }
1089
+ }
1090
+ return acc;
1091
+ }, {});
1092
+ };
1093
+
1094
+ // src/runtime/utils/idUtils.tsx
1095
+ import { customAlphabet } from "nanoid/non-secure";
1096
+ var generateId = customAlphabet(
1097
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
1098
+ 7
1099
+ );
1100
+ var optimisticPrefix = "__optimistic__";
1101
+ var generateOptimisticId = () => `${optimisticPrefix}${generateId()}`;
1102
+
1103
+ // src/runtime/utils/MessageRepository.tsx
1104
+ var findHead = (message) => {
1105
+ if (message.next) return findHead(message.next);
1106
+ return message;
1107
+ };
1108
+ var MessageRepository = class {
1109
+ messages = /* @__PURE__ */ new Map();
1110
+ // message_id -> item
1111
+ head = null;
1112
+ root = {
1113
+ children: []
1114
+ };
1115
+ performOp(newParent, child, operation) {
1116
+ const parentOrRoot = child.prev ?? this.root;
1117
+ const newParentOrRoot = newParent ?? this.root;
1118
+ if (operation === "relink" && parentOrRoot === newParentOrRoot) return;
1119
+ if (operation !== "link") {
1120
+ parentOrRoot.children = parentOrRoot.children.filter(
1121
+ (m) => m !== child.current.id
1122
+ );
1123
+ if (child.prev?.next === child) {
1124
+ const fallbackId = child.prev.children.at(-1);
1125
+ const fallback = fallbackId ? this.messages.get(fallbackId) : null;
1126
+ if (fallback === void 0) {
1127
+ throw new Error(
1128
+ "MessageRepository(performOp/cut): Fallback sibling message not found. This is likely an internal bug in assistant-ui."
1129
+ );
1130
+ }
1131
+ child.prev.next = fallback;
1132
+ }
1133
+ }
1134
+ if (operation !== "cut") {
1135
+ newParentOrRoot.children = [
1136
+ ...newParentOrRoot.children,
1137
+ child.current.id
1138
+ ];
1139
+ if (newParent && (findHead(child) === this.head || newParent.next === null)) {
1140
+ newParent.next = child;
1141
+ }
1142
+ child.prev = newParent;
1143
+ }
1144
+ }
1145
+ getMessages() {
1146
+ const messages = new Array(this.head?.level ?? 0);
1147
+ for (let current = this.head; current; current = current.prev) {
1148
+ messages[current.level] = current.current;
1149
+ }
1150
+ return messages;
1151
+ }
1152
+ addOrUpdateMessage(parentId, message) {
1153
+ const existingItem = this.messages.get(message.id);
1154
+ const prev = parentId ? this.messages.get(parentId) : null;
1155
+ if (prev === void 0)
1156
+ throw new Error(
1157
+ "MessageRepository(addOrUpdateMessage): Parent message not found. This is likely an internal bug in assistant-ui."
1158
+ );
1159
+ if (existingItem) {
1160
+ existingItem.current = message;
1161
+ this.performOp(prev, existingItem, "relink");
1162
+ return;
1163
+ }
1164
+ const newItem = {
1165
+ prev,
1166
+ current: message,
1167
+ next: null,
1168
+ children: [],
1169
+ level: prev ? prev.level + 1 : 0
1170
+ };
1171
+ this.messages.set(message.id, newItem);
1172
+ this.performOp(prev, newItem, "link");
1173
+ if (this.head === prev) {
1174
+ this.head = newItem;
1175
+ }
1176
+ }
1177
+ appendOptimisticMessage(parentId, message) {
1178
+ let optimisticId;
1179
+ do {
1180
+ optimisticId = generateOptimisticId();
1181
+ } while (this.messages.has(optimisticId));
1182
+ this.addOrUpdateMessage(parentId, {
1183
+ ...message,
1184
+ id: optimisticId,
1185
+ createdAt: /* @__PURE__ */ new Date(),
1186
+ ...message.role === "assistant" ? { status: "in_progress" } : void 0
1187
+ });
1188
+ return optimisticId;
1189
+ }
1190
+ deleteMessage(messageId, replacementId) {
1191
+ const message = this.messages.get(messageId);
1192
+ if (!message)
1193
+ throw new Error(
1194
+ "MessageRepository(deleteMessage): Optimistic message not found. This is likely an internal bug in assistant-ui."
1195
+ );
1196
+ const replacement = replacementId === void 0 ? message.prev : replacementId === null ? null : this.messages.get(replacementId);
1197
+ if (replacement === void 0)
1198
+ throw new Error(
1199
+ "MessageRepository(deleteMessage): Replacement not found. This is likely an internal bug in assistant-ui."
1200
+ );
1201
+ for (const child of message.children) {
1202
+ const childMessage = this.messages.get(child);
1203
+ if (!childMessage)
1204
+ throw new Error(
1205
+ "MessageRepository(deleteMessage): Child message not found. This is likely an internal bug in assistant-ui."
1206
+ );
1207
+ this.performOp(replacement, childMessage, "relink");
1208
+ }
1209
+ this.performOp(null, message, "cut");
1210
+ this.messages.delete(messageId);
1211
+ if (this.head === message) {
1212
+ this.head = replacement ? findHead(replacement) : null;
1213
+ }
1214
+ }
1215
+ getBranches(messageId) {
1216
+ const message = this.messages.get(messageId);
1217
+ if (!message)
1218
+ throw new Error(
1219
+ "MessageRepository(getBranches): Message not found. This is likely an internal bug in assistant-ui."
1220
+ );
1221
+ const { children } = message.prev ?? this.root;
1222
+ return children;
1223
+ }
1224
+ switchToBranch(messageId) {
1225
+ const message = this.messages.get(messageId);
1226
+ if (!message)
1227
+ throw new Error(
1228
+ "MessageRepository(switchToBranch): Branch not found. This is likely an internal bug in assistant-ui."
1229
+ );
1230
+ if (message.prev) {
1231
+ message.prev.next = message;
1232
+ }
1233
+ this.head = findHead(message);
1234
+ }
1235
+ resetHead(messageId) {
1236
+ if (messageId === null) {
1237
+ this.head = null;
1238
+ return;
1239
+ }
1240
+ const message = this.messages.get(messageId);
1241
+ if (!message)
1242
+ throw new Error(
1243
+ "MessageRepository(resetHead): Branch not found. This is likely an internal bug in assistant-ui."
1244
+ );
1245
+ this.head = message;
1246
+ for (let current = message; current; current = current.prev) {
1247
+ if (current.prev) {
1248
+ current.prev.next = current;
1249
+ }
1250
+ }
1251
+ }
1252
+ };
1253
+
1254
+ // src/runtime/local/LocalRuntime.tsx
1255
+ var LocalRuntime = class {
1256
+ constructor(adapter) {
1257
+ this.adapter = adapter;
1258
+ }
1259
+ _subscriptions = /* @__PURE__ */ new Set();
1260
+ _configProviders = /* @__PURE__ */ new Set();
1261
+ abortController = null;
1262
+ repository = new MessageRepository();
1263
+ get messages() {
1264
+ return this.repository.getMessages();
1265
+ }
1266
+ get isRunning() {
1267
+ return this.abortController != null;
1268
+ }
1269
+ getBranches(messageId) {
1270
+ return this.repository.getBranches(messageId);
1271
+ }
1272
+ switchToBranch(branchId) {
1273
+ this.repository.switchToBranch(branchId);
1274
+ this.notifySubscribers();
1275
+ }
1276
+ async append(message) {
1277
+ const userMessageId = generateId();
1278
+ const userMessage = {
1279
+ id: userMessageId,
1280
+ role: "user",
1281
+ content: message.content,
1282
+ createdAt: /* @__PURE__ */ new Date()
1283
+ };
1284
+ this.repository.addOrUpdateMessage(message.parentId, userMessage);
1285
+ await this.startRun(userMessageId);
1286
+ }
1287
+ async startRun(parentId) {
1288
+ const id = generateId();
1289
+ this.repository.resetHead(parentId);
1290
+ const messages = this.repository.getMessages();
1291
+ const message = {
1292
+ id,
1293
+ role: "assistant",
1294
+ status: "in_progress",
1295
+ content: [{ type: "text", text: "" }],
1296
+ createdAt: /* @__PURE__ */ new Date()
1297
+ };
1298
+ this.repository.addOrUpdateMessage(parentId, { ...message });
1299
+ this.abortController?.abort();
1300
+ this.abortController = new AbortController();
1301
+ this.notifySubscribers();
1302
+ try {
1303
+ const updateHandler = ({ content }) => {
1304
+ message.content = content;
1305
+ this.repository.addOrUpdateMessage(parentId, { ...message });
1306
+ this.notifySubscribers();
1307
+ };
1308
+ const result = await this.adapter.run({
1309
+ messages,
1310
+ abortSignal: this.abortController.signal,
1311
+ config: mergeModelConfigs(this._configProviders),
1312
+ onUpdate: updateHandler
1313
+ });
1314
+ updateHandler(result);
1315
+ message.status = "done";
1316
+ this.repository.addOrUpdateMessage(parentId, { ...message });
1317
+ } catch (e) {
1318
+ message.status = "error";
1319
+ this.repository.addOrUpdateMessage(parentId, { ...message });
1320
+ console.error(e);
1321
+ } finally {
1322
+ this.abortController = null;
1323
+ this.notifySubscribers();
1324
+ }
1325
+ }
1326
+ cancelRun() {
1327
+ if (!this.abortController) return;
1328
+ this.abortController.abort();
1329
+ this.abortController = null;
1330
+ this.notifySubscribers();
1331
+ }
1332
+ notifySubscribers() {
1333
+ for (const callback of this._subscriptions) callback();
1334
+ }
1335
+ subscribe(callback) {
1336
+ this._subscriptions.add(callback);
1337
+ return () => this._subscriptions.delete(callback);
1338
+ }
1339
+ registerModelConfigProvider(provider) {
1340
+ this._configProviders.add(provider);
1341
+ return () => this._configProviders.delete(provider);
1342
+ }
1343
+ };
1344
+
1345
+ // src/runtime/local/useLocalRuntime.tsx
1346
+ var useLocalRuntime = (adapter) => {
1347
+ const [runtime] = useState3(() => new LocalRuntime(adapter));
1348
+ useInsertionEffect(() => {
1349
+ runtime.adapter = adapter;
1350
+ });
1351
+ return runtime;
1352
+ };
1353
+
1354
+ // src/context/providers/AssistantRuntimeProvider.tsx
1355
+ import { memo as memo3 } from "react";
1356
+
1357
+ // src/context/providers/AssistantProvider.tsx
1358
+ import { useEffect as useEffect7, useInsertionEffect as useInsertionEffect3, useRef as useRef5, useState as useState5 } from "react";
1359
+
1360
+ // src/context/stores/AssistantModelConfig.ts
1361
+ import { create as create4 } from "zustand";
1362
+
1363
+ // src/utils/ProxyConfigProvider.ts
1364
+ var ProxyConfigProvider = class {
1365
+ _providers = /* @__PURE__ */ new Set();
1366
+ getModelConfig() {
1367
+ return mergeModelConfigs(this._providers);
1368
+ }
1369
+ registerModelConfigProvider(provider) {
1370
+ this._providers.add(provider);
1371
+ return () => {
1372
+ this._providers.delete(provider);
1373
+ };
1374
+ }
1375
+ };
1376
+
1377
+ // src/context/stores/AssistantModelConfig.ts
1378
+ var makeAssistantModelConfigStore = () => create4(() => {
1379
+ const proxy = new ProxyConfigProvider();
1380
+ return {
1381
+ getModelConfig: () => {
1382
+ return proxy.getModelConfig();
1383
+ },
1384
+ registerModelConfigProvider: (provider) => {
1385
+ return proxy.registerModelConfigProvider(provider);
1386
+ }
1387
+ };
1388
+ });
1389
+
1390
+ // src/context/stores/AssistantToolRenderers.ts
1391
+ import { create as create5 } from "zustand";
1392
+ var makeAssistantToolRenderersStore = () => create5((set) => {
1393
+ const renderers = /* @__PURE__ */ new Map();
1394
+ return {
1395
+ getToolRenderer: (name) => {
1396
+ const arr = renderers.get(name);
1397
+ const last = arr?.at(-1);
1398
+ if (last) return last;
1399
+ return null;
1400
+ },
1401
+ setToolRenderer: (name, render) => {
1402
+ let arr = renderers.get(name);
1403
+ if (!arr) {
1404
+ arr = [];
1405
+ renderers.set(name, arr);
1406
+ }
1407
+ arr.push(render);
1408
+ set({});
1409
+ return () => {
1410
+ const index = arr.indexOf(render);
1411
+ if (index !== -1) {
1412
+ arr.splice(index, 1);
1413
+ }
1414
+ set({});
1415
+ };
1416
+ }
1417
+ };
1418
+ });
1419
+
1420
+ // src/context/providers/ThreadProvider.tsx
1421
+ import { useEffect as useEffect6, useInsertionEffect as useInsertionEffect2, useRef as useRef4, useState as useState4 } from "react";
1422
+
1423
+ // src/context/stores/Composer.ts
1424
+ import { create as create6 } from "zustand";
1425
+ var makeComposerStore = (useThread) => create6()((set, get, store) => {
1426
+ return {
1427
+ ...makeBaseComposer(set, get, store),
1428
+ isEditing: true,
1429
+ send: () => {
1430
+ const { setValue, value } = get();
1431
+ setValue("");
1432
+ useThread.getState().append({
1433
+ parentId: useThread.getState().messages.at(-1)?.id ?? null,
1434
+ content: [{ type: "text", text: value }]
1435
+ });
1436
+ },
1437
+ cancel: () => {
1438
+ const thread = useThread.getState();
1439
+ if (!thread.isRunning) return false;
1440
+ useThread.getState().cancelRun();
1441
+ return true;
1442
+ }
1443
+ };
1444
+ });
1445
+
1446
+ // src/context/stores/Thread.ts
1447
+ import { create as create7 } from "zustand";
1448
+ var makeThreadStore = (runtimeRef) => {
1449
+ const useThread = create7(() => ({
1450
+ messages: runtimeRef.current.messages,
1451
+ isRunning: runtimeRef.current.isRunning,
1452
+ getBranches: (messageId) => runtimeRef.current.getBranches(messageId),
1453
+ switchToBranch: (branchId) => runtimeRef.current.switchToBranch(branchId),
1454
+ startRun: (parentId) => runtimeRef.current.startRun(parentId),
1455
+ append: (message) => runtimeRef.current.append(message),
1456
+ cancelRun: () => runtimeRef.current.cancelRun()
1457
+ }));
1458
+ const onRuntimeUpdate = () => {
1459
+ useThread.setState({
1460
+ messages: runtimeRef.current.messages,
1461
+ isRunning: runtimeRef.current.isRunning
1462
+ });
1463
+ };
1464
+ return {
1465
+ useThread,
1466
+ onRuntimeUpdate
1467
+ };
1468
+ };
1469
+
1470
+ // src/context/stores/ThreadViewport.tsx
1471
+ import { create as create8 } from "zustand";
1472
+ var makeThreadViewportStore = () => {
1473
+ const scrollToBottomListeners = /* @__PURE__ */ new Set();
1474
+ return create8(() => ({
1475
+ isAtBottom: true,
1476
+ scrollToBottom: () => {
1477
+ for (const listener of scrollToBottomListeners) {
1478
+ listener();
1479
+ }
1480
+ },
1481
+ onScrollToBottom: (callback) => {
1482
+ scrollToBottomListeners.add(callback);
1483
+ return () => {
1484
+ scrollToBottomListeners.delete(callback);
1485
+ };
1486
+ }
1487
+ }));
1488
+ };
1489
+
1490
+ // src/context/providers/ThreadProvider.tsx
1491
+ import { jsx as jsx23, jsxs as jsxs4 } from "react/jsx-runtime";
1492
+ var ThreadProvider = ({
1493
+ children,
1494
+ runtime
1495
+ }) => {
1496
+ const runtimeRef = useRef4(runtime);
1497
+ useInsertionEffect2(() => {
1498
+ runtimeRef.current = runtime;
1499
+ });
1500
+ const [{ context, onRuntimeUpdate }] = useState4(() => {
1501
+ const { useThread, onRuntimeUpdate: onRuntimeUpdate2 } = makeThreadStore(runtimeRef);
1502
+ const useViewport = makeThreadViewportStore();
1503
+ const useComposer = makeComposerStore(useThread);
1504
+ return {
1505
+ context: {
1506
+ useViewport,
1507
+ useThread,
1508
+ useComposer
1509
+ },
1510
+ onRuntimeUpdate: onRuntimeUpdate2
1511
+ };
1512
+ });
1513
+ useEffect6(() => {
1514
+ onRuntimeUpdate();
1515
+ return runtime.subscribe(onRuntimeUpdate);
1516
+ }, [onRuntimeUpdate, runtime]);
1517
+ const RuntimeSynchronizer = runtime.unstable_synchronizer;
1518
+ return /* @__PURE__ */ jsxs4(ThreadContext.Provider, { value: context, children: [
1519
+ RuntimeSynchronizer && /* @__PURE__ */ jsx23(RuntimeSynchronizer, {}),
1520
+ children
1521
+ ] });
1522
+ };
1523
+
1524
+ // src/context/providers/AssistantProvider.tsx
1525
+ import { jsx as jsx24 } from "react/jsx-runtime";
1526
+ var AssistantProvider = ({ children, runtime }) => {
1527
+ const runtimeRef = useRef5(runtime);
1528
+ useInsertionEffect3(() => {
1529
+ runtimeRef.current = runtime;
1530
+ });
1531
+ const [context] = useState5(() => {
1532
+ const useModelConfig = makeAssistantModelConfigStore();
1533
+ const useToolRenderers = makeAssistantToolRenderersStore();
1534
+ return { useModelConfig, useToolRenderers };
1535
+ });
1536
+ const getModelCOnfig = context.useModelConfig((c) => c.getModelConfig);
1537
+ useEffect7(() => {
1538
+ return runtime.registerModelConfigProvider(getModelCOnfig);
1539
+ }, [runtime, getModelCOnfig]);
1540
+ return /* @__PURE__ */ jsx24(AssistantContext.Provider, { value: context, children: /* @__PURE__ */ jsx24(ThreadProvider, { runtime, children }) });
1541
+ };
1542
+
1543
+ // src/context/providers/AssistantRuntimeProvider.tsx
1544
+ import { jsx as jsx25 } from "react/jsx-runtime";
1545
+ var AssistantRuntimeProviderImpl = ({ children, runtime }) => {
1546
+ return /* @__PURE__ */ jsx25(AssistantProvider, { runtime, children });
1547
+ };
1548
+ var AssistantRuntimeProvider = memo3(AssistantRuntimeProviderImpl);
1549
+
1550
+ // src/internal.ts
1551
+ var internal_exports = {};
1552
+ __export(internal_exports, {
1553
+ MessageRepository: () => MessageRepository,
1554
+ ProxyConfigProvider: () => ProxyConfigProvider
1555
+ });
1556
+ export {
1557
+ actionBar_exports as ActionBarPrimitive,
1558
+ AssistantRuntimeProvider,
1559
+ branchPicker_exports as BranchPickerPrimitive,
1560
+ composer_exports as ComposerPrimitive,
1561
+ contentPart_exports as ContentPartPrimitive,
1562
+ internal_exports as INTERNAL,
1563
+ message_exports as MessagePrimitive,
1564
+ thread_exports as ThreadPrimitive,
1565
+ useBeginMessageEdit,
1566
+ useCopyMessage,
1567
+ useGoToNextBranch,
1568
+ useGoToPreviousBranch,
1569
+ useLocalRuntime,
1570
+ useReloadMessage
1571
+ };
1572
+ //# sourceMappingURL=index.mjs.map