@assistant-ui/mcp-docs-server 0.1.15 → 0.1.17

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 (55) hide show
  1. package/.docs/organized/code-examples/store-example.md +249 -147
  2. package/.docs/organized/code-examples/with-ag-ui.md +250 -186
  3. package/.docs/organized/code-examples/with-ai-sdk-v5.md +250 -199
  4. package/.docs/organized/code-examples/with-assistant-transport.md +195 -243
  5. package/.docs/organized/code-examples/with-cloud.md +277 -226
  6. package/.docs/organized/code-examples/with-custom-thread-list.md +1855 -0
  7. package/.docs/organized/code-examples/with-external-store.md +246 -180
  8. package/.docs/organized/code-examples/with-ffmpeg.md +255 -189
  9. package/.docs/organized/code-examples/with-langgraph.md +293 -242
  10. package/.docs/organized/code-examples/with-parent-id-grouping.md +243 -263
  11. package/.docs/organized/code-examples/with-react-hook-form.md +262 -196
  12. package/.docs/organized/code-examples/with-tanstack.md +1578 -0
  13. package/.docs/raw/blog/2024-07-29-hello/index.mdx +2 -2
  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/cloud/persistence/ai-sdk.mdx +2 -3
  21. package/.docs/raw/docs/cloud/persistence/langgraph.mdx +2 -3
  22. package/.docs/raw/docs/devtools.mdx +2 -3
  23. package/.docs/raw/docs/getting-started.mdx +36 -1102
  24. package/.docs/raw/docs/guides/Attachments.mdx +3 -25
  25. package/.docs/raw/docs/guides/Branching.mdx +1 -1
  26. package/.docs/raw/docs/guides/Speech.mdx +1 -1
  27. package/.docs/raw/docs/guides/ToolUI.mdx +1 -1
  28. package/.docs/raw/docs/legacy/styled/AssistantModal.mdx +2 -3
  29. package/.docs/raw/docs/legacy/styled/Decomposition.mdx +6 -5
  30. package/.docs/raw/docs/legacy/styled/Markdown.mdx +2 -3
  31. package/.docs/raw/docs/legacy/styled/Thread.mdx +2 -3
  32. package/.docs/raw/docs/react-compatibility.mdx +2 -5
  33. package/.docs/raw/docs/runtimes/ai-sdk/use-chat.mdx +3 -4
  34. package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +3 -6
  35. package/.docs/raw/docs/runtimes/custom/external-store.mdx +2 -3
  36. package/.docs/raw/docs/runtimes/custom/local.mdx +11 -41
  37. package/.docs/raw/docs/runtimes/data-stream.mdx +15 -11
  38. package/.docs/raw/docs/runtimes/langgraph/index.mdx +3 -3
  39. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +1 -1
  40. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +2 -3
  41. package/.docs/raw/docs/runtimes/langserve.mdx +2 -3
  42. package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +2 -3
  43. package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +2 -3
  44. package/.docs/raw/docs/ui/AssistantModal.mdx +3 -25
  45. package/.docs/raw/docs/ui/AssistantSidebar.mdx +2 -24
  46. package/.docs/raw/docs/ui/Attachment.mdx +3 -25
  47. package/.docs/raw/docs/ui/Markdown.mdx +2 -24
  48. package/.docs/raw/docs/ui/Mermaid.mdx +2 -24
  49. package/.docs/raw/docs/ui/Reasoning.mdx +2 -24
  50. package/.docs/raw/docs/ui/Scrollbar.mdx +4 -6
  51. package/.docs/raw/docs/ui/SyntaxHighlighting.mdx +3 -47
  52. package/.docs/raw/docs/ui/Thread.mdx +38 -53
  53. package/.docs/raw/docs/ui/ThreadList.mdx +4 -47
  54. package/.docs/raw/docs/ui/ToolFallback.mdx +2 -24
  55. package/package.json +6 -7
@@ -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,1062 +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
441
- className="aui-assistant-message-root"
442
- data-role="assistant"
443
- >
444
- <div className="aui-assistant-message-content">
445
- <MessagePrimitive.Parts
446
- components={{
447
- Text: MarkdownText,
448
- tools: { Fallback: ToolFallback },
449
- }}
450
- />
451
- <MessageError />
452
- </div>
453
-
454
- <div className="aui-assistant-message-footer">
455
- <BranchPicker />
456
- <AssistantActionBar />
457
- </div>
458
- </MessagePrimitive.Root>
459
- );
460
- };
461
-
462
- const AssistantActionBar: FC = () => {
463
- return (
464
- <ActionBarPrimitive.Root
465
- hideWhenRunning
466
- autohide="not-last"
467
- autohideFloat="single-branch"
468
- className="aui-assistant-action-bar-root"
469
- >
470
- <ActionBarPrimitive.Copy asChild>
471
- <TooltipIconButton tooltip="Copy">
472
- <MessagePrimitive.If copied>
473
- <CheckIcon />
474
- </MessagePrimitive.If>
475
- <MessagePrimitive.If copied={false}>
476
- <CopyIcon />
477
- </MessagePrimitive.If>
478
- </TooltipIconButton>
479
- </ActionBarPrimitive.Copy>
480
- <ActionBarPrimitive.Reload asChild>
481
- <TooltipIconButton tooltip="Refresh">
482
- <RefreshCwIcon />
483
- </TooltipIconButton>
484
- </ActionBarPrimitive.Reload>
485
- </ActionBarPrimitive.Root>
486
- );
487
- };
488
-
489
- const UserMessage: FC = () => {
490
- return (
491
- <MessagePrimitive.Root
492
- className="aui-user-message-root"
493
- data-role="user"
494
- >
495
- <UserMessageAttachments />
496
-
497
- <div className="aui-user-message-content-wrapper">
498
- <div className="aui-user-message-content">
499
- <MessagePrimitive.Parts />
500
- </div>
501
- <div className="aui-user-action-bar-wrapper">
502
- <UserActionBar />
503
- </div>
504
- </div>
505
-
506
- <BranchPicker className="aui-user-branch-picker" />
507
- </MessagePrimitive.Root>
508
- );
509
- };
510
-
511
- const UserActionBar: FC = () => {
512
- return (
513
- <ActionBarPrimitive.Root
514
- hideWhenRunning
515
- autohide="not-last"
516
- className="aui-user-action-bar-root"
517
- >
518
- <ActionBarPrimitive.Edit asChild>
519
- <TooltipIconButton tooltip="Edit" className="aui-user-action-edit">
520
- <PencilIcon />
521
- </TooltipIconButton>
522
- </ActionBarPrimitive.Edit>
523
- </ActionBarPrimitive.Root>
524
- );
525
- };
526
-
527
- const EditComposer: FC = () => {
528
- return (
529
- <div className="aui-edit-composer-wrapper">
530
- <ComposerPrimitive.Root className="aui-edit-composer-root">
531
- <ComposerPrimitive.Input
532
- className="aui-edit-composer-input"
533
- autoFocus
534
- />
535
-
536
- <div className="aui-edit-composer-footer">
537
- <ComposerPrimitive.Cancel asChild>
538
- <Button variant="ghost" size="sm" aria-label="Cancel edit">
539
- Cancel
540
- </Button>
541
- </ComposerPrimitive.Cancel>
542
- <ComposerPrimitive.Send asChild>
543
- <Button size="sm" aria-label="Update message">
544
- Update
545
- </Button>
546
- </ComposerPrimitive.Send>
547
- </div>
548
- </ComposerPrimitive.Root>
549
- </div>
550
- );
551
- };
552
-
553
- const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
554
- className,
555
- ...rest
556
- }) => {
557
- return (
558
- <BranchPickerPrimitive.Root
559
- hideWhenSingleBranch
560
- className={cn("aui-branch-picker-root", className)}
561
- {...rest}
562
- >
563
- <BranchPickerPrimitive.Previous asChild>
564
- <TooltipIconButton tooltip="Previous">
565
- <ChevronLeftIcon />
566
- </TooltipIconButton>
567
- </BranchPickerPrimitive.Previous>
568
- <span className="aui-branch-picker-state">
569
- <BranchPickerPrimitive.Number /> / <BranchPickerPrimitive.Count />
570
- </span>
571
- <BranchPickerPrimitive.Next asChild>
572
- <TooltipIconButton tooltip="Next">
573
- <ChevronRightIcon />
574
- </TooltipIconButton>
575
- </BranchPickerPrimitive.Next>
576
- </BranchPickerPrimitive.Root>
577
- );
578
- };
579
- ```
580
-
581
- ```tsx title="components/assistant-ui/attachment.tsx"
582
- "use client";
583
-
584
- import { PropsWithChildren, useEffect, useState, type FC } from "react";
585
- import Image from "next/image";
586
- import { XIcon, PlusIcon, FileText } from "lucide-react";
587
- import {
588
- AttachmentPrimitive,
589
- ComposerPrimitive,
590
- MessagePrimitive,
591
- useAssistantState,
592
- useAssistantApi,
593
- } from "@assistant-ui/react";
594
- import { useShallow } from "zustand/shallow";
595
- import {
596
- Tooltip,
597
- TooltipContent,
598
- TooltipTrigger,
599
- } from "@/components/ui/tooltip";
600
- import {
601
- Dialog,
602
- DialogTitle,
603
- DialogContent,
604
- DialogTrigger,
605
- } from "@/components/ui/dialog";
606
- import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
607
- import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
608
- import { cn } from "@/lib/utils";
609
-
610
- const useFileSrc = (file: File | undefined) => {
611
- const [src, setSrc] = useState<string | undefined>(undefined);
612
-
613
- useEffect(() => {
614
- if (!file) {
615
- setSrc(undefined);
616
- return;
617
- }
618
-
619
- const objectUrl = URL.createObjectURL(file);
620
- setSrc(objectUrl);
621
-
622
- return () => {
623
- URL.revokeObjectURL(objectUrl);
624
- };
625
- }, [file]);
626
-
627
- return src;
628
- };
629
-
630
- const useAttachmentSrc = () => {
631
- const { file, src } = useAssistantState(
632
- useShallow(({ attachment }): { file?: File; src?: string } => {
633
- if (attachment.type !== "image") return {};
634
- if (attachment.file) return { file: attachment.file };
635
- const src = attachment.content?.filter((c) => c.type === "image")[0]
636
- ?.image;
637
- if (!src) return {};
638
- return { src };
639
- }),
640
- );
641
-
642
- return useFileSrc(file) ?? src;
643
- };
644
-
645
- type AttachmentPreviewProps = {
646
- src: string;
647
- };
648
-
649
- const AttachmentPreview: FC<AttachmentPreviewProps> = ({ src }) => {
650
- const [isLoaded, setIsLoaded] = useState(false);
651
- return (
652
- <Image
653
- src={src}
654
- alt="Image Preview"
655
- width={1}
656
- height={1}
657
- className={
658
- isLoaded
659
- ? "aui-attachment-preview-image-loaded"
660
- : "aui-attachment-preview-image-loading"
661
- }
662
- onLoadingComplete={() => setIsLoaded(true)}
663
- priority={false}
664
- />
665
- );
666
- };
667
-
668
- const AttachmentPreviewDialog: FC<PropsWithChildren> = ({ children }) => {
669
- const src = useAttachmentSrc();
670
-
671
- if (!src) return children;
672
-
673
- return (
674
- <Dialog>
675
- <DialogTrigger className="aui-attachment-preview-trigger" asChild>
676
- {children}
677
- </DialogTrigger>
678
- <DialogContent className="aui-attachment-preview-dialog-content">
679
- <DialogTitle className="aui-sr-only">
680
- Image Attachment Preview
681
- </DialogTitle>
682
- <div className="aui-attachment-preview">
683
- <AttachmentPreview src={src} />
684
- </div>
685
- </DialogContent>
686
- </Dialog>
687
- );
688
- };
689
-
690
- const AttachmentThumb: FC = () => {
691
- const isImage = useAssistantState(
692
- ({ attachment }) => attachment.type === "image",
693
- );
694
- const src = useAttachmentSrc();
695
-
696
- return (
697
- <Avatar className="aui-attachment-tile-avatar">
698
- <AvatarImage
699
- src={src}
700
- alt="Attachment preview"
701
- className="aui-attachment-tile-image"
702
- />
703
- <AvatarFallback delayMs={isImage ? 200 : 0}>
704
- <FileText className="aui-attachment-tile-fallback-icon" />
705
- </AvatarFallback>
706
- </Avatar>
707
- );
708
- };
709
-
710
- const AttachmentUI: FC = () => {
711
- const api = useAssistantApi();
712
- const isComposer = api.attachment.source === "composer";
713
-
714
- const isImage = useAssistantState(
715
- ({ attachment }) => attachment.type === "image",
716
- );
717
- const typeLabel = useAssistantState(({ attachment }) => {
718
- const type = attachment.type;
719
- switch (type) {
720
- case "image":
721
- return "Image";
722
- case "document":
723
- return "Document";
724
- case "file":
725
- return "File";
726
- default:
727
- const _exhaustiveCheck: never = type;
728
- throw new Error(`Unknown attachment type: ${_exhaustiveCheck}`);
729
- }
730
- });
731
-
732
- return (
733
- <Tooltip>
734
- <AttachmentPrimitive.Root
735
- className={cn(
736
- "aui-attachment-root",
737
- isImage && "aui-attachment-root-composer",
738
- )}
739
- >
740
- <AttachmentPreviewDialog>
741
- <TooltipTrigger asChild>
742
- <div
743
- className={cn(
744
- "aui-attachment-tile",
745
- isComposer && "aui-attachment-tile-composer",
746
- )}
747
- role="button"
748
- id="attachment-tile"
749
- aria-label={`${typeLabel} attachment`}
750
- >
751
- <AttachmentThumb />
752
- </div>
753
- </TooltipTrigger>
754
- </AttachmentPreviewDialog>
755
- {isComposer && <AttachmentRemove />}
756
- </AttachmentPrimitive.Root>
757
- <TooltipContent side="top">
758
- <AttachmentPrimitive.Name />
759
- </TooltipContent>
760
- </Tooltip>
761
- );
762
- };
763
-
764
- const AttachmentRemove: FC = () => {
765
- return (
766
- <AttachmentPrimitive.Remove asChild>
767
- <TooltipIconButton
768
- tooltip="Remove file"
769
- className="aui-attachment-tile-remove"
770
- side="top"
771
- >
772
- <XIcon className="aui-attachment-remove-icon" />
773
- </TooltipIconButton>
774
- </AttachmentPrimitive.Remove>
775
- );
776
- };
777
-
778
- export const UserMessageAttachments: FC = () => {
779
- return (
780
- <div className="aui-user-message-attachments-end">
781
- <MessagePrimitive.Attachments components={{ Attachment: AttachmentUI }} />
782
- </div>
783
- );
784
- };
785
-
786
- export const ComposerAttachments: FC = () => {
787
- return (
788
- <div className="aui-composer-attachments">
789
- <ComposerPrimitive.Attachments
790
- components={{ Attachment: AttachmentUI }}
791
- />
792
- </div>
793
- );
794
- };
795
-
796
- export const ComposerAddAttachment: FC = () => {
797
- return (
798
- <ComposerPrimitive.AddAttachment asChild>
799
- <TooltipIconButton
800
- tooltip="Add Attachment"
801
- side="bottom"
802
- variant="ghost"
803
- size="icon"
804
- className="aui-composer-add-attachment"
805
- aria-label="Add Attachment"
806
- >
807
- <PlusIcon className="aui-attachment-add-icon" />
808
- </TooltipIconButton>
809
- </ComposerPrimitive.AddAttachment>
810
- );
811
- };
812
- ```
813
-
814
- ```tsx title="components/assistant-ui/tool-fallback.tsx"
815
- import type { ToolCallMessagePartComponent } from "@assistant-ui/react";
816
- import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
817
- import { useState } from "react";
818
- import { Button } from "@/components/ui/button";
819
-
820
- export const ToolFallback: ToolCallMessagePartComponent = ({
821
- toolName,
822
- argsText,
823
- result,
824
- }) => {
825
- const [isCollapsed, setIsCollapsed] = useState(true);
826
- return (
827
- <div className="aui-tool-fallback-root">
828
- <div className="aui-tool-fallback-header">
829
- <CheckIcon className="aui-tool-fallback-icon" />
830
- <p className="aui-tool-fallback-title">
831
- Used tool: <b>{toolName}</b>
832
- </p>
833
- <Button onClick={() => setIsCollapsed(!isCollapsed)}>
834
- {isCollapsed ? <ChevronUpIcon /> : <ChevronDownIcon />}
835
- </Button>
836
- </div>
837
- {!isCollapsed && (
838
- <div className="aui-tool-fallback-content">
839
- <div className="aui-tool-fallback-args-root">
840
- <pre className="aui-tool-fallback-args-value">
841
- {argsText}
842
- </pre>
843
- </div>
844
- {result !== undefined && (
845
- <div className="aui-tool-fallback-result-root">
846
- <p className="aui-tool-fallback-result-header">
847
- Result:
848
- </p>
849
- <pre className="aui-tool-fallback-result-content">
850
- {typeof result === "string"
851
- ? result
852
- : JSON.stringify(result, null, 2)}
853
- </pre>
854
- </div>
855
- )}
856
- </div>
857
- )}
858
- </div>
859
- );
860
- };
861
- ```
862
-
863
- ```tsx title="components/assistant-ui/thread-list.tsx"
864
- import type { FC } from "react";
865
- import {
866
- ThreadListItemPrimitive,
867
- ThreadListPrimitive,
868
- } from "@assistant-ui/react";
869
- import { ArchiveIcon, PlusIcon } from "lucide-react";
870
-
871
- import { Button } from "@/components/ui/button";
872
- import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
873
-
874
- export const ThreadList: FC = () => {
875
- return (
876
- <ThreadListPrimitive.Root className="aui-root aui-thread-list-root">
877
- <ThreadListNew />
878
- <ThreadListItems />
879
- </ThreadListPrimitive.Root>
880
- );
881
- };
882
-
883
- const ThreadListNew: FC = () => {
884
- return (
885
- <ThreadListPrimitive.New asChild>
886
- <Button className="aui-thread-list-new" variant="ghost">
887
- <PlusIcon />
888
- New Thread
889
- </Button>
890
- </ThreadListPrimitive.New>
891
- );
892
- };
893
-
894
- const ThreadListItems: FC = () => {
895
- return <ThreadListPrimitive.Items components={{ ThreadListItem }} />;
896
- };
897
-
898
- const ThreadListItem: FC = () => {
899
- return (
900
- <ThreadListItemPrimitive.Root className="aui-thread-list-item">
901
- <ThreadListItemPrimitive.Trigger className="aui-thread-list-item-trigger">
902
- <ThreadListItemTitle />
903
- </ThreadListItemPrimitive.Trigger>
904
- <ThreadListItemArchive />
905
- </ThreadListItemPrimitive.Root>
906
- );
907
- };
908
-
909
- const ThreadListItemTitle: FC = () => {
910
- return (
911
- <span className="aui-thread-list-item-title">
912
- <ThreadListItemPrimitive.Title fallback="New Chat" />
913
- </span>
914
- );
915
- };
916
-
917
- const ThreadListItemArchive: FC = () => {
918
- return (
919
- <ThreadListItemPrimitive.Archive asChild>
920
- <TooltipIconButton
921
- className="aui-thread-list-item-archive"
922
- variant="ghost"
923
- tooltip="Archive thread"
924
- >
925
- <ArchiveIcon />
926
- </TooltipIconButton>
927
- </ThreadListItemPrimitive.Archive>
928
- );
929
- };
930
- ```
931
-
932
- ```tsx title="components/assistant-ui/markdown-text.tsx"
933
- "use client";
934
-
935
- import "@assistant-ui/react-markdown/styles/dot.css";
936
-
937
- import {
938
- type CodeHeaderProps,
939
- MarkdownTextPrimitive,
940
- unstable_memoizeMarkdownComponents as memoizeMarkdownComponents,
941
- useIsMarkdownCodeBlock,
942
- } from "@assistant-ui/react-markdown";
943
- import remarkGfm from "remark-gfm";
944
- import { type FC, memo, useState } from "react";
945
- import { CheckIcon, CopyIcon } from "lucide-react";
946
-
947
- import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
948
- import { cn } from "@/lib/utils";
949
-
950
- const MarkdownTextImpl = () => {
951
- return (
952
- <MarkdownTextPrimitive
953
- remarkPlugins={[remarkGfm]}
954
- className="aui-md"
955
- components={defaultComponents}
956
- />
957
- );
958
- };
959
-
960
- export const MarkdownText = memo(MarkdownTextImpl);
961
-
962
- const CodeHeader: FC<CodeHeaderProps> = ({ language, code }) => {
963
- const { isCopied, copyToClipboard } = useCopyToClipboard();
964
- const onCopy = () => {
965
- if (!code || isCopied) return;
966
- copyToClipboard(code);
967
- };
968
-
969
- return (
970
- <div className="aui-code-header-root">
971
- <span className="aui-code-header-language">{language}</span>
972
- <TooltipIconButton tooltip="Copy" onClick={onCopy}>
973
- {!isCopied && <CopyIcon />}
974
- {isCopied && <CheckIcon />}
975
- </TooltipIconButton>
976
- </div>
977
- );
978
- };
979
-
980
- const useCopyToClipboard = ({
981
- copiedDuration = 3000,
982
- }: {
983
- copiedDuration?: number;
984
- } = {}) => {
985
- const [isCopied, setIsCopied] = useState<boolean>(false);
986
-
987
- const copyToClipboard = (value: string) => {
988
- if (!value) return;
989
-
990
- navigator.clipboard.writeText(value).then(() => {
991
- setIsCopied(true);
992
- setTimeout(() => setIsCopied(false), copiedDuration);
993
- });
994
- };
995
-
996
- return { isCopied, copyToClipboard };
997
- };
998
-
999
- const defaultComponents = memoizeMarkdownComponents({
1000
- h1: ({ className, ...props }) => (
1001
- <h1 className={cn("aui-md-h1", className)} {...props} />
1002
- ),
1003
- h2: ({ className, ...props }) => (
1004
- <h2 className={cn("aui-md-h2", className)} {...props} />
1005
- ),
1006
- h3: ({ className, ...props }) => (
1007
- <h3 className={cn("aui-md-h3", className)} {...props} />
1008
- ),
1009
- h4: ({ className, ...props }) => (
1010
- <h4 className={cn("aui-md-h4", className)} {...props} />
1011
- ),
1012
- h5: ({ className, ...props }) => (
1013
- <h5 className={cn("aui-md-h5", className)} {...props} />
1014
- ),
1015
- h6: ({ className, ...props }) => (
1016
- <h6 className={cn("aui-md-h6", className)} {...props} />
1017
- ),
1018
- p: ({ className, ...props }) => (
1019
- <p className={cn("aui-md-p", className)} {...props} />
1020
- ),
1021
- a: ({ className, ...props }) => (
1022
- <a className={cn("aui-md-a", className)} {...props} />
1023
- ),
1024
- blockquote: ({ className, ...props }) => (
1025
- <blockquote className={cn("aui-md-blockquote", className)} {...props} />
1026
- ),
1027
- ul: ({ className, ...props }) => (
1028
- <ul className={cn("aui-md-ul", className)} {...props} />
1029
- ),
1030
- ol: ({ className, ...props }) => (
1031
- <ol className={cn("aui-md-ol", className)} {...props} />
1032
- ),
1033
- hr: ({ className, ...props }) => (
1034
- <hr className={cn("aui-md-hr", className)} {...props} />
1035
- ),
1036
- table: ({ className, ...props }) => (
1037
- <table className={cn("aui-md-table", className)} {...props} />
1038
- ),
1039
- th: ({ className, ...props }) => (
1040
- <th className={cn("aui-md-th", className)} {...props} />
1041
- ),
1042
- td: ({ className, ...props }) => (
1043
- <td className={cn("aui-md-td", className)} {...props} />
1044
- ),
1045
- tr: ({ className, ...props }) => (
1046
- <tr className={cn("aui-md-tr", className)} {...props} />
1047
- ),
1048
- sup: ({ className, ...props }) => (
1049
- <sup className={cn("aui-md-sup", className)} {...props} />
1050
- ),
1051
- pre: ({ className, ...props }) => (
1052
- <pre className={cn("aui-md-pre", className)} {...props} />
1053
- ),
1054
- code: function Code({ className, ...props }) {
1055
- const isCodeBlock = useIsMarkdownCodeBlock();
1056
- return (
1057
- <code
1058
- className={cn(!isCodeBlock && "aui-md-inline-code", className)}
1059
- {...props}
1060
- />
1061
- );
1062
- },
1063
- CodeHeader,
1064
- });
1065
- ```
1066
-
1067
- ```tsx title="components/assistant-ui/tooltip-icon-button.tsx"
1068
- "use client";
1069
-
1070
- import { ComponentPropsWithRef, forwardRef } from "react";
1071
- import { Slottable } from "@radix-ui/react-slot";
1072
-
1073
- import {
1074
- Tooltip,
1075
- TooltipContent,
1076
- TooltipTrigger,
1077
- } from "@/components/ui/tooltip";
1078
- import { Button } from "@/components/ui/button";
1079
- import { cn } from "@/lib/utils";
1080
-
1081
- export type TooltipIconButtonProps = ComponentPropsWithRef<typeof Button> & {
1082
- tooltip: string;
1083
- side?: "top" | "bottom" | "left" | "right";
1084
- };
1085
-
1086
- export const TooltipIconButton = forwardRef<
1087
- HTMLButtonElement,
1088
- TooltipIconButtonProps
1089
- >(({ children, tooltip, side = "bottom", className, ...rest }, ref) => {
1090
- return (
1091
- <Tooltip>
1092
- <TooltipTrigger asChild>
1093
- <Button
1094
- variant="ghost"
1095
- size="icon"
1096
- {...rest}
1097
- className={cn("aui-button-icon", className)}
1098
- ref={ref}
1099
- >
1100
- <Slottable>{children}</Slottable>
1101
- <span className="aui-sr-only">{tooltip}</span>
1102
- </Button>
1103
- </TooltipTrigger>
1104
- <TooltipContent side={side}>{tooltip}</TooltipContent>
1105
- </Tooltip>
1106
- );
1107
- });
1108
-
1109
- TooltipIconButton.displayName = "TooltipIconButton";
1110
- ```
1111
-
1112
- ```ts title="lib/utils.ts"
1113
- import { type ClassValue, clsx } from "clsx";
1114
-
1115
- export function cn(...inputs: ClassValue[]) {
1116
- return clsx(inputs);
1117
- }
1118
- ```
1119
-
1120
- </Step>
1121
-
1122
- <Step>
1123
- 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:
1124
-
1125
- ```ts
1126
- import "@assistant-ui/styles/index.css";
1127
- import "@assistant-ui/styles/markdown.css";
1128
- // import "@assistant-ui/styles/modal.css"; // for future reference, only if you use our modal component
1129
- ```
1130
-
1131
- </Step>
1132
- </Steps>
1133
-
1134
- </Tab>
1135
- </Tabs>
81
+ <InstallCommand shadcn={["thread", "thread-list"]} manualSetupInstructions />
1136
82
 
1137
83
  </Step>
1138
84
  <Step>
@@ -1142,51 +88,39 @@ import "@assistant-ui/styles/markdown.css";
1142
88
  Install provider SDK:
1143
89
 
1144
90
  <Tabs groupId="provider" items={["OpenAI", "Anthropic", "Azure", "AWS", "Gemini", "GCP", "Groq", "Fireworks", "Cohere", "Ollama", "Chrome AI"]}>
1145
-
1146
- ```sh title="Terminal" tab="OpenAI"
1147
- npm install ai @assistant-ui/react-ai-sdk @ai-sdk/openai
1148
- ```
1149
-
1150
- ```sh title="Terminal" tab="Anthropic"
1151
- npm install ai @assistant-ui/react-ai-sdk @ai-sdk/anthropic
1152
- ```
1153
-
1154
- ```sh title="Terminal" tab="Azure"
1155
- npm install ai @assistant-ui/react-ai-sdk @ai-sdk/azure
1156
- ```
1157
-
1158
- ```sh title="Terminal" tab="AWS"
1159
- npm install ai @assistant-ui/react-ai-sdk @ai-sdk/amazon-bedrock
1160
- ```
1161
-
1162
- ```sh title="Terminal" tab="Gemini"
1163
- npm install ai @assistant-ui/react-ai-sdk @ai-sdk/google
1164
- ```
1165
-
1166
- ```sh title="Terminal" tab="GCP"
1167
- npm install ai @assistant-ui/react-ai-sdk @ai-sdk/google-vertex
1168
- ```
1169
-
1170
- ```sh title="Terminal" tab="Groq"
1171
- npm install ai @assistant-ui/react-ai-sdk @ai-sdk/openai
1172
- ```
1173
-
1174
- ```sh title="Terminal" tab="Fireworks"
1175
- npm install ai @assistant-ui/react-ai-sdk @ai-sdk/openai
1176
- ```
1177
-
1178
- ```sh title="Terminal" tab="Cohere"
1179
- npm install ai @assistant-ui/react-ai-sdk @ai-sdk/cohere
1180
- ```
1181
-
1182
- ```sh title="Terminal" tab="Ollama"
1183
- npm install ai @assistant-ui/react-ai-sdk ollama-ai-provider-v2
1184
- ```
1185
-
1186
- ```sh title="Terminal" tab="Chrome AI"
1187
- npm install ai @assistant-ui/react-ai-sdk chrome-ai
1188
- ```
1189
-
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>
1190
124
  </Tabs>
1191
125
 
1192
126
  Add an API endpoint:
@@ -1470,7 +404,7 @@ const MyApp = () => {
1470
404
  ```
1471
405
 
1472
406
  ```tsx title="/app/page.tsx" tab="AssistantModal"
1473
- // run `npx shadcn@latest add "https://r.assistant-ui.com/assistant-modal"`
407
+ // run `npx shadcn@latest add @assistant-ui/assistant-modal`
1474
408
 
1475
409
  import { AssistantRuntimeProvider } from "@assistant-ui/react";
1476
410
  import { useChatRuntime, AssistantChatTransport } from "@assistant-ui/react-ai-sdk";