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

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 (26) hide show
  1. package/.docs/organized/code-examples/store-example.md +554 -0
  2. package/.docs/organized/code-examples/with-ag-ui.md +1639 -0
  3. package/.docs/organized/code-examples/with-ai-sdk-v5.md +555 -53
  4. package/.docs/organized/code-examples/with-assistant-transport.md +553 -52
  5. package/.docs/organized/code-examples/with-cloud.md +637 -42
  6. package/.docs/organized/code-examples/with-external-store.md +584 -34
  7. package/.docs/organized/code-examples/with-ffmpeg.md +586 -52
  8. package/.docs/organized/code-examples/with-langgraph.md +636 -53
  9. package/.docs/organized/code-examples/with-parent-id-grouping.md +584 -34
  10. package/.docs/organized/code-examples/with-react-hook-form.md +587 -75
  11. package/.docs/raw/blog/2024-07-29-hello/index.mdx +0 -1
  12. package/.docs/raw/docs/cli.mdx +396 -0
  13. package/.docs/raw/docs/cloud/authorization.mdx +2 -2
  14. package/.docs/raw/docs/getting-started.mdx +31 -37
  15. package/.docs/raw/docs/guides/context-api.mdx +5 -5
  16. package/.docs/raw/docs/migrations/v0-12.mdx +2 -2
  17. package/.docs/raw/docs/runtimes/assistant-transport.mdx +891 -0
  18. package/.docs/raw/docs/runtimes/custom/custom-thread-list.mdx +9 -0
  19. package/.docs/raw/docs/runtimes/custom/local.mdx +77 -4
  20. package/.docs/raw/docs/runtimes/langgraph/index.mdx +8 -5
  21. package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +12 -10
  22. package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +50 -31
  23. package/.docs/raw/docs/ui/Reasoning.mdx +174 -0
  24. package/dist/chunk-M2RKUM66.js +3 -3
  25. package/dist/chunk-NVNFQ5ZO.js +2 -2
  26. package/package.json +15 -7
@@ -274,6 +274,247 @@ export default function Home() {
274
274
 
275
275
  ```
276
276
 
277
+ ## components/assistant-ui/attachment.tsx
278
+
279
+ ```tsx
280
+ "use client";
281
+
282
+ import { PropsWithChildren, useEffect, useState, type FC } from "react";
283
+ import Image from "next/image";
284
+ import { XIcon, PlusIcon, FileText } from "lucide-react";
285
+ import {
286
+ AttachmentPrimitive,
287
+ ComposerPrimitive,
288
+ MessagePrimitive,
289
+ useAssistantState,
290
+ useAssistantApi,
291
+ } from "@assistant-ui/react";
292
+ import { useShallow } from "zustand/shallow";
293
+ import {
294
+ Tooltip,
295
+ TooltipContent,
296
+ TooltipTrigger,
297
+ } from "@/components/ui/tooltip";
298
+ import {
299
+ Dialog,
300
+ DialogTitle,
301
+ DialogContent,
302
+ DialogTrigger,
303
+ } from "@/components/ui/dialog";
304
+ import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
305
+ import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
306
+ import { cn } from "@/lib/utils";
307
+
308
+ const useFileSrc = (file: File | undefined) => {
309
+ const [src, setSrc] = useState<string | undefined>(undefined);
310
+
311
+ useEffect(() => {
312
+ if (!file) {
313
+ setSrc(undefined);
314
+ return;
315
+ }
316
+
317
+ const objectUrl = URL.createObjectURL(file);
318
+ setSrc(objectUrl);
319
+
320
+ return () => {
321
+ URL.revokeObjectURL(objectUrl);
322
+ };
323
+ }, [file]);
324
+
325
+ return src;
326
+ };
327
+
328
+ const useAttachmentSrc = () => {
329
+ const { file, src } = useAssistantState(
330
+ useShallow(({ attachment }): { file?: File; src?: string } => {
331
+ if (attachment.type !== "image") return {};
332
+ if (attachment.file) return { file: attachment.file };
333
+ const src = attachment.content?.filter((c) => c.type === "image")[0]
334
+ ?.image;
335
+ if (!src) return {};
336
+ return { src };
337
+ }),
338
+ );
339
+
340
+ return useFileSrc(file) ?? src;
341
+ };
342
+
343
+ type AttachmentPreviewProps = {
344
+ src: string;
345
+ };
346
+
347
+ const AttachmentPreview: FC<AttachmentPreviewProps> = ({ src }) => {
348
+ const [isLoaded, setIsLoaded] = useState(false);
349
+ return (
350
+ <Image
351
+ src={src}
352
+ alt="Image Preview"
353
+ width={1}
354
+ height={1}
355
+ className={
356
+ isLoaded
357
+ ? "aui-attachment-preview-image-loaded block h-auto max-h-[80vh] w-auto max-w-full object-contain"
358
+ : "aui-attachment-preview-image-loading hidden"
359
+ }
360
+ onLoadingComplete={() => setIsLoaded(true)}
361
+ priority={false}
362
+ />
363
+ );
364
+ };
365
+
366
+ const AttachmentPreviewDialog: FC<PropsWithChildren> = ({ children }) => {
367
+ const src = useAttachmentSrc();
368
+
369
+ if (!src) return children;
370
+
371
+ return (
372
+ <Dialog>
373
+ <DialogTrigger
374
+ className="aui-attachment-preview-trigger hover:bg-accent/50 cursor-pointer transition-colors"
375
+ asChild
376
+ >
377
+ {children}
378
+ </DialogTrigger>
379
+ <DialogContent className="aui-attachment-preview-dialog-content [&_svg]:text-background [&>button]:bg-foreground/60 [&>button]:hover:[&_svg]:text-destructive p-2 sm:max-w-3xl [&>button]:rounded-full [&>button]:p-1 [&>button]:opacity-100 [&>button]:!ring-0">
380
+ <DialogTitle className="aui-sr-only sr-only">
381
+ Image Attachment Preview
382
+ </DialogTitle>
383
+ <div className="aui-attachment-preview bg-background relative mx-auto flex max-h-[80dvh] w-full items-center justify-center overflow-hidden">
384
+ <AttachmentPreview src={src} />
385
+ </div>
386
+ </DialogContent>
387
+ </Dialog>
388
+ );
389
+ };
390
+
391
+ const AttachmentThumb: FC = () => {
392
+ const isImage = useAssistantState(
393
+ ({ attachment }) => attachment.type === "image",
394
+ );
395
+ const src = useAttachmentSrc();
396
+
397
+ return (
398
+ <Avatar className="aui-attachment-tile-avatar h-full w-full rounded-none">
399
+ <AvatarImage
400
+ src={src}
401
+ alt="Attachment preview"
402
+ className="aui-attachment-tile-image object-cover"
403
+ />
404
+ <AvatarFallback delayMs={isImage ? 200 : 0}>
405
+ <FileText className="aui-attachment-tile-fallback-icon text-muted-foreground size-8" />
406
+ </AvatarFallback>
407
+ </Avatar>
408
+ );
409
+ };
410
+
411
+ const AttachmentUI: FC = () => {
412
+ const api = useAssistantApi();
413
+ const isComposer = api.attachment.source === "composer";
414
+
415
+ const isImage = useAssistantState(
416
+ ({ attachment }) => attachment.type === "image",
417
+ );
418
+ const typeLabel = useAssistantState(({ attachment }) => {
419
+ const type = attachment.type;
420
+ switch (type) {
421
+ case "image":
422
+ return "Image";
423
+ case "document":
424
+ return "Document";
425
+ case "file":
426
+ return "File";
427
+ default:
428
+ const _exhaustiveCheck: never = type;
429
+ throw new Error(`Unknown attachment type: ${_exhaustiveCheck}`);
430
+ }
431
+ });
432
+
433
+ return (
434
+ <Tooltip>
435
+ <AttachmentPrimitive.Root
436
+ className={cn(
437
+ "aui-attachment-root relative",
438
+ isImage &&
439
+ "aui-attachment-root-composer only:[&>#attachment-tile]:size-24",
440
+ )}
441
+ >
442
+ <AttachmentPreviewDialog>
443
+ <TooltipTrigger asChild>
444
+ <div
445
+ className={cn(
446
+ "aui-attachment-tile bg-muted size-14 cursor-pointer overflow-hidden rounded-[14px] border transition-opacity hover:opacity-75",
447
+ isComposer &&
448
+ "aui-attachment-tile-composer border-foreground/20",
449
+ )}
450
+ role="button"
451
+ id="attachment-tile"
452
+ aria-label={`${typeLabel} attachment`}
453
+ >
454
+ <AttachmentThumb />
455
+ </div>
456
+ </TooltipTrigger>
457
+ </AttachmentPreviewDialog>
458
+ {isComposer && <AttachmentRemove />}
459
+ </AttachmentPrimitive.Root>
460
+ <TooltipContent side="top">
461
+ <AttachmentPrimitive.Name />
462
+ </TooltipContent>
463
+ </Tooltip>
464
+ );
465
+ };
466
+
467
+ const AttachmentRemove: FC = () => {
468
+ return (
469
+ <AttachmentPrimitive.Remove asChild>
470
+ <TooltipIconButton
471
+ tooltip="Remove file"
472
+ className="aui-attachment-tile-remove text-muted-foreground hover:[&_svg]:text-destructive absolute top-1.5 right-1.5 size-3.5 rounded-full bg-white opacity-100 shadow-sm hover:!bg-white [&_svg]:text-black"
473
+ side="top"
474
+ >
475
+ <XIcon className="aui-attachment-remove-icon size-3 dark:stroke-[2.5px]" />
476
+ </TooltipIconButton>
477
+ </AttachmentPrimitive.Remove>
478
+ );
479
+ };
480
+
481
+ export const UserMessageAttachments: FC = () => {
482
+ return (
483
+ <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">
484
+ <MessagePrimitive.Attachments components={{ Attachment: AttachmentUI }} />
485
+ </div>
486
+ );
487
+ };
488
+
489
+ export const ComposerAttachments: FC = () => {
490
+ return (
491
+ <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">
492
+ <ComposerPrimitive.Attachments
493
+ components={{ Attachment: AttachmentUI }}
494
+ />
495
+ </div>
496
+ );
497
+ };
498
+
499
+ export const ComposerAddAttachment: FC = () => {
500
+ return (
501
+ <ComposerPrimitive.AddAttachment asChild>
502
+ <TooltipIconButton
503
+ tooltip="Add Attachment"
504
+ side="bottom"
505
+ variant="ghost"
506
+ size="icon"
507
+ className="aui-composer-add-attachment hover:bg-muted-foreground/15 dark:border-muted-foreground/15 dark:hover:bg-muted-foreground/30 size-[34px] rounded-full p-1 text-xs font-semibold"
508
+ aria-label="Add Attachment"
509
+ >
510
+ <PlusIcon className="aui-attachment-add-icon size-5 stroke-[1.5px]" />
511
+ </TooltipIconButton>
512
+ </ComposerPrimitive.AddAttachment>
513
+ );
514
+ };
515
+
516
+ ```
517
+
277
518
  ## components/assistant-ui/markdown-text.tsx
278
519
 
279
520
  ```tsx
@@ -282,13 +523,13 @@ export default function Home() {
282
523
  import "@assistant-ui/react-markdown/styles/dot.css";
283
524
 
284
525
  import {
285
- CodeHeaderProps,
526
+ type CodeHeaderProps,
286
527
  MarkdownTextPrimitive,
287
528
  unstable_memoizeMarkdownComponents as memoizeMarkdownComponents,
288
529
  useIsMarkdownCodeBlock,
289
530
  } from "@assistant-ui/react-markdown";
290
531
  import remarkGfm from "remark-gfm";
291
- import { FC, memo, useState } from "react";
532
+ import { type FC, memo, useState } from "react";
292
533
  import { CheckIcon, CopyIcon } from "lucide-react";
293
534
 
294
535
  import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
@@ -314,8 +555,10 @@ const CodeHeader: FC<CodeHeaderProps> = ({ language, code }) => {
314
555
  };
315
556
 
316
557
  return (
317
- <div className="flex items-center justify-between gap-4 rounded-t-lg bg-zinc-900 px-4 py-2 text-sm font-semibold text-white">
318
- <span className="lowercase [&>span]:text-xs">{language}</span>
558
+ <div className="aui-code-header-root bg-muted-foreground/15 text-foreground dark:bg-muted-foreground/20 mt-4 flex items-center justify-between gap-4 rounded-t-lg px-4 py-2 text-sm font-semibold">
559
+ <span className="aui-code-header-language lowercase [&>span]:text-xs">
560
+ {language}
561
+ </span>
319
562
  <TooltipIconButton tooltip="Copy" onClick={onCopy}>
320
563
  {!isCopied && <CopyIcon />}
321
564
  {isCopied && <CheckIcon />}
@@ -347,7 +590,7 @@ const defaultComponents = memoizeMarkdownComponents({
347
590
  h1: ({ className, ...props }) => (
348
591
  <h1
349
592
  className={cn(
350
- "mb-8 scroll-m-20 text-4xl font-extrabold tracking-tight last:mb-0",
593
+ "aui-md-h1 mb-8 scroll-m-20 text-4xl font-extrabold tracking-tight last:mb-0",
351
594
  className,
352
595
  )}
353
596
  {...props}
@@ -356,7 +599,7 @@ const defaultComponents = memoizeMarkdownComponents({
356
599
  h2: ({ className, ...props }) => (
357
600
  <h2
358
601
  className={cn(
359
- "mt-8 mb-4 scroll-m-20 text-3xl font-semibold tracking-tight first:mt-0 last:mb-0",
602
+ "aui-md-h2 mt-8 mb-4 scroll-m-20 text-3xl font-semibold tracking-tight first:mt-0 last:mb-0",
360
603
  className,
361
604
  )}
362
605
  {...props}
@@ -365,7 +608,7 @@ const defaultComponents = memoizeMarkdownComponents({
365
608
  h3: ({ className, ...props }) => (
366
609
  <h3
367
610
  className={cn(
368
- "mt-6 mb-4 scroll-m-20 text-2xl font-semibold tracking-tight first:mt-0 last:mb-0",
611
+ "aui-md-h3 mt-6 mb-4 scroll-m-20 text-2xl font-semibold tracking-tight first:mt-0 last:mb-0",
369
612
  className,
370
613
  )}
371
614
  {...props}
@@ -374,7 +617,7 @@ const defaultComponents = memoizeMarkdownComponents({
374
617
  h4: ({ className, ...props }) => (
375
618
  <h4
376
619
  className={cn(
377
- "mt-6 mb-4 scroll-m-20 text-xl font-semibold tracking-tight first:mt-0 last:mb-0",
620
+ "aui-md-h4 mt-6 mb-4 scroll-m-20 text-xl font-semibold tracking-tight first:mt-0 last:mb-0",
378
621
  className,
379
622
  )}
380
623
  {...props}
@@ -383,7 +626,7 @@ const defaultComponents = memoizeMarkdownComponents({
383
626
  h5: ({ className, ...props }) => (
384
627
  <h5
385
628
  className={cn(
386
- "my-4 text-lg font-semibold first:mt-0 last:mb-0",
629
+ "aui-md-h5 my-4 text-lg font-semibold first:mt-0 last:mb-0",
387
630
  className,
388
631
  )}
389
632
  {...props}
@@ -391,20 +634,26 @@ const defaultComponents = memoizeMarkdownComponents({
391
634
  ),
392
635
  h6: ({ className, ...props }) => (
393
636
  <h6
394
- className={cn("my-4 font-semibold first:mt-0 last:mb-0", className)}
637
+ className={cn(
638
+ "aui-md-h6 my-4 font-semibold first:mt-0 last:mb-0",
639
+ className,
640
+ )}
395
641
  {...props}
396
642
  />
397
643
  ),
398
644
  p: ({ className, ...props }) => (
399
645
  <p
400
- className={cn("mt-5 mb-5 leading-7 first:mt-0 last:mb-0", className)}
646
+ className={cn(
647
+ "aui-md-p mt-5 mb-5 leading-7 first:mt-0 last:mb-0",
648
+ className,
649
+ )}
401
650
  {...props}
402
651
  />
403
652
  ),
404
653
  a: ({ className, ...props }) => (
405
654
  <a
406
655
  className={cn(
407
- "text-primary font-medium underline underline-offset-4",
656
+ "aui-md-a text-primary font-medium underline underline-offset-4",
408
657
  className,
409
658
  )}
410
659
  {...props}
@@ -412,29 +661,29 @@ const defaultComponents = memoizeMarkdownComponents({
412
661
  ),
413
662
  blockquote: ({ className, ...props }) => (
414
663
  <blockquote
415
- className={cn("border-l-2 pl-6 italic", className)}
664
+ className={cn("aui-md-blockquote border-l-2 pl-6 italic", className)}
416
665
  {...props}
417
666
  />
418
667
  ),
419
668
  ul: ({ className, ...props }) => (
420
669
  <ul
421
- className={cn("my-5 ml-6 list-disc [&>li]:mt-2", className)}
670
+ className={cn("aui-md-ul my-5 ml-6 list-disc [&>li]:mt-2", className)}
422
671
  {...props}
423
672
  />
424
673
  ),
425
674
  ol: ({ className, ...props }) => (
426
675
  <ol
427
- className={cn("my-5 ml-6 list-decimal [&>li]:mt-2", className)}
676
+ className={cn("aui-md-ol my-5 ml-6 list-decimal [&>li]:mt-2", className)}
428
677
  {...props}
429
678
  />
430
679
  ),
431
680
  hr: ({ className, ...props }) => (
432
- <hr className={cn("my-5 border-b", className)} {...props} />
681
+ <hr className={cn("aui-md-hr my-5 border-b", className)} {...props} />
433
682
  ),
434
683
  table: ({ className, ...props }) => (
435
684
  <table
436
685
  className={cn(
437
- "my-5 w-full border-separate border-spacing-0 overflow-y-auto",
686
+ "aui-md-table my-5 w-full border-separate border-spacing-0 overflow-y-auto",
438
687
  className,
439
688
  )}
440
689
  {...props}
@@ -443,7 +692,7 @@ const defaultComponents = memoizeMarkdownComponents({
443
692
  th: ({ className, ...props }) => (
444
693
  <th
445
694
  className={cn(
446
- "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",
695
+ "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",
447
696
  className,
448
697
  )}
449
698
  {...props}
@@ -452,7 +701,7 @@ const defaultComponents = memoizeMarkdownComponents({
452
701
  td: ({ className, ...props }) => (
453
702
  <td
454
703
  className={cn(
455
- "border-b border-l px-4 py-2 text-left last:border-r [&[align=center]]:text-center [&[align=right]]:text-right",
704
+ "aui-md-td border-b border-l px-4 py-2 text-left last:border-r [&[align=center]]:text-center [&[align=right]]:text-right",
456
705
  className,
457
706
  )}
458
707
  {...props}
@@ -461,7 +710,7 @@ const defaultComponents = memoizeMarkdownComponents({
461
710
  tr: ({ className, ...props }) => (
462
711
  <tr
463
712
  className={cn(
464
- "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",
713
+ "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",
465
714
  className,
466
715
  )}
467
716
  {...props}
@@ -469,14 +718,14 @@ const defaultComponents = memoizeMarkdownComponents({
469
718
  ),
470
719
  sup: ({ className, ...props }) => (
471
720
  <sup
472
- className={cn("[&>a]:text-xs [&>a]:no-underline", className)}
721
+ className={cn("aui-md-sup [&>a]:text-xs [&>a]:no-underline", className)}
473
722
  {...props}
474
723
  />
475
724
  ),
476
725
  pre: ({ className, ...props }) => (
477
726
  <pre
478
727
  className={cn(
479
- "overflow-x-auto rounded-b-lg bg-black p-4 text-white",
728
+ "aui-md-pre overflow-x-auto !rounded-t-none rounded-b-lg bg-black p-4 text-white",
480
729
  className,
481
730
  )}
482
731
  {...props}
@@ -487,7 +736,8 @@ const defaultComponents = memoizeMarkdownComponents({
487
736
  return (
488
737
  <code
489
738
  className={cn(
490
- !isCodeBlock && "bg-muted rounded border font-semibold",
739
+ !isCodeBlock &&
740
+ "aui-md-inline-code bg-muted rounded border font-semibold",
491
741
  className,
492
742
  )}
493
743
  {...props}
@@ -506,15 +756,17 @@ import type { FC } from "react";
506
756
  import {
507
757
  ThreadListItemPrimitive,
508
758
  ThreadListPrimitive,
759
+ useAssistantState,
509
760
  } from "@assistant-ui/react";
510
761
  import { ArchiveIcon, PlusIcon } from "lucide-react";
511
762
 
512
763
  import { Button } from "@/components/ui/button";
513
764
  import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
765
+ import { Skeleton } from "@/components/ui/skeleton";
514
766
 
515
767
  export const ThreadList: FC = () => {
516
768
  return (
517
- <ThreadListPrimitive.Root className="flex flex-col items-stretch gap-1.5">
769
+ <ThreadListPrimitive.Root className="aui-root aui-thread-list-root flex flex-col items-stretch gap-1.5">
518
770
  <ThreadListNew />
519
771
  <ThreadListItems />
520
772
  </ThreadListPrimitive.Root>
@@ -525,7 +777,7 @@ const ThreadListNew: FC = () => {
525
777
  return (
526
778
  <ThreadListPrimitive.New asChild>
527
779
  <Button
528
- className="data-[active]:bg-muted hover:bg-muted flex items-center justify-start gap-1 rounded-lg px-2.5 py-2 text-start"
780
+ className="aui-thread-list-new hover:bg-muted data-active:bg-muted flex items-center justify-start gap-1 rounded-lg px-2.5 py-2 text-start"
529
781
  variant="ghost"
530
782
  >
531
783
  <PlusIcon />
@@ -536,13 +788,37 @@ const ThreadListNew: FC = () => {
536
788
  };
537
789
 
538
790
  const ThreadListItems: FC = () => {
791
+ const isLoading = useAssistantState(({ threads }) => threads.isLoading);
792
+
793
+ if (isLoading) {
794
+ return <ThreadListSkeleton />;
795
+ }
796
+
539
797
  return <ThreadListPrimitive.Items components={{ ThreadListItem }} />;
540
798
  };
541
799
 
800
+ const ThreadListSkeleton: FC = () => {
801
+ return (
802
+ <>
803
+ {Array.from({ length: 5 }, (_, i) => (
804
+ <div
805
+ key={i}
806
+ role="status"
807
+ aria-label="Loading threads"
808
+ aria-live="polite"
809
+ className="aui-thread-list-skeleton-wrapper flex items-center gap-2 rounded-md px-3 py-2"
810
+ >
811
+ <Skeleton className="aui-thread-list-skeleton h-[22px] flex-grow" />
812
+ </div>
813
+ ))}
814
+ </>
815
+ );
816
+ };
817
+
542
818
  const ThreadListItem: FC = () => {
543
819
  return (
544
- <ThreadListItemPrimitive.Root className="data-[active]:bg-muted hover:bg-muted focus-visible:bg-muted focus-visible:ring-ring flex items-center gap-2 rounded-lg transition-all focus-visible:ring-2 focus-visible:outline-none">
545
- <ThreadListItemPrimitive.Trigger className="flex-grow px-3 py-2 text-start">
820
+ <ThreadListItemPrimitive.Root className="aui-thread-list-item hover:bg-muted focus-visible:bg-muted focus-visible:ring-ring data-active:bg-muted flex items-center gap-2 rounded-lg transition-all focus-visible:ring-2 focus-visible:outline-none">
821
+ <ThreadListItemPrimitive.Trigger className="aui-thread-list-item-trigger flex-grow px-3 py-2 text-start">
546
822
  <ThreadListItemTitle />
547
823
  </ThreadListItemPrimitive.Trigger>
548
824
  <ThreadListItemArchive />
@@ -552,9 +828,9 @@ const ThreadListItem: FC = () => {
552
828
 
553
829
  const ThreadListItemTitle: FC = () => {
554
830
  return (
555
- <p className="text-sm">
831
+ <span className="aui-thread-list-item-title text-sm">
556
832
  <ThreadListItemPrimitive.Title fallback="New Chat" />
557
- </p>
833
+ </span>
558
834
  );
559
835
  };
560
836
 
@@ -562,7 +838,7 @@ const ThreadListItemArchive: FC = () => {
562
838
  return (
563
839
  <ThreadListItemPrimitive.Archive asChild>
564
840
  <TooltipIconButton
565
- className="hover:text-primary text-foreground mr-3 ml-auto size-4 p-0"
841
+ className="aui-thread-list-item-archive text-foreground hover:text-primary mr-3 ml-auto size-4 p-0"
566
842
  variant="ghost"
567
843
  tooltip="Archive thread"
568
844
  >
@@ -864,12 +1140,111 @@ const CircleStopIcon = () => {
864
1140
 
865
1141
  ```
866
1142
 
1143
+ ## components/assistant-ui/tool-fallback.tsx
1144
+
1145
+ ```tsx
1146
+ import type { ToolCallMessagePartComponent } from "@assistant-ui/react";
1147
+ import {
1148
+ CheckIcon,
1149
+ ChevronDownIcon,
1150
+ ChevronUpIcon,
1151
+ XCircleIcon,
1152
+ } from "lucide-react";
1153
+ import { useState } from "react";
1154
+ import { Button } from "@/components/ui/button";
1155
+ import { cn } from "@/lib/utils";
1156
+
1157
+ export const ToolFallback: ToolCallMessagePartComponent = ({
1158
+ toolName,
1159
+ argsText,
1160
+ result,
1161
+ status,
1162
+ }) => {
1163
+ const [isCollapsed, setIsCollapsed] = useState(true);
1164
+
1165
+ const isCancelled =
1166
+ status?.type === "incomplete" && status.reason === "cancelled";
1167
+ const cancelledReason =
1168
+ isCancelled && status.error
1169
+ ? typeof status.error === "string"
1170
+ ? status.error
1171
+ : JSON.stringify(status.error)
1172
+ : null;
1173
+
1174
+ return (
1175
+ <div
1176
+ className={cn(
1177
+ "aui-tool-fallback-root mb-4 flex w-full flex-col gap-3 rounded-lg border py-3",
1178
+ isCancelled && "border-muted-foreground/30 bg-muted/30",
1179
+ )}
1180
+ >
1181
+ <div className="aui-tool-fallback-header flex items-center gap-2 px-4">
1182
+ {isCancelled ? (
1183
+ <XCircleIcon className="aui-tool-fallback-icon text-muted-foreground size-4" />
1184
+ ) : (
1185
+ <CheckIcon className="aui-tool-fallback-icon size-4" />
1186
+ )}
1187
+ <p
1188
+ className={cn(
1189
+ "aui-tool-fallback-title grow",
1190
+ isCancelled && "text-muted-foreground line-through",
1191
+ )}
1192
+ >
1193
+ {isCancelled ? "Cancelled tool: " : "Used tool: "}
1194
+ <b>{toolName}</b>
1195
+ </p>
1196
+ <Button onClick={() => setIsCollapsed(!isCollapsed)}>
1197
+ {isCollapsed ? <ChevronUpIcon /> : <ChevronDownIcon />}
1198
+ </Button>
1199
+ </div>
1200
+ {!isCollapsed && (
1201
+ <div className="aui-tool-fallback-content flex flex-col gap-2 border-t pt-2">
1202
+ {cancelledReason && (
1203
+ <div className="aui-tool-fallback-cancelled-root px-4">
1204
+ <p className="aui-tool-fallback-cancelled-header text-muted-foreground font-semibold">
1205
+ Cancelled reason:
1206
+ </p>
1207
+ <p className="aui-tool-fallback-cancelled-reason text-muted-foreground">
1208
+ {cancelledReason}
1209
+ </p>
1210
+ </div>
1211
+ )}
1212
+ <div
1213
+ className={cn(
1214
+ "aui-tool-fallback-args-root px-4",
1215
+ isCancelled && "opacity-60",
1216
+ )}
1217
+ >
1218
+ <pre className="aui-tool-fallback-args-value whitespace-pre-wrap">
1219
+ {argsText}
1220
+ </pre>
1221
+ </div>
1222
+ {!isCancelled && result !== undefined && (
1223
+ <div className="aui-tool-fallback-result-root border-t border-dashed px-4 pt-2">
1224
+ <p className="aui-tool-fallback-result-header font-semibold">
1225
+ Result:
1226
+ </p>
1227
+ <pre className="aui-tool-fallback-result-content whitespace-pre-wrap">
1228
+ {typeof result === "string"
1229
+ ? result
1230
+ : JSON.stringify(result, null, 2)}
1231
+ </pre>
1232
+ </div>
1233
+ )}
1234
+ </div>
1235
+ )}
1236
+ </div>
1237
+ );
1238
+ };
1239
+
1240
+ ```
1241
+
867
1242
  ## components/assistant-ui/tooltip-icon-button.tsx
868
1243
 
869
1244
  ```tsx
870
1245
  "use client";
871
1246
 
872
- import { ComponentPropsWithoutRef, forwardRef } from "react";
1247
+ import { ComponentPropsWithRef, forwardRef } from "react";
873
1248
  import { Slottable } from "@radix-ui/react-slot";
874
1249
 
875
1250
  import {
@@ -880,7 +1255,7 @@ import {
880
1255
  import { Button } from "@/components/ui/button";
881
1256
  import { cn } from "@/lib/utils";
882
1257
 
883
- export type TooltipIconButtonProps = ComponentPropsWithoutRef<typeof Button> & {
1258
+ export type TooltipIconButtonProps = ComponentPropsWithRef<typeof Button> & {
884
1259
  tooltip: string;
885
1260
  side?: "top" | "bottom" | "left" | "right";
886
1261
  };
@@ -896,11 +1271,11 @@ export const TooltipIconButton = forwardRef<
896
1271
  variant="ghost"
897
1272
  size="icon"
898
1273
  {...rest}
899
- className={cn("size-6 p-1", className)}
1274
+ className={cn("aui-button-icon size-6 p-1", className)}
900
1275
  ref={ref}
901
1276
  >
902
1277
  <Slottable>{children}</Slottable>
903
- <span className="sr-only">{tooltip}</span>
1278
+ <span className="aui-sr-only sr-only">{tooltip}</span>
904
1279
  </Button>
905
1280
  </TooltipTrigger>
906
1281
  <TooltipContent side={side}>{tooltip}</TooltipContent>
@@ -912,6 +1287,62 @@ TooltipIconButton.displayName = "TooltipIconButton";
912
1287
 
913
1288
  ```
914
1289
 
1290
+ ## components/ui/avatar.tsx
1291
+
1292
+ ```tsx
1293
+ "use client";
1294
+
1295
+ import * as React from "react";
1296
+ import * as AvatarPrimitive from "@radix-ui/react-avatar";
1297
+
1298
+ import { cn } from "@/lib/utils";
1299
+
1300
+ const Avatar = React.forwardRef<
1301
+ React.ElementRef<typeof AvatarPrimitive.Root>,
1302
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
1303
+ >(({ className, ...props }, ref) => (
1304
+ <AvatarPrimitive.Root
1305
+ ref={ref}
1306
+ className={cn(
1307
+ "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
1308
+ className,
1309
+ )}
1310
+ {...props}
1311
+ />
1312
+ ));
1313
+ Avatar.displayName = AvatarPrimitive.Root.displayName;
1314
+
1315
+ const AvatarImage = React.forwardRef<
1316
+ React.ElementRef<typeof AvatarPrimitive.Image>,
1317
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
1318
+ >(({ className, ...props }, ref) => (
1319
+ <AvatarPrimitive.Image
1320
+ ref={ref}
1321
+ className={cn("aspect-square h-full w-full", className)}
1322
+ {...props}
1323
+ />
1324
+ ));
1325
+ AvatarImage.displayName = AvatarPrimitive.Image.displayName;
1326
+
1327
+ const AvatarFallback = React.forwardRef<
1328
+ React.ElementRef<typeof AvatarPrimitive.Fallback>,
1329
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
1330
+ >(({ className, ...props }, ref) => (
1331
+ <AvatarPrimitive.Fallback
1332
+ ref={ref}
1333
+ className={cn(
1334
+ "bg-muted flex h-full w-full items-center justify-center rounded-full",
1335
+ className,
1336
+ )}
1337
+ {...props}
1338
+ />
1339
+ ));
1340
+ AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
1341
+
1342
+ export { Avatar, AvatarImage, AvatarFallback };
1343
+
1344
+ ```
1345
+
915
1346
  ## components/ui/button.tsx
916
1347
 
917
1348
  ```tsx
@@ -977,6 +1408,166 @@ export { Button, buttonVariants };
977
1408
 
978
1409
  ```
979
1410
 
1411
+ ## components/ui/dialog.tsx
1412
+
1413
+ ```tsx
1414
+ "use client";
1415
+
1416
+ import * as React from "react";
1417
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
1418
+ import { XIcon } from "lucide-react";
1419
+
1420
+ import { cn } from "@/lib/utils";
1421
+
1422
+ function Dialog({
1423
+ ...props
1424
+ }: React.ComponentProps<typeof DialogPrimitive.Root>) {
1425
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />;
1426
+ }
1427
+
1428
+ function DialogTrigger({
1429
+ ...props
1430
+ }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
1431
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
1432
+ }
1433
+
1434
+ function DialogPortal({
1435
+ ...props
1436
+ }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
1437
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
1438
+ }
1439
+
1440
+ function DialogClose({
1441
+ ...props
1442
+ }: React.ComponentProps<typeof DialogPrimitive.Close>) {
1443
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
1444
+ }
1445
+
1446
+ function DialogOverlay({
1447
+ className,
1448
+ ...props
1449
+ }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
1450
+ return (
1451
+ <DialogPrimitive.Overlay
1452
+ data-slot="dialog-overlay"
1453
+ className={cn(
1454
+ "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80",
1455
+ className,
1456
+ )}
1457
+ {...props}
1458
+ />
1459
+ );
1460
+ }
1461
+
1462
+ function DialogContent({
1463
+ className,
1464
+ children,
1465
+ ...props
1466
+ }: React.ComponentProps<typeof DialogPrimitive.Content>) {
1467
+ return (
1468
+ <DialogPortal data-slot="dialog-portal">
1469
+ <DialogOverlay />
1470
+ <DialogPrimitive.Content
1471
+ data-slot="dialog-content"
1472
+ className={cn(
1473
+ "bg-background data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 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 p-6 shadow-lg duration-200 sm:max-w-lg",
1474
+ className,
1475
+ )}
1476
+ {...props}
1477
+ >
1478
+ {children}
1479
+ <DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4">
1480
+ <XIcon />
1481
+ <span className="sr-only">Close</span>
1482
+ </DialogPrimitive.Close>
1483
+ </DialogPrimitive.Content>
1484
+ </DialogPortal>
1485
+ );
1486
+ }
1487
+
1488
+ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
1489
+ return (
1490
+ <div
1491
+ data-slot="dialog-header"
1492
+ className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
1493
+ {...props}
1494
+ />
1495
+ );
1496
+ }
1497
+
1498
+ function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
1499
+ return (
1500
+ <div
1501
+ data-slot="dialog-footer"
1502
+ className={cn(
1503
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
1504
+ className,
1505
+ )}
1506
+ {...props}
1507
+ />
1508
+ );
1509
+ }
1510
+
1511
+ function DialogTitle({
1512
+ className,
1513
+ ...props
1514
+ }: React.ComponentProps<typeof DialogPrimitive.Title>) {
1515
+ return (
1516
+ <DialogPrimitive.Title
1517
+ data-slot="dialog-title"
1518
+ className={cn("text-lg leading-none font-semibold", className)}
1519
+ {...props}
1520
+ />
1521
+ );
1522
+ }
1523
+
1524
+ function DialogDescription({
1525
+ className,
1526
+ ...props
1527
+ }: React.ComponentProps<typeof DialogPrimitive.Description>) {
1528
+ return (
1529
+ <DialogPrimitive.Description
1530
+ data-slot="dialog-description"
1531
+ className={cn("text-muted-foreground text-sm", className)}
1532
+ {...props}
1533
+ />
1534
+ );
1535
+ }
1536
+
1537
+ export {
1538
+ Dialog,
1539
+ DialogClose,
1540
+ DialogContent,
1541
+ DialogDescription,
1542
+ DialogFooter,
1543
+ DialogHeader,
1544
+ DialogOverlay,
1545
+ DialogPortal,
1546
+ DialogTitle,
1547
+ DialogTrigger,
1548
+ };
1549
+
1550
+ ```
1551
+
1552
+ ## components/ui/skeleton.tsx
1553
+
1554
+ ```tsx
1555
+ import { cn } from "@/lib/utils";
1556
+
1557
+ function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
1558
+ return (
1559
+ <div
1560
+ data-slot="skeleton"
1561
+ className={cn("bg-accent animate-pulse rounded-md", className)}
1562
+ {...props}
1563
+ />
1564
+ );
1565
+ }
1566
+
1567
+ export { Skeleton };
1568
+
1569
+ ```
1570
+
980
1571
  ## components/ui/tooltip.tsx
981
1572
 
982
1573
  ```tsx
@@ -1083,24 +1674,28 @@ export default nextConfig;
1083
1674
  "lint": "eslint ."
1084
1675
  },
1085
1676
  "dependencies": {
1086
- "@ai-sdk/openai": "^2.0.60",
1677
+ "@ai-sdk/openai": "^2.0.73",
1087
1678
  "@assistant-ui/react": "workspace:*",
1088
1679
  "@assistant-ui/react-ai-sdk": "workspace:*",
1089
1680
  "@assistant-ui/react-markdown": "workspace:*",
1090
- "@radix-ui/react-slot": "^1.2.3",
1681
+ "@radix-ui/react-avatar": "^1.1.4",
1682
+ "@radix-ui/react-dialog": "^1.1.7",
1683
+ "@radix-ui/react-slot": "^1.2.4",
1091
1684
  "@radix-ui/react-tooltip": "^1.2.8",
1092
- "ai": "^5.0.86",
1685
+ "ai": "^5.0.102",
1093
1686
  "class-variance-authority": "^0.7.1",
1094
1687
  "clsx": "^2.1.1",
1095
1688
  "jsonwebtoken": "^9.0.2",
1096
- "lucide-react": "^0.552.0",
1689
+ "lucide-react": "^0.555.0",
1690
+ "motion": "^11.18.2",
1097
1691
  "nanoid": "5.1.6",
1098
- "next": "16.0.1",
1692
+ "next": "16.0.4",
1099
1693
  "react": "19.2.0",
1100
1694
  "react-dom": "19.2.0",
1101
1695
  "remark-gfm": "^4.0.1",
1102
- "tailwind-merge": "^3.3.1",
1103
- "tw-animate-css": "^1.4.0"
1696
+ "tailwind-merge": "^3.4.0",
1697
+ "tw-animate-css": "^1.4.0",
1698
+ "zustand": "^5.0.8"
1104
1699
  },
1105
1700
  "devDependencies": {
1106
1701
  "@assistant-ui/x-buildutils": "workspace:*",
@@ -1109,7 +1704,7 @@ export default nextConfig;
1109
1704
  "@types/react": "^19",
1110
1705
  "@types/react-dom": "^19",
1111
1706
  "postcss": "^8",
1112
- "tailwindcss": "^4.1.16",
1707
+ "tailwindcss": "^4.1.17",
1113
1708
  "typescript": "^5"
1114
1709
  }
1115
1710
  }