@assistant-ui/mcp-docs-server 0.1.19 → 0.1.21

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 (108) hide show
  1. package/.docs/organized/code-examples/with-ag-ui.md +172 -1633
  2. package/.docs/organized/code-examples/with-ai-sdk-v6.md +42 -1640
  3. package/.docs/organized/code-examples/with-assistant-transport.md +40 -1743
  4. package/.docs/organized/code-examples/with-cloud.md +71 -1745
  5. package/.docs/organized/code-examples/with-custom-thread-list.md +87 -1723
  6. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +70 -1637
  7. package/.docs/organized/code-examples/with-external-store.md +67 -1624
  8. package/.docs/organized/code-examples/with-ffmpeg.md +71 -1629
  9. package/.docs/organized/code-examples/with-langgraph.md +95 -1893
  10. package/.docs/organized/code-examples/with-parent-id-grouping.md +57 -1654
  11. package/.docs/organized/code-examples/with-react-hook-form.md +220 -2163
  12. package/.docs/organized/code-examples/with-react-router.md +66 -1318
  13. package/.docs/organized/code-examples/with-store.md +31 -31
  14. package/.docs/organized/code-examples/with-tanstack.md +77 -861
  15. package/.docs/organized/code-examples/with-tap-runtime.md +812 -0
  16. package/.docs/raw/docs/(docs)/cli.mdx +66 -0
  17. package/.docs/raw/docs/(docs)/copilots/make-assistant-tool-ui.mdx +0 -1
  18. package/.docs/raw/docs/(docs)/copilots/make-assistant-tool.mdx +0 -1
  19. package/.docs/raw/docs/(docs)/copilots/model-context.mdx +4 -4
  20. package/.docs/raw/docs/(docs)/copilots/motivation.mdx +3 -3
  21. package/.docs/raw/docs/(docs)/devtools.mdx +0 -1
  22. package/.docs/raw/docs/(docs)/guides/attachments.mdx +2 -3
  23. package/.docs/raw/docs/(docs)/guides/context-api.mdx +117 -117
  24. package/.docs/raw/docs/(docs)/guides/suggestions.mdx +296 -0
  25. package/.docs/raw/docs/(docs)/guides/tools.mdx +336 -513
  26. package/.docs/raw/docs/(docs)/index.mdx +33 -410
  27. package/.docs/raw/docs/(docs)/installation.mdx +450 -0
  28. package/.docs/raw/docs/(docs)/llm.mdx +209 -0
  29. package/.docs/raw/docs/(reference)/api-reference/context-providers/assistant-runtime-provider.mdx +0 -1
  30. package/.docs/raw/docs/(reference)/api-reference/context-providers/text-message-part-provider.mdx +0 -1
  31. package/.docs/raw/docs/(reference)/api-reference/integrations/react-data-stream.mdx +48 -3
  32. package/.docs/raw/docs/(reference)/api-reference/integrations/react-hook-form.mdx +0 -1
  33. package/.docs/raw/docs/(reference)/api-reference/integrations/vercel-ai-sdk.mdx +0 -1
  34. package/.docs/raw/docs/(reference)/api-reference/overview.mdx +9 -3
  35. package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar-more.mdx +20 -52
  36. package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar.mdx +16 -39
  37. package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-if.mdx +49 -50
  38. package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-modal.mdx +3 -11
  39. package/.docs/raw/docs/(reference)/api-reference/primitives/attachment.mdx +0 -3
  40. package/.docs/raw/docs/(reference)/api-reference/primitives/branch-picker.mdx +0 -1
  41. package/.docs/raw/docs/(reference)/api-reference/primitives/composer.mdx +5 -16
  42. package/.docs/raw/docs/(reference)/api-reference/primitives/composition.mdx +0 -1
  43. package/.docs/raw/docs/(reference)/api-reference/primitives/error.mdx +0 -1
  44. package/.docs/raw/docs/(reference)/api-reference/primitives/message-part.mdx +1 -2
  45. package/.docs/raw/docs/(reference)/api-reference/primitives/message.mdx +0 -1
  46. package/.docs/raw/docs/(reference)/api-reference/primitives/suggestion.mdx +152 -0
  47. package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list-item-more.mdx +0 -1
  48. package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list-item.mdx +1 -2
  49. package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list.mdx +1 -2
  50. package/.docs/raw/docs/(reference)/api-reference/primitives/thread.mdx +28 -40
  51. package/.docs/raw/docs/(reference)/api-reference/runtimes/assistant-runtime.mdx +0 -1
  52. package/.docs/raw/docs/(reference)/api-reference/runtimes/attachment-runtime.mdx +1 -2
  53. package/.docs/raw/docs/(reference)/api-reference/runtimes/composer-runtime.mdx +2 -3
  54. package/.docs/raw/docs/(reference)/api-reference/runtimes/message-part-runtime.mdx +1 -2
  55. package/.docs/raw/docs/(reference)/api-reference/runtimes/message-runtime.mdx +1 -2
  56. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-item-runtime.mdx +0 -1
  57. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-runtime.mdx +0 -1
  58. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-runtime.mdx +1 -2
  59. package/.docs/raw/docs/(reference)/legacy/styled/assistant-modal.mdx +0 -1
  60. package/.docs/raw/docs/(reference)/legacy/styled/decomposition.mdx +5 -5
  61. package/.docs/raw/docs/(reference)/legacy/styled/markdown.mdx +0 -1
  62. package/.docs/raw/docs/(reference)/legacy/styled/thread.mdx +0 -1
  63. package/.docs/raw/docs/(reference)/migrations/v0-12.mdx +207 -33
  64. package/.docs/raw/docs/(reference)/react-compatibility.mdx +0 -1
  65. package/.docs/raw/docs/cloud/persistence/ai-sdk.mdx +0 -1
  66. package/.docs/raw/docs/cloud/persistence/langgraph.mdx +0 -1
  67. package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +0 -1
  68. package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +118 -0
  69. package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +198 -0
  70. package/.docs/raw/docs/runtimes/assistant-transport.mdx +3 -3
  71. package/.docs/raw/docs/runtimes/custom/custom-thread-list.mdx +5 -6
  72. package/.docs/raw/docs/runtimes/custom/external-store.mdx +9 -11
  73. package/.docs/raw/docs/runtimes/custom/local.mdx +43 -36
  74. package/.docs/raw/docs/runtimes/data-stream.mdx +35 -3
  75. package/.docs/raw/docs/runtimes/langgraph/index.mdx +1 -2
  76. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +0 -1
  77. package/.docs/raw/docs/runtimes/langserve.mdx +0 -1
  78. package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +0 -1
  79. package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +0 -1
  80. package/.docs/raw/docs/ui/accordion.mdx +259 -0
  81. package/.docs/raw/docs/ui/assistant-modal.mdx +1 -3
  82. package/.docs/raw/docs/ui/assistant-sidebar.mdx +1 -3
  83. package/.docs/raw/docs/ui/attachment.mdx +0 -2
  84. package/.docs/raw/docs/ui/badge.mdx +138 -0
  85. package/.docs/raw/docs/ui/diff-viewer.mdx +279 -0
  86. package/.docs/raw/docs/ui/file.mdx +152 -0
  87. package/.docs/raw/docs/ui/image.mdx +100 -0
  88. package/.docs/raw/docs/ui/markdown.mdx +0 -1
  89. package/.docs/raw/docs/ui/mermaid.mdx +0 -1
  90. package/.docs/raw/docs/ui/model-selector.mdx +224 -0
  91. package/.docs/raw/docs/ui/part-grouping.mdx +4 -5
  92. package/.docs/raw/docs/ui/reasoning.mdx +6 -5
  93. package/.docs/raw/docs/ui/scrollbar.mdx +26 -9
  94. package/.docs/raw/docs/ui/select.mdx +245 -0
  95. package/.docs/raw/docs/ui/sources.mdx +6 -5
  96. package/.docs/raw/docs/ui/streamdown.mdx +348 -0
  97. package/.docs/raw/docs/ui/syntax-highlighting.mdx +8 -63
  98. package/.docs/raw/docs/ui/tabs.mdx +259 -0
  99. package/.docs/raw/docs/ui/thread-list.mdx +98 -16
  100. package/.docs/raw/docs/ui/thread.mdx +57 -73
  101. package/.docs/raw/docs/ui/tool-fallback.mdx +0 -1
  102. package/.docs/raw/docs/ui/tool-group.mdx +1 -3
  103. package/README.md +3 -3
  104. package/package.json +4 -4
  105. package/src/tools/tests/examples.test.ts +1 -1
  106. package/.docs/raw/docs/(docs)/about-assistantui.mdx +0 -54
  107. package/.docs/raw/docs/(docs)/mcp-docs-server.mdx +0 -321
  108. package/.docs/raw/docs/runtimes/ai-sdk/use-chat.mdx +0 -219
@@ -50,6 +50,8 @@ export async function POST(req: Request) {
50
50
  @import "tailwindcss";
51
51
  @import "tw-animate-css";
52
52
 
53
+ @source "../../../packages/ui/src";
54
+
53
55
  @custom-variant dark (&:is(.dark *));
54
56
 
55
57
  @theme inline {
@@ -241,1621 +243,12 @@ export default function Home() {
241
243
  "lib": "@/lib",
242
244
  "hooks": "@/hooks"
243
245
  },
244
- "iconLibrary": "lucide"
245
- }
246
-
247
- ```
248
-
249
- ## components/assistant-ui/attachment.tsx
250
-
251
- ```tsx
252
- "use client";
253
-
254
- import { PropsWithChildren, useEffect, useState, type FC } from "react";
255
- import Image from "next/image";
256
- import { XIcon, PlusIcon, FileText } from "lucide-react";
257
- import {
258
- AttachmentPrimitive,
259
- ComposerPrimitive,
260
- MessagePrimitive,
261
- useAssistantState,
262
- useAssistantApi,
263
- } from "@assistant-ui/react";
264
- import { useShallow } from "zustand/shallow";
265
- import {
266
- Tooltip,
267
- TooltipContent,
268
- TooltipTrigger,
269
- } from "@/components/ui/tooltip";
270
- import {
271
- Dialog,
272
- DialogTitle,
273
- DialogContent,
274
- DialogTrigger,
275
- } from "@/components/ui/dialog";
276
- import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
277
- import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
278
- import { cn } from "@/lib/utils";
279
-
280
- const useFileSrc = (file: File | undefined) => {
281
- const [src, setSrc] = useState<string | undefined>(undefined);
282
-
283
- useEffect(() => {
284
- if (!file) {
285
- setSrc(undefined);
286
- return;
287
- }
288
-
289
- const objectUrl = URL.createObjectURL(file);
290
- setSrc(objectUrl);
291
-
292
- return () => {
293
- URL.revokeObjectURL(objectUrl);
294
- };
295
- }, [file]);
296
-
297
- return src;
298
- };
299
-
300
- const useAttachmentSrc = () => {
301
- const { file, src } = useAssistantState(
302
- useShallow(({ attachment }): { file?: File; src?: string } => {
303
- if (attachment.type !== "image") return {};
304
- if (attachment.file) return { file: attachment.file };
305
- const src = attachment.content?.filter((c) => c.type === "image")[0]
306
- ?.image;
307
- if (!src) return {};
308
- return { src };
309
- }),
310
- );
311
-
312
- return useFileSrc(file) ?? src;
313
- };
314
-
315
- type AttachmentPreviewProps = {
316
- src: string;
317
- };
318
-
319
- const AttachmentPreview: FC<AttachmentPreviewProps> = ({ src }) => {
320
- const [isLoaded, setIsLoaded] = useState(false);
321
- return (
322
- <Image
323
- src={src}
324
- alt="Image Preview"
325
- width={1}
326
- height={1}
327
- className={
328
- isLoaded
329
- ? "aui-attachment-preview-image-loaded block h-auto max-h-[80vh] w-auto max-w-full object-contain"
330
- : "aui-attachment-preview-image-loading hidden"
331
- }
332
- onLoadingComplete={() => setIsLoaded(true)}
333
- priority={false}
334
- />
335
- );
336
- };
337
-
338
- const AttachmentPreviewDialog: FC<PropsWithChildren> = ({ children }) => {
339
- const src = useAttachmentSrc();
340
-
341
- if (!src) return children;
342
-
343
- return (
344
- <Dialog>
345
- <DialogTrigger
346
- className="aui-attachment-preview-trigger cursor-pointer transition-colors hover:bg-accent/50"
347
- asChild
348
- >
349
- {children}
350
- </DialogTrigger>
351
- <DialogContent className="aui-attachment-preview-dialog-content p-2 sm:max-w-3xl [&>button]:rounded-full [&>button]:bg-foreground/60 [&>button]:p-1 [&>button]:opacity-100 [&>button]:ring-0! [&_svg]:text-background [&>button]:hover:[&_svg]:text-destructive">
352
- <DialogTitle className="aui-sr-only sr-only">
353
- Image Attachment Preview
354
- </DialogTitle>
355
- <div className="aui-attachment-preview relative mx-auto flex max-h-[80dvh] w-full items-center justify-center overflow-hidden bg-background">
356
- <AttachmentPreview src={src} />
357
- </div>
358
- </DialogContent>
359
- </Dialog>
360
- );
361
- };
362
-
363
- const AttachmentThumb: FC = () => {
364
- const isImage = useAssistantState(
365
- ({ attachment }) => attachment.type === "image",
366
- );
367
- const src = useAttachmentSrc();
368
-
369
- return (
370
- <Avatar className="aui-attachment-tile-avatar h-full w-full rounded-none">
371
- <AvatarImage
372
- src={src}
373
- alt="Attachment preview"
374
- className="aui-attachment-tile-image object-cover"
375
- />
376
- <AvatarFallback delayMs={isImage ? 200 : 0}>
377
- <FileText className="aui-attachment-tile-fallback-icon size-8 text-muted-foreground" />
378
- </AvatarFallback>
379
- </Avatar>
380
- );
381
- };
382
-
383
- const AttachmentUI: FC = () => {
384
- const api = useAssistantApi();
385
- const isComposer = api.attachment.source === "composer";
386
-
387
- const isImage = useAssistantState(
388
- ({ attachment }) => attachment.type === "image",
389
- );
390
- const typeLabel = useAssistantState(({ attachment }) => {
391
- const type = attachment.type;
392
- switch (type) {
393
- case "image":
394
- return "Image";
395
- case "document":
396
- return "Document";
397
- case "file":
398
- return "File";
399
- default:
400
- const _exhaustiveCheck: never = type;
401
- throw new Error(`Unknown attachment type: ${_exhaustiveCheck}`);
402
- }
403
- });
404
-
405
- return (
406
- <Tooltip>
407
- <AttachmentPrimitive.Root
408
- className={cn(
409
- "aui-attachment-root relative",
410
- isImage &&
411
- "aui-attachment-root-composer only:[&>#attachment-tile]:size-24",
412
- )}
413
- >
414
- <AttachmentPreviewDialog>
415
- <TooltipTrigger asChild>
416
- <div
417
- className={cn(
418
- "aui-attachment-tile size-14 cursor-pointer overflow-hidden rounded-[14px] border bg-muted transition-opacity hover:opacity-75",
419
- isComposer &&
420
- "aui-attachment-tile-composer border-foreground/20",
421
- )}
422
- role="button"
423
- id="attachment-tile"
424
- aria-label={`${typeLabel} attachment`}
425
- >
426
- <AttachmentThumb />
427
- </div>
428
- </TooltipTrigger>
429
- </AttachmentPreviewDialog>
430
- {isComposer && <AttachmentRemove />}
431
- </AttachmentPrimitive.Root>
432
- <TooltipContent side="top">
433
- <AttachmentPrimitive.Name />
434
- </TooltipContent>
435
- </Tooltip>
436
- );
437
- };
438
-
439
- const AttachmentRemove: FC = () => {
440
- return (
441
- <AttachmentPrimitive.Remove asChild>
442
- <TooltipIconButton
443
- tooltip="Remove file"
444
- className="aui-attachment-tile-remove absolute top-1.5 right-1.5 size-3.5 rounded-full bg-white text-muted-foreground opacity-100 shadow-sm hover:bg-white! [&_svg]:text-black hover:[&_svg]:text-destructive"
445
- side="top"
446
- >
447
- <XIcon className="aui-attachment-remove-icon size-3 dark:stroke-[2.5px]" />
448
- </TooltipIconButton>
449
- </AttachmentPrimitive.Remove>
450
- );
451
- };
452
-
453
- export const UserMessageAttachments: FC = () => {
454
- return (
455
- <div className="aui-user-message-attachments-end col-span-full col-start-1 row-start-1 flex w-full flex-row justify-end gap-2">
456
- <MessagePrimitive.Attachments components={{ Attachment: AttachmentUI }} />
457
- </div>
458
- );
459
- };
460
-
461
- export const ComposerAttachments: FC = () => {
462
- return (
463
- <div className="aui-composer-attachments mb-2 flex w-full flex-row items-center gap-2 overflow-x-auto px-1.5 pt-0.5 pb-1 empty:hidden">
464
- <ComposerPrimitive.Attachments
465
- components={{ Attachment: AttachmentUI }}
466
- />
467
- </div>
468
- );
469
- };
470
-
471
- export const ComposerAddAttachment: FC = () => {
472
- return (
473
- <ComposerPrimitive.AddAttachment asChild>
474
- <TooltipIconButton
475
- tooltip="Add Attachment"
476
- side="bottom"
477
- variant="ghost"
478
- size="icon"
479
- className="aui-composer-add-attachment size-8.5 rounded-full p-1 font-semibold text-xs hover:bg-muted-foreground/15 dark:border-muted-foreground/15 dark:hover:bg-muted-foreground/30"
480
- aria-label="Add Attachment"
481
- >
482
- <PlusIcon className="aui-attachment-add-icon size-5 stroke-[1.5px]" />
483
- </TooltipIconButton>
484
- </ComposerPrimitive.AddAttachment>
485
- );
486
- };
487
-
488
- ```
489
-
490
- ## components/assistant-ui/markdown-text.tsx
491
-
492
- ```tsx
493
- "use client";
494
-
495
- import "@assistant-ui/react-markdown/styles/dot.css";
496
-
497
- import {
498
- type CodeHeaderProps,
499
- MarkdownTextPrimitive,
500
- unstable_memoizeMarkdownComponents as memoizeMarkdownComponents,
501
- useIsMarkdownCodeBlock,
502
- } from "@assistant-ui/react-markdown";
503
- import remarkGfm from "remark-gfm";
504
- import { type FC, memo, useState } from "react";
505
- import { CheckIcon, CopyIcon } from "lucide-react";
506
-
507
- import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
508
- import { cn } from "@/lib/utils";
509
-
510
- const MarkdownTextImpl = () => {
511
- return (
512
- <MarkdownTextPrimitive
513
- remarkPlugins={[remarkGfm]}
514
- className="aui-md"
515
- components={defaultComponents}
516
- />
517
- );
518
- };
519
-
520
- export const MarkdownText = memo(MarkdownTextImpl);
521
-
522
- const CodeHeader: FC<CodeHeaderProps> = ({ language, code }) => {
523
- const { isCopied, copyToClipboard } = useCopyToClipboard();
524
- const onCopy = () => {
525
- if (!code || isCopied) return;
526
- copyToClipboard(code);
527
- };
528
-
529
- return (
530
- <div className="aui-code-header-root mt-4 flex items-center justify-between gap-4 rounded-t-lg bg-muted-foreground/15 px-4 py-2 font-semibold text-foreground text-sm dark:bg-muted-foreground/20">
531
- <span className="aui-code-header-language lowercase [&>span]:text-xs">
532
- {language}
533
- </span>
534
- <TooltipIconButton tooltip="Copy" onClick={onCopy}>
535
- {!isCopied && <CopyIcon />}
536
- {isCopied && <CheckIcon />}
537
- </TooltipIconButton>
538
- </div>
539
- );
540
- };
541
-
542
- const useCopyToClipboard = ({
543
- copiedDuration = 3000,
544
- }: {
545
- copiedDuration?: number;
546
- } = {}) => {
547
- const [isCopied, setIsCopied] = useState<boolean>(false);
548
-
549
- const copyToClipboard = (value: string) => {
550
- if (!value) return;
551
-
552
- navigator.clipboard.writeText(value).then(() => {
553
- setIsCopied(true);
554
- setTimeout(() => setIsCopied(false), copiedDuration);
555
- });
556
- };
557
-
558
- return { isCopied, copyToClipboard };
559
- };
560
-
561
- const defaultComponents = memoizeMarkdownComponents({
562
- h1: ({ className, ...props }) => (
563
- <h1
564
- className={cn(
565
- "aui-md-h1 mb-8 scroll-m-20 font-extrabold text-4xl tracking-tight last:mb-0",
566
- className,
567
- )}
568
- {...props}
569
- />
570
- ),
571
- h2: ({ className, ...props }) => (
572
- <h2
573
- className={cn(
574
- "aui-md-h2 mt-8 mb-4 scroll-m-20 font-semibold text-3xl tracking-tight first:mt-0 last:mb-0",
575
- className,
576
- )}
577
- {...props}
578
- />
579
- ),
580
- h3: ({ className, ...props }) => (
581
- <h3
582
- className={cn(
583
- "aui-md-h3 mt-6 mb-4 scroll-m-20 font-semibold text-2xl tracking-tight first:mt-0 last:mb-0",
584
- className,
585
- )}
586
- {...props}
587
- />
588
- ),
589
- h4: ({ className, ...props }) => (
590
- <h4
591
- className={cn(
592
- "aui-md-h4 mt-6 mb-4 scroll-m-20 font-semibold text-xl tracking-tight first:mt-0 last:mb-0",
593
- className,
594
- )}
595
- {...props}
596
- />
597
- ),
598
- h5: ({ className, ...props }) => (
599
- <h5
600
- className={cn(
601
- "aui-md-h5 my-4 font-semibold text-lg first:mt-0 last:mb-0",
602
- className,
603
- )}
604
- {...props}
605
- />
606
- ),
607
- h6: ({ className, ...props }) => (
608
- <h6
609
- className={cn(
610
- "aui-md-h6 my-4 font-semibold first:mt-0 last:mb-0",
611
- className,
612
- )}
613
- {...props}
614
- />
615
- ),
616
- p: ({ className, ...props }) => (
617
- <p
618
- className={cn(
619
- "aui-md-p mt-5 mb-5 leading-7 first:mt-0 last:mb-0",
620
- className,
621
- )}
622
- {...props}
623
- />
624
- ),
625
- a: ({ className, ...props }) => (
626
- <a
627
- className={cn(
628
- "aui-md-a font-medium text-primary underline underline-offset-4",
629
- className,
630
- )}
631
- {...props}
632
- />
633
- ),
634
- blockquote: ({ className, ...props }) => (
635
- <blockquote
636
- className={cn("aui-md-blockquote border-l-2 pl-6 italic", className)}
637
- {...props}
638
- />
639
- ),
640
- ul: ({ className, ...props }) => (
641
- <ul
642
- className={cn("aui-md-ul my-5 ml-6 list-disc [&>li]:mt-2", className)}
643
- {...props}
644
- />
645
- ),
646
- ol: ({ className, ...props }) => (
647
- <ol
648
- className={cn("aui-md-ol my-5 ml-6 list-decimal [&>li]:mt-2", className)}
649
- {...props}
650
- />
651
- ),
652
- hr: ({ className, ...props }) => (
653
- <hr className={cn("aui-md-hr my-5 border-b", className)} {...props} />
654
- ),
655
- table: ({ className, ...props }) => (
656
- <table
657
- className={cn(
658
- "aui-md-table my-5 w-full border-separate border-spacing-0 overflow-y-auto",
659
- className,
660
- )}
661
- {...props}
662
- />
663
- ),
664
- th: ({ className, ...props }) => (
665
- <th
666
- className={cn(
667
- "aui-md-th bg-muted px-4 py-2 text-left font-bold first:rounded-tl-lg last:rounded-tr-lg [[align=center]]:text-center [[align=right]]:text-right",
668
- className,
669
- )}
670
- {...props}
671
- />
672
- ),
673
- td: ({ className, ...props }) => (
674
- <td
675
- className={cn(
676
- "aui-md-td border-b border-l px-4 py-2 text-left last:border-r [[align=center]]:text-center [[align=right]]:text-right",
677
- className,
678
- )}
679
- {...props}
680
- />
681
- ),
682
- tr: ({ className, ...props }) => (
683
- <tr
684
- className={cn(
685
- "aui-md-tr m-0 border-b p-0 first:border-t [&:last-child>td:first-child]:rounded-bl-lg [&:last-child>td:last-child]:rounded-br-lg",
686
- className,
687
- )}
688
- {...props}
689
- />
690
- ),
691
- sup: ({ className, ...props }) => (
692
- <sup
693
- className={cn("aui-md-sup [&>a]:text-xs [&>a]:no-underline", className)}
694
- {...props}
695
- />
696
- ),
697
- pre: ({ className, ...props }) => (
698
- <pre
699
- className={cn(
700
- "aui-md-pre overflow-x-auto rounded-t-none! rounded-b-lg bg-black p-4 text-white",
701
- className,
702
- )}
703
- {...props}
704
- />
705
- ),
706
- code: function Code({ className, ...props }) {
707
- const isCodeBlock = useIsMarkdownCodeBlock();
708
- return (
709
- <code
710
- className={cn(
711
- !isCodeBlock &&
712
- "aui-md-inline-code rounded border bg-muted font-semibold",
713
- className,
714
- )}
715
- {...props}
716
- />
717
- );
718
- },
719
- CodeHeader,
720
- });
721
-
722
- ```
723
-
724
- ## components/assistant-ui/thread.tsx
725
-
726
- ```tsx
727
- import {
728
- ComposerAddAttachment,
729
- ComposerAttachments,
730
- UserMessageAttachments,
731
- } from "@/components/assistant-ui/attachment";
732
- import { MarkdownText } from "@/components/assistant-ui/markdown-text";
733
- import { ToolFallback } from "@/components/assistant-ui/tool-fallback";
734
- import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
735
- import { Button } from "@/components/ui/button";
736
- import { cn } from "@/lib/utils";
737
- import {
738
- ActionBarMorePrimitive,
739
- ActionBarPrimitive,
740
- AssistantIf,
741
- BranchPickerPrimitive,
742
- ComposerPrimitive,
743
- ErrorPrimitive,
744
- MessagePrimitive,
745
- ThreadPrimitive,
746
- } from "@assistant-ui/react";
747
- import {
748
- ArrowDownIcon,
749
- ArrowUpIcon,
750
- CheckIcon,
751
- ChevronLeftIcon,
752
- ChevronRightIcon,
753
- CopyIcon,
754
- DownloadIcon,
755
- MoreHorizontalIcon,
756
- PencilIcon,
757
- RefreshCwIcon,
758
- SquareIcon,
759
- } from "lucide-react";
760
- import type { FC } from "react";
761
-
762
- export const Thread: FC = () => {
763
- return (
764
- <ThreadPrimitive.Root
765
- className="aui-root aui-thread-root @container flex h-full flex-col bg-background"
766
- style={{
767
- ["--thread-max-width" as string]: "44rem",
768
- }}
769
- >
770
- <ThreadPrimitive.Viewport
771
- turnAnchor="top"
772
- className="aui-thread-viewport relative flex flex-1 flex-col overflow-x-auto overflow-y-scroll scroll-smooth px-4 pt-4"
773
- >
774
- <AssistantIf condition={({ thread }) => thread.isEmpty}>
775
- <ThreadWelcome />
776
- </AssistantIf>
777
-
778
- <ThreadPrimitive.Messages
779
- components={{
780
- UserMessage,
781
- EditComposer,
782
- AssistantMessage,
783
- }}
784
- />
785
-
786
- <ThreadPrimitive.ViewportFooter className="aui-thread-viewport-footer sticky bottom-0 mx-auto mt-auto flex w-full max-w-(--thread-max-width) flex-col gap-4 overflow-visible rounded-t-3xl bg-background pb-4 md:pb-6">
787
- <ThreadScrollToBottom />
788
- <Composer />
789
- </ThreadPrimitive.ViewportFooter>
790
- </ThreadPrimitive.Viewport>
791
- </ThreadPrimitive.Root>
792
- );
793
- };
794
-
795
- const ThreadScrollToBottom: FC = () => {
796
- return (
797
- <ThreadPrimitive.ScrollToBottom asChild>
798
- <TooltipIconButton
799
- tooltip="Scroll to bottom"
800
- variant="outline"
801
- className="aui-thread-scroll-to-bottom absolute -top-12 z-10 self-center rounded-full p-4 disabled:invisible dark:bg-background dark:hover:bg-accent"
802
- >
803
- <ArrowDownIcon />
804
- </TooltipIconButton>
805
- </ThreadPrimitive.ScrollToBottom>
806
- );
807
- };
808
-
809
- const ThreadWelcome: FC = () => {
810
- return (
811
- <div className="aui-thread-welcome-root mx-auto my-auto flex w-full max-w-(--thread-max-width) grow flex-col">
812
- <div className="aui-thread-welcome-center flex w-full grow flex-col items-center justify-center">
813
- <div className="aui-thread-welcome-message flex size-full flex-col justify-center px-4">
814
- <h1 className="aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in font-semibold text-2xl duration-200">
815
- Hello there!
816
- </h1>
817
- <p className="aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in text-muted-foreground text-xl delay-75 duration-200">
818
- How can I help you today?
819
- </p>
820
- </div>
821
- </div>
822
- <ThreadSuggestions />
823
- </div>
824
- );
825
- };
826
-
827
- const SUGGESTIONS = [
828
- {
829
- title: "What's the weather",
830
- label: "in San Francisco?",
831
- prompt: "What's the weather in San Francisco?",
832
- },
833
- {
834
- title: "Explain React hooks",
835
- label: "like useState and useEffect",
836
- prompt: "Explain React hooks like useState and useEffect",
837
- },
838
- ] as const;
839
-
840
- const ThreadSuggestions: FC = () => {
841
- return (
842
- <div className="aui-thread-welcome-suggestions grid w-full @md:grid-cols-2 gap-2 pb-4">
843
- {SUGGESTIONS.map((suggestion, index) => (
844
- <div
845
- key={suggestion.prompt}
846
- className="aui-thread-welcome-suggestion-display fade-in slide-in-from-bottom-2 @md:nth-[n+3]:block nth-[n+3]:hidden animate-in fill-mode-both duration-200"
847
- style={{ animationDelay: `${100 + index * 50}ms` }}
848
- >
849
- <ThreadPrimitive.Suggestion prompt={suggestion.prompt} send asChild>
850
- <Button
851
- variant="ghost"
852
- className="aui-thread-welcome-suggestion h-auto w-full @md:flex-col flex-wrap items-start justify-start gap-1 rounded-2xl border px-4 py-3 text-left text-sm transition-colors hover:bg-muted"
853
- aria-label={suggestion.prompt}
854
- >
855
- <span className="aui-thread-welcome-suggestion-text-1 font-medium">
856
- {suggestion.title}
857
- </span>
858
- <span className="aui-thread-welcome-suggestion-text-2 text-muted-foreground">
859
- {suggestion.label}
860
- </span>
861
- </Button>
862
- </ThreadPrimitive.Suggestion>
863
- </div>
864
- ))}
865
- </div>
866
- );
867
- };
868
-
869
- const Composer: FC = () => {
870
- return (
871
- <ComposerPrimitive.Root className="aui-composer-root relative flex w-full flex-col">
872
- <ComposerPrimitive.AttachmentDropzone className="aui-composer-attachment-dropzone flex w-full flex-col rounded-2xl border border-input bg-background px-1 pt-2 outline-none transition-shadow has-[textarea:focus-visible]:border-ring has-[textarea:focus-visible]:ring-2 has-[textarea:focus-visible]:ring-ring/20 data-[dragging=true]:border-ring data-[dragging=true]:border-dashed data-[dragging=true]:bg-accent/50">
873
- <ComposerAttachments />
874
- <ComposerPrimitive.Input
875
- placeholder="Send a message..."
876
- className="aui-composer-input mb-1 max-h-32 min-h-14 w-full resize-none bg-transparent px-4 pt-2 pb-3 text-sm outline-none placeholder:text-muted-foreground focus-visible:ring-0"
877
- rows={1}
878
- autoFocus
879
- aria-label="Message input"
880
- />
881
- <ComposerAction />
882
- </ComposerPrimitive.AttachmentDropzone>
883
- </ComposerPrimitive.Root>
884
- );
885
- };
886
-
887
- const ComposerAction: FC = () => {
888
- return (
889
- <div className="aui-composer-action-wrapper relative mx-2 mb-2 flex items-center justify-between">
890
- <ComposerAddAttachment />
891
-
892
- <AssistantIf condition={({ thread }) => !thread.isRunning}>
893
- <ComposerPrimitive.Send asChild>
894
- <TooltipIconButton
895
- tooltip="Send message"
896
- side="bottom"
897
- type="submit"
898
- variant="default"
899
- size="icon"
900
- className="aui-composer-send size-8 rounded-full"
901
- aria-label="Send message"
902
- >
903
- <ArrowUpIcon className="aui-composer-send-icon size-4" />
904
- </TooltipIconButton>
905
- </ComposerPrimitive.Send>
906
- </AssistantIf>
907
-
908
- <AssistantIf condition={({ thread }) => thread.isRunning}>
909
- <ComposerPrimitive.Cancel asChild>
910
- <Button
911
- type="button"
912
- variant="default"
913
- size="icon"
914
- className="aui-composer-cancel size-8 rounded-full"
915
- aria-label="Stop generating"
916
- >
917
- <SquareIcon className="aui-composer-cancel-icon size-3 fill-current" />
918
- </Button>
919
- </ComposerPrimitive.Cancel>
920
- </AssistantIf>
921
- </div>
922
- );
923
- };
924
-
925
- const MessageError: FC = () => {
926
- return (
927
- <MessagePrimitive.Error>
928
- <ErrorPrimitive.Root className="aui-message-error-root mt-2 rounded-md border border-destructive bg-destructive/10 p-3 text-destructive text-sm dark:bg-destructive/5 dark:text-red-200">
929
- <ErrorPrimitive.Message className="aui-message-error-message line-clamp-2" />
930
- </ErrorPrimitive.Root>
931
- </MessagePrimitive.Error>
932
- );
933
- };
934
-
935
- const AssistantMessage: FC = () => {
936
- return (
937
- <MessagePrimitive.Root
938
- className="aui-assistant-message-root fade-in slide-in-from-bottom-1 relative mx-auto w-full max-w-(--thread-max-width) animate-in py-3 duration-150"
939
- data-role="assistant"
940
- >
941
- <div className="aui-assistant-message-content wrap-break-word px-2 text-foreground leading-relaxed">
942
- <MessagePrimitive.Parts
943
- components={{
944
- Text: MarkdownText,
945
- tools: { Fallback: ToolFallback },
946
- }}
947
- />
948
- <MessageError />
949
- </div>
950
-
951
- <div className="aui-assistant-message-footer mt-1 ml-2 flex">
952
- <BranchPicker />
953
- <AssistantActionBar />
954
- </div>
955
- </MessagePrimitive.Root>
956
- );
957
- };
958
-
959
- const AssistantActionBar: FC = () => {
960
- return (
961
- <ActionBarPrimitive.Root
962
- hideWhenRunning
963
- autohide="not-last"
964
- autohideFloat="single-branch"
965
- className="aui-assistant-action-bar-root col-start-3 row-start-2 -ml-1 flex gap-1 text-muted-foreground data-floating:absolute data-floating:rounded-md data-floating:border data-floating:bg-background data-floating:p-1 data-floating:shadow-sm"
966
- >
967
- <ActionBarPrimitive.Copy asChild>
968
- <TooltipIconButton tooltip="Copy">
969
- <AssistantIf condition={({ message }) => message.isCopied}>
970
- <CheckIcon />
971
- </AssistantIf>
972
- <AssistantIf condition={({ message }) => !message.isCopied}>
973
- <CopyIcon />
974
- </AssistantIf>
975
- </TooltipIconButton>
976
- </ActionBarPrimitive.Copy>
977
- <ActionBarPrimitive.Reload asChild>
978
- <TooltipIconButton tooltip="Refresh">
979
- <RefreshCwIcon />
980
- </TooltipIconButton>
981
- </ActionBarPrimitive.Reload>
982
- <ActionBarMorePrimitive.Root>
983
- <ActionBarMorePrimitive.Trigger asChild>
984
- <TooltipIconButton
985
- tooltip="More"
986
- className="data-[state=open]:bg-accent"
987
- >
988
- <MoreHorizontalIcon />
989
- </TooltipIconButton>
990
- </ActionBarMorePrimitive.Trigger>
991
- <ActionBarMorePrimitive.Content
992
- side="bottom"
993
- align="start"
994
- className="aui-action-bar-more-content z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md"
995
- >
996
- <ActionBarPrimitive.ExportMarkdown asChild>
997
- <ActionBarMorePrimitive.Item className="aui-action-bar-more-item flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground">
998
- <DownloadIcon className="size-4" />
999
- Export as Markdown
1000
- </ActionBarMorePrimitive.Item>
1001
- </ActionBarPrimitive.ExportMarkdown>
1002
- </ActionBarMorePrimitive.Content>
1003
- </ActionBarMorePrimitive.Root>
1004
- </ActionBarPrimitive.Root>
1005
- );
1006
- };
1007
-
1008
- const UserMessage: FC = () => {
1009
- return (
1010
- <MessagePrimitive.Root
1011
- className="aui-user-message-root fade-in slide-in-from-bottom-1 mx-auto grid w-full max-w-(--thread-max-width) animate-in auto-rows-auto grid-cols-[minmax(72px,1fr)_auto] content-start gap-y-2 px-2 py-3 duration-150 [&:where(>*)]:col-start-2"
1012
- data-role="user"
1013
- >
1014
- <UserMessageAttachments />
1015
-
1016
- <div className="aui-user-message-content-wrapper relative col-start-2 min-w-0">
1017
- <div className="aui-user-message-content wrap-break-word rounded-2xl bg-muted px-4 py-2.5 text-foreground">
1018
- <MessagePrimitive.Parts />
1019
- </div>
1020
- <div className="aui-user-action-bar-wrapper absolute top-1/2 left-0 -translate-x-full -translate-y-1/2 pr-2">
1021
- <UserActionBar />
1022
- </div>
1023
- </div>
1024
-
1025
- <BranchPicker className="aui-user-branch-picker col-span-full col-start-1 row-start-3 -mr-1 justify-end" />
1026
- </MessagePrimitive.Root>
1027
- );
1028
- };
1029
-
1030
- const UserActionBar: FC = () => {
1031
- return (
1032
- <ActionBarPrimitive.Root
1033
- hideWhenRunning
1034
- autohide="not-last"
1035
- className="aui-user-action-bar-root flex flex-col items-end"
1036
- >
1037
- <ActionBarPrimitive.Edit asChild>
1038
- <TooltipIconButton tooltip="Edit" className="aui-user-action-edit p-4">
1039
- <PencilIcon />
1040
- </TooltipIconButton>
1041
- </ActionBarPrimitive.Edit>
1042
- </ActionBarPrimitive.Root>
1043
- );
1044
- };
1045
-
1046
- const EditComposer: FC = () => {
1047
- return (
1048
- <MessagePrimitive.Root className="aui-edit-composer-wrapper mx-auto flex w-full max-w-(--thread-max-width) flex-col px-2 py-3">
1049
- <ComposerPrimitive.Root className="aui-edit-composer-root ml-auto flex w-full max-w-[85%] flex-col rounded-2xl bg-muted">
1050
- <ComposerPrimitive.Input
1051
- className="aui-edit-composer-input min-h-14 w-full resize-none bg-transparent p-4 text-foreground text-sm outline-none"
1052
- autoFocus
1053
- />
1054
- <div className="aui-edit-composer-footer mx-3 mb-3 flex items-center gap-2 self-end">
1055
- <ComposerPrimitive.Cancel asChild>
1056
- <Button variant="ghost" size="sm">
1057
- Cancel
1058
- </Button>
1059
- </ComposerPrimitive.Cancel>
1060
- <ComposerPrimitive.Send asChild>
1061
- <Button size="sm">Update</Button>
1062
- </ComposerPrimitive.Send>
1063
- </div>
1064
- </ComposerPrimitive.Root>
1065
- </MessagePrimitive.Root>
1066
- );
1067
- };
1068
-
1069
- const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
1070
- className,
1071
- ...rest
1072
- }) => {
1073
- return (
1074
- <BranchPickerPrimitive.Root
1075
- hideWhenSingleBranch
1076
- className={cn(
1077
- "aui-branch-picker-root mr-2 -ml-2 inline-flex items-center text-muted-foreground text-xs",
1078
- className,
1079
- )}
1080
- {...rest}
1081
- >
1082
- <BranchPickerPrimitive.Previous asChild>
1083
- <TooltipIconButton tooltip="Previous">
1084
- <ChevronLeftIcon />
1085
- </TooltipIconButton>
1086
- </BranchPickerPrimitive.Previous>
1087
- <span className="aui-branch-picker-state font-medium">
1088
- <BranchPickerPrimitive.Number /> / <BranchPickerPrimitive.Count />
1089
- </span>
1090
- <BranchPickerPrimitive.Next asChild>
1091
- <TooltipIconButton tooltip="Next">
1092
- <ChevronRightIcon />
1093
- </TooltipIconButton>
1094
- </BranchPickerPrimitive.Next>
1095
- </BranchPickerPrimitive.Root>
1096
- );
1097
- };
1098
-
1099
- ```
1100
-
1101
- ## components/assistant-ui/tool-fallback.tsx
1102
-
1103
- ```tsx
1104
- "use client";
1105
-
1106
- import { memo, useCallback, useRef, useState } from "react";
1107
- import {
1108
- AlertCircleIcon,
1109
- CheckIcon,
1110
- ChevronDownIcon,
1111
- LoaderIcon,
1112
- XCircleIcon,
1113
- } from "lucide-react";
1114
- import {
1115
- useScrollLock,
1116
- type ToolCallMessagePartStatus,
1117
- type ToolCallMessagePartComponent,
1118
- } from "@assistant-ui/react";
1119
- import {
1120
- Collapsible,
1121
- CollapsibleContent,
1122
- CollapsibleTrigger,
1123
- } from "@/components/ui/collapsible";
1124
- import { cn } from "@/lib/utils";
1125
-
1126
- const ANIMATION_DURATION = 200;
1127
-
1128
- export type ToolFallbackRootProps = Omit<
1129
- React.ComponentProps<typeof Collapsible>,
1130
- "open" | "onOpenChange"
1131
- > & {
1132
- open?: boolean;
1133
- onOpenChange?: (open: boolean) => void;
1134
- defaultOpen?: boolean;
1135
- };
1136
-
1137
- function ToolFallbackRoot({
1138
- className,
1139
- open: controlledOpen,
1140
- onOpenChange: controlledOnOpenChange,
1141
- defaultOpen = false,
1142
- children,
1143
- ...props
1144
- }: ToolFallbackRootProps) {
1145
- const collapsibleRef = useRef<HTMLDivElement>(null);
1146
- const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
1147
- const lockScroll = useScrollLock(collapsibleRef, ANIMATION_DURATION);
1148
-
1149
- const isControlled = controlledOpen !== undefined;
1150
- const isOpen = isControlled ? controlledOpen : uncontrolledOpen;
1151
-
1152
- const handleOpenChange = useCallback(
1153
- (open: boolean) => {
1154
- if (!open) {
1155
- lockScroll();
1156
- }
1157
- if (!isControlled) {
1158
- setUncontrolledOpen(open);
1159
- }
1160
- controlledOnOpenChange?.(open);
1161
- },
1162
- [lockScroll, isControlled, controlledOnOpenChange],
1163
- );
1164
-
1165
- return (
1166
- <Collapsible
1167
- ref={collapsibleRef}
1168
- data-slot="tool-fallback-root"
1169
- open={isOpen}
1170
- onOpenChange={handleOpenChange}
1171
- className={cn(
1172
- "aui-tool-fallback-root group/tool-fallback-root w-full rounded-lg border py-3",
1173
- className,
1174
- )}
1175
- style={
1176
- {
1177
- "--animation-duration": `${ANIMATION_DURATION}ms`,
1178
- } as React.CSSProperties
1179
- }
1180
- {...props}
1181
- >
1182
- {children}
1183
- </Collapsible>
1184
- );
1185
- }
1186
-
1187
- type ToolStatus = ToolCallMessagePartStatus["type"];
1188
-
1189
- const statusIconMap: Record<ToolStatus, React.ElementType> = {
1190
- running: LoaderIcon,
1191
- complete: CheckIcon,
1192
- incomplete: XCircleIcon,
1193
- "requires-action": AlertCircleIcon,
1194
- };
1195
-
1196
- function ToolFallbackTrigger({
1197
- toolName,
1198
- status,
1199
- className,
1200
- ...props
1201
- }: React.ComponentProps<typeof CollapsibleTrigger> & {
1202
- toolName: string;
1203
- status?: ToolCallMessagePartStatus;
1204
- }) {
1205
- const statusType = status?.type ?? "complete";
1206
- const isRunning = statusType === "running";
1207
- const isCancelled =
1208
- status?.type === "incomplete" && status.reason === "cancelled";
1209
-
1210
- const Icon = statusIconMap[statusType];
1211
- const label = isCancelled ? "Cancelled tool" : "Used tool";
1212
-
1213
- return (
1214
- <CollapsibleTrigger
1215
- data-slot="tool-fallback-trigger"
1216
- className={cn(
1217
- "aui-tool-fallback-trigger group/trigger flex w-full items-center gap-2 px-4 text-sm transition-colors",
1218
- className,
1219
- )}
1220
- {...props}
1221
- >
1222
- <Icon
1223
- data-slot="tool-fallback-trigger-icon"
1224
- className={cn(
1225
- "aui-tool-fallback-trigger-icon size-4 shrink-0",
1226
- isCancelled && "text-muted-foreground",
1227
- isRunning && "animate-spin",
1228
- )}
1229
- />
1230
- <span
1231
- data-slot="tool-fallback-trigger-label"
1232
- className={cn(
1233
- "aui-tool-fallback-trigger-label-wrapper relative inline-block grow text-left leading-none",
1234
- isCancelled && "text-muted-foreground line-through",
1235
- )}
1236
- >
1237
- <span>
1238
- {label}: <b>{toolName}</b>
1239
- </span>
1240
- {isRunning && (
1241
- <span
1242
- aria-hidden
1243
- data-slot="tool-fallback-trigger-shimmer"
1244
- className="aui-tool-fallback-trigger-shimmer shimmer pointer-events-none absolute inset-0 motion-reduce:animate-none"
1245
- >
1246
- {label}: <b>{toolName}</b>
1247
- </span>
1248
- )}
1249
- </span>
1250
- <ChevronDownIcon
1251
- data-slot="tool-fallback-trigger-chevron"
1252
- className={cn(
1253
- "aui-tool-fallback-trigger-chevron size-4 shrink-0",
1254
- "transition-transform duration-(--animation-duration) ease-out",
1255
- "group-data-[state=closed]/trigger:-rotate-90",
1256
- "group-data-[state=open]/trigger:rotate-0",
1257
- )}
1258
- />
1259
- </CollapsibleTrigger>
1260
- );
1261
- }
1262
-
1263
- function ToolFallbackContent({
1264
- className,
1265
- children,
1266
- ...props
1267
- }: React.ComponentProps<typeof CollapsibleContent>) {
1268
- return (
1269
- <CollapsibleContent
1270
- data-slot="tool-fallback-content"
1271
- className={cn(
1272
- "aui-tool-fallback-content relative overflow-hidden text-sm outline-none",
1273
- "group/collapsible-content ease-out",
1274
- "data-[state=closed]:animate-collapsible-up",
1275
- "data-[state=open]:animate-collapsible-down",
1276
- "data-[state=closed]:fill-mode-forwards",
1277
- "data-[state=closed]:pointer-events-none",
1278
- "data-[state=open]:duration-(--animation-duration)",
1279
- "data-[state=closed]:duration-(--animation-duration)",
1280
- className,
1281
- )}
1282
- {...props}
1283
- >
1284
- <div className="mt-3 flex flex-col gap-2 border-t pt-2">{children}</div>
1285
- </CollapsibleContent>
1286
- );
1287
- }
1288
-
1289
- function ToolFallbackArgs({
1290
- argsText,
1291
- className,
1292
- ...props
1293
- }: React.ComponentProps<"div"> & {
1294
- argsText?: string;
1295
- }) {
1296
- if (!argsText) return null;
1297
-
1298
- return (
1299
- <div
1300
- data-slot="tool-fallback-args"
1301
- className={cn("aui-tool-fallback-args px-4", className)}
1302
- {...props}
1303
- >
1304
- <pre className="aui-tool-fallback-args-value whitespace-pre-wrap">
1305
- {argsText}
1306
- </pre>
1307
- </div>
1308
- );
1309
- }
1310
-
1311
- function ToolFallbackResult({
1312
- result,
1313
- className,
1314
- ...props
1315
- }: React.ComponentProps<"div"> & {
1316
- result?: unknown;
1317
- }) {
1318
- if (result === undefined) return null;
1319
-
1320
- return (
1321
- <div
1322
- data-slot="tool-fallback-result"
1323
- className={cn(
1324
- "aui-tool-fallback-result border-t border-dashed px-4 pt-2",
1325
- className,
1326
- )}
1327
- {...props}
1328
- >
1329
- <p className="aui-tool-fallback-result-header font-semibold">Result:</p>
1330
- <pre className="aui-tool-fallback-result-content whitespace-pre-wrap">
1331
- {typeof result === "string" ? result : JSON.stringify(result, null, 2)}
1332
- </pre>
1333
- </div>
1334
- );
1335
- }
1336
-
1337
- function ToolFallbackError({
1338
- status,
1339
- className,
1340
- ...props
1341
- }: React.ComponentProps<"div"> & {
1342
- status?: ToolCallMessagePartStatus;
1343
- }) {
1344
- if (status?.type !== "incomplete") return null;
1345
-
1346
- const error = status.error;
1347
- const errorText = error
1348
- ? typeof error === "string"
1349
- ? error
1350
- : JSON.stringify(error)
1351
- : null;
1352
-
1353
- if (!errorText) return null;
1354
-
1355
- const isCancelled = status.reason === "cancelled";
1356
- const headerText = isCancelled ? "Cancelled reason:" : "Error:";
1357
-
1358
- return (
1359
- <div
1360
- data-slot="tool-fallback-error"
1361
- className={cn("aui-tool-fallback-error px-4", className)}
1362
- {...props}
1363
- >
1364
- <p className="aui-tool-fallback-error-header font-semibold text-muted-foreground">
1365
- {headerText}
1366
- </p>
1367
- <p className="aui-tool-fallback-error-reason text-muted-foreground">
1368
- {errorText}
1369
- </p>
1370
- </div>
1371
- );
1372
- }
1373
-
1374
- const ToolFallbackImpl: ToolCallMessagePartComponent = ({
1375
- toolName,
1376
- argsText,
1377
- result,
1378
- status,
1379
- }) => {
1380
- const isCancelled =
1381
- status?.type === "incomplete" && status.reason === "cancelled";
1382
-
1383
- return (
1384
- <ToolFallbackRoot
1385
- className={cn(isCancelled && "border-muted-foreground/30 bg-muted/30")}
1386
- >
1387
- <ToolFallbackTrigger toolName={toolName} status={status} />
1388
- <ToolFallbackContent>
1389
- <ToolFallbackError status={status} />
1390
- <ToolFallbackArgs
1391
- argsText={argsText}
1392
- className={cn(isCancelled && "opacity-60")}
1393
- />
1394
- {!isCancelled && <ToolFallbackResult result={result} />}
1395
- </ToolFallbackContent>
1396
- </ToolFallbackRoot>
1397
- );
1398
- };
1399
-
1400
- const ToolFallback = memo(
1401
- ToolFallbackImpl,
1402
- ) as unknown as ToolCallMessagePartComponent & {
1403
- Root: typeof ToolFallbackRoot;
1404
- Trigger: typeof ToolFallbackTrigger;
1405
- Content: typeof ToolFallbackContent;
1406
- Args: typeof ToolFallbackArgs;
1407
- Result: typeof ToolFallbackResult;
1408
- Error: typeof ToolFallbackError;
1409
- };
1410
-
1411
- ToolFallback.displayName = "ToolFallback";
1412
- ToolFallback.Root = ToolFallbackRoot;
1413
- ToolFallback.Trigger = ToolFallbackTrigger;
1414
- ToolFallback.Content = ToolFallbackContent;
1415
- ToolFallback.Args = ToolFallbackArgs;
1416
- ToolFallback.Result = ToolFallbackResult;
1417
- ToolFallback.Error = ToolFallbackError;
1418
-
1419
- export {
1420
- ToolFallback,
1421
- ToolFallbackRoot,
1422
- ToolFallbackTrigger,
1423
- ToolFallbackContent,
1424
- ToolFallbackArgs,
1425
- ToolFallbackResult,
1426
- ToolFallbackError,
1427
- };
1428
-
1429
- ```
1430
-
1431
- ## components/assistant-ui/tooltip-icon-button.tsx
1432
-
1433
- ```tsx
1434
- "use client";
1435
-
1436
- import { ComponentPropsWithRef, forwardRef } from "react";
1437
- import { Slottable } from "@radix-ui/react-slot";
1438
-
1439
- import {
1440
- Tooltip,
1441
- TooltipContent,
1442
- TooltipTrigger,
1443
- } from "@/components/ui/tooltip";
1444
- import { Button } from "@/components/ui/button";
1445
- import { cn } from "@/lib/utils";
1446
-
1447
- export type TooltipIconButtonProps = ComponentPropsWithRef<typeof Button> & {
1448
- tooltip: string;
1449
- side?: "top" | "bottom" | "left" | "right";
1450
- };
1451
-
1452
- export const TooltipIconButton = forwardRef<
1453
- HTMLButtonElement,
1454
- TooltipIconButtonProps
1455
- >(({ children, tooltip, side = "bottom", className, ...rest }, ref) => {
1456
- return (
1457
- <Tooltip>
1458
- <TooltipTrigger asChild>
1459
- <Button
1460
- variant="ghost"
1461
- size="icon"
1462
- {...rest}
1463
- className={cn("aui-button-icon size-6 p-1", className)}
1464
- ref={ref}
1465
- >
1466
- <Slottable>{children}</Slottable>
1467
- <span className="aui-sr-only sr-only">{tooltip}</span>
1468
- </Button>
1469
- </TooltipTrigger>
1470
- <TooltipContent side={side}>{tooltip}</TooltipContent>
1471
- </Tooltip>
1472
- );
1473
- });
1474
-
1475
- TooltipIconButton.displayName = "TooltipIconButton";
1476
-
1477
- ```
1478
-
1479
- ## components/ui/avatar.tsx
1480
-
1481
- ```tsx
1482
- "use client";
1483
-
1484
- import * as React from "react";
1485
- import * as AvatarPrimitive from "@radix-ui/react-avatar";
1486
-
1487
- import { cn } from "@/lib/utils";
1488
-
1489
- function Avatar({
1490
- className,
1491
- ...props
1492
- }: React.ComponentProps<typeof AvatarPrimitive.Root>) {
1493
- return (
1494
- <AvatarPrimitive.Root
1495
- data-slot="avatar"
1496
- className={cn(
1497
- "relative flex size-8 shrink-0 overflow-hidden rounded-full",
1498
- className,
1499
- )}
1500
- {...props}
1501
- />
1502
- );
1503
- }
1504
-
1505
- function AvatarImage({
1506
- className,
1507
- ...props
1508
- }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
1509
- return (
1510
- <AvatarPrimitive.Image
1511
- data-slot="avatar-image"
1512
- className={cn("aspect-square size-full", className)}
1513
- {...props}
1514
- />
1515
- );
1516
- }
1517
-
1518
- function AvatarFallback({
1519
- className,
1520
- ...props
1521
- }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
1522
- return (
1523
- <AvatarPrimitive.Fallback
1524
- data-slot="avatar-fallback"
1525
- className={cn(
1526
- "flex size-full items-center justify-center rounded-full bg-muted",
1527
- className,
1528
- )}
1529
- {...props}
1530
- />
1531
- );
1532
- }
1533
-
1534
- export { Avatar, AvatarImage, AvatarFallback };
1535
-
1536
- ```
1537
-
1538
- ## components/ui/button.tsx
1539
-
1540
- ```tsx
1541
- import * as React from "react";
1542
- import { Slot } from "@radix-ui/react-slot";
1543
- import { cva, type VariantProps } from "class-variance-authority";
1544
-
1545
- import { cn } from "@/lib/utils";
1546
-
1547
- const buttonVariants = cva(
1548
- "inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm outline-none transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
1549
- {
1550
- variants: {
1551
- variant: {
1552
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
1553
- destructive:
1554
- "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
1555
- outline:
1556
- "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
1557
- secondary:
1558
- "bg-secondary text-secondary-foreground hover:bg-secondary/80",
1559
- ghost:
1560
- "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
1561
- link: "text-primary underline-offset-4 hover:underline",
1562
- },
1563
- size: {
1564
- default: "h-9 px-4 py-2 has-[>svg]:px-3",
1565
- sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
1566
- lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
1567
- icon: "size-9",
1568
- "icon-sm": "size-8",
1569
- "icon-lg": "size-10",
1570
- },
1571
- },
1572
- defaultVariants: {
1573
- variant: "default",
1574
- size: "default",
1575
- },
1576
- },
1577
- );
1578
-
1579
- function Button({
1580
- className,
1581
- variant = "default",
1582
- size = "default",
1583
- asChild = false,
1584
- ...props
1585
- }: React.ComponentProps<"button"> &
1586
- VariantProps<typeof buttonVariants> & {
1587
- asChild?: boolean;
1588
- }) {
1589
- const Comp = asChild ? Slot : "button";
1590
-
1591
- return (
1592
- <Comp
1593
- data-slot="button"
1594
- data-variant={variant}
1595
- data-size={size}
1596
- className={cn(buttonVariants({ variant, size, className }))}
1597
- {...props}
1598
- />
1599
- );
1600
- }
1601
-
1602
- export { Button, buttonVariants };
1603
-
1604
- ```
1605
-
1606
- ## components/ui/collapsible.tsx
1607
-
1608
- ```tsx
1609
- "use client";
1610
-
1611
- import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
1612
-
1613
- function Collapsible({
1614
- ...props
1615
- }: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
1616
- return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />;
1617
- }
1618
-
1619
- function CollapsibleTrigger({
1620
- ...props
1621
- }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
1622
- return (
1623
- <CollapsiblePrimitive.CollapsibleTrigger
1624
- data-slot="collapsible-trigger"
1625
- {...props}
1626
- />
1627
- );
1628
- }
1629
-
1630
- function CollapsibleContent({
1631
- ...props
1632
- }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
1633
- return (
1634
- <CollapsiblePrimitive.CollapsibleContent
1635
- data-slot="collapsible-content"
1636
- {...props}
1637
- />
1638
- );
1639
- }
1640
-
1641
- export { Collapsible, CollapsibleTrigger, CollapsibleContent };
1642
-
1643
- ```
1644
-
1645
- ## components/ui/dialog.tsx
1646
-
1647
- ```tsx
1648
- "use client";
1649
-
1650
- import * as React from "react";
1651
- import * as DialogPrimitive from "@radix-ui/react-dialog";
1652
- import { XIcon } from "lucide-react";
1653
-
1654
- import { cn } from "@/lib/utils";
1655
-
1656
- function Dialog({
1657
- ...props
1658
- }: React.ComponentProps<typeof DialogPrimitive.Root>) {
1659
- return <DialogPrimitive.Root data-slot="dialog" {...props} />;
1660
- }
1661
-
1662
- function DialogTrigger({
1663
- ...props
1664
- }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
1665
- return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
1666
- }
1667
-
1668
- function DialogPortal({
1669
- ...props
1670
- }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
1671
- return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
1672
- }
1673
-
1674
- function DialogClose({
1675
- ...props
1676
- }: React.ComponentProps<typeof DialogPrimitive.Close>) {
1677
- return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
1678
- }
1679
-
1680
- function DialogOverlay({
1681
- className,
1682
- ...props
1683
- }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
1684
- return (
1685
- <DialogPrimitive.Overlay
1686
- data-slot="dialog-overlay"
1687
- className={cn(
1688
- "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=open]:animate-in",
1689
- className,
1690
- )}
1691
- {...props}
1692
- />
1693
- );
1694
- }
1695
-
1696
- function DialogContent({
1697
- className,
1698
- children,
1699
- showCloseButton = true,
1700
- ...props
1701
- }: React.ComponentProps<typeof DialogPrimitive.Content> & {
1702
- showCloseButton?: boolean;
1703
- }) {
1704
- return (
1705
- <DialogPortal data-slot="dialog-portal">
1706
- <DialogOverlay />
1707
- <DialogPrimitive.Content
1708
- data-slot="dialog-content"
1709
- className={cn(
1710
- "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg outline-none duration-200 data-[state=closed]:animate-out data-[state=open]:animate-in sm:max-w-lg",
1711
- className,
1712
- )}
1713
- {...props}
1714
- >
1715
- {children}
1716
- {showCloseButton && (
1717
- <DialogPrimitive.Close
1718
- data-slot="dialog-close"
1719
- className="absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0"
1720
- >
1721
- <XIcon />
1722
- <span className="sr-only">Close</span>
1723
- </DialogPrimitive.Close>
1724
- )}
1725
- </DialogPrimitive.Content>
1726
- </DialogPortal>
1727
- );
1728
- }
1729
-
1730
- function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
1731
- return (
1732
- <div
1733
- data-slot="dialog-header"
1734
- className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
1735
- {...props}
1736
- />
1737
- );
1738
- }
1739
-
1740
- function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
1741
- return (
1742
- <div
1743
- data-slot="dialog-footer"
1744
- className={cn(
1745
- "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
1746
- className,
1747
- )}
1748
- {...props}
1749
- />
1750
- );
1751
- }
1752
-
1753
- function DialogTitle({
1754
- className,
1755
- ...props
1756
- }: React.ComponentProps<typeof DialogPrimitive.Title>) {
1757
- return (
1758
- <DialogPrimitive.Title
1759
- data-slot="dialog-title"
1760
- className={cn("font-semibold text-lg leading-none", className)}
1761
- {...props}
1762
- />
1763
- );
1764
- }
1765
-
1766
- function DialogDescription({
1767
- className,
1768
- ...props
1769
- }: React.ComponentProps<typeof DialogPrimitive.Description>) {
1770
- return (
1771
- <DialogPrimitive.Description
1772
- data-slot="dialog-description"
1773
- className={cn("text-muted-foreground text-sm", className)}
1774
- {...props}
1775
- />
1776
- );
1777
- }
1778
-
1779
- export {
1780
- Dialog,
1781
- DialogClose,
1782
- DialogContent,
1783
- DialogDescription,
1784
- DialogFooter,
1785
- DialogHeader,
1786
- DialogOverlay,
1787
- DialogPortal,
1788
- DialogTitle,
1789
- DialogTrigger,
1790
- };
1791
-
1792
- ```
1793
-
1794
- ## components/ui/tooltip.tsx
1795
-
1796
- ```tsx
1797
- "use client";
1798
-
1799
- import * as React from "react";
1800
- import * as TooltipPrimitive from "@radix-ui/react-tooltip";
1801
-
1802
- import { cn } from "@/lib/utils";
1803
-
1804
- function TooltipProvider({
1805
- delayDuration = 0,
1806
- ...props
1807
- }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
1808
- return (
1809
- <TooltipPrimitive.Provider
1810
- data-slot="tooltip-provider"
1811
- delayDuration={delayDuration}
1812
- {...props}
1813
- />
1814
- );
1815
- }
1816
-
1817
- function Tooltip({
1818
- ...props
1819
- }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
1820
- return (
1821
- <TooltipProvider>
1822
- <TooltipPrimitive.Root data-slot="tooltip" {...props} />
1823
- </TooltipProvider>
1824
- );
1825
- }
1826
-
1827
- function TooltipTrigger({
1828
- ...props
1829
- }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
1830
- return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
1831
- }
1832
-
1833
- function TooltipContent({
1834
- className,
1835
- sideOffset = 0,
1836
- children,
1837
- ...props
1838
- }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
1839
- return (
1840
- <TooltipPrimitive.Portal>
1841
- <TooltipPrimitive.Content
1842
- data-slot="tooltip-content"
1843
- sideOffset={sideOffset}
1844
- className={cn(
1845
- "fade-in-0 zoom-in-95 data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in text-balance rounded-md bg-foreground px-3 py-1.5 text-background text-xs data-[state=closed]:animate-out",
1846
- className,
1847
- )}
1848
- {...props}
1849
- >
1850
- {children}
1851
- <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" />
1852
- </TooltipPrimitive.Content>
1853
- </TooltipPrimitive.Portal>
1854
- );
246
+ "iconLibrary": "lucide",
247
+ "registries": {
248
+ "@assistant-ui": "https://r.assistant-ui.com/{name}.json"
249
+ }
1855
250
  }
1856
251
 
1857
- export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
1858
-
1859
252
  ```
1860
253
 
1861
254
  ## lib/utils.ts
@@ -1896,32 +289,31 @@ export default nextConfig;
1896
289
  "start": "next start"
1897
290
  },
1898
291
  "dependencies": {
1899
- "@ai-sdk/openai": "^3.0.13",
292
+ "@ai-sdk/openai": "^3.0.25",
1900
293
  "@assistant-ui/react": "workspace:*",
1901
294
  "@assistant-ui/react-ai-sdk": "workspace:*",
1902
295
  "@assistant-ui/react-markdown": "workspace:*",
296
+ "@assistant-ui/ui": "workspace:*",
1903
297
  "@radix-ui/react-avatar": "^1.1.11",
1904
298
  "@radix-ui/react-collapsible": "^1.1.12",
1905
299
  "@radix-ui/react-dialog": "^1.1.15",
1906
300
  "@radix-ui/react-slot": "^1.2.4",
1907
301
  "@radix-ui/react-tooltip": "^1.2.8",
1908
- "ai": "^6.0.42",
302
+ "ai": "^6.0.69",
1909
303
  "class-variance-authority": "^0.7.1",
1910
304
  "clsx": "^2.1.1",
1911
- "lucide-react": "^0.562.0",
1912
- "next": "^16.1.4",
1913
- "react": "^19.2.3",
1914
- "react-dom": "^19.2.3",
1915
- "remark-gfm": "^4.0.1",
305
+ "lucide-react": "^0.563.0",
306
+ "next": "^16.1.6",
307
+ "react": "^19.2.4",
308
+ "react-dom": "^19.2.4",
1916
309
  "tailwind-merge": "^3.4.0",
1917
- "zod": "^4.3.5",
1918
- "zustand": "^5.0.10"
310
+ "zod": "^4.3.6"
1919
311
  },
1920
312
  "devDependencies": {
1921
313
  "@assistant-ui/x-buildutils": "workspace:*",
1922
314
  "@tailwindcss/postcss": "^4.1.18",
1923
- "@types/node": "^25.0.9",
1924
- "@types/react": "^19.2.9",
315
+ "@types/node": "^25.2.0",
316
+ "@types/react": "^19.2.10",
1925
317
  "@types/react-dom": "^19.2.3",
1926
318
  "postcss": "^8.5.6",
1927
319
  "tailwindcss": "^4.1.18",
@@ -1935,33 +327,31 @@ export default nextConfig;
1935
327
  ## README.md
1936
328
 
1937
329
  ```markdown
1938
- # AI SDK v5 Example
330
+ # AI SDK v6 Example
1939
331
 
1940
- This example demonstrates how to use `@assistant-ui/react-ai-sdk-v5` with the Vercel AI SDK v5.
332
+ This example demonstrates how to use `@assistant-ui/react-ai-sdk` with the Vercel AI SDK v6.
1941
333
 
1942
- ## Getting Started
334
+ ## Quick Start
1943
335
 
1944
- 1. Install dependencies:
336
+ ### Using CLI (Recommended)
1945
337
 
1946
338
  ```bash
1947
- npm install
339
+ npx assistant-ui@latest create my-app --example with-ai-sdk-v6
340
+ cd my-app
1948
341
  ```
1949
342
 
1950
- 2. Set up your environment variables:
343
+ ### Environment Variables
1951
344
 
1952
- ```bash
1953
- cp .env.example .env.local
1954
- ```
1955
-
1956
- Add your Anthropic API key to `.env.local`:
345
+ Create `.env.local`:
1957
346
 
1958
347
  ```
1959
348
  ANTHROPIC_API_KEY=your-api-key-here
1960
349
  ```
1961
350
 
1962
- 3. Run the development server:
351
+ ### Run
1963
352
 
1964
353
  ```bash
354
+ npm install
1965
355
  npm run dev
1966
356
  ```
1967
357
 
@@ -1969,10 +359,10 @@ Open [http://localhost:3000](http://localhost:3000) to see the result.
1969
359
 
1970
360
  ## Key Features
1971
361
 
1972
- - Uses the new AI SDK v5 with `@ai-sdk/react` and `@ai-sdk/anthropic`
362
+ - Uses the new AI SDK v6 with `@ai-sdk/react` and `@ai-sdk/anthropic`
1973
363
  - Integrates with `@assistant-ui/react` using the new `useChatRuntime` hook
1974
364
  - No RSC support (client-side only)
1975
- - Simplified integration with the `useChatRuntime` hook that wraps AI SDK v5's `useChat`
365
+ - Simplified integration with the `useChatRuntime` hook that wraps AI SDK v6's `useChat`
1976
366
  - Automatically uses `AssistantChatTransport` to pass system messages and frontend tools to the backend
1977
367
 
1978
368
  ## Custom Transport Configuration
@@ -2007,7 +397,12 @@ const runtime = useChatRuntime({
2007
397
 
2008
398
  ## API Route
2009
399
 
2010
- The API route at `/api/chat` uses the new `streamText` function from AI SDK v5 to handle chat completions.
400
+ The API route at `/api/chat` uses the new `streamText` function from AI SDK v6 to handle chat completions.
401
+
402
+ ## Related Documentation
403
+
404
+ - [assistant-ui Documentation](https://www.assistant-ui.com/docs)
405
+ - [AI SDK Integration Guide](https://www.assistant-ui.com/docs/runtimes/ai-sdk)
2011
406
 
2012
407
  ```
2013
408
 
@@ -2017,7 +412,14 @@ The API route at `/api/chat` uses the new `streamText` function from AI SDK v5 t
2017
412
  {
2018
413
  "extends": "@assistant-ui/x-buildutils/ts/next",
2019
414
  "compilerOptions": {
2020
- "paths": { "@/*": ["./*"] }
415
+ "paths": {
416
+ "@/*": ["./*"],
417
+ "@/components/assistant-ui/*": [
418
+ "../../packages/ui/src/components/assistant-ui/*"
419
+ ],
420
+ "@/components/ui/*": ["../../packages/ui/src/components/ui/*"],
421
+ "@assistant-ui/ui/*": ["../../packages/ui/src/*"]
422
+ }
2021
423
  },
2022
424
  "include": ["**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
2023
425
  "exclude": ["node_modules"]