@assistant-ui/mcp-docs-server 0.1.14 → 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.
@@ -335,6 +335,247 @@ export default function Home() {
335
335
 
336
336
  ```
337
337
 
338
+ ## components/assistant-ui/attachment.tsx
339
+
340
+ ```tsx
341
+ "use client";
342
+
343
+ import { PropsWithChildren, useEffect, useState, type FC } from "react";
344
+ import Image from "next/image";
345
+ import { XIcon, PlusIcon, FileText } from "lucide-react";
346
+ import {
347
+ AttachmentPrimitive,
348
+ ComposerPrimitive,
349
+ MessagePrimitive,
350
+ useAssistantState,
351
+ useAssistantApi,
352
+ } from "@assistant-ui/react";
353
+ import { useShallow } from "zustand/shallow";
354
+ import {
355
+ Tooltip,
356
+ TooltipContent,
357
+ TooltipTrigger,
358
+ } from "@/components/ui/tooltip";
359
+ import {
360
+ Dialog,
361
+ DialogTitle,
362
+ DialogContent,
363
+ DialogTrigger,
364
+ } from "@/components/ui/dialog";
365
+ import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
366
+ import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
367
+ import { cn } from "@/lib/utils";
368
+
369
+ const useFileSrc = (file: File | undefined) => {
370
+ const [src, setSrc] = useState<string | undefined>(undefined);
371
+
372
+ useEffect(() => {
373
+ if (!file) {
374
+ setSrc(undefined);
375
+ return;
376
+ }
377
+
378
+ const objectUrl = URL.createObjectURL(file);
379
+ setSrc(objectUrl);
380
+
381
+ return () => {
382
+ URL.revokeObjectURL(objectUrl);
383
+ };
384
+ }, [file]);
385
+
386
+ return src;
387
+ };
388
+
389
+ const useAttachmentSrc = () => {
390
+ const { file, src } = useAssistantState(
391
+ useShallow(({ attachment }): { file?: File; src?: string } => {
392
+ if (attachment.type !== "image") return {};
393
+ if (attachment.file) return { file: attachment.file };
394
+ const src = attachment.content?.filter((c) => c.type === "image")[0]
395
+ ?.image;
396
+ if (!src) return {};
397
+ return { src };
398
+ }),
399
+ );
400
+
401
+ return useFileSrc(file) ?? src;
402
+ };
403
+
404
+ type AttachmentPreviewProps = {
405
+ src: string;
406
+ };
407
+
408
+ const AttachmentPreview: FC<AttachmentPreviewProps> = ({ src }) => {
409
+ const [isLoaded, setIsLoaded] = useState(false);
410
+ return (
411
+ <Image
412
+ src={src}
413
+ alt="Image Preview"
414
+ width={1}
415
+ height={1}
416
+ className={
417
+ isLoaded
418
+ ? "aui-attachment-preview-image-loaded block h-auto max-h-[80vh] w-auto max-w-full object-contain"
419
+ : "aui-attachment-preview-image-loading hidden"
420
+ }
421
+ onLoadingComplete={() => setIsLoaded(true)}
422
+ priority={false}
423
+ />
424
+ );
425
+ };
426
+
427
+ const AttachmentPreviewDialog: FC<PropsWithChildren> = ({ children }) => {
428
+ const src = useAttachmentSrc();
429
+
430
+ if (!src) return children;
431
+
432
+ return (
433
+ <Dialog>
434
+ <DialogTrigger
435
+ className="aui-attachment-preview-trigger hover:bg-accent/50 cursor-pointer transition-colors"
436
+ asChild
437
+ >
438
+ {children}
439
+ </DialogTrigger>
440
+ <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">
441
+ <DialogTitle className="aui-sr-only sr-only">
442
+ Image Attachment Preview
443
+ </DialogTitle>
444
+ <div className="aui-attachment-preview bg-background relative mx-auto flex max-h-[80dvh] w-full items-center justify-center overflow-hidden">
445
+ <AttachmentPreview src={src} />
446
+ </div>
447
+ </DialogContent>
448
+ </Dialog>
449
+ );
450
+ };
451
+
452
+ const AttachmentThumb: FC = () => {
453
+ const isImage = useAssistantState(
454
+ ({ attachment }) => attachment.type === "image",
455
+ );
456
+ const src = useAttachmentSrc();
457
+
458
+ return (
459
+ <Avatar className="aui-attachment-tile-avatar h-full w-full rounded-none">
460
+ <AvatarImage
461
+ src={src}
462
+ alt="Attachment preview"
463
+ className="aui-attachment-tile-image object-cover"
464
+ />
465
+ <AvatarFallback delayMs={isImage ? 200 : 0}>
466
+ <FileText className="aui-attachment-tile-fallback-icon text-muted-foreground size-8" />
467
+ </AvatarFallback>
468
+ </Avatar>
469
+ );
470
+ };
471
+
472
+ const AttachmentUI: FC = () => {
473
+ const api = useAssistantApi();
474
+ const isComposer = api.attachment.source === "composer";
475
+
476
+ const isImage = useAssistantState(
477
+ ({ attachment }) => attachment.type === "image",
478
+ );
479
+ const typeLabel = useAssistantState(({ attachment }) => {
480
+ const type = attachment.type;
481
+ switch (type) {
482
+ case "image":
483
+ return "Image";
484
+ case "document":
485
+ return "Document";
486
+ case "file":
487
+ return "File";
488
+ default:
489
+ const _exhaustiveCheck: never = type;
490
+ throw new Error(`Unknown attachment type: ${_exhaustiveCheck}`);
491
+ }
492
+ });
493
+
494
+ return (
495
+ <Tooltip>
496
+ <AttachmentPrimitive.Root
497
+ className={cn(
498
+ "aui-attachment-root relative",
499
+ isImage &&
500
+ "aui-attachment-root-composer only:[&>#attachment-tile]:size-24",
501
+ )}
502
+ >
503
+ <AttachmentPreviewDialog>
504
+ <TooltipTrigger asChild>
505
+ <div
506
+ className={cn(
507
+ "aui-attachment-tile bg-muted size-14 cursor-pointer overflow-hidden rounded-[14px] border transition-opacity hover:opacity-75",
508
+ isComposer &&
509
+ "aui-attachment-tile-composer border-foreground/20",
510
+ )}
511
+ role="button"
512
+ id="attachment-tile"
513
+ aria-label={`${typeLabel} attachment`}
514
+ >
515
+ <AttachmentThumb />
516
+ </div>
517
+ </TooltipTrigger>
518
+ </AttachmentPreviewDialog>
519
+ {isComposer && <AttachmentRemove />}
520
+ </AttachmentPrimitive.Root>
521
+ <TooltipContent side="top">
522
+ <AttachmentPrimitive.Name />
523
+ </TooltipContent>
524
+ </Tooltip>
525
+ );
526
+ };
527
+
528
+ const AttachmentRemove: FC = () => {
529
+ return (
530
+ <AttachmentPrimitive.Remove asChild>
531
+ <TooltipIconButton
532
+ tooltip="Remove file"
533
+ 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"
534
+ side="top"
535
+ >
536
+ <XIcon className="aui-attachment-remove-icon size-3 dark:stroke-[2.5px]" />
537
+ </TooltipIconButton>
538
+ </AttachmentPrimitive.Remove>
539
+ );
540
+ };
541
+
542
+ export const UserMessageAttachments: FC = () => {
543
+ return (
544
+ <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">
545
+ <MessagePrimitive.Attachments components={{ Attachment: AttachmentUI }} />
546
+ </div>
547
+ );
548
+ };
549
+
550
+ export const ComposerAttachments: FC = () => {
551
+ return (
552
+ <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">
553
+ <ComposerPrimitive.Attachments
554
+ components={{ Attachment: AttachmentUI }}
555
+ />
556
+ </div>
557
+ );
558
+ };
559
+
560
+ export const ComposerAddAttachment: FC = () => {
561
+ return (
562
+ <ComposerPrimitive.AddAttachment asChild>
563
+ <TooltipIconButton
564
+ tooltip="Add Attachment"
565
+ side="bottom"
566
+ variant="ghost"
567
+ size="icon"
568
+ 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"
569
+ aria-label="Add Attachment"
570
+ >
571
+ <PlusIcon className="aui-attachment-add-icon size-5 stroke-[1.5px]" />
572
+ </TooltipIconButton>
573
+ </ComposerPrimitive.AddAttachment>
574
+ );
575
+ };
576
+
577
+ ```
578
+
338
579
  ## components/assistant-ui/markdown-text.tsx
339
580
 
340
581
  ```tsx
@@ -343,13 +584,13 @@ export default function Home() {
343
584
  import "@assistant-ui/react-markdown/styles/dot.css";
344
585
 
345
586
  import {
346
- CodeHeaderProps,
587
+ type CodeHeaderProps,
347
588
  MarkdownTextPrimitive,
348
589
  unstable_memoizeMarkdownComponents as memoizeMarkdownComponents,
349
590
  useIsMarkdownCodeBlock,
350
591
  } from "@assistant-ui/react-markdown";
351
592
  import remarkGfm from "remark-gfm";
352
- import { FC, memo, useState } from "react";
593
+ import { type FC, memo, useState } from "react";
353
594
  import { CheckIcon, CopyIcon } from "lucide-react";
354
595
 
355
596
  import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
@@ -375,8 +616,10 @@ const CodeHeader: FC<CodeHeaderProps> = ({ language, code }) => {
375
616
  };
376
617
 
377
618
  return (
378
- <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">
379
- <span className="lowercase [&>span]:text-xs">{language}</span>
619
+ <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">
620
+ <span className="aui-code-header-language lowercase [&>span]:text-xs">
621
+ {language}
622
+ </span>
380
623
  <TooltipIconButton tooltip="Copy" onClick={onCopy}>
381
624
  {!isCopied && <CopyIcon />}
382
625
  {isCopied && <CheckIcon />}
@@ -408,7 +651,7 @@ const defaultComponents = memoizeMarkdownComponents({
408
651
  h1: ({ className, ...props }) => (
409
652
  <h1
410
653
  className={cn(
411
- "mb-8 scroll-m-20 text-4xl font-extrabold tracking-tight last:mb-0",
654
+ "aui-md-h1 mb-8 scroll-m-20 text-4xl font-extrabold tracking-tight last:mb-0",
412
655
  className,
413
656
  )}
414
657
  {...props}
@@ -417,7 +660,7 @@ const defaultComponents = memoizeMarkdownComponents({
417
660
  h2: ({ className, ...props }) => (
418
661
  <h2
419
662
  className={cn(
420
- "mt-8 mb-4 scroll-m-20 text-3xl font-semibold tracking-tight first:mt-0 last:mb-0",
663
+ "aui-md-h2 mt-8 mb-4 scroll-m-20 text-3xl font-semibold tracking-tight first:mt-0 last:mb-0",
421
664
  className,
422
665
  )}
423
666
  {...props}
@@ -426,7 +669,7 @@ const defaultComponents = memoizeMarkdownComponents({
426
669
  h3: ({ className, ...props }) => (
427
670
  <h3
428
671
  className={cn(
429
- "mt-6 mb-4 scroll-m-20 text-2xl font-semibold tracking-tight first:mt-0 last:mb-0",
672
+ "aui-md-h3 mt-6 mb-4 scroll-m-20 text-2xl font-semibold tracking-tight first:mt-0 last:mb-0",
430
673
  className,
431
674
  )}
432
675
  {...props}
@@ -435,7 +678,7 @@ const defaultComponents = memoizeMarkdownComponents({
435
678
  h4: ({ className, ...props }) => (
436
679
  <h4
437
680
  className={cn(
438
- "mt-6 mb-4 scroll-m-20 text-xl font-semibold tracking-tight first:mt-0 last:mb-0",
681
+ "aui-md-h4 mt-6 mb-4 scroll-m-20 text-xl font-semibold tracking-tight first:mt-0 last:mb-0",
439
682
  className,
440
683
  )}
441
684
  {...props}
@@ -444,7 +687,7 @@ const defaultComponents = memoizeMarkdownComponents({
444
687
  h5: ({ className, ...props }) => (
445
688
  <h5
446
689
  className={cn(
447
- "my-4 text-lg font-semibold first:mt-0 last:mb-0",
690
+ "aui-md-h5 my-4 text-lg font-semibold first:mt-0 last:mb-0",
448
691
  className,
449
692
  )}
450
693
  {...props}
@@ -452,20 +695,26 @@ const defaultComponents = memoizeMarkdownComponents({
452
695
  ),
453
696
  h6: ({ className, ...props }) => (
454
697
  <h6
455
- className={cn("my-4 font-semibold first:mt-0 last:mb-0", className)}
698
+ className={cn(
699
+ "aui-md-h6 my-4 font-semibold first:mt-0 last:mb-0",
700
+ className,
701
+ )}
456
702
  {...props}
457
703
  />
458
704
  ),
459
705
  p: ({ className, ...props }) => (
460
706
  <p
461
- className={cn("mt-5 mb-5 leading-7 first:mt-0 last:mb-0", className)}
707
+ className={cn(
708
+ "aui-md-p mt-5 mb-5 leading-7 first:mt-0 last:mb-0",
709
+ className,
710
+ )}
462
711
  {...props}
463
712
  />
464
713
  ),
465
714
  a: ({ className, ...props }) => (
466
715
  <a
467
716
  className={cn(
468
- "text-primary font-medium underline underline-offset-4",
717
+ "aui-md-a text-primary font-medium underline underline-offset-4",
469
718
  className,
470
719
  )}
471
720
  {...props}
@@ -473,29 +722,29 @@ const defaultComponents = memoizeMarkdownComponents({
473
722
  ),
474
723
  blockquote: ({ className, ...props }) => (
475
724
  <blockquote
476
- className={cn("border-l-2 pl-6 italic", className)}
725
+ className={cn("aui-md-blockquote border-l-2 pl-6 italic", className)}
477
726
  {...props}
478
727
  />
479
728
  ),
480
729
  ul: ({ className, ...props }) => (
481
730
  <ul
482
- className={cn("my-5 ml-6 list-disc [&>li]:mt-2", className)}
731
+ className={cn("aui-md-ul my-5 ml-6 list-disc [&>li]:mt-2", className)}
483
732
  {...props}
484
733
  />
485
734
  ),
486
735
  ol: ({ className, ...props }) => (
487
736
  <ol
488
- className={cn("my-5 ml-6 list-decimal [&>li]:mt-2", className)}
737
+ className={cn("aui-md-ol my-5 ml-6 list-decimal [&>li]:mt-2", className)}
489
738
  {...props}
490
739
  />
491
740
  ),
492
741
  hr: ({ className, ...props }) => (
493
- <hr className={cn("my-5 border-b", className)} {...props} />
742
+ <hr className={cn("aui-md-hr my-5 border-b", className)} {...props} />
494
743
  ),
495
744
  table: ({ className, ...props }) => (
496
745
  <table
497
746
  className={cn(
498
- "my-5 w-full border-separate border-spacing-0 overflow-y-auto",
747
+ "aui-md-table my-5 w-full border-separate border-spacing-0 overflow-y-auto",
499
748
  className,
500
749
  )}
501
750
  {...props}
@@ -504,7 +753,7 @@ const defaultComponents = memoizeMarkdownComponents({
504
753
  th: ({ className, ...props }) => (
505
754
  <th
506
755
  className={cn(
507
- "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",
756
+ "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",
508
757
  className,
509
758
  )}
510
759
  {...props}
@@ -513,7 +762,7 @@ const defaultComponents = memoizeMarkdownComponents({
513
762
  td: ({ className, ...props }) => (
514
763
  <td
515
764
  className={cn(
516
- "border-b border-l px-4 py-2 text-left last:border-r [&[align=center]]:text-center [&[align=right]]:text-right",
765
+ "aui-md-td border-b border-l px-4 py-2 text-left last:border-r [&[align=center]]:text-center [&[align=right]]:text-right",
517
766
  className,
518
767
  )}
519
768
  {...props}
@@ -522,7 +771,7 @@ const defaultComponents = memoizeMarkdownComponents({
522
771
  tr: ({ className, ...props }) => (
523
772
  <tr
524
773
  className={cn(
525
- "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",
774
+ "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",
526
775
  className,
527
776
  )}
528
777
  {...props}
@@ -530,14 +779,14 @@ const defaultComponents = memoizeMarkdownComponents({
530
779
  ),
531
780
  sup: ({ className, ...props }) => (
532
781
  <sup
533
- className={cn("[&>a]:text-xs [&>a]:no-underline", className)}
782
+ className={cn("aui-md-sup [&>a]:text-xs [&>a]:no-underline", className)}
534
783
  {...props}
535
784
  />
536
785
  ),
537
786
  pre: ({ className, ...props }) => (
538
787
  <pre
539
788
  className={cn(
540
- "overflow-x-auto rounded-b-lg bg-black p-4 text-white",
789
+ "aui-md-pre overflow-x-auto !rounded-t-none rounded-b-lg bg-black p-4 text-white",
541
790
  className,
542
791
  )}
543
792
  {...props}
@@ -548,7 +797,8 @@ const defaultComponents = memoizeMarkdownComponents({
548
797
  return (
549
798
  <code
550
799
  className={cn(
551
- !isCodeBlock && "bg-muted rounded border font-semibold",
800
+ !isCodeBlock &&
801
+ "aui-md-inline-code bg-muted rounded border font-semibold",
552
802
  className,
553
803
  )}
554
804
  {...props}
@@ -567,15 +817,17 @@ import type { FC } from "react";
567
817
  import {
568
818
  ThreadListItemPrimitive,
569
819
  ThreadListPrimitive,
820
+ useAssistantState,
570
821
  } from "@assistant-ui/react";
571
822
  import { ArchiveIcon, PlusIcon } from "lucide-react";
572
823
 
573
824
  import { Button } from "@/components/ui/button";
574
825
  import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
826
+ import { Skeleton } from "@/components/ui/skeleton";
575
827
 
576
828
  export const ThreadList: FC = () => {
577
829
  return (
578
- <ThreadListPrimitive.Root className="flex flex-col items-stretch gap-1.5">
830
+ <ThreadListPrimitive.Root className="aui-root aui-thread-list-root flex flex-col items-stretch gap-1.5">
579
831
  <ThreadListNew />
580
832
  <ThreadListItems />
581
833
  </ThreadListPrimitive.Root>
@@ -586,7 +838,7 @@ const ThreadListNew: FC = () => {
586
838
  return (
587
839
  <ThreadListPrimitive.New asChild>
588
840
  <Button
589
- className="data-[active]:bg-muted hover:bg-muted flex items-center justify-start gap-1 rounded-lg px-2.5 py-2 text-start"
841
+ 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"
590
842
  variant="ghost"
591
843
  >
592
844
  <PlusIcon />
@@ -597,13 +849,37 @@ const ThreadListNew: FC = () => {
597
849
  };
598
850
 
599
851
  const ThreadListItems: FC = () => {
852
+ const isLoading = useAssistantState(({ threads }) => threads.isLoading);
853
+
854
+ if (isLoading) {
855
+ return <ThreadListSkeleton />;
856
+ }
857
+
600
858
  return <ThreadListPrimitive.Items components={{ ThreadListItem }} />;
601
859
  };
602
860
 
861
+ const ThreadListSkeleton: FC = () => {
862
+ return (
863
+ <>
864
+ {Array.from({ length: 5 }, (_, i) => (
865
+ <div
866
+ key={i}
867
+ role="status"
868
+ aria-label="Loading threads"
869
+ aria-live="polite"
870
+ className="aui-thread-list-skeleton-wrapper flex items-center gap-2 rounded-md px-3 py-2"
871
+ >
872
+ <Skeleton className="aui-thread-list-skeleton h-[22px] flex-grow" />
873
+ </div>
874
+ ))}
875
+ </>
876
+ );
877
+ };
878
+
603
879
  const ThreadListItem: FC = () => {
604
880
  return (
605
- <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">
606
- <ThreadListItemPrimitive.Trigger className="flex-grow px-3 py-2 text-start">
881
+ <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">
882
+ <ThreadListItemPrimitive.Trigger className="aui-thread-list-item-trigger flex-grow px-3 py-2 text-start">
607
883
  <ThreadListItemTitle />
608
884
  </ThreadListItemPrimitive.Trigger>
609
885
  <ThreadListItemArchive />
@@ -613,9 +889,9 @@ const ThreadListItem: FC = () => {
613
889
 
614
890
  const ThreadListItemTitle: FC = () => {
615
891
  return (
616
- <p className="text-sm">
892
+ <span className="aui-thread-list-item-title text-sm">
617
893
  <ThreadListItemPrimitive.Title fallback="New Chat" />
618
- </p>
894
+ </span>
619
895
  );
620
896
  };
621
897
 
@@ -623,7 +899,7 @@ const ThreadListItemArchive: FC = () => {
623
899
  return (
624
900
  <ThreadListItemPrimitive.Archive asChild>
625
901
  <TooltipIconButton
626
- className="hover:text-primary text-foreground mr-3 ml-auto size-4 p-0"
902
+ className="aui-thread-list-item-archive text-foreground hover:text-primary mr-3 ml-auto size-4 p-0"
627
903
  variant="ghost"
628
904
  tooltip="Archive thread"
629
905
  >
@@ -925,12 +1201,111 @@ const CircleStopIcon = () => {
925
1201
 
926
1202
  ```
927
1203
 
1204
+ ## components/assistant-ui/tool-fallback.tsx
1205
+
1206
+ ```tsx
1207
+ import type { ToolCallMessagePartComponent } from "@assistant-ui/react";
1208
+ import {
1209
+ CheckIcon,
1210
+ ChevronDownIcon,
1211
+ ChevronUpIcon,
1212
+ XCircleIcon,
1213
+ } from "lucide-react";
1214
+ import { useState } from "react";
1215
+ import { Button } from "@/components/ui/button";
1216
+ import { cn } from "@/lib/utils";
1217
+
1218
+ export const ToolFallback: ToolCallMessagePartComponent = ({
1219
+ toolName,
1220
+ argsText,
1221
+ result,
1222
+ status,
1223
+ }) => {
1224
+ const [isCollapsed, setIsCollapsed] = useState(true);
1225
+
1226
+ const isCancelled =
1227
+ status?.type === "incomplete" && status.reason === "cancelled";
1228
+ const cancelledReason =
1229
+ isCancelled && status.error
1230
+ ? typeof status.error === "string"
1231
+ ? status.error
1232
+ : JSON.stringify(status.error)
1233
+ : null;
1234
+
1235
+ return (
1236
+ <div
1237
+ className={cn(
1238
+ "aui-tool-fallback-root mb-4 flex w-full flex-col gap-3 rounded-lg border py-3",
1239
+ isCancelled && "border-muted-foreground/30 bg-muted/30",
1240
+ )}
1241
+ >
1242
+ <div className="aui-tool-fallback-header flex items-center gap-2 px-4">
1243
+ {isCancelled ? (
1244
+ <XCircleIcon className="aui-tool-fallback-icon text-muted-foreground size-4" />
1245
+ ) : (
1246
+ <CheckIcon className="aui-tool-fallback-icon size-4" />
1247
+ )}
1248
+ <p
1249
+ className={cn(
1250
+ "aui-tool-fallback-title grow",
1251
+ isCancelled && "text-muted-foreground line-through",
1252
+ )}
1253
+ >
1254
+ {isCancelled ? "Cancelled tool: " : "Used tool: "}
1255
+ <b>{toolName}</b>
1256
+ </p>
1257
+ <Button onClick={() => setIsCollapsed(!isCollapsed)}>
1258
+ {isCollapsed ? <ChevronUpIcon /> : <ChevronDownIcon />}
1259
+ </Button>
1260
+ </div>
1261
+ {!isCollapsed && (
1262
+ <div className="aui-tool-fallback-content flex flex-col gap-2 border-t pt-2">
1263
+ {cancelledReason && (
1264
+ <div className="aui-tool-fallback-cancelled-root px-4">
1265
+ <p className="aui-tool-fallback-cancelled-header text-muted-foreground font-semibold">
1266
+ Cancelled reason:
1267
+ </p>
1268
+ <p className="aui-tool-fallback-cancelled-reason text-muted-foreground">
1269
+ {cancelledReason}
1270
+ </p>
1271
+ </div>
1272
+ )}
1273
+ <div
1274
+ className={cn(
1275
+ "aui-tool-fallback-args-root px-4",
1276
+ isCancelled && "opacity-60",
1277
+ )}
1278
+ >
1279
+ <pre className="aui-tool-fallback-args-value whitespace-pre-wrap">
1280
+ {argsText}
1281
+ </pre>
1282
+ </div>
1283
+ {!isCancelled && result !== undefined && (
1284
+ <div className="aui-tool-fallback-result-root border-t border-dashed px-4 pt-2">
1285
+ <p className="aui-tool-fallback-result-header font-semibold">
1286
+ Result:
1287
+ </p>
1288
+ <pre className="aui-tool-fallback-result-content whitespace-pre-wrap">
1289
+ {typeof result === "string"
1290
+ ? result
1291
+ : JSON.stringify(result, null, 2)}
1292
+ </pre>
1293
+ </div>
1294
+ )}
1295
+ </div>
1296
+ )}
1297
+ </div>
1298
+ );
1299
+ };
1300
+
1301
+ ```
1302
+
928
1303
  ## components/assistant-ui/tooltip-icon-button.tsx
929
1304
 
930
1305
  ```tsx
931
1306
  "use client";
932
1307
 
933
- import { ComponentPropsWithoutRef, forwardRef } from "react";
1308
+ import { ComponentPropsWithRef, forwardRef } from "react";
934
1309
  import { Slottable } from "@radix-ui/react-slot";
935
1310
 
936
1311
  import {
@@ -941,7 +1316,7 @@ import {
941
1316
  import { Button } from "@/components/ui/button";
942
1317
  import { cn } from "@/lib/utils";
943
1318
 
944
- export type TooltipIconButtonProps = ComponentPropsWithoutRef<typeof Button> & {
1319
+ export type TooltipIconButtonProps = ComponentPropsWithRef<typeof Button> & {
945
1320
  tooltip: string;
946
1321
  side?: "top" | "bottom" | "left" | "right";
947
1322
  };
@@ -957,11 +1332,11 @@ export const TooltipIconButton = forwardRef<
957
1332
  variant="ghost"
958
1333
  size="icon"
959
1334
  {...rest}
960
- className={cn("size-6 p-1", className)}
1335
+ className={cn("aui-button-icon size-6 p-1", className)}
961
1336
  ref={ref}
962
1337
  >
963
1338
  <Slottable>{children}</Slottable>
964
- <span className="sr-only">{tooltip}</span>
1339
+ <span className="aui-sr-only sr-only">{tooltip}</span>
965
1340
  </Button>
966
1341
  </TooltipTrigger>
967
1342
  <TooltipContent side={side}>{tooltip}</TooltipContent>
@@ -1311,6 +1686,62 @@ export const ToolFallback: ToolCallMessagePartComponent = ({
1311
1686
 
1312
1687
  ```
1313
1688
 
1689
+ ## components/ui/avatar.tsx
1690
+
1691
+ ```tsx
1692
+ "use client";
1693
+
1694
+ import * as React from "react";
1695
+ import * as AvatarPrimitive from "@radix-ui/react-avatar";
1696
+
1697
+ import { cn } from "@/lib/utils";
1698
+
1699
+ const Avatar = React.forwardRef<
1700
+ React.ElementRef<typeof AvatarPrimitive.Root>,
1701
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
1702
+ >(({ className, ...props }, ref) => (
1703
+ <AvatarPrimitive.Root
1704
+ ref={ref}
1705
+ className={cn(
1706
+ "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
1707
+ className,
1708
+ )}
1709
+ {...props}
1710
+ />
1711
+ ));
1712
+ Avatar.displayName = AvatarPrimitive.Root.displayName;
1713
+
1714
+ const AvatarImage = React.forwardRef<
1715
+ React.ElementRef<typeof AvatarPrimitive.Image>,
1716
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
1717
+ >(({ className, ...props }, ref) => (
1718
+ <AvatarPrimitive.Image
1719
+ ref={ref}
1720
+ className={cn("aspect-square h-full w-full", className)}
1721
+ {...props}
1722
+ />
1723
+ ));
1724
+ AvatarImage.displayName = AvatarPrimitive.Image.displayName;
1725
+
1726
+ const AvatarFallback = React.forwardRef<
1727
+ React.ElementRef<typeof AvatarPrimitive.Fallback>,
1728
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
1729
+ >(({ className, ...props }, ref) => (
1730
+ <AvatarPrimitive.Fallback
1731
+ ref={ref}
1732
+ className={cn(
1733
+ "bg-muted flex h-full w-full items-center justify-center rounded-full",
1734
+ className,
1735
+ )}
1736
+ {...props}
1737
+ />
1738
+ ));
1739
+ AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
1740
+
1741
+ export { Avatar, AvatarImage, AvatarFallback };
1742
+
1743
+ ```
1744
+
1314
1745
  ## components/ui/button.tsx
1315
1746
 
1316
1747
  ```tsx
@@ -1463,6 +1894,166 @@ export {
1463
1894
 
1464
1895
  ```
1465
1896
 
1897
+ ## components/ui/dialog.tsx
1898
+
1899
+ ```tsx
1900
+ "use client";
1901
+
1902
+ import * as React from "react";
1903
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
1904
+ import { XIcon } from "lucide-react";
1905
+
1906
+ import { cn } from "@/lib/utils";
1907
+
1908
+ function Dialog({
1909
+ ...props
1910
+ }: React.ComponentProps<typeof DialogPrimitive.Root>) {
1911
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />;
1912
+ }
1913
+
1914
+ function DialogTrigger({
1915
+ ...props
1916
+ }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
1917
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
1918
+ }
1919
+
1920
+ function DialogPortal({
1921
+ ...props
1922
+ }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
1923
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
1924
+ }
1925
+
1926
+ function DialogClose({
1927
+ ...props
1928
+ }: React.ComponentProps<typeof DialogPrimitive.Close>) {
1929
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
1930
+ }
1931
+
1932
+ function DialogOverlay({
1933
+ className,
1934
+ ...props
1935
+ }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
1936
+ return (
1937
+ <DialogPrimitive.Overlay
1938
+ data-slot="dialog-overlay"
1939
+ className={cn(
1940
+ "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",
1941
+ className,
1942
+ )}
1943
+ {...props}
1944
+ />
1945
+ );
1946
+ }
1947
+
1948
+ function DialogContent({
1949
+ className,
1950
+ children,
1951
+ ...props
1952
+ }: React.ComponentProps<typeof DialogPrimitive.Content>) {
1953
+ return (
1954
+ <DialogPortal data-slot="dialog-portal">
1955
+ <DialogOverlay />
1956
+ <DialogPrimitive.Content
1957
+ data-slot="dialog-content"
1958
+ className={cn(
1959
+ "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",
1960
+ className,
1961
+ )}
1962
+ {...props}
1963
+ >
1964
+ {children}
1965
+ <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">
1966
+ <XIcon />
1967
+ <span className="sr-only">Close</span>
1968
+ </DialogPrimitive.Close>
1969
+ </DialogPrimitive.Content>
1970
+ </DialogPortal>
1971
+ );
1972
+ }
1973
+
1974
+ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
1975
+ return (
1976
+ <div
1977
+ data-slot="dialog-header"
1978
+ className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
1979
+ {...props}
1980
+ />
1981
+ );
1982
+ }
1983
+
1984
+ function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
1985
+ return (
1986
+ <div
1987
+ data-slot="dialog-footer"
1988
+ className={cn(
1989
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
1990
+ className,
1991
+ )}
1992
+ {...props}
1993
+ />
1994
+ );
1995
+ }
1996
+
1997
+ function DialogTitle({
1998
+ className,
1999
+ ...props
2000
+ }: React.ComponentProps<typeof DialogPrimitive.Title>) {
2001
+ return (
2002
+ <DialogPrimitive.Title
2003
+ data-slot="dialog-title"
2004
+ className={cn("text-lg leading-none font-semibold", className)}
2005
+ {...props}
2006
+ />
2007
+ );
2008
+ }
2009
+
2010
+ function DialogDescription({
2011
+ className,
2012
+ ...props
2013
+ }: React.ComponentProps<typeof DialogPrimitive.Description>) {
2014
+ return (
2015
+ <DialogPrimitive.Description
2016
+ data-slot="dialog-description"
2017
+ className={cn("text-muted-foreground text-sm", className)}
2018
+ {...props}
2019
+ />
2020
+ );
2021
+ }
2022
+
2023
+ export {
2024
+ Dialog,
2025
+ DialogClose,
2026
+ DialogContent,
2027
+ DialogDescription,
2028
+ DialogFooter,
2029
+ DialogHeader,
2030
+ DialogOverlay,
2031
+ DialogPortal,
2032
+ DialogTitle,
2033
+ DialogTrigger,
2034
+ };
2035
+
2036
+ ```
2037
+
2038
+ ## components/ui/skeleton.tsx
2039
+
2040
+ ```tsx
2041
+ import { cn } from "@/lib/utils";
2042
+
2043
+ function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
2044
+ return (
2045
+ <div
2046
+ data-slot="skeleton"
2047
+ className={cn("bg-accent animate-pulse rounded-md", className)}
2048
+ {...props}
2049
+ />
2050
+ );
2051
+ }
2052
+
2053
+ export { Skeleton };
2054
+
2055
+ ```
2056
+
1466
2057
  ## components/ui/tooltip.tsx
1467
2058
 
1468
2059
  ```tsx
@@ -1619,18 +2210,6 @@ export function cn(...inputs: ClassValue[]) {
1619
2210
 
1620
2211
  ```
1621
2212
 
1622
- ## next-env.d.ts
1623
-
1624
- ```typescript
1625
- /// <reference types="next" />
1626
- /// <reference types="next/image-types/global" />
1627
- import "./.next/types/routes.d.ts";
1628
-
1629
- // NOTE: This file should not be edited
1630
- // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
1631
-
1632
- ```
1633
-
1634
2213
  ## next.config.ts
1635
2214
 
1636
2215
  ```typescript
@@ -1668,21 +2247,25 @@ export default nextConfig;
1668
2247
  "@assistant-ui/react": "workspace:*",
1669
2248
  "@assistant-ui/react-langgraph": "workspace:*",
1670
2249
  "@assistant-ui/react-markdown": "workspace:*",
1671
- "@langchain/langgraph-sdk": "^1.0.0",
2250
+ "@langchain/langgraph-sdk": "^1.0.2",
2251
+ "@radix-ui/react-avatar": "^1.1.4",
2252
+ "@radix-ui/react-dialog": "^1.1.7",
1672
2253
  "@radix-ui/react-slot": "^1.2.4",
1673
2254
  "@radix-ui/react-tooltip": "^1.2.8",
1674
2255
  "class-variance-authority": "^0.7.1",
1675
2256
  "clsx": "^2.1.1",
1676
2257
  "js-cookie": "^3.0.5",
1677
2258
  "jsonwebtoken": "^9.0.2",
1678
- "lucide-react": "^0.554.0",
2259
+ "lucide-react": "^0.555.0",
2260
+ "motion": "^11.18.2",
1679
2261
  "nanoid": "5.1.6",
1680
- "next": "16.0.3",
2262
+ "next": "16.0.4",
1681
2263
  "react": "19.2.0",
1682
2264
  "react-dom": "19.2.0",
1683
2265
  "remark-gfm": "^4.0.1",
1684
2266
  "tailwind-merge": "^3.4.0",
1685
- "tw-animate-css": "^1.4.0"
2267
+ "tw-animate-css": "^1.4.0",
2268
+ "zustand": "^5.0.8"
1686
2269
  },
1687
2270
  "devDependencies": {
1688
2271
  "@assistant-ui/x-buildutils": "workspace:*",