@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.
@@ -288,6 +288,247 @@ export default function Home() {
288
288
 
289
289
  ```
290
290
 
291
+ ## components/assistant-ui/attachment.tsx
292
+
293
+ ```tsx
294
+ "use client";
295
+
296
+ import { PropsWithChildren, useEffect, useState, type FC } from "react";
297
+ import Image from "next/image";
298
+ import { XIcon, PlusIcon, FileText } from "lucide-react";
299
+ import {
300
+ AttachmentPrimitive,
301
+ ComposerPrimitive,
302
+ MessagePrimitive,
303
+ useAssistantState,
304
+ useAssistantApi,
305
+ } from "@assistant-ui/react";
306
+ import { useShallow } from "zustand/shallow";
307
+ import {
308
+ Tooltip,
309
+ TooltipContent,
310
+ TooltipTrigger,
311
+ } from "@/components/ui/tooltip";
312
+ import {
313
+ Dialog,
314
+ DialogTitle,
315
+ DialogContent,
316
+ DialogTrigger,
317
+ } from "@/components/ui/dialog";
318
+ import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
319
+ import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
320
+ import { cn } from "@/lib/utils";
321
+
322
+ const useFileSrc = (file: File | undefined) => {
323
+ const [src, setSrc] = useState<string | undefined>(undefined);
324
+
325
+ useEffect(() => {
326
+ if (!file) {
327
+ setSrc(undefined);
328
+ return;
329
+ }
330
+
331
+ const objectUrl = URL.createObjectURL(file);
332
+ setSrc(objectUrl);
333
+
334
+ return () => {
335
+ URL.revokeObjectURL(objectUrl);
336
+ };
337
+ }, [file]);
338
+
339
+ return src;
340
+ };
341
+
342
+ const useAttachmentSrc = () => {
343
+ const { file, src } = useAssistantState(
344
+ useShallow(({ attachment }): { file?: File; src?: string } => {
345
+ if (attachment.type !== "image") return {};
346
+ if (attachment.file) return { file: attachment.file };
347
+ const src = attachment.content?.filter((c) => c.type === "image")[0]
348
+ ?.image;
349
+ if (!src) return {};
350
+ return { src };
351
+ }),
352
+ );
353
+
354
+ return useFileSrc(file) ?? src;
355
+ };
356
+
357
+ type AttachmentPreviewProps = {
358
+ src: string;
359
+ };
360
+
361
+ const AttachmentPreview: FC<AttachmentPreviewProps> = ({ src }) => {
362
+ const [isLoaded, setIsLoaded] = useState(false);
363
+ return (
364
+ <Image
365
+ src={src}
366
+ alt="Image Preview"
367
+ width={1}
368
+ height={1}
369
+ className={
370
+ isLoaded
371
+ ? "aui-attachment-preview-image-loaded block h-auto max-h-[80vh] w-auto max-w-full object-contain"
372
+ : "aui-attachment-preview-image-loading hidden"
373
+ }
374
+ onLoadingComplete={() => setIsLoaded(true)}
375
+ priority={false}
376
+ />
377
+ );
378
+ };
379
+
380
+ const AttachmentPreviewDialog: FC<PropsWithChildren> = ({ children }) => {
381
+ const src = useAttachmentSrc();
382
+
383
+ if (!src) return children;
384
+
385
+ return (
386
+ <Dialog>
387
+ <DialogTrigger
388
+ className="aui-attachment-preview-trigger hover:bg-accent/50 cursor-pointer transition-colors"
389
+ asChild
390
+ >
391
+ {children}
392
+ </DialogTrigger>
393
+ <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">
394
+ <DialogTitle className="aui-sr-only sr-only">
395
+ Image Attachment Preview
396
+ </DialogTitle>
397
+ <div className="aui-attachment-preview bg-background relative mx-auto flex max-h-[80dvh] w-full items-center justify-center overflow-hidden">
398
+ <AttachmentPreview src={src} />
399
+ </div>
400
+ </DialogContent>
401
+ </Dialog>
402
+ );
403
+ };
404
+
405
+ const AttachmentThumb: FC = () => {
406
+ const isImage = useAssistantState(
407
+ ({ attachment }) => attachment.type === "image",
408
+ );
409
+ const src = useAttachmentSrc();
410
+
411
+ return (
412
+ <Avatar className="aui-attachment-tile-avatar h-full w-full rounded-none">
413
+ <AvatarImage
414
+ src={src}
415
+ alt="Attachment preview"
416
+ className="aui-attachment-tile-image object-cover"
417
+ />
418
+ <AvatarFallback delayMs={isImage ? 200 : 0}>
419
+ <FileText className="aui-attachment-tile-fallback-icon text-muted-foreground size-8" />
420
+ </AvatarFallback>
421
+ </Avatar>
422
+ );
423
+ };
424
+
425
+ const AttachmentUI: FC = () => {
426
+ const api = useAssistantApi();
427
+ const isComposer = api.attachment.source === "composer";
428
+
429
+ const isImage = useAssistantState(
430
+ ({ attachment }) => attachment.type === "image",
431
+ );
432
+ const typeLabel = useAssistantState(({ attachment }) => {
433
+ const type = attachment.type;
434
+ switch (type) {
435
+ case "image":
436
+ return "Image";
437
+ case "document":
438
+ return "Document";
439
+ case "file":
440
+ return "File";
441
+ default:
442
+ const _exhaustiveCheck: never = type;
443
+ throw new Error(`Unknown attachment type: ${_exhaustiveCheck}`);
444
+ }
445
+ });
446
+
447
+ return (
448
+ <Tooltip>
449
+ <AttachmentPrimitive.Root
450
+ className={cn(
451
+ "aui-attachment-root relative",
452
+ isImage &&
453
+ "aui-attachment-root-composer only:[&>#attachment-tile]:size-24",
454
+ )}
455
+ >
456
+ <AttachmentPreviewDialog>
457
+ <TooltipTrigger asChild>
458
+ <div
459
+ className={cn(
460
+ "aui-attachment-tile bg-muted size-14 cursor-pointer overflow-hidden rounded-[14px] border transition-opacity hover:opacity-75",
461
+ isComposer &&
462
+ "aui-attachment-tile-composer border-foreground/20",
463
+ )}
464
+ role="button"
465
+ id="attachment-tile"
466
+ aria-label={`${typeLabel} attachment`}
467
+ >
468
+ <AttachmentThumb />
469
+ </div>
470
+ </TooltipTrigger>
471
+ </AttachmentPreviewDialog>
472
+ {isComposer && <AttachmentRemove />}
473
+ </AttachmentPrimitive.Root>
474
+ <TooltipContent side="top">
475
+ <AttachmentPrimitive.Name />
476
+ </TooltipContent>
477
+ </Tooltip>
478
+ );
479
+ };
480
+
481
+ const AttachmentRemove: FC = () => {
482
+ return (
483
+ <AttachmentPrimitive.Remove asChild>
484
+ <TooltipIconButton
485
+ tooltip="Remove file"
486
+ 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"
487
+ side="top"
488
+ >
489
+ <XIcon className="aui-attachment-remove-icon size-3 dark:stroke-[2.5px]" />
490
+ </TooltipIconButton>
491
+ </AttachmentPrimitive.Remove>
492
+ );
493
+ };
494
+
495
+ export const UserMessageAttachments: FC = () => {
496
+ return (
497
+ <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">
498
+ <MessagePrimitive.Attachments components={{ Attachment: AttachmentUI }} />
499
+ </div>
500
+ );
501
+ };
502
+
503
+ export const ComposerAttachments: FC = () => {
504
+ return (
505
+ <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">
506
+ <ComposerPrimitive.Attachments
507
+ components={{ Attachment: AttachmentUI }}
508
+ />
509
+ </div>
510
+ );
511
+ };
512
+
513
+ export const ComposerAddAttachment: FC = () => {
514
+ return (
515
+ <ComposerPrimitive.AddAttachment asChild>
516
+ <TooltipIconButton
517
+ tooltip="Add Attachment"
518
+ side="bottom"
519
+ variant="ghost"
520
+ size="icon"
521
+ 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"
522
+ aria-label="Add Attachment"
523
+ >
524
+ <PlusIcon className="aui-attachment-add-icon size-5 stroke-[1.5px]" />
525
+ </TooltipIconButton>
526
+ </ComposerPrimitive.AddAttachment>
527
+ );
528
+ };
529
+
530
+ ```
531
+
291
532
  ## components/assistant-ui/markdown-text.tsx
292
533
 
293
534
  ```tsx
@@ -296,13 +537,13 @@ export default function Home() {
296
537
  import "@assistant-ui/react-markdown/styles/dot.css";
297
538
 
298
539
  import {
299
- CodeHeaderProps,
540
+ type CodeHeaderProps,
300
541
  MarkdownTextPrimitive,
301
542
  unstable_memoizeMarkdownComponents as memoizeMarkdownComponents,
302
543
  useIsMarkdownCodeBlock,
303
544
  } from "@assistant-ui/react-markdown";
304
545
  import remarkGfm from "remark-gfm";
305
- import { FC, memo, useState } from "react";
546
+ import { type FC, memo, useState } from "react";
306
547
  import { CheckIcon, CopyIcon } from "lucide-react";
307
548
 
308
549
  import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
@@ -328,8 +569,10 @@ const CodeHeader: FC<CodeHeaderProps> = ({ language, code }) => {
328
569
  };
329
570
 
330
571
  return (
331
- <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">
332
- <span className="lowercase [&>span]:text-xs">{language}</span>
572
+ <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">
573
+ <span className="aui-code-header-language lowercase [&>span]:text-xs">
574
+ {language}
575
+ </span>
333
576
  <TooltipIconButton tooltip="Copy" onClick={onCopy}>
334
577
  {!isCopied && <CopyIcon />}
335
578
  {isCopied && <CheckIcon />}
@@ -361,7 +604,7 @@ const defaultComponents = memoizeMarkdownComponents({
361
604
  h1: ({ className, ...props }) => (
362
605
  <h1
363
606
  className={cn(
364
- "mb-8 scroll-m-20 text-4xl font-extrabold tracking-tight last:mb-0",
607
+ "aui-md-h1 mb-8 scroll-m-20 text-4xl font-extrabold tracking-tight last:mb-0",
365
608
  className,
366
609
  )}
367
610
  {...props}
@@ -370,7 +613,7 @@ const defaultComponents = memoizeMarkdownComponents({
370
613
  h2: ({ className, ...props }) => (
371
614
  <h2
372
615
  className={cn(
373
- "mt-8 mb-4 scroll-m-20 text-3xl font-semibold tracking-tight first:mt-0 last:mb-0",
616
+ "aui-md-h2 mt-8 mb-4 scroll-m-20 text-3xl font-semibold tracking-tight first:mt-0 last:mb-0",
374
617
  className,
375
618
  )}
376
619
  {...props}
@@ -379,7 +622,7 @@ const defaultComponents = memoizeMarkdownComponents({
379
622
  h3: ({ className, ...props }) => (
380
623
  <h3
381
624
  className={cn(
382
- "mt-6 mb-4 scroll-m-20 text-2xl font-semibold tracking-tight first:mt-0 last:mb-0",
625
+ "aui-md-h3 mt-6 mb-4 scroll-m-20 text-2xl font-semibold tracking-tight first:mt-0 last:mb-0",
383
626
  className,
384
627
  )}
385
628
  {...props}
@@ -388,7 +631,7 @@ const defaultComponents = memoizeMarkdownComponents({
388
631
  h4: ({ className, ...props }) => (
389
632
  <h4
390
633
  className={cn(
391
- "mt-6 mb-4 scroll-m-20 text-xl font-semibold tracking-tight first:mt-0 last:mb-0",
634
+ "aui-md-h4 mt-6 mb-4 scroll-m-20 text-xl font-semibold tracking-tight first:mt-0 last:mb-0",
392
635
  className,
393
636
  )}
394
637
  {...props}
@@ -397,7 +640,7 @@ const defaultComponents = memoizeMarkdownComponents({
397
640
  h5: ({ className, ...props }) => (
398
641
  <h5
399
642
  className={cn(
400
- "my-4 text-lg font-semibold first:mt-0 last:mb-0",
643
+ "aui-md-h5 my-4 text-lg font-semibold first:mt-0 last:mb-0",
401
644
  className,
402
645
  )}
403
646
  {...props}
@@ -405,20 +648,26 @@ const defaultComponents = memoizeMarkdownComponents({
405
648
  ),
406
649
  h6: ({ className, ...props }) => (
407
650
  <h6
408
- className={cn("my-4 font-semibold first:mt-0 last:mb-0", className)}
651
+ className={cn(
652
+ "aui-md-h6 my-4 font-semibold first:mt-0 last:mb-0",
653
+ className,
654
+ )}
409
655
  {...props}
410
656
  />
411
657
  ),
412
658
  p: ({ className, ...props }) => (
413
659
  <p
414
- className={cn("mt-5 mb-5 leading-7 first:mt-0 last:mb-0", className)}
660
+ className={cn(
661
+ "aui-md-p mt-5 mb-5 leading-7 first:mt-0 last:mb-0",
662
+ className,
663
+ )}
415
664
  {...props}
416
665
  />
417
666
  ),
418
667
  a: ({ className, ...props }) => (
419
668
  <a
420
669
  className={cn(
421
- "text-primary font-medium underline underline-offset-4",
670
+ "aui-md-a text-primary font-medium underline underline-offset-4",
422
671
  className,
423
672
  )}
424
673
  {...props}
@@ -426,29 +675,29 @@ const defaultComponents = memoizeMarkdownComponents({
426
675
  ),
427
676
  blockquote: ({ className, ...props }) => (
428
677
  <blockquote
429
- className={cn("border-l-2 pl-6 italic", className)}
678
+ className={cn("aui-md-blockquote border-l-2 pl-6 italic", className)}
430
679
  {...props}
431
680
  />
432
681
  ),
433
682
  ul: ({ className, ...props }) => (
434
683
  <ul
435
- className={cn("my-5 ml-6 list-disc [&>li]:mt-2", className)}
684
+ className={cn("aui-md-ul my-5 ml-6 list-disc [&>li]:mt-2", className)}
436
685
  {...props}
437
686
  />
438
687
  ),
439
688
  ol: ({ className, ...props }) => (
440
689
  <ol
441
- className={cn("my-5 ml-6 list-decimal [&>li]:mt-2", className)}
690
+ className={cn("aui-md-ol my-5 ml-6 list-decimal [&>li]:mt-2", className)}
442
691
  {...props}
443
692
  />
444
693
  ),
445
694
  hr: ({ className, ...props }) => (
446
- <hr className={cn("my-5 border-b", className)} {...props} />
695
+ <hr className={cn("aui-md-hr my-5 border-b", className)} {...props} />
447
696
  ),
448
697
  table: ({ className, ...props }) => (
449
698
  <table
450
699
  className={cn(
451
- "my-5 w-full border-separate border-spacing-0 overflow-y-auto",
700
+ "aui-md-table my-5 w-full border-separate border-spacing-0 overflow-y-auto",
452
701
  className,
453
702
  )}
454
703
  {...props}
@@ -457,7 +706,7 @@ const defaultComponents = memoizeMarkdownComponents({
457
706
  th: ({ className, ...props }) => (
458
707
  <th
459
708
  className={cn(
460
- "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",
709
+ "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",
461
710
  className,
462
711
  )}
463
712
  {...props}
@@ -466,7 +715,7 @@ const defaultComponents = memoizeMarkdownComponents({
466
715
  td: ({ className, ...props }) => (
467
716
  <td
468
717
  className={cn(
469
- "border-b border-l px-4 py-2 text-left last:border-r [&[align=center]]:text-center [&[align=right]]:text-right",
718
+ "aui-md-td border-b border-l px-4 py-2 text-left last:border-r [&[align=center]]:text-center [&[align=right]]:text-right",
470
719
  className,
471
720
  )}
472
721
  {...props}
@@ -475,7 +724,7 @@ const defaultComponents = memoizeMarkdownComponents({
475
724
  tr: ({ className, ...props }) => (
476
725
  <tr
477
726
  className={cn(
478
- "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",
727
+ "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",
479
728
  className,
480
729
  )}
481
730
  {...props}
@@ -483,14 +732,14 @@ const defaultComponents = memoizeMarkdownComponents({
483
732
  ),
484
733
  sup: ({ className, ...props }) => (
485
734
  <sup
486
- className={cn("[&>a]:text-xs [&>a]:no-underline", className)}
735
+ className={cn("aui-md-sup [&>a]:text-xs [&>a]:no-underline", className)}
487
736
  {...props}
488
737
  />
489
738
  ),
490
739
  pre: ({ className, ...props }) => (
491
740
  <pre
492
741
  className={cn(
493
- "overflow-x-auto rounded-b-lg bg-black p-4 text-white",
742
+ "aui-md-pre overflow-x-auto !rounded-t-none rounded-b-lg bg-black p-4 text-white",
494
743
  className,
495
744
  )}
496
745
  {...props}
@@ -501,7 +750,8 @@ const defaultComponents = memoizeMarkdownComponents({
501
750
  return (
502
751
  <code
503
752
  className={cn(
504
- !isCodeBlock && "bg-muted rounded border font-semibold",
753
+ !isCodeBlock &&
754
+ "aui-md-inline-code bg-muted rounded border font-semibold",
505
755
  className,
506
756
  )}
507
757
  {...props}
@@ -803,12 +1053,111 @@ const CircleStopIcon = () => {
803
1053
 
804
1054
  ```
805
1055
 
1056
+ ## components/assistant-ui/tool-fallback.tsx
1057
+
1058
+ ```tsx
1059
+ import type { ToolCallMessagePartComponent } from "@assistant-ui/react";
1060
+ import {
1061
+ CheckIcon,
1062
+ ChevronDownIcon,
1063
+ ChevronUpIcon,
1064
+ XCircleIcon,
1065
+ } from "lucide-react";
1066
+ import { useState } from "react";
1067
+ import { Button } from "@/components/ui/button";
1068
+ import { cn } from "@/lib/utils";
1069
+
1070
+ export const ToolFallback: ToolCallMessagePartComponent = ({
1071
+ toolName,
1072
+ argsText,
1073
+ result,
1074
+ status,
1075
+ }) => {
1076
+ const [isCollapsed, setIsCollapsed] = useState(true);
1077
+
1078
+ const isCancelled =
1079
+ status?.type === "incomplete" && status.reason === "cancelled";
1080
+ const cancelledReason =
1081
+ isCancelled && status.error
1082
+ ? typeof status.error === "string"
1083
+ ? status.error
1084
+ : JSON.stringify(status.error)
1085
+ : null;
1086
+
1087
+ return (
1088
+ <div
1089
+ className={cn(
1090
+ "aui-tool-fallback-root mb-4 flex w-full flex-col gap-3 rounded-lg border py-3",
1091
+ isCancelled && "border-muted-foreground/30 bg-muted/30",
1092
+ )}
1093
+ >
1094
+ <div className="aui-tool-fallback-header flex items-center gap-2 px-4">
1095
+ {isCancelled ? (
1096
+ <XCircleIcon className="aui-tool-fallback-icon text-muted-foreground size-4" />
1097
+ ) : (
1098
+ <CheckIcon className="aui-tool-fallback-icon size-4" />
1099
+ )}
1100
+ <p
1101
+ className={cn(
1102
+ "aui-tool-fallback-title grow",
1103
+ isCancelled && "text-muted-foreground line-through",
1104
+ )}
1105
+ >
1106
+ {isCancelled ? "Cancelled tool: " : "Used tool: "}
1107
+ <b>{toolName}</b>
1108
+ </p>
1109
+ <Button onClick={() => setIsCollapsed(!isCollapsed)}>
1110
+ {isCollapsed ? <ChevronUpIcon /> : <ChevronDownIcon />}
1111
+ </Button>
1112
+ </div>
1113
+ {!isCollapsed && (
1114
+ <div className="aui-tool-fallback-content flex flex-col gap-2 border-t pt-2">
1115
+ {cancelledReason && (
1116
+ <div className="aui-tool-fallback-cancelled-root px-4">
1117
+ <p className="aui-tool-fallback-cancelled-header text-muted-foreground font-semibold">
1118
+ Cancelled reason:
1119
+ </p>
1120
+ <p className="aui-tool-fallback-cancelled-reason text-muted-foreground">
1121
+ {cancelledReason}
1122
+ </p>
1123
+ </div>
1124
+ )}
1125
+ <div
1126
+ className={cn(
1127
+ "aui-tool-fallback-args-root px-4",
1128
+ isCancelled && "opacity-60",
1129
+ )}
1130
+ >
1131
+ <pre className="aui-tool-fallback-args-value whitespace-pre-wrap">
1132
+ {argsText}
1133
+ </pre>
1134
+ </div>
1135
+ {!isCancelled && result !== undefined && (
1136
+ <div className="aui-tool-fallback-result-root border-t border-dashed px-4 pt-2">
1137
+ <p className="aui-tool-fallback-result-header font-semibold">
1138
+ Result:
1139
+ </p>
1140
+ <pre className="aui-tool-fallback-result-content whitespace-pre-wrap">
1141
+ {typeof result === "string"
1142
+ ? result
1143
+ : JSON.stringify(result, null, 2)}
1144
+ </pre>
1145
+ </div>
1146
+ )}
1147
+ </div>
1148
+ )}
1149
+ </div>
1150
+ );
1151
+ };
1152
+
1153
+ ```
1154
+
806
1155
  ## components/assistant-ui/tooltip-icon-button.tsx
807
1156
 
808
1157
  ```tsx
809
1158
  "use client";
810
1159
 
811
- import { ComponentPropsWithoutRef, forwardRef } from "react";
1160
+ import { ComponentPropsWithRef, forwardRef } from "react";
812
1161
  import { Slottable } from "@radix-ui/react-slot";
813
1162
 
814
1163
  import {
@@ -819,7 +1168,7 @@ import {
819
1168
  import { Button } from "@/components/ui/button";
820
1169
  import { cn } from "@/lib/utils";
821
1170
 
822
- export type TooltipIconButtonProps = ComponentPropsWithoutRef<typeof Button> & {
1171
+ export type TooltipIconButtonProps = ComponentPropsWithRef<typeof Button> & {
823
1172
  tooltip: string;
824
1173
  side?: "top" | "bottom" | "left" | "right";
825
1174
  };
@@ -835,11 +1184,11 @@ export const TooltipIconButton = forwardRef<
835
1184
  variant="ghost"
836
1185
  size="icon"
837
1186
  {...rest}
838
- className={cn("size-6 p-1", className)}
1187
+ className={cn("aui-button-icon size-6 p-1", className)}
839
1188
  ref={ref}
840
1189
  >
841
1190
  <Slottable>{children}</Slottable>
842
- <span className="sr-only">{tooltip}</span>
1191
+ <span className="aui-sr-only sr-only">{tooltip}</span>
843
1192
  </Button>
844
1193
  </TooltipTrigger>
845
1194
  <TooltipContent side={side}>{tooltip}</TooltipContent>
@@ -851,6 +1200,62 @@ TooltipIconButton.displayName = "TooltipIconButton";
851
1200
 
852
1201
  ```
853
1202
 
1203
+ ## components/ui/avatar.tsx
1204
+
1205
+ ```tsx
1206
+ "use client";
1207
+
1208
+ import * as React from "react";
1209
+ import * as AvatarPrimitive from "@radix-ui/react-avatar";
1210
+
1211
+ import { cn } from "@/lib/utils";
1212
+
1213
+ const Avatar = React.forwardRef<
1214
+ React.ElementRef<typeof AvatarPrimitive.Root>,
1215
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
1216
+ >(({ className, ...props }, ref) => (
1217
+ <AvatarPrimitive.Root
1218
+ ref={ref}
1219
+ className={cn(
1220
+ "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
1221
+ className,
1222
+ )}
1223
+ {...props}
1224
+ />
1225
+ ));
1226
+ Avatar.displayName = AvatarPrimitive.Root.displayName;
1227
+
1228
+ const AvatarImage = React.forwardRef<
1229
+ React.ElementRef<typeof AvatarPrimitive.Image>,
1230
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
1231
+ >(({ className, ...props }, ref) => (
1232
+ <AvatarPrimitive.Image
1233
+ ref={ref}
1234
+ className={cn("aspect-square h-full w-full", className)}
1235
+ {...props}
1236
+ />
1237
+ ));
1238
+ AvatarImage.displayName = AvatarPrimitive.Image.displayName;
1239
+
1240
+ const AvatarFallback = React.forwardRef<
1241
+ React.ElementRef<typeof AvatarPrimitive.Fallback>,
1242
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
1243
+ >(({ className, ...props }, ref) => (
1244
+ <AvatarPrimitive.Fallback
1245
+ ref={ref}
1246
+ className={cn(
1247
+ "bg-muted flex h-full w-full items-center justify-center rounded-full",
1248
+ className,
1249
+ )}
1250
+ {...props}
1251
+ />
1252
+ ));
1253
+ AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
1254
+
1255
+ export { Avatar, AvatarImage, AvatarFallback };
1256
+
1257
+ ```
1258
+
854
1259
  ## components/ui/button.tsx
855
1260
 
856
1261
  ```tsx
@@ -916,6 +1321,147 @@ export { Button, buttonVariants };
916
1321
 
917
1322
  ```
918
1323
 
1324
+ ## components/ui/dialog.tsx
1325
+
1326
+ ```tsx
1327
+ "use client";
1328
+
1329
+ import * as React from "react";
1330
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
1331
+ import { XIcon } from "lucide-react";
1332
+
1333
+ import { cn } from "@/lib/utils";
1334
+
1335
+ function Dialog({
1336
+ ...props
1337
+ }: React.ComponentProps<typeof DialogPrimitive.Root>) {
1338
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />;
1339
+ }
1340
+
1341
+ function DialogTrigger({
1342
+ ...props
1343
+ }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
1344
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
1345
+ }
1346
+
1347
+ function DialogPortal({
1348
+ ...props
1349
+ }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
1350
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
1351
+ }
1352
+
1353
+ function DialogClose({
1354
+ ...props
1355
+ }: React.ComponentProps<typeof DialogPrimitive.Close>) {
1356
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
1357
+ }
1358
+
1359
+ function DialogOverlay({
1360
+ className,
1361
+ ...props
1362
+ }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
1363
+ return (
1364
+ <DialogPrimitive.Overlay
1365
+ data-slot="dialog-overlay"
1366
+ className={cn(
1367
+ "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",
1368
+ className,
1369
+ )}
1370
+ {...props}
1371
+ />
1372
+ );
1373
+ }
1374
+
1375
+ function DialogContent({
1376
+ className,
1377
+ children,
1378
+ ...props
1379
+ }: React.ComponentProps<typeof DialogPrimitive.Content>) {
1380
+ return (
1381
+ <DialogPortal data-slot="dialog-portal">
1382
+ <DialogOverlay />
1383
+ <DialogPrimitive.Content
1384
+ data-slot="dialog-content"
1385
+ className={cn(
1386
+ "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",
1387
+ className,
1388
+ )}
1389
+ {...props}
1390
+ >
1391
+ {children}
1392
+ <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">
1393
+ <XIcon />
1394
+ <span className="sr-only">Close</span>
1395
+ </DialogPrimitive.Close>
1396
+ </DialogPrimitive.Content>
1397
+ </DialogPortal>
1398
+ );
1399
+ }
1400
+
1401
+ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
1402
+ return (
1403
+ <div
1404
+ data-slot="dialog-header"
1405
+ className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
1406
+ {...props}
1407
+ />
1408
+ );
1409
+ }
1410
+
1411
+ function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
1412
+ return (
1413
+ <div
1414
+ data-slot="dialog-footer"
1415
+ className={cn(
1416
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
1417
+ className,
1418
+ )}
1419
+ {...props}
1420
+ />
1421
+ );
1422
+ }
1423
+
1424
+ function DialogTitle({
1425
+ className,
1426
+ ...props
1427
+ }: React.ComponentProps<typeof DialogPrimitive.Title>) {
1428
+ return (
1429
+ <DialogPrimitive.Title
1430
+ data-slot="dialog-title"
1431
+ className={cn("text-lg leading-none font-semibold", className)}
1432
+ {...props}
1433
+ />
1434
+ );
1435
+ }
1436
+
1437
+ function DialogDescription({
1438
+ className,
1439
+ ...props
1440
+ }: React.ComponentProps<typeof DialogPrimitive.Description>) {
1441
+ return (
1442
+ <DialogPrimitive.Description
1443
+ data-slot="dialog-description"
1444
+ className={cn("text-muted-foreground text-sm", className)}
1445
+ {...props}
1446
+ />
1447
+ );
1448
+ }
1449
+
1450
+ export {
1451
+ Dialog,
1452
+ DialogClose,
1453
+ DialogContent,
1454
+ DialogDescription,
1455
+ DialogFooter,
1456
+ DialogHeader,
1457
+ DialogOverlay,
1458
+ DialogPortal,
1459
+ DialogTitle,
1460
+ DialogTrigger,
1461
+ };
1462
+
1463
+ ```
1464
+
919
1465
  ## components/ui/tooltip.tsx
920
1466
 
921
1467
  ```tsx
@@ -1022,22 +1568,26 @@ export default nextConfig;
1022
1568
  "lint": "eslint ."
1023
1569
  },
1024
1570
  "dependencies": {
1025
- "@ai-sdk/openai": "^2.0.68",
1571
+ "@ai-sdk/openai": "^2.0.73",
1026
1572
  "@assistant-ui/react": "workspace:*",
1027
1573
  "@assistant-ui/react-markdown": "workspace:*",
1028
1574
  "@assistant-ui/react-ag-ui": "workspace:*",
1029
1575
  "@ag-ui/client": "^0.0.41",
1576
+ "@radix-ui/react-avatar": "^1.1.4",
1577
+ "@radix-ui/react-dialog": "^1.1.7",
1030
1578
  "@radix-ui/react-slot": "^1.2.4",
1031
1579
  "@radix-ui/react-tooltip": "^1.2.8",
1032
1580
  "class-variance-authority": "^0.7.1",
1033
1581
  "clsx": "^2.1.1",
1034
- "lucide-react": "^0.554.0",
1035
- "next": "16.0.3",
1582
+ "lucide-react": "^0.555.0",
1583
+ "motion": "^11.18.2",
1584
+ "next": "16.0.4",
1036
1585
  "react": "19.2.0",
1037
1586
  "react-dom": "19.2.0",
1038
1587
  "remark-gfm": "^4.0.1",
1039
1588
  "tailwind-merge": "^3.4.0",
1040
- "tw-animate-css": "^1.4.0"
1589
+ "tw-animate-css": "^1.4.0",
1590
+ "zustand": "^5.0.8"
1041
1591
  },
1042
1592
  "devDependencies": {
1043
1593
  "@assistant-ui/x-buildutils": "workspace:*",
@@ -1045,7 +1595,7 @@ export default nextConfig;
1045
1595
  "@types/react": "^19",
1046
1596
  "@types/react-dom": "^19",
1047
1597
  "eslint": "^9",
1048
- "eslint-config-next": "16.0.3",
1598
+ "eslint-config-next": "16.0.4",
1049
1599
  "postcss": "^8",
1050
1600
  "tailwindcss": "^4.1.17",
1051
1601
  "typescript": "^5"