@assistant-ui/mcp-docs-server 0.1.14 → 0.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/.docs/organized/code-examples/store-example.md +628 -0
  2. package/.docs/organized/code-examples/with-ag-ui.md +792 -178
  3. package/.docs/organized/code-examples/with-ai-sdk-v5.md +762 -209
  4. package/.docs/organized/code-examples/with-assistant-transport.md +707 -254
  5. package/.docs/organized/code-examples/with-cloud.md +848 -202
  6. package/.docs/organized/code-examples/with-custom-thread-list.md +1855 -0
  7. package/.docs/organized/code-examples/with-external-store.md +788 -172
  8. package/.docs/organized/code-examples/with-ffmpeg.md +796 -196
  9. package/.docs/organized/code-examples/with-langgraph.md +864 -230
  10. package/.docs/organized/code-examples/with-parent-id-grouping.md +785 -255
  11. package/.docs/organized/code-examples/with-react-hook-form.md +804 -226
  12. package/.docs/organized/code-examples/with-tanstack.md +1574 -0
  13. package/.docs/raw/blog/2024-07-29-hello/index.mdx +2 -3
  14. package/.docs/raw/docs/api-reference/overview.mdx +6 -6
  15. package/.docs/raw/docs/api-reference/primitives/ActionBar.mdx +85 -4
  16. package/.docs/raw/docs/api-reference/primitives/AssistantIf.mdx +200 -0
  17. package/.docs/raw/docs/api-reference/primitives/Composer.mdx +0 -20
  18. package/.docs/raw/docs/api-reference/primitives/Message.mdx +0 -45
  19. package/.docs/raw/docs/api-reference/primitives/Thread.mdx +0 -50
  20. package/.docs/raw/docs/cli.mdx +396 -0
  21. package/.docs/raw/docs/cloud/persistence/ai-sdk.mdx +2 -3
  22. package/.docs/raw/docs/cloud/persistence/langgraph.mdx +2 -3
  23. package/.docs/raw/docs/devtools.mdx +2 -3
  24. package/.docs/raw/docs/getting-started.mdx +37 -1109
  25. package/.docs/raw/docs/guides/Attachments.mdx +3 -25
  26. package/.docs/raw/docs/guides/Branching.mdx +1 -1
  27. package/.docs/raw/docs/guides/Speech.mdx +1 -1
  28. package/.docs/raw/docs/guides/ToolUI.mdx +1 -1
  29. package/.docs/raw/docs/legacy/styled/AssistantModal.mdx +2 -3
  30. package/.docs/raw/docs/legacy/styled/Decomposition.mdx +6 -5
  31. package/.docs/raw/docs/legacy/styled/Markdown.mdx +2 -3
  32. package/.docs/raw/docs/legacy/styled/Thread.mdx +2 -3
  33. package/.docs/raw/docs/react-compatibility.mdx +2 -5
  34. package/.docs/raw/docs/runtimes/ai-sdk/use-chat.mdx +3 -4
  35. package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +3 -6
  36. package/.docs/raw/docs/runtimes/assistant-transport.mdx +891 -0
  37. package/.docs/raw/docs/runtimes/custom/external-store.mdx +2 -3
  38. package/.docs/raw/docs/runtimes/custom/local.mdx +11 -41
  39. package/.docs/raw/docs/runtimes/data-stream.mdx +15 -11
  40. package/.docs/raw/docs/runtimes/langgraph/index.mdx +4 -4
  41. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +1 -1
  42. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +2 -3
  43. package/.docs/raw/docs/runtimes/langserve.mdx +2 -3
  44. package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +2 -3
  45. package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +2 -3
  46. package/.docs/raw/docs/ui/AssistantModal.mdx +3 -25
  47. package/.docs/raw/docs/ui/AssistantSidebar.mdx +2 -24
  48. package/.docs/raw/docs/ui/Attachment.mdx +3 -25
  49. package/.docs/raw/docs/ui/Markdown.mdx +2 -24
  50. package/.docs/raw/docs/ui/Mermaid.mdx +2 -24
  51. package/.docs/raw/docs/ui/Reasoning.mdx +2 -24
  52. package/.docs/raw/docs/ui/Scrollbar.mdx +4 -6
  53. package/.docs/raw/docs/ui/SyntaxHighlighting.mdx +3 -47
  54. package/.docs/raw/docs/ui/Thread.mdx +38 -53
  55. package/.docs/raw/docs/ui/ThreadList.mdx +4 -47
  56. package/.docs/raw/docs/ui/ToolFallback.mdx +2 -24
  57. package/package.json +15 -8
@@ -219,7 +219,7 @@ import Link from "next/link";
219
219
 
220
220
  const SetFormFieldTool = () => {
221
221
  return (
222
- <p className="text-center font-mono text-sm font-bold text-blue-500">
222
+ <p className="text-center font-bold font-mono text-blue-500 text-sm">
223
223
  set_form_field(...)
224
224
  </p>
225
225
  );
@@ -227,7 +227,7 @@ const SetFormFieldTool = () => {
227
227
 
228
228
  const SubmitFormTool = () => {
229
229
  return (
230
- <p className="text-center font-mono text-sm font-bold text-blue-500">
230
+ <p className="text-center font-bold font-mono text-blue-500 text-sm">
231
231
  submit_form(...)
232
232
  </p>
233
233
  );
@@ -260,7 +260,7 @@ export default function Home() {
260
260
  <AssistantSidebar>
261
261
  <div className="h-full overflow-y-scroll">
262
262
  <main className="container py-8">
263
- <h1 className="mb-2 text-2xl font-semibold">
263
+ <h1 className="mb-2 font-semibold text-2xl">
264
264
  Simon&apos;s Hackathon
265
265
  </h1>
266
266
  <p>
@@ -316,39 +316,16 @@ export default function Home() {
316
316
  ## components/assistant-ui/assistant-sidebar.tsx
317
317
 
318
318
  ```tsx
319
- "use client";
320
-
321
319
  import {
322
320
  ResizableHandle,
323
321
  ResizablePanel,
324
322
  ResizablePanelGroup,
325
323
  } from "@/components/ui/resizable";
326
- import { useMediaQuery } from "@react-hook/media-query";
327
324
  import type { FC, PropsWithChildren } from "react";
328
- import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
325
+
329
326
  import { Thread } from "@/components/assistant-ui/thread";
330
327
 
331
328
  export const AssistantSidebar: FC<PropsWithChildren> = ({ children }) => {
332
- const isSmall = useMediaQuery("(max-width: 768px)");
333
-
334
- if (isSmall) {
335
- return (
336
- <Tabs
337
- defaultValue="app"
338
- className="mx-auto flex h-full max-w-[480px] flex-col px-4 pt-4"
339
- >
340
- <TabsList className="grid w-full grid-cols-2">
341
- <TabsTrigger value="app">Form</TabsTrigger>
342
- <TabsTrigger value="thread">Chat</TabsTrigger>
343
- </TabsList>
344
- <TabsContent value="app">{children}</TabsContent>
345
- <TabsContent value="thread">
346
- <Thread />
347
- </TabsContent>
348
- </Tabs>
349
- );
350
- }
351
-
352
329
  return (
353
330
  <ResizablePanelGroup direction="horizontal">
354
331
  <ResizablePanel>{children}</ResizablePanel>
@@ -362,6 +339,247 @@ export const AssistantSidebar: FC<PropsWithChildren> = ({ children }) => {
362
339
 
363
340
  ```
364
341
 
342
+ ## components/assistant-ui/attachment.tsx
343
+
344
+ ```tsx
345
+ "use client";
346
+
347
+ import { PropsWithChildren, useEffect, useState, type FC } from "react";
348
+ import Image from "next/image";
349
+ import { XIcon, PlusIcon, FileText } from "lucide-react";
350
+ import {
351
+ AttachmentPrimitive,
352
+ ComposerPrimitive,
353
+ MessagePrimitive,
354
+ useAssistantState,
355
+ useAssistantApi,
356
+ } from "@assistant-ui/react";
357
+ import { useShallow } from "zustand/shallow";
358
+ import {
359
+ Tooltip,
360
+ TooltipContent,
361
+ TooltipTrigger,
362
+ } from "@/components/ui/tooltip";
363
+ import {
364
+ Dialog,
365
+ DialogTitle,
366
+ DialogContent,
367
+ DialogTrigger,
368
+ } from "@/components/ui/dialog";
369
+ import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
370
+ import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
371
+ import { cn } from "@/lib/utils";
372
+
373
+ const useFileSrc = (file: File | undefined) => {
374
+ const [src, setSrc] = useState<string | undefined>(undefined);
375
+
376
+ useEffect(() => {
377
+ if (!file) {
378
+ setSrc(undefined);
379
+ return;
380
+ }
381
+
382
+ const objectUrl = URL.createObjectURL(file);
383
+ setSrc(objectUrl);
384
+
385
+ return () => {
386
+ URL.revokeObjectURL(objectUrl);
387
+ };
388
+ }, [file]);
389
+
390
+ return src;
391
+ };
392
+
393
+ const useAttachmentSrc = () => {
394
+ const { file, src } = useAssistantState(
395
+ useShallow(({ attachment }): { file?: File; src?: string } => {
396
+ if (attachment.type !== "image") return {};
397
+ if (attachment.file) return { file: attachment.file };
398
+ const src = attachment.content?.filter((c) => c.type === "image")[0]
399
+ ?.image;
400
+ if (!src) return {};
401
+ return { src };
402
+ }),
403
+ );
404
+
405
+ return useFileSrc(file) ?? src;
406
+ };
407
+
408
+ type AttachmentPreviewProps = {
409
+ src: string;
410
+ };
411
+
412
+ const AttachmentPreview: FC<AttachmentPreviewProps> = ({ src }) => {
413
+ const [isLoaded, setIsLoaded] = useState(false);
414
+ return (
415
+ <Image
416
+ src={src}
417
+ alt="Image Preview"
418
+ width={1}
419
+ height={1}
420
+ className={
421
+ isLoaded
422
+ ? "aui-attachment-preview-image-loaded block h-auto max-h-[80vh] w-auto max-w-full object-contain"
423
+ : "aui-attachment-preview-image-loading hidden"
424
+ }
425
+ onLoadingComplete={() => setIsLoaded(true)}
426
+ priority={false}
427
+ />
428
+ );
429
+ };
430
+
431
+ const AttachmentPreviewDialog: FC<PropsWithChildren> = ({ children }) => {
432
+ const src = useAttachmentSrc();
433
+
434
+ if (!src) return children;
435
+
436
+ return (
437
+ <Dialog>
438
+ <DialogTrigger
439
+ className="aui-attachment-preview-trigger cursor-pointer transition-colors hover:bg-accent/50"
440
+ asChild
441
+ >
442
+ {children}
443
+ </DialogTrigger>
444
+ <DialogContent className="aui-attachment-preview-dialog-content p-2 sm:max-w-3xl [&>button]:rounded-full [&>button]:bg-foreground/60 [&>button]:p-1 [&>button]:opacity-100 [&>button]:ring-0! [&_svg]:text-background [&>button]:hover:[&_svg]:text-destructive">
445
+ <DialogTitle className="aui-sr-only sr-only">
446
+ Image Attachment Preview
447
+ </DialogTitle>
448
+ <div className="aui-attachment-preview relative mx-auto flex max-h-[80dvh] w-full items-center justify-center overflow-hidden bg-background">
449
+ <AttachmentPreview src={src} />
450
+ </div>
451
+ </DialogContent>
452
+ </Dialog>
453
+ );
454
+ };
455
+
456
+ const AttachmentThumb: FC = () => {
457
+ const isImage = useAssistantState(
458
+ ({ attachment }) => attachment.type === "image",
459
+ );
460
+ const src = useAttachmentSrc();
461
+
462
+ return (
463
+ <Avatar className="aui-attachment-tile-avatar h-full w-full rounded-none">
464
+ <AvatarImage
465
+ src={src}
466
+ alt="Attachment preview"
467
+ className="aui-attachment-tile-image object-cover"
468
+ />
469
+ <AvatarFallback delayMs={isImage ? 200 : 0}>
470
+ <FileText className="aui-attachment-tile-fallback-icon size-8 text-muted-foreground" />
471
+ </AvatarFallback>
472
+ </Avatar>
473
+ );
474
+ };
475
+
476
+ const AttachmentUI: FC = () => {
477
+ const api = useAssistantApi();
478
+ const isComposer = api.attachment.source === "composer";
479
+
480
+ const isImage = useAssistantState(
481
+ ({ attachment }) => attachment.type === "image",
482
+ );
483
+ const typeLabel = useAssistantState(({ attachment }) => {
484
+ const type = attachment.type;
485
+ switch (type) {
486
+ case "image":
487
+ return "Image";
488
+ case "document":
489
+ return "Document";
490
+ case "file":
491
+ return "File";
492
+ default:
493
+ const _exhaustiveCheck: never = type;
494
+ throw new Error(`Unknown attachment type: ${_exhaustiveCheck}`);
495
+ }
496
+ });
497
+
498
+ return (
499
+ <Tooltip>
500
+ <AttachmentPrimitive.Root
501
+ className={cn(
502
+ "aui-attachment-root relative",
503
+ isImage &&
504
+ "aui-attachment-root-composer only:[&>#attachment-tile]:size-24",
505
+ )}
506
+ >
507
+ <AttachmentPreviewDialog>
508
+ <TooltipTrigger asChild>
509
+ <div
510
+ className={cn(
511
+ "aui-attachment-tile size-14 cursor-pointer overflow-hidden rounded-[14px] border bg-muted transition-opacity hover:opacity-75",
512
+ isComposer &&
513
+ "aui-attachment-tile-composer border-foreground/20",
514
+ )}
515
+ role="button"
516
+ id="attachment-tile"
517
+ aria-label={`${typeLabel} attachment`}
518
+ >
519
+ <AttachmentThumb />
520
+ </div>
521
+ </TooltipTrigger>
522
+ </AttachmentPreviewDialog>
523
+ {isComposer && <AttachmentRemove />}
524
+ </AttachmentPrimitive.Root>
525
+ <TooltipContent side="top">
526
+ <AttachmentPrimitive.Name />
527
+ </TooltipContent>
528
+ </Tooltip>
529
+ );
530
+ };
531
+
532
+ const AttachmentRemove: FC = () => {
533
+ return (
534
+ <AttachmentPrimitive.Remove asChild>
535
+ <TooltipIconButton
536
+ tooltip="Remove file"
537
+ className="aui-attachment-tile-remove absolute top-1.5 right-1.5 size-3.5 rounded-full bg-white text-muted-foreground opacity-100 shadow-sm hover:bg-white! [&_svg]:text-black hover:[&_svg]:text-destructive"
538
+ side="top"
539
+ >
540
+ <XIcon className="aui-attachment-remove-icon size-3 dark:stroke-[2.5px]" />
541
+ </TooltipIconButton>
542
+ </AttachmentPrimitive.Remove>
543
+ );
544
+ };
545
+
546
+ export const UserMessageAttachments: FC = () => {
547
+ return (
548
+ <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">
549
+ <MessagePrimitive.Attachments components={{ Attachment: AttachmentUI }} />
550
+ </div>
551
+ );
552
+ };
553
+
554
+ export const ComposerAttachments: FC = () => {
555
+ return (
556
+ <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">
557
+ <ComposerPrimitive.Attachments
558
+ components={{ Attachment: AttachmentUI }}
559
+ />
560
+ </div>
561
+ );
562
+ };
563
+
564
+ export const ComposerAddAttachment: FC = () => {
565
+ return (
566
+ <ComposerPrimitive.AddAttachment asChild>
567
+ <TooltipIconButton
568
+ tooltip="Add Attachment"
569
+ side="bottom"
570
+ variant="ghost"
571
+ size="icon"
572
+ className="aui-composer-add-attachment size-[34px] rounded-full p-1 font-semibold text-xs hover:bg-muted-foreground/15 dark:border-muted-foreground/15 dark:hover:bg-muted-foreground/30"
573
+ aria-label="Add Attachment"
574
+ >
575
+ <PlusIcon className="aui-attachment-add-icon size-5 stroke-[1.5px]" />
576
+ </TooltipIconButton>
577
+ </ComposerPrimitive.AddAttachment>
578
+ );
579
+ };
580
+
581
+ ```
582
+
365
583
  ## components/assistant-ui/markdown-text.tsx
366
584
 
367
585
  ```tsx
@@ -370,13 +588,13 @@ export const AssistantSidebar: FC<PropsWithChildren> = ({ children }) => {
370
588
  import "@assistant-ui/react-markdown/styles/dot.css";
371
589
 
372
590
  import {
373
- CodeHeaderProps,
591
+ type CodeHeaderProps,
374
592
  MarkdownTextPrimitive,
375
593
  unstable_memoizeMarkdownComponents as memoizeMarkdownComponents,
376
594
  useIsMarkdownCodeBlock,
377
595
  } from "@assistant-ui/react-markdown";
378
596
  import remarkGfm from "remark-gfm";
379
- import { FC, memo, useState } from "react";
597
+ import { type FC, memo, useState } from "react";
380
598
  import { CheckIcon, CopyIcon } from "lucide-react";
381
599
 
382
600
  import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
@@ -402,8 +620,10 @@ const CodeHeader: FC<CodeHeaderProps> = ({ language, code }) => {
402
620
  };
403
621
 
404
622
  return (
405
- <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">
406
- <span className="lowercase [&>span]:text-xs">{language}</span>
623
+ <div className="aui-code-header-root mt-4 flex items-center justify-between gap-4 rounded-t-lg bg-muted-foreground/15 px-4 py-2 font-semibold text-foreground text-sm dark:bg-muted-foreground/20">
624
+ <span className="aui-code-header-language lowercase [&>span]:text-xs">
625
+ {language}
626
+ </span>
407
627
  <TooltipIconButton tooltip="Copy" onClick={onCopy}>
408
628
  {!isCopied && <CopyIcon />}
409
629
  {isCopied && <CheckIcon />}
@@ -435,7 +655,7 @@ const defaultComponents = memoizeMarkdownComponents({
435
655
  h1: ({ className, ...props }) => (
436
656
  <h1
437
657
  className={cn(
438
- "mb-8 scroll-m-20 text-4xl font-extrabold tracking-tight last:mb-0",
658
+ "aui-md-h1 mb-8 scroll-m-20 font-extrabold text-4xl tracking-tight last:mb-0",
439
659
  className,
440
660
  )}
441
661
  {...props}
@@ -444,7 +664,7 @@ const defaultComponents = memoizeMarkdownComponents({
444
664
  h2: ({ className, ...props }) => (
445
665
  <h2
446
666
  className={cn(
447
- "mt-8 mb-4 scroll-m-20 text-3xl font-semibold tracking-tight first:mt-0 last:mb-0",
667
+ "aui-md-h2 mt-8 mb-4 scroll-m-20 font-semibold text-3xl tracking-tight first:mt-0 last:mb-0",
448
668
  className,
449
669
  )}
450
670
  {...props}
@@ -453,7 +673,7 @@ const defaultComponents = memoizeMarkdownComponents({
453
673
  h3: ({ className, ...props }) => (
454
674
  <h3
455
675
  className={cn(
456
- "mt-6 mb-4 scroll-m-20 text-2xl font-semibold tracking-tight first:mt-0 last:mb-0",
676
+ "aui-md-h3 mt-6 mb-4 scroll-m-20 font-semibold text-2xl tracking-tight first:mt-0 last:mb-0",
457
677
  className,
458
678
  )}
459
679
  {...props}
@@ -462,7 +682,7 @@ const defaultComponents = memoizeMarkdownComponents({
462
682
  h4: ({ className, ...props }) => (
463
683
  <h4
464
684
  className={cn(
465
- "mt-6 mb-4 scroll-m-20 text-xl font-semibold tracking-tight first:mt-0 last:mb-0",
685
+ "aui-md-h4 mt-6 mb-4 scroll-m-20 font-semibold text-xl tracking-tight first:mt-0 last:mb-0",
466
686
  className,
467
687
  )}
468
688
  {...props}
@@ -471,7 +691,7 @@ const defaultComponents = memoizeMarkdownComponents({
471
691
  h5: ({ className, ...props }) => (
472
692
  <h5
473
693
  className={cn(
474
- "my-4 text-lg font-semibold first:mt-0 last:mb-0",
694
+ "aui-md-h5 my-4 font-semibold text-lg first:mt-0 last:mb-0",
475
695
  className,
476
696
  )}
477
697
  {...props}
@@ -479,20 +699,26 @@ const defaultComponents = memoizeMarkdownComponents({
479
699
  ),
480
700
  h6: ({ className, ...props }) => (
481
701
  <h6
482
- className={cn("my-4 font-semibold first:mt-0 last:mb-0", className)}
702
+ className={cn(
703
+ "aui-md-h6 my-4 font-semibold first:mt-0 last:mb-0",
704
+ className,
705
+ )}
483
706
  {...props}
484
707
  />
485
708
  ),
486
709
  p: ({ className, ...props }) => (
487
710
  <p
488
- className={cn("mt-5 mb-5 leading-7 first:mt-0 last:mb-0", className)}
711
+ className={cn(
712
+ "aui-md-p mt-5 mb-5 leading-7 first:mt-0 last:mb-0",
713
+ className,
714
+ )}
489
715
  {...props}
490
716
  />
491
717
  ),
492
718
  a: ({ className, ...props }) => (
493
719
  <a
494
720
  className={cn(
495
- "text-primary font-medium underline underline-offset-4",
721
+ "aui-md-a font-medium text-primary underline underline-offset-4",
496
722
  className,
497
723
  )}
498
724
  {...props}
@@ -500,29 +726,29 @@ const defaultComponents = memoizeMarkdownComponents({
500
726
  ),
501
727
  blockquote: ({ className, ...props }) => (
502
728
  <blockquote
503
- className={cn("border-l-2 pl-6 italic", className)}
729
+ className={cn("aui-md-blockquote border-l-2 pl-6 italic", className)}
504
730
  {...props}
505
731
  />
506
732
  ),
507
733
  ul: ({ className, ...props }) => (
508
734
  <ul
509
- className={cn("my-5 ml-6 list-disc [&>li]:mt-2", className)}
735
+ className={cn("aui-md-ul my-5 ml-6 list-disc [&>li]:mt-2", className)}
510
736
  {...props}
511
737
  />
512
738
  ),
513
739
  ol: ({ className, ...props }) => (
514
740
  <ol
515
- className={cn("my-5 ml-6 list-decimal [&>li]:mt-2", className)}
741
+ className={cn("aui-md-ol my-5 ml-6 list-decimal [&>li]:mt-2", className)}
516
742
  {...props}
517
743
  />
518
744
  ),
519
745
  hr: ({ className, ...props }) => (
520
- <hr className={cn("my-5 border-b", className)} {...props} />
746
+ <hr className={cn("aui-md-hr my-5 border-b", className)} {...props} />
521
747
  ),
522
748
  table: ({ className, ...props }) => (
523
749
  <table
524
750
  className={cn(
525
- "my-5 w-full border-separate border-spacing-0 overflow-y-auto",
751
+ "aui-md-table my-5 w-full border-separate border-spacing-0 overflow-y-auto",
526
752
  className,
527
753
  )}
528
754
  {...props}
@@ -531,7 +757,7 @@ const defaultComponents = memoizeMarkdownComponents({
531
757
  th: ({ className, ...props }) => (
532
758
  <th
533
759
  className={cn(
534
- "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",
760
+ "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",
535
761
  className,
536
762
  )}
537
763
  {...props}
@@ -540,7 +766,7 @@ const defaultComponents = memoizeMarkdownComponents({
540
766
  td: ({ className, ...props }) => (
541
767
  <td
542
768
  className={cn(
543
- "border-b border-l px-4 py-2 text-left last:border-r [&[align=center]]:text-center [&[align=right]]:text-right",
769
+ "aui-md-td border-b border-l px-4 py-2 text-left last:border-r [[align=center]]:text-center [[align=right]]:text-right",
544
770
  className,
545
771
  )}
546
772
  {...props}
@@ -549,7 +775,7 @@ const defaultComponents = memoizeMarkdownComponents({
549
775
  tr: ({ className, ...props }) => (
550
776
  <tr
551
777
  className={cn(
552
- "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",
778
+ "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",
553
779
  className,
554
780
  )}
555
781
  {...props}
@@ -557,14 +783,14 @@ const defaultComponents = memoizeMarkdownComponents({
557
783
  ),
558
784
  sup: ({ className, ...props }) => (
559
785
  <sup
560
- className={cn("[&>a]:text-xs [&>a]:no-underline", className)}
786
+ className={cn("aui-md-sup [&>a]:text-xs [&>a]:no-underline", className)}
561
787
  {...props}
562
788
  />
563
789
  ),
564
790
  pre: ({ className, ...props }) => (
565
791
  <pre
566
792
  className={cn(
567
- "overflow-x-auto rounded-b-lg bg-black p-4 text-white",
793
+ "aui-md-pre overflow-x-auto rounded-t-none! rounded-b-lg bg-black p-4 text-white",
568
794
  className,
569
795
  )}
570
796
  {...props}
@@ -575,7 +801,8 @@ const defaultComponents = memoizeMarkdownComponents({
575
801
  return (
576
802
  <code
577
803
  className={cn(
578
- !isCodeBlock && "bg-muted rounded border font-semibold",
804
+ !isCodeBlock &&
805
+ "aui-md-inline-code rounded border bg-muted font-semibold",
579
806
  className,
580
807
  )}
581
808
  {...props}
@@ -590,57 +817,67 @@ const defaultComponents = memoizeMarkdownComponents({
590
817
  ## components/assistant-ui/thread.tsx
591
818
 
592
819
  ```tsx
820
+ import {
821
+ ComposerAddAttachment,
822
+ ComposerAttachments,
823
+ UserMessageAttachments,
824
+ } from "@/components/assistant-ui/attachment";
825
+ import { MarkdownText } from "@/components/assistant-ui/markdown-text";
826
+ import { ToolFallback } from "@/components/assistant-ui/tool-fallback";
827
+ import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
828
+ import { Button } from "@/components/ui/button";
829
+ import { cn } from "@/lib/utils";
593
830
  import {
594
831
  ActionBarPrimitive,
832
+ AssistantIf,
595
833
  BranchPickerPrimitive,
596
834
  ComposerPrimitive,
835
+ ErrorPrimitive,
597
836
  MessagePrimitive,
598
837
  ThreadPrimitive,
599
838
  } from "@assistant-ui/react";
600
- import type { FC } from "react";
601
839
  import {
602
840
  ArrowDownIcon,
841
+ ArrowUpIcon,
603
842
  CheckIcon,
604
843
  ChevronLeftIcon,
605
844
  ChevronRightIcon,
606
845
  CopyIcon,
846
+ DownloadIcon,
607
847
  PencilIcon,
608
848
  RefreshCwIcon,
609
- SendHorizontalIcon,
849
+ SquareIcon,
610
850
  } from "lucide-react";
611
- import { cn } from "@/lib/utils";
612
-
613
- import { Button } from "@/components/ui/button";
614
- import { MarkdownText } from "@/components/assistant-ui/markdown-text";
615
- import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
851
+ import type { FC } from "react";
616
852
 
617
853
  export const Thread: FC = () => {
618
854
  return (
619
855
  <ThreadPrimitive.Root
620
- className="bg-background box-border flex h-full flex-col overflow-hidden"
856
+ className="aui-root aui-thread-root @container flex h-full flex-col bg-background"
621
857
  style={{
622
- ["--thread-max-width" as string]: "42rem",
858
+ ["--thread-max-width" as string]: "44rem",
623
859
  }}
624
860
  >
625
- <ThreadPrimitive.Viewport className="flex h-full flex-col items-center overflow-y-scroll scroll-smooth bg-inherit px-4 pt-8">
626
- <ThreadWelcome />
861
+ <ThreadPrimitive.Viewport
862
+ turnAnchor="top"
863
+ className="aui-thread-viewport relative flex flex-1 flex-col overflow-x-auto overflow-y-scroll scroll-smooth px-4 pt-4"
864
+ >
865
+ <AssistantIf condition={({ thread }) => thread.isEmpty}>
866
+ <ThreadWelcome />
867
+ </AssistantIf>
627
868
 
628
869
  <ThreadPrimitive.Messages
629
870
  components={{
630
- UserMessage: UserMessage,
631
- EditComposer: EditComposer,
632
- AssistantMessage: AssistantMessage,
871
+ UserMessage,
872
+ EditComposer,
873
+ AssistantMessage,
633
874
  }}
634
875
  />
635
876
 
636
- <ThreadPrimitive.If empty={false}>
637
- <div className="min-h-8 flex-grow" />
638
- </ThreadPrimitive.If>
639
-
640
- <div className="sticky bottom-0 mt-3 flex w-full max-w-[var(--thread-max-width)] flex-col items-center justify-end rounded-t-lg bg-inherit pb-4">
877
+ <ThreadPrimitive.ViewportFooter className="aui-thread-viewport-footer sticky bottom-0 mx-auto mt-auto flex w-full max-w-(--thread-max-width) flex-col gap-4 overflow-visible rounded-t-3xl bg-background pb-4 md:pb-6">
641
878
  <ThreadScrollToBottom />
642
879
  <Composer />
643
- </div>
880
+ </ThreadPrimitive.ViewportFooter>
644
881
  </ThreadPrimitive.Viewport>
645
882
  </ThreadPrimitive.Root>
646
883
  );
@@ -652,7 +889,7 @@ const ThreadScrollToBottom: FC = () => {
652
889
  <TooltipIconButton
653
890
  tooltip="Scroll to bottom"
654
891
  variant="outline"
655
- className="absolute -top-8 rounded-full disabled:invisible"
892
+ className="aui-thread-scroll-to-bottom -top-12 absolute z-10 self-center rounded-full p-4 disabled:invisible dark:bg-background dark:hover:bg-accent"
656
893
  >
657
894
  <ArrowDownIcon />
658
895
  </TooltipIconButton>
@@ -662,175 +899,247 @@ const ThreadScrollToBottom: FC = () => {
662
899
 
663
900
  const ThreadWelcome: FC = () => {
664
901
  return (
665
- <ThreadPrimitive.Empty>
666
- <div className="flex w-full max-w-[var(--thread-max-width)] flex-grow flex-col">
667
- <div className="flex w-full flex-grow flex-col items-center justify-center">
668
- <p className="mt-4 font-medium">How can I help you today?</p>
902
+ <div className="aui-thread-welcome-root mx-auto my-auto flex w-full max-w-(--thread-max-width) grow flex-col">
903
+ <div className="aui-thread-welcome-center flex w-full grow flex-col items-center justify-center">
904
+ <div className="aui-thread-welcome-message flex size-full flex-col justify-center px-4">
905
+ <h1 className="aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in font-semibold text-2xl duration-200">
906
+ Hello there!
907
+ </h1>
908
+ <p className="aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in text-muted-foreground text-xl delay-75 duration-200">
909
+ How can I help you today?
910
+ </p>
669
911
  </div>
670
- <ThreadWelcomeSuggestions />
671
912
  </div>
672
- </ThreadPrimitive.Empty>
913
+ <ThreadSuggestions />
914
+ </div>
673
915
  );
674
916
  };
675
917
 
676
- const ThreadWelcomeSuggestions: FC = () => {
918
+ const SUGGESTIONS = [
919
+ {
920
+ title: "What's the weather",
921
+ label: "in San Francisco?",
922
+ prompt: "What's the weather in San Francisco?",
923
+ },
924
+ {
925
+ title: "Explain React hooks",
926
+ label: "like useState and useEffect",
927
+ prompt: "Explain React hooks like useState and useEffect",
928
+ },
929
+ ] as const;
930
+
931
+ const ThreadSuggestions: FC = () => {
677
932
  return (
678
- <div className="mt-3 flex w-full items-stretch justify-center gap-4">
679
- <ThreadPrimitive.Suggestion
680
- className="hover:bg-muted/80 flex max-w-sm grow basis-0 flex-col items-center justify-center rounded-lg border p-3 transition-colors ease-in"
681
- prompt="What is the weather in Tokyo?"
682
- method="replace"
683
- autoSend
684
- >
685
- <span className="line-clamp-2 text-sm font-semibold text-ellipsis">
686
- What is the weather in Tokyo?
687
- </span>
688
- </ThreadPrimitive.Suggestion>
689
- <ThreadPrimitive.Suggestion
690
- className="hover:bg-muted/80 flex max-w-sm grow basis-0 flex-col items-center justify-center rounded-lg border p-3 transition-colors ease-in"
691
- prompt="What is assistant-ui?"
692
- method="replace"
693
- autoSend
694
- >
695
- <span className="line-clamp-2 text-sm font-semibold text-ellipsis">
696
- What is assistant-ui?
697
- </span>
698
- </ThreadPrimitive.Suggestion>
933
+ <div className="aui-thread-welcome-suggestions grid w-full @md:grid-cols-2 gap-2 pb-4">
934
+ {SUGGESTIONS.map((suggestion, index) => (
935
+ <div
936
+ key={suggestion.prompt}
937
+ className="aui-thread-welcome-suggestion-display fade-in slide-in-from-bottom-2 @md:nth-[n+3]:block nth-[n+3]:hidden animate-in fill-mode-both duration-200"
938
+ style={{ animationDelay: `${100 + index * 50}ms` }}
939
+ >
940
+ <ThreadPrimitive.Suggestion prompt={suggestion.prompt} send asChild>
941
+ <Button
942
+ variant="ghost"
943
+ className="aui-thread-welcome-suggestion h-auto w-full @md:flex-col flex-wrap items-start justify-start gap-1 rounded-2xl border px-4 py-3 text-left text-sm transition-colors hover:bg-muted"
944
+ aria-label={suggestion.prompt}
945
+ >
946
+ <span className="aui-thread-welcome-suggestion-text-1 font-medium">
947
+ {suggestion.title}
948
+ </span>
949
+ <span className="aui-thread-welcome-suggestion-text-2 text-muted-foreground">
950
+ {suggestion.label}
951
+ </span>
952
+ </Button>
953
+ </ThreadPrimitive.Suggestion>
954
+ </div>
955
+ ))}
699
956
  </div>
700
957
  );
701
958
  };
702
959
 
703
960
  const Composer: FC = () => {
704
961
  return (
705
- <ComposerPrimitive.Root className="focus-within:border-ring/20 flex w-full flex-wrap items-end rounded-lg border bg-inherit px-2.5 shadow-sm transition-colors ease-in">
706
- <ComposerPrimitive.Input
707
- rows={1}
708
- autoFocus
709
- placeholder="Write a message..."
710
- className="placeholder:text-muted-foreground max-h-40 flex-grow resize-none border-none bg-transparent px-2 py-4 text-sm outline-none focus:ring-0 disabled:cursor-not-allowed"
711
- />
712
- <ComposerAction />
962
+ <ComposerPrimitive.Root className="aui-composer-root relative flex w-full flex-col">
963
+ <ComposerPrimitive.AttachmentDropzone className="aui-composer-attachment-dropzone flex w-full flex-col rounded-2xl border border-input bg-background px-1 pt-2 outline-none transition-shadow has-[textarea:focus-visible]:border-ring has-[textarea:focus-visible]:ring-2 has-[textarea:focus-visible]:ring-ring/20 data-[dragging=true]:border-ring data-[dragging=true]:border-dashed data-[dragging=true]:bg-accent/50">
964
+ <ComposerAttachments />
965
+ <ComposerPrimitive.Input
966
+ placeholder="Send a message..."
967
+ className="aui-composer-input mb-1 max-h-32 min-h-14 w-full resize-none bg-transparent px-4 pt-2 pb-3 text-sm outline-none placeholder:text-muted-foreground focus-visible:ring-0"
968
+ rows={1}
969
+ autoFocus
970
+ aria-label="Message input"
971
+ />
972
+ <ComposerAction />
973
+ </ComposerPrimitive.AttachmentDropzone>
713
974
  </ComposerPrimitive.Root>
714
975
  );
715
976
  };
716
977
 
717
978
  const ComposerAction: FC = () => {
718
979
  return (
719
- <>
720
- <ThreadPrimitive.If running={false}>
980
+ <div className="aui-composer-action-wrapper relative mx-2 mb-2 flex items-center justify-between">
981
+ <ComposerAddAttachment />
982
+
983
+ <AssistantIf condition={({ thread }) => !thread.isRunning}>
721
984
  <ComposerPrimitive.Send asChild>
722
985
  <TooltipIconButton
723
- tooltip="Send"
986
+ tooltip="Send message"
987
+ side="bottom"
988
+ type="submit"
724
989
  variant="default"
725
- className="my-2.5 size-8 p-2 transition-opacity ease-in"
990
+ size="icon"
991
+ className="aui-composer-send size-8 rounded-full"
992
+ aria-label="Send message"
726
993
  >
727
- <SendHorizontalIcon />
994
+ <ArrowUpIcon className="aui-composer-send-icon size-4" />
728
995
  </TooltipIconButton>
729
996
  </ComposerPrimitive.Send>
730
- </ThreadPrimitive.If>
731
- <ThreadPrimitive.If running>
997
+ </AssistantIf>
998
+
999
+ <AssistantIf condition={({ thread }) => thread.isRunning}>
732
1000
  <ComposerPrimitive.Cancel asChild>
733
- <TooltipIconButton
734
- tooltip="Cancel"
1001
+ <Button
1002
+ type="button"
735
1003
  variant="default"
736
- className="my-2.5 size-8 p-2 transition-opacity ease-in"
1004
+ size="icon"
1005
+ className="aui-composer-cancel size-8 rounded-full"
1006
+ aria-label="Stop generating"
737
1007
  >
738
- <CircleStopIcon />
739
- </TooltipIconButton>
1008
+ <SquareIcon className="aui-composer-cancel-icon size-3 fill-current" />
1009
+ </Button>
740
1010
  </ComposerPrimitive.Cancel>
741
- </ThreadPrimitive.If>
742
- </>
1011
+ </AssistantIf>
1012
+ </div>
743
1013
  );
744
1014
  };
745
1015
 
746
- const UserMessage: FC = () => {
1016
+ const MessageError: FC = () => {
747
1017
  return (
748
- <MessagePrimitive.Root className="grid w-full max-w-[var(--thread-max-width)] auto-rows-auto grid-cols-[minmax(72px,1fr)_auto] gap-y-2 py-4 [&:where(>*)]:col-start-2">
749
- <UserActionBar />
1018
+ <MessagePrimitive.Error>
1019
+ <ErrorPrimitive.Root className="aui-message-error-root mt-2 rounded-md border border-destructive bg-destructive/10 p-3 text-destructive text-sm dark:bg-destructive/5 dark:text-red-200">
1020
+ <ErrorPrimitive.Message className="aui-message-error-message line-clamp-2" />
1021
+ </ErrorPrimitive.Root>
1022
+ </MessagePrimitive.Error>
1023
+ );
1024
+ };
750
1025
 
751
- <div className="bg-muted text-foreground col-start-2 row-start-2 max-w-[calc(var(--thread-max-width)*0.8)] rounded-3xl px-5 py-2.5 break-words">
752
- <MessagePrimitive.Parts />
1026
+ const AssistantMessage: FC = () => {
1027
+ return (
1028
+ <MessagePrimitive.Root
1029
+ className="aui-assistant-message-root fade-in slide-in-from-bottom-1 relative mx-auto w-full max-w-(--thread-max-width) animate-in py-3 duration-150"
1030
+ data-role="assistant"
1031
+ >
1032
+ <div className="aui-assistant-message-content wrap-break-word px-2 text-foreground leading-relaxed">
1033
+ <MessagePrimitive.Parts
1034
+ components={{
1035
+ Text: MarkdownText,
1036
+ tools: { Fallback: ToolFallback },
1037
+ }}
1038
+ />
1039
+ <MessageError />
753
1040
  </div>
754
1041
 
755
- <BranchPicker className="col-span-full col-start-1 row-start-3 -mr-1 justify-end" />
1042
+ <div className="aui-assistant-message-footer mt-1 ml-2 flex">
1043
+ <BranchPicker />
1044
+ <AssistantActionBar />
1045
+ </div>
756
1046
  </MessagePrimitive.Root>
757
1047
  );
758
1048
  };
759
1049
 
760
- const UserActionBar: FC = () => {
1050
+ const AssistantActionBar: FC = () => {
761
1051
  return (
762
1052
  <ActionBarPrimitive.Root
763
1053
  hideWhenRunning
764
1054
  autohide="not-last"
765
- className="col-start-1 row-start-2 mt-2.5 mr-3 flex flex-col items-end"
1055
+ autohideFloat="single-branch"
1056
+ className="aui-assistant-action-bar-root -ml-1 col-start-3 row-start-2 flex gap-1 text-muted-foreground data-floating:absolute data-floating:rounded-md data-floating:border data-floating:bg-background data-floating:p-1 data-floating:shadow-sm"
766
1057
  >
767
- <ActionBarPrimitive.Edit asChild>
768
- <TooltipIconButton tooltip="Edit">
769
- <PencilIcon />
1058
+ <ActionBarPrimitive.Copy asChild>
1059
+ <TooltipIconButton tooltip="Copy">
1060
+ <AssistantIf condition={({ message }) => message.isCopied}>
1061
+ <CheckIcon />
1062
+ </AssistantIf>
1063
+ <AssistantIf condition={({ message }) => !message.isCopied}>
1064
+ <CopyIcon />
1065
+ </AssistantIf>
770
1066
  </TooltipIconButton>
771
- </ActionBarPrimitive.Edit>
1067
+ </ActionBarPrimitive.Copy>
1068
+ <ActionBarPrimitive.ExportMarkdown asChild>
1069
+ <TooltipIconButton tooltip="Export as Markdown">
1070
+ <DownloadIcon />
1071
+ </TooltipIconButton>
1072
+ </ActionBarPrimitive.ExportMarkdown>
1073
+ <ActionBarPrimitive.Reload asChild>
1074
+ <TooltipIconButton tooltip="Refresh">
1075
+ <RefreshCwIcon />
1076
+ </TooltipIconButton>
1077
+ </ActionBarPrimitive.Reload>
772
1078
  </ActionBarPrimitive.Root>
773
1079
  );
774
1080
  };
775
1081
 
776
- const EditComposer: FC = () => {
1082
+ const UserMessage: FC = () => {
777
1083
  return (
778
- <ComposerPrimitive.Root className="bg-muted my-4 flex w-full max-w-[var(--thread-max-width)] flex-col gap-2 rounded-xl">
779
- <ComposerPrimitive.Input className="text-foreground flex h-8 w-full resize-none bg-transparent p-4 pb-0 outline-none" />
780
-
781
- <div className="mx-3 mb-3 flex items-center justify-center gap-2 self-end">
782
- <ComposerPrimitive.Cancel asChild>
783
- <Button variant="ghost">Cancel</Button>
784
- </ComposerPrimitive.Cancel>
785
- <ComposerPrimitive.Send asChild>
786
- <Button>Send</Button>
787
- </ComposerPrimitive.Send>
788
- </div>
789
- </ComposerPrimitive.Root>
790
- );
791
- };
1084
+ <MessagePrimitive.Root
1085
+ className="aui-user-message-root fade-in slide-in-from-bottom-1 mx-auto grid w-full max-w-(--thread-max-width) animate-in auto-rows-auto grid-cols-[minmax(72px,1fr)_auto] content-start gap-y-2 px-2 py-3 duration-150 [&:where(>*)]:col-start-2"
1086
+ data-role="user"
1087
+ >
1088
+ <UserMessageAttachments />
792
1089
 
793
- const AssistantMessage: FC = () => {
794
- return (
795
- <MessagePrimitive.Root className="relative grid w-full max-w-[var(--thread-max-width)] grid-cols-[auto_auto_1fr] grid-rows-[auto_1fr] py-4">
796
- <div className="text-foreground col-span-2 col-start-2 row-start-1 my-1.5 max-w-[calc(var(--thread-max-width)*0.8)] leading-7 break-words">
797
- <MessagePrimitive.Parts components={{ Text: MarkdownText }} />
1090
+ <div className="aui-user-message-content-wrapper relative col-start-2 min-w-0">
1091
+ <div className="aui-user-message-content wrap-break-word rounded-2xl bg-muted px-4 py-2.5 text-foreground">
1092
+ <MessagePrimitive.Parts />
1093
+ </div>
1094
+ <div className="aui-user-action-bar-wrapper -translate-x-full -translate-y-1/2 absolute top-1/2 left-0 pr-2">
1095
+ <UserActionBar />
1096
+ </div>
798
1097
  </div>
799
1098
 
800
- <AssistantActionBar />
801
-
802
- <BranchPicker className="col-start-2 row-start-2 mr-2 -ml-2" />
1099
+ <BranchPicker className="aui-user-branch-picker -mr-1 col-span-full col-start-1 row-start-3 justify-end" />
803
1100
  </MessagePrimitive.Root>
804
1101
  );
805
1102
  };
806
1103
 
807
- const AssistantActionBar: FC = () => {
1104
+ const UserActionBar: FC = () => {
808
1105
  return (
809
1106
  <ActionBarPrimitive.Root
810
1107
  hideWhenRunning
811
1108
  autohide="not-last"
812
- autohideFloat="single-branch"
813
- className="text-muted-foreground data-[floating]:bg-background col-start-3 row-start-2 -ml-1 flex gap-1 data-[floating]:absolute data-[floating]:rounded-md data-[floating]:border data-[floating]:p-1 data-[floating]:shadow-sm"
1109
+ className="aui-user-action-bar-root flex flex-col items-end"
814
1110
  >
815
- <ActionBarPrimitive.Copy asChild>
816
- <TooltipIconButton tooltip="Copy">
817
- <MessagePrimitive.If copied>
818
- <CheckIcon />
819
- </MessagePrimitive.If>
820
- <MessagePrimitive.If copied={false}>
821
- <CopyIcon />
822
- </MessagePrimitive.If>
823
- </TooltipIconButton>
824
- </ActionBarPrimitive.Copy>
825
- <ActionBarPrimitive.Reload asChild>
826
- <TooltipIconButton tooltip="Refresh">
827
- <RefreshCwIcon />
1111
+ <ActionBarPrimitive.Edit asChild>
1112
+ <TooltipIconButton tooltip="Edit" className="aui-user-action-edit p-4">
1113
+ <PencilIcon />
828
1114
  </TooltipIconButton>
829
- </ActionBarPrimitive.Reload>
1115
+ </ActionBarPrimitive.Edit>
830
1116
  </ActionBarPrimitive.Root>
831
1117
  );
832
1118
  };
833
1119
 
1120
+ const EditComposer: FC = () => {
1121
+ return (
1122
+ <MessagePrimitive.Root className="aui-edit-composer-wrapper mx-auto flex w-full max-w-(--thread-max-width) flex-col px-2 py-3">
1123
+ <ComposerPrimitive.Root className="aui-edit-composer-root ml-auto flex w-full max-w-[85%] flex-col rounded-2xl bg-muted">
1124
+ <ComposerPrimitive.Input
1125
+ className="aui-edit-composer-input min-h-14 w-full resize-none bg-transparent p-4 text-foreground text-sm outline-none"
1126
+ autoFocus
1127
+ />
1128
+ <div className="aui-edit-composer-footer mx-3 mb-3 flex items-center gap-2 self-end">
1129
+ <ComposerPrimitive.Cancel asChild>
1130
+ <Button variant="ghost" size="sm">
1131
+ Cancel
1132
+ </Button>
1133
+ </ComposerPrimitive.Cancel>
1134
+ <ComposerPrimitive.Send asChild>
1135
+ <Button size="sm">Update</Button>
1136
+ </ComposerPrimitive.Send>
1137
+ </div>
1138
+ </ComposerPrimitive.Root>
1139
+ </MessagePrimitive.Root>
1140
+ );
1141
+ };
1142
+
834
1143
  const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
835
1144
  className,
836
1145
  ...rest
@@ -839,7 +1148,7 @@ const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
839
1148
  <BranchPickerPrimitive.Root
840
1149
  hideWhenSingleBranch
841
1150
  className={cn(
842
- "text-muted-foreground inline-flex items-center text-xs",
1151
+ "aui-branch-picker-root -ml-2 mr-2 inline-flex items-center text-muted-foreground text-xs",
843
1152
  className,
844
1153
  )}
845
1154
  {...rest}
@@ -849,7 +1158,7 @@ const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
849
1158
  <ChevronLeftIcon />
850
1159
  </TooltipIconButton>
851
1160
  </BranchPickerPrimitive.Previous>
852
- <span className="font-medium">
1161
+ <span className="aui-branch-picker-state font-medium">
853
1162
  <BranchPickerPrimitive.Number /> / <BranchPickerPrimitive.Count />
854
1163
  </span>
855
1164
  <BranchPickerPrimitive.Next asChild>
@@ -861,17 +1170,102 @@ const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
861
1170
  );
862
1171
  };
863
1172
 
864
- const CircleStopIcon = () => {
1173
+ ```
1174
+
1175
+ ## components/assistant-ui/tool-fallback.tsx
1176
+
1177
+ ```tsx
1178
+ import type { ToolCallMessagePartComponent } from "@assistant-ui/react";
1179
+ import {
1180
+ CheckIcon,
1181
+ ChevronDownIcon,
1182
+ ChevronUpIcon,
1183
+ XCircleIcon,
1184
+ } from "lucide-react";
1185
+ import { useState } from "react";
1186
+ import { Button } from "@/components/ui/button";
1187
+ import { cn } from "@/lib/utils";
1188
+
1189
+ export const ToolFallback: ToolCallMessagePartComponent = ({
1190
+ toolName,
1191
+ argsText,
1192
+ result,
1193
+ status,
1194
+ }) => {
1195
+ const [isCollapsed, setIsCollapsed] = useState(true);
1196
+
1197
+ const isCancelled =
1198
+ status?.type === "incomplete" && status.reason === "cancelled";
1199
+ const cancelledReason =
1200
+ isCancelled && status.error
1201
+ ? typeof status.error === "string"
1202
+ ? status.error
1203
+ : JSON.stringify(status.error)
1204
+ : null;
1205
+
865
1206
  return (
866
- <svg
867
- xmlns="http://www.w3.org/2000/svg"
868
- viewBox="0 0 16 16"
869
- fill="currentColor"
870
- width="16"
871
- height="16"
1207
+ <div
1208
+ className={cn(
1209
+ "aui-tool-fallback-root mb-4 flex w-full flex-col gap-3 rounded-lg border py-3",
1210
+ isCancelled && "border-muted-foreground/30 bg-muted/30",
1211
+ )}
872
1212
  >
873
- <rect width="10" height="10" x="3" y="3" rx="2" />
874
- </svg>
1213
+ <div className="aui-tool-fallback-header flex items-center gap-2 px-4">
1214
+ {isCancelled ? (
1215
+ <XCircleIcon className="aui-tool-fallback-icon size-4 text-muted-foreground" />
1216
+ ) : (
1217
+ <CheckIcon className="aui-tool-fallback-icon size-4" />
1218
+ )}
1219
+ <p
1220
+ className={cn(
1221
+ "aui-tool-fallback-title grow",
1222
+ isCancelled && "text-muted-foreground line-through",
1223
+ )}
1224
+ >
1225
+ {isCancelled ? "Cancelled tool: " : "Used tool: "}
1226
+ <b>{toolName}</b>
1227
+ </p>
1228
+ <Button onClick={() => setIsCollapsed(!isCollapsed)}>
1229
+ {isCollapsed ? <ChevronUpIcon /> : <ChevronDownIcon />}
1230
+ </Button>
1231
+ </div>
1232
+ {!isCollapsed && (
1233
+ <div className="aui-tool-fallback-content flex flex-col gap-2 border-t pt-2">
1234
+ {cancelledReason && (
1235
+ <div className="aui-tool-fallback-cancelled-root px-4">
1236
+ <p className="aui-tool-fallback-cancelled-header font-semibold text-muted-foreground">
1237
+ Cancelled reason:
1238
+ </p>
1239
+ <p className="aui-tool-fallback-cancelled-reason text-muted-foreground">
1240
+ {cancelledReason}
1241
+ </p>
1242
+ </div>
1243
+ )}
1244
+ <div
1245
+ className={cn(
1246
+ "aui-tool-fallback-args-root px-4",
1247
+ isCancelled && "opacity-60",
1248
+ )}
1249
+ >
1250
+ <pre className="aui-tool-fallback-args-value whitespace-pre-wrap">
1251
+ {argsText}
1252
+ </pre>
1253
+ </div>
1254
+ {!isCancelled && result !== undefined && (
1255
+ <div className="aui-tool-fallback-result-root border-t border-dashed px-4 pt-2">
1256
+ <p className="aui-tool-fallback-result-header font-semibold">
1257
+ Result:
1258
+ </p>
1259
+ <pre className="aui-tool-fallback-result-content whitespace-pre-wrap">
1260
+ {typeof result === "string"
1261
+ ? result
1262
+ : JSON.stringify(result, null, 2)}
1263
+ </pre>
1264
+ </div>
1265
+ )}
1266
+ </div>
1267
+ )}
1268
+ </div>
875
1269
  );
876
1270
  };
877
1271
 
@@ -882,7 +1276,7 @@ const CircleStopIcon = () => {
882
1276
  ```tsx
883
1277
  "use client";
884
1278
 
885
- import { ComponentPropsWithoutRef, forwardRef } from "react";
1279
+ import { ComponentPropsWithRef, forwardRef } from "react";
886
1280
  import { Slottable } from "@radix-ui/react-slot";
887
1281
 
888
1282
  import {
@@ -893,7 +1287,7 @@ import {
893
1287
  import { Button } from "@/components/ui/button";
894
1288
  import { cn } from "@/lib/utils";
895
1289
 
896
- export type TooltipIconButtonProps = ComponentPropsWithoutRef<typeof Button> & {
1290
+ export type TooltipIconButtonProps = ComponentPropsWithRef<typeof Button> & {
897
1291
  tooltip: string;
898
1292
  side?: "top" | "bottom" | "left" | "right";
899
1293
  };
@@ -909,11 +1303,11 @@ export const TooltipIconButton = forwardRef<
909
1303
  variant="ghost"
910
1304
  size="icon"
911
1305
  {...rest}
912
- className={cn("size-6 p-1", className)}
1306
+ className={cn("aui-button-icon size-6 p-1", className)}
913
1307
  ref={ref}
914
1308
  >
915
1309
  <Slottable>{children}</Slottable>
916
- <span className="sr-only">{tooltip}</span>
1310
+ <span className="aui-sr-only sr-only">{tooltip}</span>
917
1311
  </Button>
918
1312
  </TooltipTrigger>
919
1313
  <TooltipContent side={side}>{tooltip}</TooltipContent>
@@ -1074,6 +1468,62 @@ export const SignupForm: FC = () => {
1074
1468
 
1075
1469
  ```
1076
1470
 
1471
+ ## components/ui/avatar.tsx
1472
+
1473
+ ```tsx
1474
+ "use client";
1475
+
1476
+ import * as React from "react";
1477
+ import * as AvatarPrimitive from "@radix-ui/react-avatar";
1478
+
1479
+ import { cn } from "@/lib/utils";
1480
+
1481
+ const Avatar = React.forwardRef<
1482
+ React.ElementRef<typeof AvatarPrimitive.Root>,
1483
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
1484
+ >(({ className, ...props }, ref) => (
1485
+ <AvatarPrimitive.Root
1486
+ ref={ref}
1487
+ className={cn(
1488
+ "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
1489
+ className,
1490
+ )}
1491
+ {...props}
1492
+ />
1493
+ ));
1494
+ Avatar.displayName = AvatarPrimitive.Root.displayName;
1495
+
1496
+ const AvatarImage = React.forwardRef<
1497
+ React.ElementRef<typeof AvatarPrimitive.Image>,
1498
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
1499
+ >(({ className, ...props }, ref) => (
1500
+ <AvatarPrimitive.Image
1501
+ ref={ref}
1502
+ className={cn("aspect-square h-full w-full", className)}
1503
+ {...props}
1504
+ />
1505
+ ));
1506
+ AvatarImage.displayName = AvatarPrimitive.Image.displayName;
1507
+
1508
+ const AvatarFallback = React.forwardRef<
1509
+ React.ElementRef<typeof AvatarPrimitive.Fallback>,
1510
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
1511
+ >(({ className, ...props }, ref) => (
1512
+ <AvatarPrimitive.Fallback
1513
+ ref={ref}
1514
+ className={cn(
1515
+ "flex h-full w-full items-center justify-center rounded-full bg-muted",
1516
+ className,
1517
+ )}
1518
+ {...props}
1519
+ />
1520
+ ));
1521
+ AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
1522
+
1523
+ export { Avatar, AvatarImage, AvatarFallback };
1524
+
1525
+ ```
1526
+
1077
1527
  ## components/ui/button.tsx
1078
1528
 
1079
1529
  ```tsx
@@ -1084,16 +1534,16 @@ import { cva, type VariantProps } from "class-variance-authority";
1084
1534
  import { cn } from "@/lib/utils";
1085
1535
 
1086
1536
  const buttonVariants = cva(
1087
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
1537
+ "inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm outline-none transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
1088
1538
  {
1089
1539
  variants: {
1090
1540
  variant: {
1091
1541
  default:
1092
1542
  "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
1093
1543
  destructive:
1094
- "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
1544
+ "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
1095
1545
  outline:
1096
- "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
1546
+ "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
1097
1547
  secondary:
1098
1548
  "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
1099
1549
  ghost:
@@ -1102,7 +1552,7 @@ const buttonVariants = cva(
1102
1552
  },
1103
1553
  size: {
1104
1554
  default: "h-9 px-4 py-2 has-[>svg]:px-3",
1105
- sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
1555
+ sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
1106
1556
  lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
1107
1557
  icon: "size-9",
1108
1558
  },
@@ -1139,6 +1589,147 @@ export { Button, buttonVariants };
1139
1589
 
1140
1590
  ```
1141
1591
 
1592
+ ## components/ui/dialog.tsx
1593
+
1594
+ ```tsx
1595
+ "use client";
1596
+
1597
+ import * as React from "react";
1598
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
1599
+ import { XIcon } from "lucide-react";
1600
+
1601
+ import { cn } from "@/lib/utils";
1602
+
1603
+ function Dialog({
1604
+ ...props
1605
+ }: React.ComponentProps<typeof DialogPrimitive.Root>) {
1606
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />;
1607
+ }
1608
+
1609
+ function DialogTrigger({
1610
+ ...props
1611
+ }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
1612
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
1613
+ }
1614
+
1615
+ function DialogPortal({
1616
+ ...props
1617
+ }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
1618
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
1619
+ }
1620
+
1621
+ function DialogClose({
1622
+ ...props
1623
+ }: React.ComponentProps<typeof DialogPrimitive.Close>) {
1624
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
1625
+ }
1626
+
1627
+ function DialogOverlay({
1628
+ className,
1629
+ ...props
1630
+ }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
1631
+ return (
1632
+ <DialogPrimitive.Overlay
1633
+ data-slot="dialog-overlay"
1634
+ className={cn(
1635
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80 data-[state=closed]:animate-out data-[state=open]:animate-in",
1636
+ className,
1637
+ )}
1638
+ {...props}
1639
+ />
1640
+ );
1641
+ }
1642
+
1643
+ function DialogContent({
1644
+ className,
1645
+ children,
1646
+ ...props
1647
+ }: React.ComponentProps<typeof DialogPrimitive.Content>) {
1648
+ return (
1649
+ <DialogPortal data-slot="dialog-portal">
1650
+ <DialogOverlay />
1651
+ <DialogPrimitive.Content
1652
+ data-slot="dialog-content"
1653
+ className={cn(
1654
+ "data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 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 bg-background p-6 shadow-lg duration-200 data-[state=closed]:animate-out data-[state=open]:animate-in sm:max-w-lg",
1655
+ className,
1656
+ )}
1657
+ {...props}
1658
+ >
1659
+ {children}
1660
+ <DialogPrimitive.Close className="absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0">
1661
+ <XIcon />
1662
+ <span className="sr-only">Close</span>
1663
+ </DialogPrimitive.Close>
1664
+ </DialogPrimitive.Content>
1665
+ </DialogPortal>
1666
+ );
1667
+ }
1668
+
1669
+ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
1670
+ return (
1671
+ <div
1672
+ data-slot="dialog-header"
1673
+ className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
1674
+ {...props}
1675
+ />
1676
+ );
1677
+ }
1678
+
1679
+ function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
1680
+ return (
1681
+ <div
1682
+ data-slot="dialog-footer"
1683
+ className={cn(
1684
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
1685
+ className,
1686
+ )}
1687
+ {...props}
1688
+ />
1689
+ );
1690
+ }
1691
+
1692
+ function DialogTitle({
1693
+ className,
1694
+ ...props
1695
+ }: React.ComponentProps<typeof DialogPrimitive.Title>) {
1696
+ return (
1697
+ <DialogPrimitive.Title
1698
+ data-slot="dialog-title"
1699
+ className={cn("font-semibold text-lg leading-none", className)}
1700
+ {...props}
1701
+ />
1702
+ );
1703
+ }
1704
+
1705
+ function DialogDescription({
1706
+ className,
1707
+ ...props
1708
+ }: React.ComponentProps<typeof DialogPrimitive.Description>) {
1709
+ return (
1710
+ <DialogPrimitive.Description
1711
+ data-slot="dialog-description"
1712
+ className={cn("text-muted-foreground text-sm", className)}
1713
+ {...props}
1714
+ />
1715
+ );
1716
+ }
1717
+
1718
+ export {
1719
+ Dialog,
1720
+ DialogClose,
1721
+ DialogContent,
1722
+ DialogDescription,
1723
+ DialogFooter,
1724
+ DialogHeader,
1725
+ DialogOverlay,
1726
+ DialogPortal,
1727
+ DialogTitle,
1728
+ DialogTrigger,
1729
+ };
1730
+
1731
+ ```
1732
+
1142
1733
  ## components/ui/form.tsx
1143
1734
 
1144
1735
  ```tsx
@@ -1326,9 +1917,9 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
1326
1917
  type={type}
1327
1918
  data-slot="input"
1328
1919
  className={cn(
1329
- "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
1330
- "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
1331
- "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
1920
+ "flex h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs outline-none transition-[color,box-shadow] selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:font-medium file:text-foreground file:text-sm placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30",
1921
+ "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
1922
+ "aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
1332
1923
  className,
1333
1924
  )}
1334
1925
  {...props}
@@ -1358,7 +1949,7 @@ function Label({
1358
1949
  <LabelPrimitive.Root
1359
1950
  data-slot="label"
1360
1951
  className={cn(
1361
- "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
1952
+ "flex select-none items-center gap-2 font-medium text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-50 group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50",
1362
1953
  className,
1363
1954
  )}
1364
1955
  {...props}
@@ -1414,13 +2005,13 @@ function ResizableHandle({
1414
2005
  <ResizablePrimitive.PanelResizeHandle
1415
2006
  data-slot="resizable-handle"
1416
2007
  className={cn(
1417
- "bg-border focus-visible:ring-ring relative flex w-px items-center justify-center after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:ring-1 focus-visible:ring-offset-1 focus-visible:outline-hidden data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:translate-x-0 data-[panel-group-direction=vertical]:after:-translate-y-1/2 [&[data-panel-group-direction=vertical]>div]:rotate-90",
2008
+ "after:-translate-x-1/2 data-[panel-group-direction=vertical]:after:-translate-y-1/2 relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90",
1418
2009
  className,
1419
2010
  )}
1420
2011
  {...props}
1421
2012
  >
1422
2013
  {withHandle && (
1423
- <div className="bg-border z-10 flex h-4 w-3 items-center justify-center rounded-xs border">
2014
+ <div className="z-10 flex h-4 w-3 items-center justify-center rounded-xs border bg-border">
1424
2015
  <GripVerticalIcon className="size-2.5" />
1425
2016
  </div>
1426
2017
  )}
@@ -1463,7 +2054,7 @@ function TabsList({
1463
2054
  <TabsPrimitive.List
1464
2055
  data-slot="tabs-list"
1465
2056
  className={cn(
1466
- "bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]",
2057
+ "inline-flex h-9 w-fit items-center justify-center rounded-lg bg-muted p-[3px] text-muted-foreground",
1467
2058
  className,
1468
2059
  )}
1469
2060
  {...props}
@@ -1479,7 +2070,7 @@ function TabsTrigger({
1479
2070
  <TabsPrimitive.Trigger
1480
2071
  data-slot="tabs-trigger"
1481
2072
  className={cn(
1482
- "data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
2073
+ "inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 whitespace-nowrap rounded-md border border-transparent px-2 py-1 font-medium text-foreground text-sm transition-[color,box-shadow] focus-visible:border-ring focus-visible:outline-1 focus-visible:outline-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:shadow-sm dark:text-muted-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 dark:data-[state=active]:text-foreground [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
1483
2074
  className,
1484
2075
  )}
1485
2076
  {...props}
@@ -1555,13 +2146,13 @@ function TooltipContent({
1555
2146
  data-slot="tooltip-content"
1556
2147
  sideOffset={sideOffset}
1557
2148
  className={cn(
1558
- "bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-[--radix-tooltip-content-transform-origin] rounded-md px-3 py-1.5 text-xs text-balance",
2149
+ "fade-in-0 zoom-in-95 data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-[--radix-tooltip-content-transform-origin] animate-in text-balance rounded-md bg-primary px-3 py-1.5 text-primary-foreground text-xs data-[state=closed]:animate-out",
1559
2150
  className,
1560
2151
  )}
1561
2152
  {...props}
1562
2153
  >
1563
2154
  {children}
1564
- <TooltipPrimitive.Arrow className="bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
2155
+ <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-primary fill-primary" />
1565
2156
  </TooltipPrimitive.Content>
1566
2157
  </TooltipPrimitive.Portal>
1567
2158
  );
@@ -1601,18 +2192,6 @@ export function cn(...inputs: ClassValue[]) {
1601
2192
 
1602
2193
  ```
1603
2194
 
1604
- ## next-env.d.ts
1605
-
1606
- ```typescript
1607
- /// <reference types="next" />
1608
- /// <reference types="next/image-types/global" />
1609
- import "./.next/types/routes.d.ts";
1610
-
1611
- // NOTE: This file should not be edited
1612
- // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
1613
-
1614
- ```
1615
-
1616
2195
  ## next.config.ts
1617
2196
 
1618
2197
  ```typescript
@@ -1636,38 +2215,37 @@ export default nextConfig;
1636
2215
  "scripts": {
1637
2216
  "dev": "next dev --turbo",
1638
2217
  "build": "next build",
1639
- "start": "next start",
1640
- "lint": "eslint ."
2218
+ "start": "next start"
1641
2219
  },
1642
2220
  "dependencies": {
1643
- "@ai-sdk/openai": "^2.0.68",
2221
+ "@ai-sdk/openai": "^2.0.77",
1644
2222
  "@assistant-ui/react": "workspace:*",
1645
2223
  "@assistant-ui/react-ai-sdk": "workspace:*",
1646
2224
  "@assistant-ui/react-hook-form": "workspace:*",
1647
2225
  "@assistant-ui/react-markdown": "workspace:*",
1648
2226
  "@hookform/resolvers": "^5.2.2",
1649
2227
  "@radix-ui/react-avatar": "^1.1.11",
2228
+ "@radix-ui/react-dialog": "^1.1.15",
1650
2229
  "@radix-ui/react-icons": "^1.3.2",
1651
2230
  "@radix-ui/react-label": "^2.1.8",
1652
2231
  "@radix-ui/react-slot": "^1.2.4",
1653
2232
  "@radix-ui/react-tabs": "^1.1.13",
1654
2233
  "@radix-ui/react-tooltip": "^1.2.8",
1655
2234
  "@react-hook/media-query": "^1.1.1",
1656
- "ai": "^5.0.93",
2235
+ "ai": "^5.0.107",
1657
2236
  "class-variance-authority": "^0.7.1",
1658
2237
  "clsx": "^2.1.1",
1659
- "json-schema-to-zod": "^2.6.1",
1660
- "lucide-react": "^0.554.0",
1661
- "next": "16.0.3",
1662
- "react": "19.2.0",
1663
- "react-dom": "19.2.0",
1664
- "react-hook-form": "^7.66.1",
2238
+ "lucide-react": "^0.556.0",
2239
+ "next": "16.0.7",
2240
+ "react": "19.2.1",
2241
+ "react-dom": "19.2.1",
2242
+ "react-hook-form": "^7.68.0",
1665
2243
  "react-resizable-panels": "^3.0.6",
1666
2244
  "remark-gfm": "^4.0.1",
1667
2245
  "tailwind-merge": "^3.4.0",
1668
2246
  "tw-animate-css": "^1.4.0",
1669
- "zod": "^4.1.12",
1670
- "zustand": "^5.0.8"
2247
+ "zod": "^4.1.13",
2248
+ "zustand": "^5.0.9"
1671
2249
  },
1672
2250
  "devDependencies": {
1673
2251
  "@assistant-ui/x-buildutils": "workspace:*",