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