@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.
@@ -244,6 +244,247 @@ export default function Home() {
244
244
 
245
245
  ```
246
246
 
247
+ ## components/assistant-ui/attachment.tsx
248
+
249
+ ```tsx
250
+ "use client";
251
+
252
+ import { PropsWithChildren, useEffect, useState, type FC } from "react";
253
+ import Image from "next/image";
254
+ import { XIcon, PlusIcon, FileText } from "lucide-react";
255
+ import {
256
+ AttachmentPrimitive,
257
+ ComposerPrimitive,
258
+ MessagePrimitive,
259
+ useAssistantState,
260
+ useAssistantApi,
261
+ } from "@assistant-ui/react";
262
+ import { useShallow } from "zustand/shallow";
263
+ import {
264
+ Tooltip,
265
+ TooltipContent,
266
+ TooltipTrigger,
267
+ } from "@/components/ui/tooltip";
268
+ import {
269
+ Dialog,
270
+ DialogTitle,
271
+ DialogContent,
272
+ DialogTrigger,
273
+ } from "@/components/ui/dialog";
274
+ import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
275
+ import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
276
+ import { cn } from "@/lib/utils";
277
+
278
+ const useFileSrc = (file: File | undefined) => {
279
+ const [src, setSrc] = useState<string | undefined>(undefined);
280
+
281
+ useEffect(() => {
282
+ if (!file) {
283
+ setSrc(undefined);
284
+ return;
285
+ }
286
+
287
+ const objectUrl = URL.createObjectURL(file);
288
+ setSrc(objectUrl);
289
+
290
+ return () => {
291
+ URL.revokeObjectURL(objectUrl);
292
+ };
293
+ }, [file]);
294
+
295
+ return src;
296
+ };
297
+
298
+ const useAttachmentSrc = () => {
299
+ const { file, src } = useAssistantState(
300
+ useShallow(({ attachment }): { file?: File; src?: string } => {
301
+ if (attachment.type !== "image") return {};
302
+ if (attachment.file) return { file: attachment.file };
303
+ const src = attachment.content?.filter((c) => c.type === "image")[0]
304
+ ?.image;
305
+ if (!src) return {};
306
+ return { src };
307
+ }),
308
+ );
309
+
310
+ return useFileSrc(file) ?? src;
311
+ };
312
+
313
+ type AttachmentPreviewProps = {
314
+ src: string;
315
+ };
316
+
317
+ const AttachmentPreview: FC<AttachmentPreviewProps> = ({ src }) => {
318
+ const [isLoaded, setIsLoaded] = useState(false);
319
+ return (
320
+ <Image
321
+ src={src}
322
+ alt="Image Preview"
323
+ width={1}
324
+ height={1}
325
+ className={
326
+ isLoaded
327
+ ? "aui-attachment-preview-image-loaded block h-auto max-h-[80vh] w-auto max-w-full object-contain"
328
+ : "aui-attachment-preview-image-loading hidden"
329
+ }
330
+ onLoadingComplete={() => setIsLoaded(true)}
331
+ priority={false}
332
+ />
333
+ );
334
+ };
335
+
336
+ const AttachmentPreviewDialog: FC<PropsWithChildren> = ({ children }) => {
337
+ const src = useAttachmentSrc();
338
+
339
+ if (!src) return children;
340
+
341
+ return (
342
+ <Dialog>
343
+ <DialogTrigger
344
+ className="aui-attachment-preview-trigger hover:bg-accent/50 cursor-pointer transition-colors"
345
+ asChild
346
+ >
347
+ {children}
348
+ </DialogTrigger>
349
+ <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">
350
+ <DialogTitle className="aui-sr-only sr-only">
351
+ Image Attachment Preview
352
+ </DialogTitle>
353
+ <div className="aui-attachment-preview bg-background relative mx-auto flex max-h-[80dvh] w-full items-center justify-center overflow-hidden">
354
+ <AttachmentPreview src={src} />
355
+ </div>
356
+ </DialogContent>
357
+ </Dialog>
358
+ );
359
+ };
360
+
361
+ const AttachmentThumb: FC = () => {
362
+ const isImage = useAssistantState(
363
+ ({ attachment }) => attachment.type === "image",
364
+ );
365
+ const src = useAttachmentSrc();
366
+
367
+ return (
368
+ <Avatar className="aui-attachment-tile-avatar h-full w-full rounded-none">
369
+ <AvatarImage
370
+ src={src}
371
+ alt="Attachment preview"
372
+ className="aui-attachment-tile-image object-cover"
373
+ />
374
+ <AvatarFallback delayMs={isImage ? 200 : 0}>
375
+ <FileText className="aui-attachment-tile-fallback-icon text-muted-foreground size-8" />
376
+ </AvatarFallback>
377
+ </Avatar>
378
+ );
379
+ };
380
+
381
+ const AttachmentUI: FC = () => {
382
+ const api = useAssistantApi();
383
+ const isComposer = api.attachment.source === "composer";
384
+
385
+ const isImage = useAssistantState(
386
+ ({ attachment }) => attachment.type === "image",
387
+ );
388
+ const typeLabel = useAssistantState(({ attachment }) => {
389
+ const type = attachment.type;
390
+ switch (type) {
391
+ case "image":
392
+ return "Image";
393
+ case "document":
394
+ return "Document";
395
+ case "file":
396
+ return "File";
397
+ default:
398
+ const _exhaustiveCheck: never = type;
399
+ throw new Error(`Unknown attachment type: ${_exhaustiveCheck}`);
400
+ }
401
+ });
402
+
403
+ return (
404
+ <Tooltip>
405
+ <AttachmentPrimitive.Root
406
+ className={cn(
407
+ "aui-attachment-root relative",
408
+ isImage &&
409
+ "aui-attachment-root-composer only:[&>#attachment-tile]:size-24",
410
+ )}
411
+ >
412
+ <AttachmentPreviewDialog>
413
+ <TooltipTrigger asChild>
414
+ <div
415
+ className={cn(
416
+ "aui-attachment-tile bg-muted size-14 cursor-pointer overflow-hidden rounded-[14px] border transition-opacity hover:opacity-75",
417
+ isComposer &&
418
+ "aui-attachment-tile-composer border-foreground/20",
419
+ )}
420
+ role="button"
421
+ id="attachment-tile"
422
+ aria-label={`${typeLabel} attachment`}
423
+ >
424
+ <AttachmentThumb />
425
+ </div>
426
+ </TooltipTrigger>
427
+ </AttachmentPreviewDialog>
428
+ {isComposer && <AttachmentRemove />}
429
+ </AttachmentPrimitive.Root>
430
+ <TooltipContent side="top">
431
+ <AttachmentPrimitive.Name />
432
+ </TooltipContent>
433
+ </Tooltip>
434
+ );
435
+ };
436
+
437
+ const AttachmentRemove: FC = () => {
438
+ return (
439
+ <AttachmentPrimitive.Remove asChild>
440
+ <TooltipIconButton
441
+ tooltip="Remove file"
442
+ 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"
443
+ side="top"
444
+ >
445
+ <XIcon className="aui-attachment-remove-icon size-3 dark:stroke-[2.5px]" />
446
+ </TooltipIconButton>
447
+ </AttachmentPrimitive.Remove>
448
+ );
449
+ };
450
+
451
+ export const UserMessageAttachments: FC = () => {
452
+ return (
453
+ <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">
454
+ <MessagePrimitive.Attachments components={{ Attachment: AttachmentUI }} />
455
+ </div>
456
+ );
457
+ };
458
+
459
+ export const ComposerAttachments: FC = () => {
460
+ return (
461
+ <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">
462
+ <ComposerPrimitive.Attachments
463
+ components={{ Attachment: AttachmentUI }}
464
+ />
465
+ </div>
466
+ );
467
+ };
468
+
469
+ export const ComposerAddAttachment: FC = () => {
470
+ return (
471
+ <ComposerPrimitive.AddAttachment asChild>
472
+ <TooltipIconButton
473
+ tooltip="Add Attachment"
474
+ side="bottom"
475
+ variant="ghost"
476
+ size="icon"
477
+ 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"
478
+ aria-label="Add Attachment"
479
+ >
480
+ <PlusIcon className="aui-attachment-add-icon size-5 stroke-[1.5px]" />
481
+ </TooltipIconButton>
482
+ </ComposerPrimitive.AddAttachment>
483
+ );
484
+ };
485
+
486
+ ```
487
+
247
488
  ## components/assistant-ui/markdown-text.tsx
248
489
 
249
490
  ```tsx
@@ -284,8 +525,10 @@ const CodeHeader: FC<CodeHeaderProps> = ({ language, code }) => {
284
525
  };
285
526
 
286
527
  return (
287
- <div className="mt-4 flex items-center justify-between gap-4 rounded-t-lg bg-zinc-900 px-4 py-2 text-sm font-semibold text-white">
288
- <span className="lowercase [&>span]:text-xs">{language}</span>
528
+ <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">
529
+ <span className="aui-code-header-language lowercase [&>span]:text-xs">
530
+ {language}
531
+ </span>
289
532
  <TooltipIconButton tooltip="Copy" onClick={onCopy}>
290
533
  {!isCopied && <CopyIcon />}
291
534
  {isCopied && <CheckIcon />}
@@ -317,7 +560,7 @@ const defaultComponents = memoizeMarkdownComponents({
317
560
  h1: ({ className, ...props }) => (
318
561
  <h1
319
562
  className={cn(
320
- "mb-8 scroll-m-20 text-4xl font-extrabold tracking-tight last:mb-0",
563
+ "aui-md-h1 mb-8 scroll-m-20 text-4xl font-extrabold tracking-tight last:mb-0",
321
564
  className,
322
565
  )}
323
566
  {...props}
@@ -326,7 +569,7 @@ const defaultComponents = memoizeMarkdownComponents({
326
569
  h2: ({ className, ...props }) => (
327
570
  <h2
328
571
  className={cn(
329
- "mt-8 mb-4 scroll-m-20 text-3xl font-semibold tracking-tight first:mt-0 last:mb-0",
572
+ "aui-md-h2 mt-8 mb-4 scroll-m-20 text-3xl font-semibold tracking-tight first:mt-0 last:mb-0",
330
573
  className,
331
574
  )}
332
575
  {...props}
@@ -335,7 +578,7 @@ const defaultComponents = memoizeMarkdownComponents({
335
578
  h3: ({ className, ...props }) => (
336
579
  <h3
337
580
  className={cn(
338
- "mt-6 mb-4 scroll-m-20 text-2xl font-semibold tracking-tight first:mt-0 last:mb-0",
581
+ "aui-md-h3 mt-6 mb-4 scroll-m-20 text-2xl font-semibold tracking-tight first:mt-0 last:mb-0",
339
582
  className,
340
583
  )}
341
584
  {...props}
@@ -344,7 +587,7 @@ const defaultComponents = memoizeMarkdownComponents({
344
587
  h4: ({ className, ...props }) => (
345
588
  <h4
346
589
  className={cn(
347
- "mt-6 mb-4 scroll-m-20 text-xl font-semibold tracking-tight first:mt-0 last:mb-0",
590
+ "aui-md-h4 mt-6 mb-4 scroll-m-20 text-xl font-semibold tracking-tight first:mt-0 last:mb-0",
348
591
  className,
349
592
  )}
350
593
  {...props}
@@ -353,7 +596,7 @@ const defaultComponents = memoizeMarkdownComponents({
353
596
  h5: ({ className, ...props }) => (
354
597
  <h5
355
598
  className={cn(
356
- "my-4 text-lg font-semibold first:mt-0 last:mb-0",
599
+ "aui-md-h5 my-4 text-lg font-semibold first:mt-0 last:mb-0",
357
600
  className,
358
601
  )}
359
602
  {...props}
@@ -361,20 +604,26 @@ const defaultComponents = memoizeMarkdownComponents({
361
604
  ),
362
605
  h6: ({ className, ...props }) => (
363
606
  <h6
364
- className={cn("my-4 font-semibold first:mt-0 last:mb-0", className)}
607
+ className={cn(
608
+ "aui-md-h6 my-4 font-semibold first:mt-0 last:mb-0",
609
+ className,
610
+ )}
365
611
  {...props}
366
612
  />
367
613
  ),
368
614
  p: ({ className, ...props }) => (
369
615
  <p
370
- className={cn("mt-5 mb-5 leading-7 first:mt-0 last:mb-0", className)}
616
+ className={cn(
617
+ "aui-md-p mt-5 mb-5 leading-7 first:mt-0 last:mb-0",
618
+ className,
619
+ )}
371
620
  {...props}
372
621
  />
373
622
  ),
374
623
  a: ({ className, ...props }) => (
375
624
  <a
376
625
  className={cn(
377
- "text-primary font-medium underline underline-offset-4",
626
+ "aui-md-a text-primary font-medium underline underline-offset-4",
378
627
  className,
379
628
  )}
380
629
  {...props}
@@ -382,29 +631,29 @@ const defaultComponents = memoizeMarkdownComponents({
382
631
  ),
383
632
  blockquote: ({ className, ...props }) => (
384
633
  <blockquote
385
- className={cn("border-l-2 pl-6 italic", className)}
634
+ className={cn("aui-md-blockquote border-l-2 pl-6 italic", className)}
386
635
  {...props}
387
636
  />
388
637
  ),
389
638
  ul: ({ className, ...props }) => (
390
639
  <ul
391
- className={cn("my-5 ml-6 list-disc [&>li]:mt-2", className)}
640
+ className={cn("aui-md-ul my-5 ml-6 list-disc [&>li]:mt-2", className)}
392
641
  {...props}
393
642
  />
394
643
  ),
395
644
  ol: ({ className, ...props }) => (
396
645
  <ol
397
- className={cn("my-5 ml-6 list-decimal [&>li]:mt-2", className)}
646
+ className={cn("aui-md-ol my-5 ml-6 list-decimal [&>li]:mt-2", className)}
398
647
  {...props}
399
648
  />
400
649
  ),
401
650
  hr: ({ className, ...props }) => (
402
- <hr className={cn("my-5 border-b", className)} {...props} />
651
+ <hr className={cn("aui-md-hr my-5 border-b", className)} {...props} />
403
652
  ),
404
653
  table: ({ className, ...props }) => (
405
654
  <table
406
655
  className={cn(
407
- "my-5 w-full border-separate border-spacing-0 overflow-y-auto",
656
+ "aui-md-table my-5 w-full border-separate border-spacing-0 overflow-y-auto",
408
657
  className,
409
658
  )}
410
659
  {...props}
@@ -413,7 +662,7 @@ const defaultComponents = memoizeMarkdownComponents({
413
662
  th: ({ className, ...props }) => (
414
663
  <th
415
664
  className={cn(
416
- "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",
665
+ "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",
417
666
  className,
418
667
  )}
419
668
  {...props}
@@ -422,7 +671,7 @@ const defaultComponents = memoizeMarkdownComponents({
422
671
  td: ({ className, ...props }) => (
423
672
  <td
424
673
  className={cn(
425
- "border-b border-l px-4 py-2 text-left last:border-r [&[align=center]]:text-center [&[align=right]]:text-right",
674
+ "aui-md-td border-b border-l px-4 py-2 text-left last:border-r [&[align=center]]:text-center [&[align=right]]:text-right",
426
675
  className,
427
676
  )}
428
677
  {...props}
@@ -431,7 +680,7 @@ const defaultComponents = memoizeMarkdownComponents({
431
680
  tr: ({ className, ...props }) => (
432
681
  <tr
433
682
  className={cn(
434
- "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",
683
+ "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",
435
684
  className,
436
685
  )}
437
686
  {...props}
@@ -439,14 +688,14 @@ const defaultComponents = memoizeMarkdownComponents({
439
688
  ),
440
689
  sup: ({ className, ...props }) => (
441
690
  <sup
442
- className={cn("[&>a]:text-xs [&>a]:no-underline", className)}
691
+ className={cn("aui-md-sup [&>a]:text-xs [&>a]:no-underline", className)}
443
692
  {...props}
444
693
  />
445
694
  ),
446
695
  pre: ({ className, ...props }) => (
447
696
  <pre
448
697
  className={cn(
449
- "overflow-x-auto !rounded-t-none rounded-b-lg bg-black p-4 text-white",
698
+ "aui-md-pre overflow-x-auto !rounded-t-none rounded-b-lg bg-black p-4 text-white",
450
699
  className,
451
700
  )}
452
701
  {...props}
@@ -457,7 +706,8 @@ const defaultComponents = memoizeMarkdownComponents({
457
706
  return (
458
707
  <code
459
708
  className={cn(
460
- !isCodeBlock && "bg-muted rounded border font-semibold",
709
+ !isCodeBlock &&
710
+ "aui-md-inline-code bg-muted rounded border font-semibold",
461
711
  className,
462
712
  )}
463
713
  {...props}
@@ -777,37 +1027,88 @@ const CircleStopIcon = () => {
777
1027
  ## components/assistant-ui/tool-fallback.tsx
778
1028
 
779
1029
  ```tsx
780
- import { ToolCallMessagePartComponent } from "@assistant-ui/react";
781
- import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
1030
+ import type { ToolCallMessagePartComponent } from "@assistant-ui/react";
1031
+ import {
1032
+ CheckIcon,
1033
+ ChevronDownIcon,
1034
+ ChevronUpIcon,
1035
+ XCircleIcon,
1036
+ } from "lucide-react";
782
1037
  import { useState } from "react";
783
- import { Button } from "../ui/button";
1038
+ import { Button } from "@/components/ui/button";
1039
+ import { cn } from "@/lib/utils";
784
1040
 
785
1041
  export const ToolFallback: ToolCallMessagePartComponent = ({
786
1042
  toolName,
787
1043
  argsText,
788
1044
  result,
1045
+ status,
789
1046
  }) => {
790
1047
  const [isCollapsed, setIsCollapsed] = useState(true);
1048
+
1049
+ const isCancelled =
1050
+ status?.type === "incomplete" && status.reason === "cancelled";
1051
+ const cancelledReason =
1052
+ isCancelled && status.error
1053
+ ? typeof status.error === "string"
1054
+ ? status.error
1055
+ : JSON.stringify(status.error)
1056
+ : null;
1057
+
791
1058
  return (
792
- <div className="mb-4 flex w-full flex-col gap-3 rounded-lg border py-3">
793
- <div className="flex items-center gap-2 px-4">
794
- <CheckIcon className="size-4" />
795
- <p className="flex-grow">
796
- Used tool: <b>{toolName}</b>
1059
+ <div
1060
+ className={cn(
1061
+ "aui-tool-fallback-root mb-4 flex w-full flex-col gap-3 rounded-lg border py-3",
1062
+ isCancelled && "border-muted-foreground/30 bg-muted/30",
1063
+ )}
1064
+ >
1065
+ <div className="aui-tool-fallback-header flex items-center gap-2 px-4">
1066
+ {isCancelled ? (
1067
+ <XCircleIcon className="aui-tool-fallback-icon text-muted-foreground size-4" />
1068
+ ) : (
1069
+ <CheckIcon className="aui-tool-fallback-icon size-4" />
1070
+ )}
1071
+ <p
1072
+ className={cn(
1073
+ "aui-tool-fallback-title grow",
1074
+ isCancelled && "text-muted-foreground line-through",
1075
+ )}
1076
+ >
1077
+ {isCancelled ? "Cancelled tool: " : "Used tool: "}
1078
+ <b>{toolName}</b>
797
1079
  </p>
798
1080
  <Button onClick={() => setIsCollapsed(!isCollapsed)}>
799
1081
  {isCollapsed ? <ChevronUpIcon /> : <ChevronDownIcon />}
800
1082
  </Button>
801
1083
  </div>
802
1084
  {!isCollapsed && (
803
- <div className="flex flex-col gap-2 border-t pt-2">
804
- <div className="px-4">
805
- <pre className="whitespace-pre-wrap">{argsText}</pre>
1085
+ <div className="aui-tool-fallback-content flex flex-col gap-2 border-t pt-2">
1086
+ {cancelledReason && (
1087
+ <div className="aui-tool-fallback-cancelled-root px-4">
1088
+ <p className="aui-tool-fallback-cancelled-header text-muted-foreground font-semibold">
1089
+ Cancelled reason:
1090
+ </p>
1091
+ <p className="aui-tool-fallback-cancelled-reason text-muted-foreground">
1092
+ {cancelledReason}
1093
+ </p>
1094
+ </div>
1095
+ )}
1096
+ <div
1097
+ className={cn(
1098
+ "aui-tool-fallback-args-root px-4",
1099
+ isCancelled && "opacity-60",
1100
+ )}
1101
+ >
1102
+ <pre className="aui-tool-fallback-args-value whitespace-pre-wrap">
1103
+ {argsText}
1104
+ </pre>
806
1105
  </div>
807
- {result !== undefined && (
808
- <div className="border-t border-dashed px-4 pt-2">
809
- <p className="font-semibold">Result:</p>
810
- <pre className="whitespace-pre-wrap">
1106
+ {!isCancelled && result !== undefined && (
1107
+ <div className="aui-tool-fallback-result-root border-t border-dashed px-4 pt-2">
1108
+ <p className="aui-tool-fallback-result-header font-semibold">
1109
+ Result:
1110
+ </p>
1111
+ <pre className="aui-tool-fallback-result-content whitespace-pre-wrap">
811
1112
  {typeof result === "string"
812
1113
  ? result
813
1114
  : JSON.stringify(result, null, 2)}
@@ -827,7 +1128,7 @@ export const ToolFallback: ToolCallMessagePartComponent = ({
827
1128
  ```tsx
828
1129
  "use client";
829
1130
 
830
- import { ComponentPropsWithoutRef, forwardRef } from "react";
1131
+ import { ComponentPropsWithRef, forwardRef } from "react";
831
1132
  import { Slottable } from "@radix-ui/react-slot";
832
1133
 
833
1134
  import {
@@ -838,7 +1139,7 @@ import {
838
1139
  import { Button } from "@/components/ui/button";
839
1140
  import { cn } from "@/lib/utils";
840
1141
 
841
- export type TooltipIconButtonProps = ComponentPropsWithoutRef<typeof Button> & {
1142
+ export type TooltipIconButtonProps = ComponentPropsWithRef<typeof Button> & {
842
1143
  tooltip: string;
843
1144
  side?: "top" | "bottom" | "left" | "right";
844
1145
  };
@@ -854,11 +1155,11 @@ export const TooltipIconButton = forwardRef<
854
1155
  variant="ghost"
855
1156
  size="icon"
856
1157
  {...rest}
857
- className={cn("size-6 p-1", className)}
1158
+ className={cn("aui-button-icon size-6 p-1", className)}
858
1159
  ref={ref}
859
1160
  >
860
1161
  <Slottable>{children}</Slottable>
861
- <span className="sr-only">{tooltip}</span>
1162
+ <span className="aui-sr-only sr-only">{tooltip}</span>
862
1163
  </Button>
863
1164
  </TooltipTrigger>
864
1165
  <TooltipContent side={side}>{tooltip}</TooltipContent>
@@ -870,6 +1171,62 @@ TooltipIconButton.displayName = "TooltipIconButton";
870
1171
 
871
1172
  ```
872
1173
 
1174
+ ## components/ui/avatar.tsx
1175
+
1176
+ ```tsx
1177
+ "use client";
1178
+
1179
+ import * as React from "react";
1180
+ import * as AvatarPrimitive from "@radix-ui/react-avatar";
1181
+
1182
+ import { cn } from "@/lib/utils";
1183
+
1184
+ const Avatar = React.forwardRef<
1185
+ React.ElementRef<typeof AvatarPrimitive.Root>,
1186
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
1187
+ >(({ className, ...props }, ref) => (
1188
+ <AvatarPrimitive.Root
1189
+ ref={ref}
1190
+ className={cn(
1191
+ "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
1192
+ className,
1193
+ )}
1194
+ {...props}
1195
+ />
1196
+ ));
1197
+ Avatar.displayName = AvatarPrimitive.Root.displayName;
1198
+
1199
+ const AvatarImage = React.forwardRef<
1200
+ React.ElementRef<typeof AvatarPrimitive.Image>,
1201
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
1202
+ >(({ className, ...props }, ref) => (
1203
+ <AvatarPrimitive.Image
1204
+ ref={ref}
1205
+ className={cn("aspect-square h-full w-full", className)}
1206
+ {...props}
1207
+ />
1208
+ ));
1209
+ AvatarImage.displayName = AvatarPrimitive.Image.displayName;
1210
+
1211
+ const AvatarFallback = React.forwardRef<
1212
+ React.ElementRef<typeof AvatarPrimitive.Fallback>,
1213
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
1214
+ >(({ className, ...props }, ref) => (
1215
+ <AvatarPrimitive.Fallback
1216
+ ref={ref}
1217
+ className={cn(
1218
+ "bg-muted flex h-full w-full items-center justify-center rounded-full",
1219
+ className,
1220
+ )}
1221
+ {...props}
1222
+ />
1223
+ ));
1224
+ AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
1225
+
1226
+ export { Avatar, AvatarImage, AvatarFallback };
1227
+
1228
+ ```
1229
+
873
1230
  ## components/ui/button.tsx
874
1231
 
875
1232
  ```tsx
@@ -935,6 +1292,147 @@ export { Button, buttonVariants };
935
1292
 
936
1293
  ```
937
1294
 
1295
+ ## components/ui/dialog.tsx
1296
+
1297
+ ```tsx
1298
+ "use client";
1299
+
1300
+ import * as React from "react";
1301
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
1302
+ import { XIcon } from "lucide-react";
1303
+
1304
+ import { cn } from "@/lib/utils";
1305
+
1306
+ function Dialog({
1307
+ ...props
1308
+ }: React.ComponentProps<typeof DialogPrimitive.Root>) {
1309
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />;
1310
+ }
1311
+
1312
+ function DialogTrigger({
1313
+ ...props
1314
+ }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
1315
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
1316
+ }
1317
+
1318
+ function DialogPortal({
1319
+ ...props
1320
+ }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
1321
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
1322
+ }
1323
+
1324
+ function DialogClose({
1325
+ ...props
1326
+ }: React.ComponentProps<typeof DialogPrimitive.Close>) {
1327
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
1328
+ }
1329
+
1330
+ function DialogOverlay({
1331
+ className,
1332
+ ...props
1333
+ }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
1334
+ return (
1335
+ <DialogPrimitive.Overlay
1336
+ data-slot="dialog-overlay"
1337
+ className={cn(
1338
+ "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",
1339
+ className,
1340
+ )}
1341
+ {...props}
1342
+ />
1343
+ );
1344
+ }
1345
+
1346
+ function DialogContent({
1347
+ className,
1348
+ children,
1349
+ ...props
1350
+ }: React.ComponentProps<typeof DialogPrimitive.Content>) {
1351
+ return (
1352
+ <DialogPortal data-slot="dialog-portal">
1353
+ <DialogOverlay />
1354
+ <DialogPrimitive.Content
1355
+ data-slot="dialog-content"
1356
+ className={cn(
1357
+ "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",
1358
+ className,
1359
+ )}
1360
+ {...props}
1361
+ >
1362
+ {children}
1363
+ <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">
1364
+ <XIcon />
1365
+ <span className="sr-only">Close</span>
1366
+ </DialogPrimitive.Close>
1367
+ </DialogPrimitive.Content>
1368
+ </DialogPortal>
1369
+ );
1370
+ }
1371
+
1372
+ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
1373
+ return (
1374
+ <div
1375
+ data-slot="dialog-header"
1376
+ className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
1377
+ {...props}
1378
+ />
1379
+ );
1380
+ }
1381
+
1382
+ function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
1383
+ return (
1384
+ <div
1385
+ data-slot="dialog-footer"
1386
+ className={cn(
1387
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
1388
+ className,
1389
+ )}
1390
+ {...props}
1391
+ />
1392
+ );
1393
+ }
1394
+
1395
+ function DialogTitle({
1396
+ className,
1397
+ ...props
1398
+ }: React.ComponentProps<typeof DialogPrimitive.Title>) {
1399
+ return (
1400
+ <DialogPrimitive.Title
1401
+ data-slot="dialog-title"
1402
+ className={cn("text-lg leading-none font-semibold", className)}
1403
+ {...props}
1404
+ />
1405
+ );
1406
+ }
1407
+
1408
+ function DialogDescription({
1409
+ className,
1410
+ ...props
1411
+ }: React.ComponentProps<typeof DialogPrimitive.Description>) {
1412
+ return (
1413
+ <DialogPrimitive.Description
1414
+ data-slot="dialog-description"
1415
+ className={cn("text-muted-foreground text-sm", className)}
1416
+ {...props}
1417
+ />
1418
+ );
1419
+ }
1420
+
1421
+ export {
1422
+ Dialog,
1423
+ DialogClose,
1424
+ DialogContent,
1425
+ DialogDescription,
1426
+ DialogFooter,
1427
+ DialogHeader,
1428
+ DialogOverlay,
1429
+ DialogPortal,
1430
+ DialogTitle,
1431
+ DialogTrigger,
1432
+ };
1433
+
1434
+ ```
1435
+
938
1436
  ## components/ui/tooltip.tsx
939
1437
 
940
1438
  ```tsx
@@ -1035,31 +1533,35 @@ export default nextConfig;
1035
1533
  "version": "0.0.0",
1036
1534
  "type": "module",
1037
1535
  "dependencies": {
1038
- "@ai-sdk/openai": "^2.0.68",
1039
- "@ai-sdk/react": "^2.0.93",
1536
+ "@ai-sdk/openai": "^2.0.73",
1537
+ "@ai-sdk/react": "^2.0.102",
1040
1538
  "@assistant-ui/react": "workspace:^",
1041
1539
  "@assistant-ui/react-ai-sdk": "workspace:*",
1042
1540
  "@assistant-ui/react-markdown": "workspace:^",
1541
+ "@radix-ui/react-avatar": "^1.1.4",
1542
+ "@radix-ui/react-dialog": "^1.1.7",
1043
1543
  "@radix-ui/react-slot": "^1.2.4",
1044
1544
  "@radix-ui/react-tooltip": "^1.2.8",
1045
1545
  "@tailwindcss/postcss": "^4.1.17",
1046
- "ai": "^5.0.93",
1546
+ "ai": "^5.0.102",
1047
1547
  "class-variance-authority": "^0.7.1",
1048
1548
  "clsx": "^2.1.1",
1049
- "lucide-react": "^0.554.0",
1050
- "next": "16.0.3",
1549
+ "lucide-react": "^0.555.0",
1550
+ "motion": "^11.18.2",
1551
+ "next": "16.0.4",
1051
1552
  "postcss": "^8.5.6",
1052
1553
  "react": "19.2.0",
1053
1554
  "react-dom": "19.2.0",
1054
1555
  "remark-gfm": "^4.0.1",
1055
1556
  "tailwind-merge": "^3.4.0",
1056
1557
  "tailwindcss": "^4.1.17",
1057
- "zod": "^4.1.12"
1558
+ "zod": "^4.1.13",
1559
+ "zustand": "^5.0.8"
1058
1560
  },
1059
1561
  "devDependencies": {
1060
1562
  "@assistant-ui/x-buildutils": "workspace:*",
1061
1563
  "@types/node": "^24.10.1",
1062
- "@types/react": "^19.2.5",
1564
+ "@types/react": "^19.2.7",
1063
1565
  "@types/react-dom": "^19.2.3",
1064
1566
  "tw-animate-css": "^1.4.0",
1065
1567
  "typescript": "^5.9.3"