@assistant-ui/mcp-docs-server 0.1.14 → 0.1.16

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.
Files changed (57) hide show
  1. package/.docs/organized/code-examples/store-example.md +628 -0
  2. package/.docs/organized/code-examples/with-ag-ui.md +792 -178
  3. package/.docs/organized/code-examples/with-ai-sdk-v5.md +762 -209
  4. package/.docs/organized/code-examples/with-assistant-transport.md +707 -254
  5. package/.docs/organized/code-examples/with-cloud.md +848 -202
  6. package/.docs/organized/code-examples/with-custom-thread-list.md +1855 -0
  7. package/.docs/organized/code-examples/with-external-store.md +788 -172
  8. package/.docs/organized/code-examples/with-ffmpeg.md +796 -196
  9. package/.docs/organized/code-examples/with-langgraph.md +864 -230
  10. package/.docs/organized/code-examples/with-parent-id-grouping.md +785 -255
  11. package/.docs/organized/code-examples/with-react-hook-form.md +804 -226
  12. package/.docs/organized/code-examples/with-tanstack.md +1574 -0
  13. package/.docs/raw/blog/2024-07-29-hello/index.mdx +2 -3
  14. package/.docs/raw/docs/api-reference/overview.mdx +6 -6
  15. package/.docs/raw/docs/api-reference/primitives/ActionBar.mdx +85 -4
  16. package/.docs/raw/docs/api-reference/primitives/AssistantIf.mdx +200 -0
  17. package/.docs/raw/docs/api-reference/primitives/Composer.mdx +0 -20
  18. package/.docs/raw/docs/api-reference/primitives/Message.mdx +0 -45
  19. package/.docs/raw/docs/api-reference/primitives/Thread.mdx +0 -50
  20. package/.docs/raw/docs/cli.mdx +396 -0
  21. package/.docs/raw/docs/cloud/persistence/ai-sdk.mdx +2 -3
  22. package/.docs/raw/docs/cloud/persistence/langgraph.mdx +2 -3
  23. package/.docs/raw/docs/devtools.mdx +2 -3
  24. package/.docs/raw/docs/getting-started.mdx +37 -1109
  25. package/.docs/raw/docs/guides/Attachments.mdx +3 -25
  26. package/.docs/raw/docs/guides/Branching.mdx +1 -1
  27. package/.docs/raw/docs/guides/Speech.mdx +1 -1
  28. package/.docs/raw/docs/guides/ToolUI.mdx +1 -1
  29. package/.docs/raw/docs/legacy/styled/AssistantModal.mdx +2 -3
  30. package/.docs/raw/docs/legacy/styled/Decomposition.mdx +6 -5
  31. package/.docs/raw/docs/legacy/styled/Markdown.mdx +2 -3
  32. package/.docs/raw/docs/legacy/styled/Thread.mdx +2 -3
  33. package/.docs/raw/docs/react-compatibility.mdx +2 -5
  34. package/.docs/raw/docs/runtimes/ai-sdk/use-chat.mdx +3 -4
  35. package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +3 -6
  36. package/.docs/raw/docs/runtimes/assistant-transport.mdx +891 -0
  37. package/.docs/raw/docs/runtimes/custom/external-store.mdx +2 -3
  38. package/.docs/raw/docs/runtimes/custom/local.mdx +11 -41
  39. package/.docs/raw/docs/runtimes/data-stream.mdx +15 -11
  40. package/.docs/raw/docs/runtimes/langgraph/index.mdx +4 -4
  41. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +1 -1
  42. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +2 -3
  43. package/.docs/raw/docs/runtimes/langserve.mdx +2 -3
  44. package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +2 -3
  45. package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +2 -3
  46. package/.docs/raw/docs/ui/AssistantModal.mdx +3 -25
  47. package/.docs/raw/docs/ui/AssistantSidebar.mdx +2 -24
  48. package/.docs/raw/docs/ui/Attachment.mdx +3 -25
  49. package/.docs/raw/docs/ui/Markdown.mdx +2 -24
  50. package/.docs/raw/docs/ui/Mermaid.mdx +2 -24
  51. package/.docs/raw/docs/ui/Reasoning.mdx +2 -24
  52. package/.docs/raw/docs/ui/Scrollbar.mdx +4 -6
  53. package/.docs/raw/docs/ui/SyntaxHighlighting.mdx +3 -47
  54. package/.docs/raw/docs/ui/Thread.mdx +38 -53
  55. package/.docs/raw/docs/ui/ThreadList.mdx +4 -47
  56. package/.docs/raw/docs/ui/ToolFallback.mdx +2 -24
  57. package/package.json +15 -8
@@ -6,6 +6,7 @@ import { Step, Steps } from "fumadocs-ui/components/steps";
6
6
  import { Tab, Tabs } from "fumadocs-ui/components/tabs";
7
7
  import { Callout } from "fumadocs-ui/components/callout";
8
8
  import { Card, Cards } from "fumadocs-ui/components/card";
9
+ import { InstallCommand } from "@/components/docs/install-command";
9
10
 
10
11
  ## Start with a new project
11
12
 
@@ -77,1068 +78,7 @@ npm run dev
77
78
 
78
79
  ### Add assistant-ui
79
80
 
80
- <Tabs items={["With Tailwind (Recommended)", "Without Tailwind"]}>
81
- <Tab>
82
-
83
- ```sh npm2yarn
84
- npx assistant-ui add thread thread-list
85
- ```
86
-
87
- </Tab>
88
- <Tab>
89
- <Steps>
90
- <Step>
91
-
92
- Add the following packages:
93
-
94
- ```sh
95
- npm install \
96
- @assistant-ui/react \
97
- @assistant-ui/react-markdown \
98
- @assistant-ui/styles \
99
- @radix-ui/react-avatar \
100
- @radix-ui/react-dialog \
101
- @radix-ui/react-slot \
102
- @radix-ui/react-tooltip \
103
- class-variance-authority \
104
- clsx \
105
- lucide-react \
106
- motion \
107
- remark-gfm \
108
- zustand
109
- ```
110
-
111
- </Step>
112
-
113
- <Step>
114
-
115
- Copy the following components into your project:
116
-
117
- ```tsx title="components/ui/button.tsx"
118
- import * as React from "react";
119
- import { Slot } from "@radix-ui/react-slot";
120
- import { cva, type VariantProps } from "class-variance-authority";
121
-
122
- import { cn } from "@/lib/utils";
123
-
124
- const buttonVariants = cva("aui-button", {
125
- variants: {
126
- variant: {
127
- default: "aui-button-primary",
128
- outline: "aui-button-outline",
129
- ghost: "aui-button-ghost",
130
- },
131
- size: {
132
- default: "aui-button-medium",
133
- icon: "aui-button-icon",
134
- },
135
- },
136
- defaultVariants: {
137
- variant: "default",
138
- size: "default",
139
- },
140
- });
141
-
142
- function Button({
143
- className,
144
- variant,
145
- size,
146
- asChild = false,
147
- ...props
148
- }: React.ComponentProps<"button"> &
149
- VariantProps<typeof buttonVariants> & {
150
- asChild?: boolean;
151
- }) {
152
- const Comp = asChild ? Slot : "button";
153
-
154
- return (
155
- <Comp
156
- data-slot="button"
157
- className={cn(buttonVariants({ variant, size, className }))}
158
- {...props}
159
- />
160
- );
161
- }
162
-
163
- export { Button, buttonVariants };
164
- ```
165
-
166
- ```tsx title="components/ui/tooltip.tsx"
167
- "use client";
168
-
169
- import * as React from "react";
170
- import * as TooltipPrimitive from "@radix-ui/react-tooltip";
171
-
172
- import { cn } from "@/lib/utils";
173
-
174
- const TooltipProvider = TooltipPrimitive.Provider;
175
-
176
- const Tooltip = TooltipPrimitive.Root;
177
-
178
- const TooltipTrigger = TooltipPrimitive.Trigger;
179
-
180
- const TooltipContent = React.forwardRef<
181
- React.ElementRef<typeof TooltipPrimitive.Content>,
182
- React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
183
- >(({ className, sideOffset = 4, ...props }, ref) => (
184
- <TooltipPrimitive.Portal>
185
- <TooltipPrimitive.Content
186
- ref={ref}
187
- sideOffset={sideOffset}
188
- className={cn("aui-tooltip-content", className)}
189
- {...props}
190
- />
191
- </TooltipPrimitive.Portal>
192
- ));
193
- TooltipContent.displayName = TooltipPrimitive.Content.displayName;
194
-
195
- export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
196
- ```
197
-
198
- ```tsx title="components/assistant-ui/thread.tsx"
199
- import {
200
- ArrowDownIcon,
201
- ArrowUpIcon,
202
- CheckIcon,
203
- ChevronLeftIcon,
204
- ChevronRightIcon,
205
- CopyIcon,
206
- PencilIcon,
207
- RefreshCwIcon,
208
- Square,
209
- } from "lucide-react";
210
-
211
- import {
212
- ActionBarPrimitive,
213
- BranchPickerPrimitive,
214
- ComposerPrimitive,
215
- ErrorPrimitive,
216
- MessagePrimitive,
217
- ThreadPrimitive,
218
- } from "@assistant-ui/react";
219
-
220
- import type { FC } from "react";
221
- import { LazyMotion, MotionConfig, domAnimation } from "motion/react";
222
- import * as m from "motion/react-m";
223
-
224
- import { Button } from "@/components/ui/button";
225
- import { MarkdownText } from "@/components/assistant-ui/markdown-text";
226
- import { ToolFallback } from "@/components/assistant-ui/tool-fallback";
227
- import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
228
- import {
229
- ComposerAddAttachment,
230
- ComposerAttachments,
231
- UserMessageAttachments,
232
- } from "@/components/assistant-ui/attachment";
233
-
234
- import { cn } from "@/lib/utils";
235
-
236
- export const Thread: FC = () => {
237
- return (
238
- <LazyMotion features={domAnimation}>
239
- <MotionConfig reducedMotion="user">
240
- <ThreadPrimitive.Root
241
- className="aui-root aui-thread-root"
242
- style={{
243
- ["--thread-max-width" as string]: "44rem",
244
- }}
245
- >
246
- <ThreadPrimitive.Viewport className="aui-thread-viewport">
247
- <ThreadPrimitive.If empty>
248
- <ThreadWelcome />
249
- </ThreadPrimitive.If>
250
-
251
- <ThreadPrimitive.Messages
252
- components={{
253
- UserMessage,
254
- EditComposer,
255
- AssistantMessage,
256
- }}
257
- />
258
-
259
- <ThreadPrimitive.If empty={false}>
260
- <div className="aui-thread-viewport-spacer" />
261
- </ThreadPrimitive.If>
262
-
263
- <Composer />
264
- </ThreadPrimitive.Viewport>
265
- </ThreadPrimitive.Root>
266
- </MotionConfig>
267
- </LazyMotion>
268
- );
269
- };
270
-
271
- const ThreadScrollToBottom: FC = () => {
272
- return (
273
- <ThreadPrimitive.ScrollToBottom asChild>
274
- <TooltipIconButton
275
- tooltip="Scroll to bottom"
276
- variant="outline"
277
- className="aui-thread-scroll-to-bottom"
278
- >
279
- <ArrowDownIcon />
280
- </TooltipIconButton>
281
- </ThreadPrimitive.ScrollToBottom>
282
- );
283
- };
284
-
285
- const ThreadWelcome: FC = () => {
286
- return (
287
- <div className="aui-thread-welcome-root">
288
- <div className="aui-thread-welcome-center">
289
- <div className="aui-thread-welcome-message">
290
- <m.div
291
- initial={{ opacity: 0, y: 10 }}
292
- animate={{ opacity: 1, y: 0 }}
293
- exit={{ opacity: 0, y: 10 }}
294
- className="aui-thread-welcome-message-motion-1"
295
- >
296
- Hello there!
297
- </m.div>
298
- <m.div
299
- initial={{ opacity: 0, y: 10 }}
300
- animate={{ opacity: 1, y: 0 }}
301
- exit={{ opacity: 0, y: 10 }}
302
- transition={{ delay: 0.1 }}
303
- className="aui-thread-welcome-message-motion-2"
304
- >
305
- How can I help you today?
306
- </m.div>
307
- </div>
308
- </div>
309
- <ThreadSuggestions />
310
- </div>
311
- );
312
- };
313
-
314
- const ThreadSuggestions: FC = () => {
315
- return (
316
- <div className="aui-thread-welcome-suggestions">
317
- {[
318
- {
319
- title: "What's the weather",
320
- label: "in San Francisco?",
321
- action: "What's the weather in San Francisco?",
322
- },
323
- {
324
- title: "Explain React hooks",
325
- label: "like useState and useEffect",
326
- action: "Explain React hooks like useState and useEffect",
327
- },
328
- {
329
- title: "Write a SQL query",
330
- label: "to find top customers",
331
- action: "Write a SQL query to find top customers",
332
- },
333
- {
334
- title: "Create a meal plan",
335
- label: "for healthy weight loss",
336
- action: "Create a meal plan for healthy weight loss",
337
- },
338
- ].map((suggestedAction, index) => (
339
- <m.div
340
- initial={{ opacity: 0, y: 20 }}
341
- animate={{ opacity: 1, y: 0 }}
342
- exit={{ opacity: 0, y: 20 }}
343
- transition={{ delay: 0.05 * index }}
344
- key={`suggested-action-${suggestedAction.title}-${index}`}
345
- className="aui-thread-welcome-suggestion-display"
346
- >
347
- <ThreadPrimitive.Suggestion
348
- prompt={suggestedAction.action}
349
- send
350
- asChild
351
- >
352
- <Button
353
- variant="ghost"
354
- className="aui-thread-welcome-suggestion"
355
- aria-label={suggestedAction.action}
356
- >
357
- <span className="aui-thread-welcome-suggestion-text-1">
358
- {suggestedAction.title}
359
- </span>
360
- <span className="aui-thread-welcome-suggestion-text-2">
361
- {suggestedAction.label}
362
- </span>
363
- </Button>
364
- </ThreadPrimitive.Suggestion>
365
- </m.div>
366
- ))}
367
- </div>
368
- );
369
- };
370
-
371
- const Composer: FC = () => {
372
- return (
373
- <div className="aui-composer-wrapper">
374
- <ThreadScrollToBottom />
375
- <ComposerPrimitive.Root className="aui-composer-root">
376
- <ComposerAttachments />
377
- <ComposerPrimitive.Input
378
- placeholder="Send a message..."
379
- className="aui-composer-input"
380
- rows={1}
381
- autoFocus
382
- aria-label="Message input"
383
- />
384
- <ComposerAction />
385
- </ComposerPrimitive.Root>
386
- </div>
387
- );
388
- };
389
-
390
- const ComposerAction: FC = () => {
391
- return (
392
- <div className="aui-composer-action-wrapper">
393
- <ComposerAddAttachment />
394
-
395
- <ThreadPrimitive.If running={false}>
396
- <ComposerPrimitive.Send asChild>
397
- <TooltipIconButton
398
- tooltip="Send message"
399
- side="bottom"
400
- type="submit"
401
- variant="default"
402
- size="icon"
403
- className="aui-composer-send"
404
- aria-label="Send message"
405
- >
406
- <ArrowUpIcon className="aui-composer-send-icon" />
407
- </TooltipIconButton>
408
- </ComposerPrimitive.Send>
409
- </ThreadPrimitive.If>
410
-
411
- <ThreadPrimitive.If running>
412
- <ComposerPrimitive.Cancel asChild>
413
- <Button
414
- type="button"
415
- variant="default"
416
- size="icon"
417
- className="aui-composer-cancel"
418
- aria-label="Stop generating"
419
- >
420
- <Square className="aui-composer-cancel-icon" />
421
- </Button>
422
- </ComposerPrimitive.Cancel>
423
- </ThreadPrimitive.If>
424
- </div>
425
- );
426
- };
427
-
428
- const MessageError: FC = () => {
429
- return (
430
- <MessagePrimitive.Error>
431
- <ErrorPrimitive.Root className="aui-message-error-root">
432
- <ErrorPrimitive.Message className="aui-message-error-message" />
433
- </ErrorPrimitive.Root>
434
- </MessagePrimitive.Error>
435
- );
436
- };
437
-
438
- const AssistantMessage: FC = () => {
439
- return (
440
- <MessagePrimitive.Root asChild>
441
- <div
442
- className="aui-assistant-message-root"
443
- data-role="assistant"
444
- >
445
- <div className="aui-assistant-message-content">
446
- <MessagePrimitive.Parts
447
- components={{
448
- Text: MarkdownText,
449
- tools: { Fallback: ToolFallback },
450
- }}
451
- />
452
- <MessageError />
453
- </div>
454
-
455
- <div className="aui-assistant-message-footer">
456
- <BranchPicker />
457
- <AssistantActionBar />
458
- </div>
459
- </div>
460
- </MessagePrimitive.Root>
461
- );
462
- };
463
-
464
- const AssistantActionBar: FC = () => {
465
- return (
466
- <ActionBarPrimitive.Root
467
- hideWhenRunning
468
- autohide="not-last"
469
- autohideFloat="single-branch"
470
- className="aui-assistant-action-bar-root"
471
- >
472
- <ActionBarPrimitive.Copy asChild>
473
- <TooltipIconButton tooltip="Copy">
474
- <MessagePrimitive.If copied>
475
- <CheckIcon />
476
- </MessagePrimitive.If>
477
- <MessagePrimitive.If copied={false}>
478
- <CopyIcon />
479
- </MessagePrimitive.If>
480
- </TooltipIconButton>
481
- </ActionBarPrimitive.Copy>
482
- <ActionBarPrimitive.Reload asChild>
483
- <TooltipIconButton tooltip="Refresh">
484
- <RefreshCwIcon />
485
- </TooltipIconButton>
486
- </ActionBarPrimitive.Reload>
487
- </ActionBarPrimitive.Root>
488
- );
489
- };
490
-
491
- const UserMessage: FC = () => {
492
- return (
493
- <MessagePrimitive.Root asChild>
494
- <div
495
- className="aui-user-message-root"
496
- data-role="user"
497
- >
498
- <UserMessageAttachments />
499
-
500
- <div className="aui-user-message-content-wrapper">
501
- <div className="aui-user-message-content">
502
- <MessagePrimitive.Parts />
503
- </div>
504
- <div className="aui-user-action-bar-wrapper">
505
- <UserActionBar />
506
- </div>
507
- </div>
508
-
509
- <BranchPicker className="aui-user-branch-picker" />
510
- </div>
511
- </MessagePrimitive.Root>
512
- );
513
- };
514
-
515
- const UserActionBar: FC = () => {
516
- return (
517
- <ActionBarPrimitive.Root
518
- hideWhenRunning
519
- autohide="not-last"
520
- className="aui-user-action-bar-root"
521
- >
522
- <ActionBarPrimitive.Edit asChild>
523
- <TooltipIconButton tooltip="Edit" className="aui-user-action-edit">
524
- <PencilIcon />
525
- </TooltipIconButton>
526
- </ActionBarPrimitive.Edit>
527
- </ActionBarPrimitive.Root>
528
- );
529
- };
530
-
531
- const EditComposer: FC = () => {
532
- return (
533
- <div className="aui-edit-composer-wrapper">
534
- <ComposerPrimitive.Root className="aui-edit-composer-root">
535
- <ComposerPrimitive.Input
536
- className="aui-edit-composer-input"
537
- autoFocus
538
- />
539
-
540
- <div className="aui-edit-composer-footer">
541
- <ComposerPrimitive.Cancel asChild>
542
- <Button variant="ghost" size="sm" aria-label="Cancel edit">
543
- Cancel
544
- </Button>
545
- </ComposerPrimitive.Cancel>
546
- <ComposerPrimitive.Send asChild>
547
- <Button size="sm" aria-label="Update message">
548
- Update
549
- </Button>
550
- </ComposerPrimitive.Send>
551
- </div>
552
- </ComposerPrimitive.Root>
553
- </div>
554
- );
555
- };
556
-
557
- const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
558
- className,
559
- ...rest
560
- }) => {
561
- return (
562
- <BranchPickerPrimitive.Root
563
- hideWhenSingleBranch
564
- className={cn("aui-branch-picker-root", className)}
565
- {...rest}
566
- >
567
- <BranchPickerPrimitive.Previous asChild>
568
- <TooltipIconButton tooltip="Previous">
569
- <ChevronLeftIcon />
570
- </TooltipIconButton>
571
- </BranchPickerPrimitive.Previous>
572
- <span className="aui-branch-picker-state">
573
- <BranchPickerPrimitive.Number /> / <BranchPickerPrimitive.Count />
574
- </span>
575
- <BranchPickerPrimitive.Next asChild>
576
- <TooltipIconButton tooltip="Next">
577
- <ChevronRightIcon />
578
- </TooltipIconButton>
579
- </BranchPickerPrimitive.Next>
580
- </BranchPickerPrimitive.Root>
581
- );
582
- };
583
- ```
584
-
585
- ```tsx title="components/assistant-ui/attachment.tsx"
586
- "use client";
587
-
588
- import { PropsWithChildren, useEffect, useState, type FC } from "react";
589
- import Image from "next/image";
590
- import { XIcon, PlusIcon, FileText } from "lucide-react";
591
- import {
592
- AttachmentPrimitive,
593
- ComposerPrimitive,
594
- MessagePrimitive,
595
- useAssistantState,
596
- useAssistantApi,
597
- } from "@assistant-ui/react";
598
- import { useShallow } from "zustand/shallow";
599
- import {
600
- Tooltip,
601
- TooltipContent,
602
- TooltipTrigger,
603
- } from "@/components/ui/tooltip";
604
- import {
605
- Dialog,
606
- DialogTitle,
607
- DialogContent,
608
- DialogTrigger,
609
- } from "@/components/ui/dialog";
610
- import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
611
- import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
612
- import { cn } from "@/lib/utils";
613
-
614
- const useFileSrc = (file: File | undefined) => {
615
- const [src, setSrc] = useState<string | undefined>(undefined);
616
-
617
- useEffect(() => {
618
- if (!file) {
619
- setSrc(undefined);
620
- return;
621
- }
622
-
623
- const objectUrl = URL.createObjectURL(file);
624
- setSrc(objectUrl);
625
-
626
- return () => {
627
- URL.revokeObjectURL(objectUrl);
628
- };
629
- }, [file]);
630
-
631
- return src;
632
- };
633
-
634
- const useAttachmentSrc = () => {
635
- const { file, src } = useAssistantState(
636
- useShallow(({ attachment }): { file?: File; src?: string } => {
637
- if (attachment.type !== "image") return {};
638
- if (attachment.file) return { file: attachment.file };
639
- const src = attachment.content?.filter((c) => c.type === "image")[0]
640
- ?.image;
641
- if (!src) return {};
642
- return { src };
643
- }),
644
- );
645
-
646
- return useFileSrc(file) ?? src;
647
- };
648
-
649
- type AttachmentPreviewProps = {
650
- src: string;
651
- };
652
-
653
- const AttachmentPreview: FC<AttachmentPreviewProps> = ({ src }) => {
654
- const [isLoaded, setIsLoaded] = useState(false);
655
- return (
656
- <Image
657
- src={src}
658
- alt="Image Preview"
659
- width={1}
660
- height={1}
661
- className={
662
- isLoaded
663
- ? "aui-attachment-preview-image-loaded"
664
- : "aui-attachment-preview-image-loading"
665
- }
666
- onLoadingComplete={() => setIsLoaded(true)}
667
- priority={false}
668
- />
669
- );
670
- };
671
-
672
- const AttachmentPreviewDialog: FC<PropsWithChildren> = ({ children }) => {
673
- const src = useAttachmentSrc();
674
-
675
- if (!src) return children;
676
-
677
- return (
678
- <Dialog>
679
- <DialogTrigger className="aui-attachment-preview-trigger" asChild>
680
- {children}
681
- </DialogTrigger>
682
- <DialogContent className="aui-attachment-preview-dialog-content">
683
- <DialogTitle className="aui-sr-only">
684
- Image Attachment Preview
685
- </DialogTitle>
686
- <div className="aui-attachment-preview">
687
- <AttachmentPreview src={src} />
688
- </div>
689
- </DialogContent>
690
- </Dialog>
691
- );
692
- };
693
-
694
- const AttachmentThumb: FC = () => {
695
- const isImage = useAssistantState(
696
- ({ attachment }) => attachment.type === "image",
697
- );
698
- const src = useAttachmentSrc();
699
-
700
- return (
701
- <Avatar className="aui-attachment-tile-avatar">
702
- <AvatarImage
703
- src={src}
704
- alt="Attachment preview"
705
- className="aui-attachment-tile-image"
706
- />
707
- <AvatarFallback delayMs={isImage ? 200 : 0}>
708
- <FileText className="aui-attachment-tile-fallback-icon" />
709
- </AvatarFallback>
710
- </Avatar>
711
- );
712
- };
713
-
714
- const AttachmentUI: FC = () => {
715
- const api = useAssistantApi();
716
- const isComposer = api.attachment.source === "composer";
717
-
718
- const isImage = useAssistantState(
719
- ({ attachment }) => attachment.type === "image",
720
- );
721
- const typeLabel = useAssistantState(({ attachment }) => {
722
- const type = attachment.type;
723
- switch (type) {
724
- case "image":
725
- return "Image";
726
- case "document":
727
- return "Document";
728
- case "file":
729
- return "File";
730
- default:
731
- const _exhaustiveCheck: never = type;
732
- throw new Error(`Unknown attachment type: ${_exhaustiveCheck}`);
733
- }
734
- });
735
-
736
- return (
737
- <Tooltip>
738
- <AttachmentPrimitive.Root
739
- className={cn(
740
- "aui-attachment-root",
741
- isImage && "aui-attachment-root-composer",
742
- )}
743
- >
744
- <AttachmentPreviewDialog>
745
- <TooltipTrigger asChild>
746
- <div
747
- className={cn(
748
- "aui-attachment-tile",
749
- isComposer && "aui-attachment-tile-composer",
750
- )}
751
- role="button"
752
- id="attachment-tile"
753
- aria-label={`${typeLabel} attachment`}
754
- >
755
- <AttachmentThumb />
756
- </div>
757
- </TooltipTrigger>
758
- </AttachmentPreviewDialog>
759
- {isComposer && <AttachmentRemove />}
760
- </AttachmentPrimitive.Root>
761
- <TooltipContent side="top">
762
- <AttachmentPrimitive.Name />
763
- </TooltipContent>
764
- </Tooltip>
765
- );
766
- };
767
-
768
- const AttachmentRemove: FC = () => {
769
- return (
770
- <AttachmentPrimitive.Remove asChild>
771
- <TooltipIconButton
772
- tooltip="Remove file"
773
- className="aui-attachment-tile-remove"
774
- side="top"
775
- >
776
- <XIcon className="aui-attachment-remove-icon" />
777
- </TooltipIconButton>
778
- </AttachmentPrimitive.Remove>
779
- );
780
- };
781
-
782
- export const UserMessageAttachments: FC = () => {
783
- return (
784
- <div className="aui-user-message-attachments-end">
785
- <MessagePrimitive.Attachments components={{ Attachment: AttachmentUI }} />
786
- </div>
787
- );
788
- };
789
-
790
- export const ComposerAttachments: FC = () => {
791
- return (
792
- <div className="aui-composer-attachments">
793
- <ComposerPrimitive.Attachments
794
- components={{ Attachment: AttachmentUI }}
795
- />
796
- </div>
797
- );
798
- };
799
-
800
- export const ComposerAddAttachment: FC = () => {
801
- return (
802
- <ComposerPrimitive.AddAttachment asChild>
803
- <TooltipIconButton
804
- tooltip="Add Attachment"
805
- side="bottom"
806
- variant="ghost"
807
- size="icon"
808
- className="aui-composer-add-attachment"
809
- aria-label="Add Attachment"
810
- >
811
- <PlusIcon className="aui-attachment-add-icon" />
812
- </TooltipIconButton>
813
- </ComposerPrimitive.AddAttachment>
814
- );
815
- };
816
- ```
817
-
818
- ```tsx title="components/assistant-ui/tool-fallback.tsx"
819
- import type { ToolCallMessagePartComponent } from "@assistant-ui/react";
820
- import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
821
- import { useState } from "react";
822
- import { Button } from "@/components/ui/button";
823
-
824
- export const ToolFallback: ToolCallMessagePartComponent = ({
825
- toolName,
826
- argsText,
827
- result,
828
- }) => {
829
- const [isCollapsed, setIsCollapsed] = useState(true);
830
- return (
831
- <div className="aui-tool-fallback-root">
832
- <div className="aui-tool-fallback-header">
833
- <CheckIcon className="aui-tool-fallback-icon" />
834
- <p className="aui-tool-fallback-title">
835
- Used tool: <b>{toolName}</b>
836
- </p>
837
- <Button onClick={() => setIsCollapsed(!isCollapsed)}>
838
- {isCollapsed ? <ChevronUpIcon /> : <ChevronDownIcon />}
839
- </Button>
840
- </div>
841
- {!isCollapsed && (
842
- <div className="aui-tool-fallback-content">
843
- <div className="aui-tool-fallback-args-root">
844
- <pre className="aui-tool-fallback-args-value">
845
- {argsText}
846
- </pre>
847
- </div>
848
- {result !== undefined && (
849
- <div className="aui-tool-fallback-result-root">
850
- <p className="aui-tool-fallback-result-header">
851
- Result:
852
- </p>
853
- <pre className="aui-tool-fallback-result-content">
854
- {typeof result === "string"
855
- ? result
856
- : JSON.stringify(result, null, 2)}
857
- </pre>
858
- </div>
859
- )}
860
- </div>
861
- )}
862
- </div>
863
- );
864
- };
865
- ```
866
-
867
- ```
868
-
869
- ```tsx title="components/assistant-ui/thread-list.tsx"
870
- import type { FC } from "react";
871
- import {
872
- ThreadListItemPrimitive,
873
- ThreadListPrimitive,
874
- } from "@assistant-ui/react";
875
- import { ArchiveIcon, PlusIcon } from "lucide-react";
876
-
877
- import { Button } from "@/components/ui/button";
878
- import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
879
-
880
- export const ThreadList: FC = () => {
881
- return (
882
- <ThreadListPrimitive.Root className="aui-root aui-thread-list-root">
883
- <ThreadListNew />
884
- <ThreadListItems />
885
- </ThreadListPrimitive.Root>
886
- );
887
- };
888
-
889
- const ThreadListNew: FC = () => {
890
- return (
891
- <ThreadListPrimitive.New asChild>
892
- <Button className="aui-thread-list-new" variant="ghost">
893
- <PlusIcon />
894
- New Thread
895
- </Button>
896
- </ThreadListPrimitive.New>
897
- );
898
- };
899
-
900
- const ThreadListItems: FC = () => {
901
- return <ThreadListPrimitive.Items components={{ ThreadListItem }} />;
902
- };
903
-
904
- const ThreadListItem: FC = () => {
905
- return (
906
- <ThreadListItemPrimitive.Root className="aui-thread-list-item">
907
- <ThreadListItemPrimitive.Trigger className="aui-thread-list-item-trigger">
908
- <ThreadListItemTitle />
909
- </ThreadListItemPrimitive.Trigger>
910
- <ThreadListItemArchive />
911
- </ThreadListItemPrimitive.Root>
912
- );
913
- };
914
-
915
- const ThreadListItemTitle: FC = () => {
916
- return (
917
- <span className="aui-thread-list-item-title">
918
- <ThreadListItemPrimitive.Title fallback="New Chat" />
919
- </span>
920
- );
921
- };
922
-
923
- const ThreadListItemArchive: FC = () => {
924
- return (
925
- <ThreadListItemPrimitive.Archive asChild>
926
- <TooltipIconButton
927
- className="aui-thread-list-item-archive"
928
- variant="ghost"
929
- tooltip="Archive thread"
930
- >
931
- <ArchiveIcon />
932
- </TooltipIconButton>
933
- </ThreadListItemPrimitive.Archive>
934
- );
935
- };
936
- ```
937
-
938
- ```tsx title="components/assistant-ui/markdown-text.tsx"
939
- "use client";
940
-
941
- import "@assistant-ui/react-markdown/styles/dot.css";
942
-
943
- import {
944
- type CodeHeaderProps,
945
- MarkdownTextPrimitive,
946
- unstable_memoizeMarkdownComponents as memoizeMarkdownComponents,
947
- useIsMarkdownCodeBlock,
948
- } from "@assistant-ui/react-markdown";
949
- import remarkGfm from "remark-gfm";
950
- import { type FC, memo, useState } from "react";
951
- import { CheckIcon, CopyIcon } from "lucide-react";
952
-
953
- import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
954
- import { cn } from "@/lib/utils";
955
-
956
- const MarkdownTextImpl = () => {
957
- return (
958
- <MarkdownTextPrimitive
959
- remarkPlugins={[remarkGfm]}
960
- className="aui-md"
961
- components={defaultComponents}
962
- />
963
- );
964
- };
965
-
966
- export const MarkdownText = memo(MarkdownTextImpl);
967
-
968
- const CodeHeader: FC<CodeHeaderProps> = ({ language, code }) => {
969
- const { isCopied, copyToClipboard } = useCopyToClipboard();
970
- const onCopy = () => {
971
- if (!code || isCopied) return;
972
- copyToClipboard(code);
973
- };
974
-
975
- return (
976
- <div className="aui-code-header-root">
977
- <span className="aui-code-header-language">{language}</span>
978
- <TooltipIconButton tooltip="Copy" onClick={onCopy}>
979
- {!isCopied && <CopyIcon />}
980
- {isCopied && <CheckIcon />}
981
- </TooltipIconButton>
982
- </div>
983
- );
984
- };
985
-
986
- const useCopyToClipboard = ({
987
- copiedDuration = 3000,
988
- }: {
989
- copiedDuration?: number;
990
- } = {}) => {
991
- const [isCopied, setIsCopied] = useState<boolean>(false);
992
-
993
- const copyToClipboard = (value: string) => {
994
- if (!value) return;
995
-
996
- navigator.clipboard.writeText(value).then(() => {
997
- setIsCopied(true);
998
- setTimeout(() => setIsCopied(false), copiedDuration);
999
- });
1000
- };
1001
-
1002
- return { isCopied, copyToClipboard };
1003
- };
1004
-
1005
- const defaultComponents = memoizeMarkdownComponents({
1006
- h1: ({ className, ...props }) => (
1007
- <h1 className={cn("aui-md-h1", className)} {...props} />
1008
- ),
1009
- h2: ({ className, ...props }) => (
1010
- <h2 className={cn("aui-md-h2", className)} {...props} />
1011
- ),
1012
- h3: ({ className, ...props }) => (
1013
- <h3 className={cn("aui-md-h3", className)} {...props} />
1014
- ),
1015
- h4: ({ className, ...props }) => (
1016
- <h4 className={cn("aui-md-h4", className)} {...props} />
1017
- ),
1018
- h5: ({ className, ...props }) => (
1019
- <h5 className={cn("aui-md-h5", className)} {...props} />
1020
- ),
1021
- h6: ({ className, ...props }) => (
1022
- <h6 className={cn("aui-md-h6", className)} {...props} />
1023
- ),
1024
- p: ({ className, ...props }) => (
1025
- <p className={cn("aui-md-p", className)} {...props} />
1026
- ),
1027
- a: ({ className, ...props }) => (
1028
- <a className={cn("aui-md-a", className)} {...props} />
1029
- ),
1030
- blockquote: ({ className, ...props }) => (
1031
- <blockquote className={cn("aui-md-blockquote", className)} {...props} />
1032
- ),
1033
- ul: ({ className, ...props }) => (
1034
- <ul className={cn("aui-md-ul", className)} {...props} />
1035
- ),
1036
- ol: ({ className, ...props }) => (
1037
- <ol className={cn("aui-md-ol", className)} {...props} />
1038
- ),
1039
- hr: ({ className, ...props }) => (
1040
- <hr className={cn("aui-md-hr", className)} {...props} />
1041
- ),
1042
- table: ({ className, ...props }) => (
1043
- <table className={cn("aui-md-table", className)} {...props} />
1044
- ),
1045
- th: ({ className, ...props }) => (
1046
- <th className={cn("aui-md-th", className)} {...props} />
1047
- ),
1048
- td: ({ className, ...props }) => (
1049
- <td className={cn("aui-md-td", className)} {...props} />
1050
- ),
1051
- tr: ({ className, ...props }) => (
1052
- <tr className={cn("aui-md-tr", className)} {...props} />
1053
- ),
1054
- sup: ({ className, ...props }) => (
1055
- <sup className={cn("aui-md-sup", className)} {...props} />
1056
- ),
1057
- pre: ({ className, ...props }) => (
1058
- <pre className={cn("aui-md-pre", className)} {...props} />
1059
- ),
1060
- code: function Code({ className, ...props }) {
1061
- const isCodeBlock = useIsMarkdownCodeBlock();
1062
- return (
1063
- <code
1064
- className={cn(!isCodeBlock && "aui-md-inline-code", className)}
1065
- {...props}
1066
- />
1067
- );
1068
- },
1069
- CodeHeader,
1070
- });
1071
- ```
1072
-
1073
- ```tsx title="components/assistant-ui/tooltip-icon-button.tsx"
1074
- "use client";
1075
-
1076
- import { ComponentPropsWithRef, forwardRef } from "react";
1077
- import { Slottable } from "@radix-ui/react-slot";
1078
-
1079
- import {
1080
- Tooltip,
1081
- TooltipContent,
1082
- TooltipTrigger,
1083
- } from "@/components/ui/tooltip";
1084
- import { Button } from "@/components/ui/button";
1085
- import { cn } from "@/lib/utils";
1086
-
1087
- export type TooltipIconButtonProps = ComponentPropsWithRef<typeof Button> & {
1088
- tooltip: string;
1089
- side?: "top" | "bottom" | "left" | "right";
1090
- };
1091
-
1092
- export const TooltipIconButton = forwardRef<
1093
- HTMLButtonElement,
1094
- TooltipIconButtonProps
1095
- >(({ children, tooltip, side = "bottom", className, ...rest }, ref) => {
1096
- return (
1097
- <Tooltip>
1098
- <TooltipTrigger asChild>
1099
- <Button
1100
- variant="ghost"
1101
- size="icon"
1102
- {...rest}
1103
- className={cn("aui-button-icon", className)}
1104
- ref={ref}
1105
- >
1106
- <Slottable>{children}</Slottable>
1107
- <span className="aui-sr-only">{tooltip}</span>
1108
- </Button>
1109
- </TooltipTrigger>
1110
- <TooltipContent side={side}>{tooltip}</TooltipContent>
1111
- </Tooltip>
1112
- );
1113
- });
1114
-
1115
- TooltipIconButton.displayName = "TooltipIconButton";
1116
- ```
1117
-
1118
- ```ts title="lib/utils.ts"
1119
- import { type ClassValue, clsx } from "clsx";
1120
-
1121
- export function cn(...inputs: ClassValue[]) {
1122
- return clsx(inputs);
1123
- }
1124
- ```
1125
-
1126
- </Step>
1127
-
1128
- <Step>
1129
- The components above reference CSS class names like `aui-thread-root`, `aui-composer-input`, etc. These are normally replaced by our CLI with Tailwind class names, but in this case you'll use our pre-compiled CSS files without a need for Tailwind:
1130
-
1131
- ```ts
1132
- import "@assistant-ui/styles/index.css";
1133
- import "@assistant-ui/styles/markdown.css";
1134
- // import "@assistant-ui/styles/modal.css"; // for future reference, only if you use our modal component
1135
- ```
1136
-
1137
- </Step>
1138
- </Steps>
1139
-
1140
- </Tab>
1141
- </Tabs>
81
+ <InstallCommand shadcn={["thread", "thread-list"]} manualSetupInstructions />
1142
82
 
1143
83
  </Step>
1144
84
  <Step>
@@ -1148,51 +88,39 @@ import "@assistant-ui/styles/markdown.css";
1148
88
  Install provider SDK:
1149
89
 
1150
90
  <Tabs groupId="provider" items={["OpenAI", "Anthropic", "Azure", "AWS", "Gemini", "GCP", "Groq", "Fireworks", "Cohere", "Ollama", "Chrome AI"]}>
1151
-
1152
- ```sh title="Terminal" tab="OpenAI"
1153
- npm install ai @assistant-ui/react-ai-sdk @ai-sdk/openai
1154
- ```
1155
-
1156
- ```sh title="Terminal" tab="Anthropic"
1157
- npm install ai @assistant-ui/react-ai-sdk @ai-sdk/anthropic
1158
- ```
1159
-
1160
- ```sh title="Terminal" tab="Azure"
1161
- npm install ai @assistant-ui/react-ai-sdk @ai-sdk/azure
1162
- ```
1163
-
1164
- ```sh title="Terminal" tab="AWS"
1165
- npm install ai @assistant-ui/react-ai-sdk @ai-sdk/amazon-bedrock
1166
- ```
1167
-
1168
- ```sh title="Terminal" tab="Gemini"
1169
- npm install ai @assistant-ui/react-ai-sdk @ai-sdk/google
1170
- ```
1171
-
1172
- ```sh title="Terminal" tab="GCP"
1173
- npm install ai @assistant-ui/react-ai-sdk @ai-sdk/google-vertex
1174
- ```
1175
-
1176
- ```sh title="Terminal" tab="Groq"
1177
- npm install ai @assistant-ui/react-ai-sdk @ai-sdk/openai
1178
- ```
1179
-
1180
- ```sh title="Terminal" tab="Fireworks"
1181
- npm install ai @assistant-ui/react-ai-sdk @ai-sdk/openai
1182
- ```
1183
-
1184
- ```sh title="Terminal" tab="Cohere"
1185
- npm install ai @assistant-ui/react-ai-sdk @ai-sdk/cohere
1186
- ```
1187
-
1188
- ```sh title="Terminal" tab="Ollama"
1189
- npm install ai @assistant-ui/react-ai-sdk ollama-ai-provider
1190
- ```
1191
-
1192
- ```sh title="Terminal" tab="Chrome AI"
1193
- npm install ai @assistant-ui/react-ai-sdk chrome-ai
1194
- ```
1195
-
91
+ <Tab>
92
+ <InstallCommand npm={["ai", "@assistant-ui/react-ai-sdk", "@ai-sdk/openai"]} />
93
+ </Tab>
94
+ <Tab>
95
+ <InstallCommand npm={["ai", "@assistant-ui/react-ai-sdk", "@ai-sdk/anthropic"]} />
96
+ </Tab>
97
+ <Tab>
98
+ <InstallCommand npm={["ai", "@assistant-ui/react-ai-sdk", "@ai-sdk/azure"]} />
99
+ </Tab>
100
+ <Tab>
101
+ <InstallCommand npm={["ai", "@assistant-ui/react-ai-sdk", "@ai-sdk/amazon-bedrock"]} />
102
+ </Tab>
103
+ <Tab>
104
+ <InstallCommand npm={["ai", "@assistant-ui/react-ai-sdk", "@ai-sdk/google"]} />
105
+ </Tab>
106
+ <Tab>
107
+ <InstallCommand npm={["ai", "@assistant-ui/react-ai-sdk", "@ai-sdk/google-vertex"]} />
108
+ </Tab>
109
+ <Tab>
110
+ <InstallCommand npm={["ai", "@assistant-ui/react-ai-sdk", "@ai-sdk/openai"]} />
111
+ </Tab>
112
+ <Tab>
113
+ <InstallCommand npm={["ai", "@assistant-ui/react-ai-sdk", "@ai-sdk/openai"]} />
114
+ </Tab>
115
+ <Tab>
116
+ <InstallCommand npm={["ai", "@assistant-ui/react-ai-sdk", "@ai-sdk/cohere"]} />
117
+ </Tab>
118
+ <Tab>
119
+ <InstallCommand npm={["ai", "@assistant-ui/react-ai-sdk", "ollama-ai-provider-v2"]} />
120
+ </Tab>
121
+ <Tab>
122
+ <InstallCommand npm={["ai", "@assistant-ui/react-ai-sdk", "chrome-ai"]} />
123
+ </Tab>
1196
124
  </Tabs>
1197
125
 
1198
126
  Add an API endpoint:
@@ -1353,7 +281,7 @@ export async function POST(req: Request) {
1353
281
  ```
1354
282
 
1355
283
  ```ts title="/app/api/chat/route.ts" tab="Ollama"
1356
- import { ollama } from "ollama-ai-provider";
284
+ import { ollama } from "ollama-ai-provider-v2";
1357
285
  import { convertToModelMessages, streamText } from "ai";
1358
286
 
1359
287
  export const maxDuration = 30;
@@ -1476,7 +404,7 @@ const MyApp = () => {
1476
404
  ```
1477
405
 
1478
406
  ```tsx title="/app/page.tsx" tab="AssistantModal"
1479
- // run `npx shadcn@latest add "https://r.assistant-ui.com/assistant-modal"`
407
+ // run `npx shadcn@latest add @assistant-ui/assistant-modal`
1480
408
 
1481
409
  import { AssistantRuntimeProvider } from "@assistant-ui/react";
1482
410
  import { useChatRuntime, AssistantChatTransport } from "@assistant-ui/react-ai-sdk";