@assistant-ui/mcp-docs-server 0.1.19 → 0.1.21

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 (108) hide show
  1. package/.docs/organized/code-examples/with-ag-ui.md +172 -1633
  2. package/.docs/organized/code-examples/with-ai-sdk-v6.md +42 -1640
  3. package/.docs/organized/code-examples/with-assistant-transport.md +40 -1743
  4. package/.docs/organized/code-examples/with-cloud.md +71 -1745
  5. package/.docs/organized/code-examples/with-custom-thread-list.md +87 -1723
  6. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +70 -1637
  7. package/.docs/organized/code-examples/with-external-store.md +67 -1624
  8. package/.docs/organized/code-examples/with-ffmpeg.md +71 -1629
  9. package/.docs/organized/code-examples/with-langgraph.md +95 -1893
  10. package/.docs/organized/code-examples/with-parent-id-grouping.md +57 -1654
  11. package/.docs/organized/code-examples/with-react-hook-form.md +220 -2163
  12. package/.docs/organized/code-examples/with-react-router.md +66 -1318
  13. package/.docs/organized/code-examples/with-store.md +31 -31
  14. package/.docs/organized/code-examples/with-tanstack.md +77 -861
  15. package/.docs/organized/code-examples/with-tap-runtime.md +812 -0
  16. package/.docs/raw/docs/(docs)/cli.mdx +66 -0
  17. package/.docs/raw/docs/(docs)/copilots/make-assistant-tool-ui.mdx +0 -1
  18. package/.docs/raw/docs/(docs)/copilots/make-assistant-tool.mdx +0 -1
  19. package/.docs/raw/docs/(docs)/copilots/model-context.mdx +4 -4
  20. package/.docs/raw/docs/(docs)/copilots/motivation.mdx +3 -3
  21. package/.docs/raw/docs/(docs)/devtools.mdx +0 -1
  22. package/.docs/raw/docs/(docs)/guides/attachments.mdx +2 -3
  23. package/.docs/raw/docs/(docs)/guides/context-api.mdx +117 -117
  24. package/.docs/raw/docs/(docs)/guides/suggestions.mdx +296 -0
  25. package/.docs/raw/docs/(docs)/guides/tools.mdx +336 -513
  26. package/.docs/raw/docs/(docs)/index.mdx +33 -410
  27. package/.docs/raw/docs/(docs)/installation.mdx +450 -0
  28. package/.docs/raw/docs/(docs)/llm.mdx +209 -0
  29. package/.docs/raw/docs/(reference)/api-reference/context-providers/assistant-runtime-provider.mdx +0 -1
  30. package/.docs/raw/docs/(reference)/api-reference/context-providers/text-message-part-provider.mdx +0 -1
  31. package/.docs/raw/docs/(reference)/api-reference/integrations/react-data-stream.mdx +48 -3
  32. package/.docs/raw/docs/(reference)/api-reference/integrations/react-hook-form.mdx +0 -1
  33. package/.docs/raw/docs/(reference)/api-reference/integrations/vercel-ai-sdk.mdx +0 -1
  34. package/.docs/raw/docs/(reference)/api-reference/overview.mdx +9 -3
  35. package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar-more.mdx +20 -52
  36. package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar.mdx +16 -39
  37. package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-if.mdx +49 -50
  38. package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-modal.mdx +3 -11
  39. package/.docs/raw/docs/(reference)/api-reference/primitives/attachment.mdx +0 -3
  40. package/.docs/raw/docs/(reference)/api-reference/primitives/branch-picker.mdx +0 -1
  41. package/.docs/raw/docs/(reference)/api-reference/primitives/composer.mdx +5 -16
  42. package/.docs/raw/docs/(reference)/api-reference/primitives/composition.mdx +0 -1
  43. package/.docs/raw/docs/(reference)/api-reference/primitives/error.mdx +0 -1
  44. package/.docs/raw/docs/(reference)/api-reference/primitives/message-part.mdx +1 -2
  45. package/.docs/raw/docs/(reference)/api-reference/primitives/message.mdx +0 -1
  46. package/.docs/raw/docs/(reference)/api-reference/primitives/suggestion.mdx +152 -0
  47. package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list-item-more.mdx +0 -1
  48. package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list-item.mdx +1 -2
  49. package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list.mdx +1 -2
  50. package/.docs/raw/docs/(reference)/api-reference/primitives/thread.mdx +28 -40
  51. package/.docs/raw/docs/(reference)/api-reference/runtimes/assistant-runtime.mdx +0 -1
  52. package/.docs/raw/docs/(reference)/api-reference/runtimes/attachment-runtime.mdx +1 -2
  53. package/.docs/raw/docs/(reference)/api-reference/runtimes/composer-runtime.mdx +2 -3
  54. package/.docs/raw/docs/(reference)/api-reference/runtimes/message-part-runtime.mdx +1 -2
  55. package/.docs/raw/docs/(reference)/api-reference/runtimes/message-runtime.mdx +1 -2
  56. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-item-runtime.mdx +0 -1
  57. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-runtime.mdx +0 -1
  58. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-runtime.mdx +1 -2
  59. package/.docs/raw/docs/(reference)/legacy/styled/assistant-modal.mdx +0 -1
  60. package/.docs/raw/docs/(reference)/legacy/styled/decomposition.mdx +5 -5
  61. package/.docs/raw/docs/(reference)/legacy/styled/markdown.mdx +0 -1
  62. package/.docs/raw/docs/(reference)/legacy/styled/thread.mdx +0 -1
  63. package/.docs/raw/docs/(reference)/migrations/v0-12.mdx +207 -33
  64. package/.docs/raw/docs/(reference)/react-compatibility.mdx +0 -1
  65. package/.docs/raw/docs/cloud/persistence/ai-sdk.mdx +0 -1
  66. package/.docs/raw/docs/cloud/persistence/langgraph.mdx +0 -1
  67. package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +0 -1
  68. package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +118 -0
  69. package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +198 -0
  70. package/.docs/raw/docs/runtimes/assistant-transport.mdx +3 -3
  71. package/.docs/raw/docs/runtimes/custom/custom-thread-list.mdx +5 -6
  72. package/.docs/raw/docs/runtimes/custom/external-store.mdx +9 -11
  73. package/.docs/raw/docs/runtimes/custom/local.mdx +43 -36
  74. package/.docs/raw/docs/runtimes/data-stream.mdx +35 -3
  75. package/.docs/raw/docs/runtimes/langgraph/index.mdx +1 -2
  76. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +0 -1
  77. package/.docs/raw/docs/runtimes/langserve.mdx +0 -1
  78. package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +0 -1
  79. package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +0 -1
  80. package/.docs/raw/docs/ui/accordion.mdx +259 -0
  81. package/.docs/raw/docs/ui/assistant-modal.mdx +1 -3
  82. package/.docs/raw/docs/ui/assistant-sidebar.mdx +1 -3
  83. package/.docs/raw/docs/ui/attachment.mdx +0 -2
  84. package/.docs/raw/docs/ui/badge.mdx +138 -0
  85. package/.docs/raw/docs/ui/diff-viewer.mdx +279 -0
  86. package/.docs/raw/docs/ui/file.mdx +152 -0
  87. package/.docs/raw/docs/ui/image.mdx +100 -0
  88. package/.docs/raw/docs/ui/markdown.mdx +0 -1
  89. package/.docs/raw/docs/ui/mermaid.mdx +0 -1
  90. package/.docs/raw/docs/ui/model-selector.mdx +224 -0
  91. package/.docs/raw/docs/ui/part-grouping.mdx +4 -5
  92. package/.docs/raw/docs/ui/reasoning.mdx +6 -5
  93. package/.docs/raw/docs/ui/scrollbar.mdx +26 -9
  94. package/.docs/raw/docs/ui/select.mdx +245 -0
  95. package/.docs/raw/docs/ui/sources.mdx +6 -5
  96. package/.docs/raw/docs/ui/streamdown.mdx +348 -0
  97. package/.docs/raw/docs/ui/syntax-highlighting.mdx +8 -63
  98. package/.docs/raw/docs/ui/tabs.mdx +259 -0
  99. package/.docs/raw/docs/ui/thread-list.mdx +98 -16
  100. package/.docs/raw/docs/ui/thread.mdx +57 -73
  101. package/.docs/raw/docs/ui/tool-fallback.mdx +0 -1
  102. package/.docs/raw/docs/ui/tool-group.mdx +1 -3
  103. package/README.md +3 -3
  104. package/package.json +4 -4
  105. package/src/tools/tests/examples.test.ts +1 -1
  106. package/.docs/raw/docs/(docs)/about-assistantui.mdx +0 -54
  107. package/.docs/raw/docs/(docs)/mcp-docs-server.mdx +0 -321
  108. package/.docs/raw/docs/runtimes/ai-sdk/use-chat.mdx +0 -219
@@ -33,6 +33,8 @@ export async function POST(req: Request) {
33
33
  @import "tailwindcss";
34
34
  @import "tw-animate-css";
35
35
 
36
+ @source "../../../packages/ui/src";
37
+
36
38
  @custom-variant dark (&:is(.dark *));
37
39
 
38
40
  @theme inline {
@@ -266,7 +268,7 @@ export default dynamic(() => Promise.resolve(NoSSRWrapper), {
266
268
  import {
267
269
  useAssistantInstructions,
268
270
  useAssistantTool,
269
- useAssistantState,
271
+ useAuiState,
270
272
  } from "@assistant-ui/react";
271
273
  import { z } from "zod";
272
274
  import { FFmpeg } from "@ffmpeg/ffmpeg";
@@ -415,9 +417,7 @@ const FfmpegTool: FC<{ file: File }> = ({ file }) => {
415
417
 
416
418
  export default function Home() {
417
419
  const [lastFile, setLastFile] = useState<File | null>(null);
418
- const attachments = useAssistantState(
419
- ({ thread }) => thread.composer.attachments,
420
- );
420
+ const attachments = useAuiState(({ thread }) => thread.composer.attachments);
421
421
  useEffect(() => {
422
422
  const lastAttachment = attachments[attachments.length - 1];
423
423
  if (!lastAttachment) return;
@@ -468,1621 +468,12 @@ export default function Home() {
468
468
  "lib": "@/lib",
469
469
  "hooks": "@/hooks"
470
470
  },
471
- "iconLibrary": "lucide"
472
- }
473
-
474
- ```
475
-
476
- ## components/assistant-ui/attachment.tsx
477
-
478
- ```tsx
479
- "use client";
480
-
481
- import { PropsWithChildren, useEffect, useState, type FC } from "react";
482
- import Image from "next/image";
483
- import { XIcon, PlusIcon, FileText } from "lucide-react";
484
- import {
485
- AttachmentPrimitive,
486
- ComposerPrimitive,
487
- MessagePrimitive,
488
- useAssistantState,
489
- useAssistantApi,
490
- } from "@assistant-ui/react";
491
- import { useShallow } from "zustand/shallow";
492
- import {
493
- Tooltip,
494
- TooltipContent,
495
- TooltipTrigger,
496
- } from "@/components/ui/tooltip";
497
- import {
498
- Dialog,
499
- DialogTitle,
500
- DialogContent,
501
- DialogTrigger,
502
- } from "@/components/ui/dialog";
503
- import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
504
- import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
505
- import { cn } from "@/lib/utils";
506
-
507
- const useFileSrc = (file: File | undefined) => {
508
- const [src, setSrc] = useState<string | undefined>(undefined);
509
-
510
- useEffect(() => {
511
- if (!file) {
512
- setSrc(undefined);
513
- return;
514
- }
515
-
516
- const objectUrl = URL.createObjectURL(file);
517
- setSrc(objectUrl);
518
-
519
- return () => {
520
- URL.revokeObjectURL(objectUrl);
521
- };
522
- }, [file]);
523
-
524
- return src;
525
- };
526
-
527
- const useAttachmentSrc = () => {
528
- const { file, src } = useAssistantState(
529
- useShallow(({ attachment }): { file?: File; src?: string } => {
530
- if (attachment.type !== "image") return {};
531
- if (attachment.file) return { file: attachment.file };
532
- const src = attachment.content?.filter((c) => c.type === "image")[0]
533
- ?.image;
534
- if (!src) return {};
535
- return { src };
536
- }),
537
- );
538
-
539
- return useFileSrc(file) ?? src;
540
- };
541
-
542
- type AttachmentPreviewProps = {
543
- src: string;
544
- };
545
-
546
- const AttachmentPreview: FC<AttachmentPreviewProps> = ({ src }) => {
547
- const [isLoaded, setIsLoaded] = useState(false);
548
- return (
549
- <Image
550
- src={src}
551
- alt="Image Preview"
552
- width={1}
553
- height={1}
554
- className={
555
- isLoaded
556
- ? "aui-attachment-preview-image-loaded block h-auto max-h-[80vh] w-auto max-w-full object-contain"
557
- : "aui-attachment-preview-image-loading hidden"
558
- }
559
- onLoadingComplete={() => setIsLoaded(true)}
560
- priority={false}
561
- />
562
- );
563
- };
564
-
565
- const AttachmentPreviewDialog: FC<PropsWithChildren> = ({ children }) => {
566
- const src = useAttachmentSrc();
567
-
568
- if (!src) return children;
569
-
570
- return (
571
- <Dialog>
572
- <DialogTrigger
573
- className="aui-attachment-preview-trigger cursor-pointer transition-colors hover:bg-accent/50"
574
- asChild
575
- >
576
- {children}
577
- </DialogTrigger>
578
- <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">
579
- <DialogTitle className="aui-sr-only sr-only">
580
- Image Attachment Preview
581
- </DialogTitle>
582
- <div className="aui-attachment-preview relative mx-auto flex max-h-[80dvh] w-full items-center justify-center overflow-hidden bg-background">
583
- <AttachmentPreview src={src} />
584
- </div>
585
- </DialogContent>
586
- </Dialog>
587
- );
588
- };
589
-
590
- const AttachmentThumb: FC = () => {
591
- const isImage = useAssistantState(
592
- ({ attachment }) => attachment.type === "image",
593
- );
594
- const src = useAttachmentSrc();
595
-
596
- return (
597
- <Avatar className="aui-attachment-tile-avatar h-full w-full rounded-none">
598
- <AvatarImage
599
- src={src}
600
- alt="Attachment preview"
601
- className="aui-attachment-tile-image object-cover"
602
- />
603
- <AvatarFallback delayMs={isImage ? 200 : 0}>
604
- <FileText className="aui-attachment-tile-fallback-icon size-8 text-muted-foreground" />
605
- </AvatarFallback>
606
- </Avatar>
607
- );
608
- };
609
-
610
- const AttachmentUI: FC = () => {
611
- const api = useAssistantApi();
612
- const isComposer = api.attachment.source === "composer";
613
-
614
- const isImage = useAssistantState(
615
- ({ attachment }) => attachment.type === "image",
616
- );
617
- const typeLabel = useAssistantState(({ attachment }) => {
618
- const type = attachment.type;
619
- switch (type) {
620
- case "image":
621
- return "Image";
622
- case "document":
623
- return "Document";
624
- case "file":
625
- return "File";
626
- default:
627
- const _exhaustiveCheck: never = type;
628
- throw new Error(`Unknown attachment type: ${_exhaustiveCheck}`);
629
- }
630
- });
631
-
632
- return (
633
- <Tooltip>
634
- <AttachmentPrimitive.Root
635
- className={cn(
636
- "aui-attachment-root relative",
637
- isImage &&
638
- "aui-attachment-root-composer only:[&>#attachment-tile]:size-24",
639
- )}
640
- >
641
- <AttachmentPreviewDialog>
642
- <TooltipTrigger asChild>
643
- <div
644
- className={cn(
645
- "aui-attachment-tile size-14 cursor-pointer overflow-hidden rounded-[14px] border bg-muted transition-opacity hover:opacity-75",
646
- isComposer &&
647
- "aui-attachment-tile-composer border-foreground/20",
648
- )}
649
- role="button"
650
- id="attachment-tile"
651
- aria-label={`${typeLabel} attachment`}
652
- >
653
- <AttachmentThumb />
654
- </div>
655
- </TooltipTrigger>
656
- </AttachmentPreviewDialog>
657
- {isComposer && <AttachmentRemove />}
658
- </AttachmentPrimitive.Root>
659
- <TooltipContent side="top">
660
- <AttachmentPrimitive.Name />
661
- </TooltipContent>
662
- </Tooltip>
663
- );
664
- };
665
-
666
- const AttachmentRemove: FC = () => {
667
- return (
668
- <AttachmentPrimitive.Remove asChild>
669
- <TooltipIconButton
670
- tooltip="Remove file"
671
- 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"
672
- side="top"
673
- >
674
- <XIcon className="aui-attachment-remove-icon size-3 dark:stroke-[2.5px]" />
675
- </TooltipIconButton>
676
- </AttachmentPrimitive.Remove>
677
- );
678
- };
679
-
680
- export const UserMessageAttachments: FC = () => {
681
- return (
682
- <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">
683
- <MessagePrimitive.Attachments components={{ Attachment: AttachmentUI }} />
684
- </div>
685
- );
686
- };
687
-
688
- export const ComposerAttachments: FC = () => {
689
- return (
690
- <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">
691
- <ComposerPrimitive.Attachments
692
- components={{ Attachment: AttachmentUI }}
693
- />
694
- </div>
695
- );
696
- };
697
-
698
- export const ComposerAddAttachment: FC = () => {
699
- return (
700
- <ComposerPrimitive.AddAttachment asChild>
701
- <TooltipIconButton
702
- tooltip="Add Attachment"
703
- side="bottom"
704
- variant="ghost"
705
- size="icon"
706
- className="aui-composer-add-attachment size-8.5 rounded-full p-1 font-semibold text-xs hover:bg-muted-foreground/15 dark:border-muted-foreground/15 dark:hover:bg-muted-foreground/30"
707
- aria-label="Add Attachment"
708
- >
709
- <PlusIcon className="aui-attachment-add-icon size-5 stroke-[1.5px]" />
710
- </TooltipIconButton>
711
- </ComposerPrimitive.AddAttachment>
712
- );
713
- };
714
-
715
- ```
716
-
717
- ## components/assistant-ui/markdown-text.tsx
718
-
719
- ```tsx
720
- "use client";
721
-
722
- import "@assistant-ui/react-markdown/styles/dot.css";
723
-
724
- import {
725
- type CodeHeaderProps,
726
- MarkdownTextPrimitive,
727
- unstable_memoizeMarkdownComponents as memoizeMarkdownComponents,
728
- useIsMarkdownCodeBlock,
729
- } from "@assistant-ui/react-markdown";
730
- import remarkGfm from "remark-gfm";
731
- import { type FC, memo, useState } from "react";
732
- import { CheckIcon, CopyIcon } from "lucide-react";
733
-
734
- import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
735
- import { cn } from "@/lib/utils";
736
-
737
- const MarkdownTextImpl = () => {
738
- return (
739
- <MarkdownTextPrimitive
740
- remarkPlugins={[remarkGfm]}
741
- className="aui-md"
742
- components={defaultComponents}
743
- />
744
- );
745
- };
746
-
747
- export const MarkdownText = memo(MarkdownTextImpl);
748
-
749
- const CodeHeader: FC<CodeHeaderProps> = ({ language, code }) => {
750
- const { isCopied, copyToClipboard } = useCopyToClipboard();
751
- const onCopy = () => {
752
- if (!code || isCopied) return;
753
- copyToClipboard(code);
754
- };
755
-
756
- return (
757
- <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">
758
- <span className="aui-code-header-language lowercase [&>span]:text-xs">
759
- {language}
760
- </span>
761
- <TooltipIconButton tooltip="Copy" onClick={onCopy}>
762
- {!isCopied && <CopyIcon />}
763
- {isCopied && <CheckIcon />}
764
- </TooltipIconButton>
765
- </div>
766
- );
767
- };
768
-
769
- const useCopyToClipboard = ({
770
- copiedDuration = 3000,
771
- }: {
772
- copiedDuration?: number;
773
- } = {}) => {
774
- const [isCopied, setIsCopied] = useState<boolean>(false);
775
-
776
- const copyToClipboard = (value: string) => {
777
- if (!value) return;
778
-
779
- navigator.clipboard.writeText(value).then(() => {
780
- setIsCopied(true);
781
- setTimeout(() => setIsCopied(false), copiedDuration);
782
- });
783
- };
784
-
785
- return { isCopied, copyToClipboard };
786
- };
787
-
788
- const defaultComponents = memoizeMarkdownComponents({
789
- h1: ({ className, ...props }) => (
790
- <h1
791
- className={cn(
792
- "aui-md-h1 mb-8 scroll-m-20 font-extrabold text-4xl tracking-tight last:mb-0",
793
- className,
794
- )}
795
- {...props}
796
- />
797
- ),
798
- h2: ({ className, ...props }) => (
799
- <h2
800
- className={cn(
801
- "aui-md-h2 mt-8 mb-4 scroll-m-20 font-semibold text-3xl tracking-tight first:mt-0 last:mb-0",
802
- className,
803
- )}
804
- {...props}
805
- />
806
- ),
807
- h3: ({ className, ...props }) => (
808
- <h3
809
- className={cn(
810
- "aui-md-h3 mt-6 mb-4 scroll-m-20 font-semibold text-2xl tracking-tight first:mt-0 last:mb-0",
811
- className,
812
- )}
813
- {...props}
814
- />
815
- ),
816
- h4: ({ className, ...props }) => (
817
- <h4
818
- className={cn(
819
- "aui-md-h4 mt-6 mb-4 scroll-m-20 font-semibold text-xl tracking-tight first:mt-0 last:mb-0",
820
- className,
821
- )}
822
- {...props}
823
- />
824
- ),
825
- h5: ({ className, ...props }) => (
826
- <h5
827
- className={cn(
828
- "aui-md-h5 my-4 font-semibold text-lg first:mt-0 last:mb-0",
829
- className,
830
- )}
831
- {...props}
832
- />
833
- ),
834
- h6: ({ className, ...props }) => (
835
- <h6
836
- className={cn(
837
- "aui-md-h6 my-4 font-semibold first:mt-0 last:mb-0",
838
- className,
839
- )}
840
- {...props}
841
- />
842
- ),
843
- p: ({ className, ...props }) => (
844
- <p
845
- className={cn(
846
- "aui-md-p mt-5 mb-5 leading-7 first:mt-0 last:mb-0",
847
- className,
848
- )}
849
- {...props}
850
- />
851
- ),
852
- a: ({ className, ...props }) => (
853
- <a
854
- className={cn(
855
- "aui-md-a font-medium text-primary underline underline-offset-4",
856
- className,
857
- )}
858
- {...props}
859
- />
860
- ),
861
- blockquote: ({ className, ...props }) => (
862
- <blockquote
863
- className={cn("aui-md-blockquote border-l-2 pl-6 italic", className)}
864
- {...props}
865
- />
866
- ),
867
- ul: ({ className, ...props }) => (
868
- <ul
869
- className={cn("aui-md-ul my-5 ml-6 list-disc [&>li]:mt-2", className)}
870
- {...props}
871
- />
872
- ),
873
- ol: ({ className, ...props }) => (
874
- <ol
875
- className={cn("aui-md-ol my-5 ml-6 list-decimal [&>li]:mt-2", className)}
876
- {...props}
877
- />
878
- ),
879
- hr: ({ className, ...props }) => (
880
- <hr className={cn("aui-md-hr my-5 border-b", className)} {...props} />
881
- ),
882
- table: ({ className, ...props }) => (
883
- <table
884
- className={cn(
885
- "aui-md-table my-5 w-full border-separate border-spacing-0 overflow-y-auto",
886
- className,
887
- )}
888
- {...props}
889
- />
890
- ),
891
- th: ({ className, ...props }) => (
892
- <th
893
- className={cn(
894
- "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",
895
- className,
896
- )}
897
- {...props}
898
- />
899
- ),
900
- td: ({ className, ...props }) => (
901
- <td
902
- className={cn(
903
- "aui-md-td border-b border-l px-4 py-2 text-left last:border-r [[align=center]]:text-center [[align=right]]:text-right",
904
- className,
905
- )}
906
- {...props}
907
- />
908
- ),
909
- tr: ({ className, ...props }) => (
910
- <tr
911
- className={cn(
912
- "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",
913
- className,
914
- )}
915
- {...props}
916
- />
917
- ),
918
- sup: ({ className, ...props }) => (
919
- <sup
920
- className={cn("aui-md-sup [&>a]:text-xs [&>a]:no-underline", className)}
921
- {...props}
922
- />
923
- ),
924
- pre: ({ className, ...props }) => (
925
- <pre
926
- className={cn(
927
- "aui-md-pre overflow-x-auto rounded-t-none! rounded-b-lg bg-black p-4 text-white",
928
- className,
929
- )}
930
- {...props}
931
- />
932
- ),
933
- code: function Code({ className, ...props }) {
934
- const isCodeBlock = useIsMarkdownCodeBlock();
935
- return (
936
- <code
937
- className={cn(
938
- !isCodeBlock &&
939
- "aui-md-inline-code rounded border bg-muted font-semibold",
940
- className,
941
- )}
942
- {...props}
943
- />
944
- );
945
- },
946
- CodeHeader,
947
- });
948
-
949
- ```
950
-
951
- ## components/assistant-ui/thread.tsx
952
-
953
- ```tsx
954
- import {
955
- ComposerAddAttachment,
956
- ComposerAttachments,
957
- UserMessageAttachments,
958
- } from "@/components/assistant-ui/attachment";
959
- import { MarkdownText } from "@/components/assistant-ui/markdown-text";
960
- import { ToolFallback } from "@/components/assistant-ui/tool-fallback";
961
- import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
962
- import { Button } from "@/components/ui/button";
963
- import { cn } from "@/lib/utils";
964
- import {
965
- ActionBarMorePrimitive,
966
- ActionBarPrimitive,
967
- AssistantIf,
968
- BranchPickerPrimitive,
969
- ComposerPrimitive,
970
- ErrorPrimitive,
971
- MessagePrimitive,
972
- ThreadPrimitive,
973
- } from "@assistant-ui/react";
974
- import {
975
- ArrowDownIcon,
976
- ArrowUpIcon,
977
- CheckIcon,
978
- ChevronLeftIcon,
979
- ChevronRightIcon,
980
- CopyIcon,
981
- DownloadIcon,
982
- MoreHorizontalIcon,
983
- PencilIcon,
984
- RefreshCwIcon,
985
- SquareIcon,
986
- } from "lucide-react";
987
- import type { FC } from "react";
988
-
989
- export const Thread: FC = () => {
990
- return (
991
- <ThreadPrimitive.Root
992
- className="aui-root aui-thread-root @container flex h-full flex-col bg-background"
993
- style={{
994
- ["--thread-max-width" as string]: "44rem",
995
- }}
996
- >
997
- <ThreadPrimitive.Viewport
998
- turnAnchor="top"
999
- className="aui-thread-viewport relative flex flex-1 flex-col overflow-x-auto overflow-y-scroll scroll-smooth px-4 pt-4"
1000
- >
1001
- <AssistantIf condition={({ thread }) => thread.isEmpty}>
1002
- <ThreadWelcome />
1003
- </AssistantIf>
1004
-
1005
- <ThreadPrimitive.Messages
1006
- components={{
1007
- UserMessage,
1008
- EditComposer,
1009
- AssistantMessage,
1010
- }}
1011
- />
1012
-
1013
- <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">
1014
- <ThreadScrollToBottom />
1015
- <Composer />
1016
- </ThreadPrimitive.ViewportFooter>
1017
- </ThreadPrimitive.Viewport>
1018
- </ThreadPrimitive.Root>
1019
- );
1020
- };
1021
-
1022
- const ThreadScrollToBottom: FC = () => {
1023
- return (
1024
- <ThreadPrimitive.ScrollToBottom asChild>
1025
- <TooltipIconButton
1026
- tooltip="Scroll to bottom"
1027
- variant="outline"
1028
- className="aui-thread-scroll-to-bottom absolute -top-12 z-10 self-center rounded-full p-4 disabled:invisible dark:bg-background dark:hover:bg-accent"
1029
- >
1030
- <ArrowDownIcon />
1031
- </TooltipIconButton>
1032
- </ThreadPrimitive.ScrollToBottom>
1033
- );
1034
- };
1035
-
1036
- const ThreadWelcome: FC = () => {
1037
- return (
1038
- <div className="aui-thread-welcome-root mx-auto my-auto flex w-full max-w-(--thread-max-width) grow flex-col">
1039
- <div className="aui-thread-welcome-center flex w-full grow flex-col items-center justify-center">
1040
- <div className="aui-thread-welcome-message flex size-full flex-col justify-center px-4">
1041
- <h1 className="aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in font-semibold text-2xl duration-200">
1042
- Hello there!
1043
- </h1>
1044
- <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">
1045
- How can I help you today?
1046
- </p>
1047
- </div>
1048
- </div>
1049
- <ThreadSuggestions />
1050
- </div>
1051
- );
1052
- };
1053
-
1054
- const SUGGESTIONS = [
1055
- {
1056
- title: "What's the weather",
1057
- label: "in San Francisco?",
1058
- prompt: "What's the weather in San Francisco?",
1059
- },
1060
- {
1061
- title: "Explain React hooks",
1062
- label: "like useState and useEffect",
1063
- prompt: "Explain React hooks like useState and useEffect",
1064
- },
1065
- ] as const;
1066
-
1067
- const ThreadSuggestions: FC = () => {
1068
- return (
1069
- <div className="aui-thread-welcome-suggestions grid w-full @md:grid-cols-2 gap-2 pb-4">
1070
- {SUGGESTIONS.map((suggestion, index) => (
1071
- <div
1072
- key={suggestion.prompt}
1073
- 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"
1074
- style={{ animationDelay: `${100 + index * 50}ms` }}
1075
- >
1076
- <ThreadPrimitive.Suggestion prompt={suggestion.prompt} send asChild>
1077
- <Button
1078
- variant="ghost"
1079
- 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"
1080
- aria-label={suggestion.prompt}
1081
- >
1082
- <span className="aui-thread-welcome-suggestion-text-1 font-medium">
1083
- {suggestion.title}
1084
- </span>
1085
- <span className="aui-thread-welcome-suggestion-text-2 text-muted-foreground">
1086
- {suggestion.label}
1087
- </span>
1088
- </Button>
1089
- </ThreadPrimitive.Suggestion>
1090
- </div>
1091
- ))}
1092
- </div>
1093
- );
1094
- };
1095
-
1096
- const Composer: FC = () => {
1097
- return (
1098
- <ComposerPrimitive.Root className="aui-composer-root relative flex w-full flex-col">
1099
- <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">
1100
- <ComposerAttachments />
1101
- <ComposerPrimitive.Input
1102
- placeholder="Send a message..."
1103
- 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"
1104
- rows={1}
1105
- autoFocus
1106
- aria-label="Message input"
1107
- />
1108
- <ComposerAction />
1109
- </ComposerPrimitive.AttachmentDropzone>
1110
- </ComposerPrimitive.Root>
1111
- );
1112
- };
1113
-
1114
- const ComposerAction: FC = () => {
1115
- return (
1116
- <div className="aui-composer-action-wrapper relative mx-2 mb-2 flex items-center justify-between">
1117
- <ComposerAddAttachment />
1118
-
1119
- <AssistantIf condition={({ thread }) => !thread.isRunning}>
1120
- <ComposerPrimitive.Send asChild>
1121
- <TooltipIconButton
1122
- tooltip="Send message"
1123
- side="bottom"
1124
- type="submit"
1125
- variant="default"
1126
- size="icon"
1127
- className="aui-composer-send size-8 rounded-full"
1128
- aria-label="Send message"
1129
- >
1130
- <ArrowUpIcon className="aui-composer-send-icon size-4" />
1131
- </TooltipIconButton>
1132
- </ComposerPrimitive.Send>
1133
- </AssistantIf>
1134
-
1135
- <AssistantIf condition={({ thread }) => thread.isRunning}>
1136
- <ComposerPrimitive.Cancel asChild>
1137
- <Button
1138
- type="button"
1139
- variant="default"
1140
- size="icon"
1141
- className="aui-composer-cancel size-8 rounded-full"
1142
- aria-label="Stop generating"
1143
- >
1144
- <SquareIcon className="aui-composer-cancel-icon size-3 fill-current" />
1145
- </Button>
1146
- </ComposerPrimitive.Cancel>
1147
- </AssistantIf>
1148
- </div>
1149
- );
1150
- };
1151
-
1152
- const MessageError: FC = () => {
1153
- return (
1154
- <MessagePrimitive.Error>
1155
- <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">
1156
- <ErrorPrimitive.Message className="aui-message-error-message line-clamp-2" />
1157
- </ErrorPrimitive.Root>
1158
- </MessagePrimitive.Error>
1159
- );
1160
- };
1161
-
1162
- const AssistantMessage: FC = () => {
1163
- return (
1164
- <MessagePrimitive.Root
1165
- 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"
1166
- data-role="assistant"
1167
- >
1168
- <div className="aui-assistant-message-content wrap-break-word px-2 text-foreground leading-relaxed">
1169
- <MessagePrimitive.Parts
1170
- components={{
1171
- Text: MarkdownText,
1172
- tools: { Fallback: ToolFallback },
1173
- }}
1174
- />
1175
- <MessageError />
1176
- </div>
1177
-
1178
- <div className="aui-assistant-message-footer mt-1 ml-2 flex">
1179
- <BranchPicker />
1180
- <AssistantActionBar />
1181
- </div>
1182
- </MessagePrimitive.Root>
1183
- );
1184
- };
1185
-
1186
- const AssistantActionBar: FC = () => {
1187
- return (
1188
- <ActionBarPrimitive.Root
1189
- hideWhenRunning
1190
- autohide="not-last"
1191
- autohideFloat="single-branch"
1192
- className="aui-assistant-action-bar-root col-start-3 row-start-2 -ml-1 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"
1193
- >
1194
- <ActionBarPrimitive.Copy asChild>
1195
- <TooltipIconButton tooltip="Copy">
1196
- <AssistantIf condition={({ message }) => message.isCopied}>
1197
- <CheckIcon />
1198
- </AssistantIf>
1199
- <AssistantIf condition={({ message }) => !message.isCopied}>
1200
- <CopyIcon />
1201
- </AssistantIf>
1202
- </TooltipIconButton>
1203
- </ActionBarPrimitive.Copy>
1204
- <ActionBarPrimitive.Reload asChild>
1205
- <TooltipIconButton tooltip="Refresh">
1206
- <RefreshCwIcon />
1207
- </TooltipIconButton>
1208
- </ActionBarPrimitive.Reload>
1209
- <ActionBarMorePrimitive.Root>
1210
- <ActionBarMorePrimitive.Trigger asChild>
1211
- <TooltipIconButton
1212
- tooltip="More"
1213
- className="data-[state=open]:bg-accent"
1214
- >
1215
- <MoreHorizontalIcon />
1216
- </TooltipIconButton>
1217
- </ActionBarMorePrimitive.Trigger>
1218
- <ActionBarMorePrimitive.Content
1219
- side="bottom"
1220
- align="start"
1221
- className="aui-action-bar-more-content z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md"
1222
- >
1223
- <ActionBarPrimitive.ExportMarkdown asChild>
1224
- <ActionBarMorePrimitive.Item className="aui-action-bar-more-item flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground">
1225
- <DownloadIcon className="size-4" />
1226
- Export as Markdown
1227
- </ActionBarMorePrimitive.Item>
1228
- </ActionBarPrimitive.ExportMarkdown>
1229
- </ActionBarMorePrimitive.Content>
1230
- </ActionBarMorePrimitive.Root>
1231
- </ActionBarPrimitive.Root>
1232
- );
1233
- };
1234
-
1235
- const UserMessage: FC = () => {
1236
- return (
1237
- <MessagePrimitive.Root
1238
- 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"
1239
- data-role="user"
1240
- >
1241
- <UserMessageAttachments />
1242
-
1243
- <div className="aui-user-message-content-wrapper relative col-start-2 min-w-0">
1244
- <div className="aui-user-message-content wrap-break-word rounded-2xl bg-muted px-4 py-2.5 text-foreground">
1245
- <MessagePrimitive.Parts />
1246
- </div>
1247
- <div className="aui-user-action-bar-wrapper absolute top-1/2 left-0 -translate-x-full -translate-y-1/2 pr-2">
1248
- <UserActionBar />
1249
- </div>
1250
- </div>
1251
-
1252
- <BranchPicker className="aui-user-branch-picker col-span-full col-start-1 row-start-3 -mr-1 justify-end" />
1253
- </MessagePrimitive.Root>
1254
- );
1255
- };
1256
-
1257
- const UserActionBar: FC = () => {
1258
- return (
1259
- <ActionBarPrimitive.Root
1260
- hideWhenRunning
1261
- autohide="not-last"
1262
- className="aui-user-action-bar-root flex flex-col items-end"
1263
- >
1264
- <ActionBarPrimitive.Edit asChild>
1265
- <TooltipIconButton tooltip="Edit" className="aui-user-action-edit p-4">
1266
- <PencilIcon />
1267
- </TooltipIconButton>
1268
- </ActionBarPrimitive.Edit>
1269
- </ActionBarPrimitive.Root>
1270
- );
1271
- };
1272
-
1273
- const EditComposer: FC = () => {
1274
- return (
1275
- <MessagePrimitive.Root className="aui-edit-composer-wrapper mx-auto flex w-full max-w-(--thread-max-width) flex-col px-2 py-3">
1276
- <ComposerPrimitive.Root className="aui-edit-composer-root ml-auto flex w-full max-w-[85%] flex-col rounded-2xl bg-muted">
1277
- <ComposerPrimitive.Input
1278
- className="aui-edit-composer-input min-h-14 w-full resize-none bg-transparent p-4 text-foreground text-sm outline-none"
1279
- autoFocus
1280
- />
1281
- <div className="aui-edit-composer-footer mx-3 mb-3 flex items-center gap-2 self-end">
1282
- <ComposerPrimitive.Cancel asChild>
1283
- <Button variant="ghost" size="sm">
1284
- Cancel
1285
- </Button>
1286
- </ComposerPrimitive.Cancel>
1287
- <ComposerPrimitive.Send asChild>
1288
- <Button size="sm">Update</Button>
1289
- </ComposerPrimitive.Send>
1290
- </div>
1291
- </ComposerPrimitive.Root>
1292
- </MessagePrimitive.Root>
1293
- );
1294
- };
1295
-
1296
- const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
1297
- className,
1298
- ...rest
1299
- }) => {
1300
- return (
1301
- <BranchPickerPrimitive.Root
1302
- hideWhenSingleBranch
1303
- className={cn(
1304
- "aui-branch-picker-root mr-2 -ml-2 inline-flex items-center text-muted-foreground text-xs",
1305
- className,
1306
- )}
1307
- {...rest}
1308
- >
1309
- <BranchPickerPrimitive.Previous asChild>
1310
- <TooltipIconButton tooltip="Previous">
1311
- <ChevronLeftIcon />
1312
- </TooltipIconButton>
1313
- </BranchPickerPrimitive.Previous>
1314
- <span className="aui-branch-picker-state font-medium">
1315
- <BranchPickerPrimitive.Number /> / <BranchPickerPrimitive.Count />
1316
- </span>
1317
- <BranchPickerPrimitive.Next asChild>
1318
- <TooltipIconButton tooltip="Next">
1319
- <ChevronRightIcon />
1320
- </TooltipIconButton>
1321
- </BranchPickerPrimitive.Next>
1322
- </BranchPickerPrimitive.Root>
1323
- );
1324
- };
1325
-
1326
- ```
1327
-
1328
- ## components/assistant-ui/tool-fallback.tsx
1329
-
1330
- ```tsx
1331
- "use client";
1332
-
1333
- import { memo, useCallback, useRef, useState } from "react";
1334
- import {
1335
- AlertCircleIcon,
1336
- CheckIcon,
1337
- ChevronDownIcon,
1338
- LoaderIcon,
1339
- XCircleIcon,
1340
- } from "lucide-react";
1341
- import {
1342
- useScrollLock,
1343
- type ToolCallMessagePartStatus,
1344
- type ToolCallMessagePartComponent,
1345
- } from "@assistant-ui/react";
1346
- import {
1347
- Collapsible,
1348
- CollapsibleContent,
1349
- CollapsibleTrigger,
1350
- } from "@/components/ui/collapsible";
1351
- import { cn } from "@/lib/utils";
1352
-
1353
- const ANIMATION_DURATION = 200;
1354
-
1355
- export type ToolFallbackRootProps = Omit<
1356
- React.ComponentProps<typeof Collapsible>,
1357
- "open" | "onOpenChange"
1358
- > & {
1359
- open?: boolean;
1360
- onOpenChange?: (open: boolean) => void;
1361
- defaultOpen?: boolean;
1362
- };
1363
-
1364
- function ToolFallbackRoot({
1365
- className,
1366
- open: controlledOpen,
1367
- onOpenChange: controlledOnOpenChange,
1368
- defaultOpen = false,
1369
- children,
1370
- ...props
1371
- }: ToolFallbackRootProps) {
1372
- const collapsibleRef = useRef<HTMLDivElement>(null);
1373
- const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
1374
- const lockScroll = useScrollLock(collapsibleRef, ANIMATION_DURATION);
1375
-
1376
- const isControlled = controlledOpen !== undefined;
1377
- const isOpen = isControlled ? controlledOpen : uncontrolledOpen;
1378
-
1379
- const handleOpenChange = useCallback(
1380
- (open: boolean) => {
1381
- if (!open) {
1382
- lockScroll();
1383
- }
1384
- if (!isControlled) {
1385
- setUncontrolledOpen(open);
1386
- }
1387
- controlledOnOpenChange?.(open);
1388
- },
1389
- [lockScroll, isControlled, controlledOnOpenChange],
1390
- );
1391
-
1392
- return (
1393
- <Collapsible
1394
- ref={collapsibleRef}
1395
- data-slot="tool-fallback-root"
1396
- open={isOpen}
1397
- onOpenChange={handleOpenChange}
1398
- className={cn(
1399
- "aui-tool-fallback-root group/tool-fallback-root w-full rounded-lg border py-3",
1400
- className,
1401
- )}
1402
- style={
1403
- {
1404
- "--animation-duration": `${ANIMATION_DURATION}ms`,
1405
- } as React.CSSProperties
1406
- }
1407
- {...props}
1408
- >
1409
- {children}
1410
- </Collapsible>
1411
- );
1412
- }
1413
-
1414
- type ToolStatus = ToolCallMessagePartStatus["type"];
1415
-
1416
- const statusIconMap: Record<ToolStatus, React.ElementType> = {
1417
- running: LoaderIcon,
1418
- complete: CheckIcon,
1419
- incomplete: XCircleIcon,
1420
- "requires-action": AlertCircleIcon,
1421
- };
1422
-
1423
- function ToolFallbackTrigger({
1424
- toolName,
1425
- status,
1426
- className,
1427
- ...props
1428
- }: React.ComponentProps<typeof CollapsibleTrigger> & {
1429
- toolName: string;
1430
- status?: ToolCallMessagePartStatus;
1431
- }) {
1432
- const statusType = status?.type ?? "complete";
1433
- const isRunning = statusType === "running";
1434
- const isCancelled =
1435
- status?.type === "incomplete" && status.reason === "cancelled";
1436
-
1437
- const Icon = statusIconMap[statusType];
1438
- const label = isCancelled ? "Cancelled tool" : "Used tool";
1439
-
1440
- return (
1441
- <CollapsibleTrigger
1442
- data-slot="tool-fallback-trigger"
1443
- className={cn(
1444
- "aui-tool-fallback-trigger group/trigger flex w-full items-center gap-2 px-4 text-sm transition-colors",
1445
- className,
1446
- )}
1447
- {...props}
1448
- >
1449
- <Icon
1450
- data-slot="tool-fallback-trigger-icon"
1451
- className={cn(
1452
- "aui-tool-fallback-trigger-icon size-4 shrink-0",
1453
- isCancelled && "text-muted-foreground",
1454
- isRunning && "animate-spin",
1455
- )}
1456
- />
1457
- <span
1458
- data-slot="tool-fallback-trigger-label"
1459
- className={cn(
1460
- "aui-tool-fallback-trigger-label-wrapper relative inline-block grow text-left leading-none",
1461
- isCancelled && "text-muted-foreground line-through",
1462
- )}
1463
- >
1464
- <span>
1465
- {label}: <b>{toolName}</b>
1466
- </span>
1467
- {isRunning && (
1468
- <span
1469
- aria-hidden
1470
- data-slot="tool-fallback-trigger-shimmer"
1471
- className="aui-tool-fallback-trigger-shimmer shimmer pointer-events-none absolute inset-0 motion-reduce:animate-none"
1472
- >
1473
- {label}: <b>{toolName}</b>
1474
- </span>
1475
- )}
1476
- </span>
1477
- <ChevronDownIcon
1478
- data-slot="tool-fallback-trigger-chevron"
1479
- className={cn(
1480
- "aui-tool-fallback-trigger-chevron size-4 shrink-0",
1481
- "transition-transform duration-(--animation-duration) ease-out",
1482
- "group-data-[state=closed]/trigger:-rotate-90",
1483
- "group-data-[state=open]/trigger:rotate-0",
1484
- )}
1485
- />
1486
- </CollapsibleTrigger>
1487
- );
1488
- }
1489
-
1490
- function ToolFallbackContent({
1491
- className,
1492
- children,
1493
- ...props
1494
- }: React.ComponentProps<typeof CollapsibleContent>) {
1495
- return (
1496
- <CollapsibleContent
1497
- data-slot="tool-fallback-content"
1498
- className={cn(
1499
- "aui-tool-fallback-content relative overflow-hidden text-sm outline-none",
1500
- "group/collapsible-content ease-out",
1501
- "data-[state=closed]:animate-collapsible-up",
1502
- "data-[state=open]:animate-collapsible-down",
1503
- "data-[state=closed]:fill-mode-forwards",
1504
- "data-[state=closed]:pointer-events-none",
1505
- "data-[state=open]:duration-(--animation-duration)",
1506
- "data-[state=closed]:duration-(--animation-duration)",
1507
- className,
1508
- )}
1509
- {...props}
1510
- >
1511
- <div className="mt-3 flex flex-col gap-2 border-t pt-2">{children}</div>
1512
- </CollapsibleContent>
1513
- );
1514
- }
1515
-
1516
- function ToolFallbackArgs({
1517
- argsText,
1518
- className,
1519
- ...props
1520
- }: React.ComponentProps<"div"> & {
1521
- argsText?: string;
1522
- }) {
1523
- if (!argsText) return null;
1524
-
1525
- return (
1526
- <div
1527
- data-slot="tool-fallback-args"
1528
- className={cn("aui-tool-fallback-args px-4", className)}
1529
- {...props}
1530
- >
1531
- <pre className="aui-tool-fallback-args-value whitespace-pre-wrap">
1532
- {argsText}
1533
- </pre>
1534
- </div>
1535
- );
1536
- }
1537
-
1538
- function ToolFallbackResult({
1539
- result,
1540
- className,
1541
- ...props
1542
- }: React.ComponentProps<"div"> & {
1543
- result?: unknown;
1544
- }) {
1545
- if (result === undefined) return null;
1546
-
1547
- return (
1548
- <div
1549
- data-slot="tool-fallback-result"
1550
- className={cn(
1551
- "aui-tool-fallback-result border-t border-dashed px-4 pt-2",
1552
- className,
1553
- )}
1554
- {...props}
1555
- >
1556
- <p className="aui-tool-fallback-result-header font-semibold">Result:</p>
1557
- <pre className="aui-tool-fallback-result-content whitespace-pre-wrap">
1558
- {typeof result === "string" ? result : JSON.stringify(result, null, 2)}
1559
- </pre>
1560
- </div>
1561
- );
1562
- }
1563
-
1564
- function ToolFallbackError({
1565
- status,
1566
- className,
1567
- ...props
1568
- }: React.ComponentProps<"div"> & {
1569
- status?: ToolCallMessagePartStatus;
1570
- }) {
1571
- if (status?.type !== "incomplete") return null;
1572
-
1573
- const error = status.error;
1574
- const errorText = error
1575
- ? typeof error === "string"
1576
- ? error
1577
- : JSON.stringify(error)
1578
- : null;
1579
-
1580
- if (!errorText) return null;
1581
-
1582
- const isCancelled = status.reason === "cancelled";
1583
- const headerText = isCancelled ? "Cancelled reason:" : "Error:";
1584
-
1585
- return (
1586
- <div
1587
- data-slot="tool-fallback-error"
1588
- className={cn("aui-tool-fallback-error px-4", className)}
1589
- {...props}
1590
- >
1591
- <p className="aui-tool-fallback-error-header font-semibold text-muted-foreground">
1592
- {headerText}
1593
- </p>
1594
- <p className="aui-tool-fallback-error-reason text-muted-foreground">
1595
- {errorText}
1596
- </p>
1597
- </div>
1598
- );
1599
- }
1600
-
1601
- const ToolFallbackImpl: ToolCallMessagePartComponent = ({
1602
- toolName,
1603
- argsText,
1604
- result,
1605
- status,
1606
- }) => {
1607
- const isCancelled =
1608
- status?.type === "incomplete" && status.reason === "cancelled";
1609
-
1610
- return (
1611
- <ToolFallbackRoot
1612
- className={cn(isCancelled && "border-muted-foreground/30 bg-muted/30")}
1613
- >
1614
- <ToolFallbackTrigger toolName={toolName} status={status} />
1615
- <ToolFallbackContent>
1616
- <ToolFallbackError status={status} />
1617
- <ToolFallbackArgs
1618
- argsText={argsText}
1619
- className={cn(isCancelled && "opacity-60")}
1620
- />
1621
- {!isCancelled && <ToolFallbackResult result={result} />}
1622
- </ToolFallbackContent>
1623
- </ToolFallbackRoot>
1624
- );
1625
- };
1626
-
1627
- const ToolFallback = memo(
1628
- ToolFallbackImpl,
1629
- ) as unknown as ToolCallMessagePartComponent & {
1630
- Root: typeof ToolFallbackRoot;
1631
- Trigger: typeof ToolFallbackTrigger;
1632
- Content: typeof ToolFallbackContent;
1633
- Args: typeof ToolFallbackArgs;
1634
- Result: typeof ToolFallbackResult;
1635
- Error: typeof ToolFallbackError;
1636
- };
1637
-
1638
- ToolFallback.displayName = "ToolFallback";
1639
- ToolFallback.Root = ToolFallbackRoot;
1640
- ToolFallback.Trigger = ToolFallbackTrigger;
1641
- ToolFallback.Content = ToolFallbackContent;
1642
- ToolFallback.Args = ToolFallbackArgs;
1643
- ToolFallback.Result = ToolFallbackResult;
1644
- ToolFallback.Error = ToolFallbackError;
1645
-
1646
- export {
1647
- ToolFallback,
1648
- ToolFallbackRoot,
1649
- ToolFallbackTrigger,
1650
- ToolFallbackContent,
1651
- ToolFallbackArgs,
1652
- ToolFallbackResult,
1653
- ToolFallbackError,
1654
- };
1655
-
1656
- ```
1657
-
1658
- ## components/assistant-ui/tooltip-icon-button.tsx
1659
-
1660
- ```tsx
1661
- "use client";
1662
-
1663
- import { ComponentPropsWithRef, forwardRef } from "react";
1664
- import { Slottable } from "@radix-ui/react-slot";
1665
-
1666
- import {
1667
- Tooltip,
1668
- TooltipContent,
1669
- TooltipTrigger,
1670
- } from "@/components/ui/tooltip";
1671
- import { Button } from "@/components/ui/button";
1672
- import { cn } from "@/lib/utils";
1673
-
1674
- export type TooltipIconButtonProps = ComponentPropsWithRef<typeof Button> & {
1675
- tooltip: string;
1676
- side?: "top" | "bottom" | "left" | "right";
1677
- };
1678
-
1679
- export const TooltipIconButton = forwardRef<
1680
- HTMLButtonElement,
1681
- TooltipIconButtonProps
1682
- >(({ children, tooltip, side = "bottom", className, ...rest }, ref) => {
1683
- return (
1684
- <Tooltip>
1685
- <TooltipTrigger asChild>
1686
- <Button
1687
- variant="ghost"
1688
- size="icon"
1689
- {...rest}
1690
- className={cn("aui-button-icon size-6 p-1", className)}
1691
- ref={ref}
1692
- >
1693
- <Slottable>{children}</Slottable>
1694
- <span className="aui-sr-only sr-only">{tooltip}</span>
1695
- </Button>
1696
- </TooltipTrigger>
1697
- <TooltipContent side={side}>{tooltip}</TooltipContent>
1698
- </Tooltip>
1699
- );
1700
- });
1701
-
1702
- TooltipIconButton.displayName = "TooltipIconButton";
1703
-
1704
- ```
1705
-
1706
- ## components/ui/avatar.tsx
1707
-
1708
- ```tsx
1709
- "use client";
1710
-
1711
- import * as React from "react";
1712
- import * as AvatarPrimitive from "@radix-ui/react-avatar";
1713
-
1714
- import { cn } from "@/lib/utils";
1715
-
1716
- function Avatar({
1717
- className,
1718
- ...props
1719
- }: React.ComponentProps<typeof AvatarPrimitive.Root>) {
1720
- return (
1721
- <AvatarPrimitive.Root
1722
- data-slot="avatar"
1723
- className={cn(
1724
- "relative flex size-8 shrink-0 overflow-hidden rounded-full",
1725
- className,
1726
- )}
1727
- {...props}
1728
- />
1729
- );
1730
- }
1731
-
1732
- function AvatarImage({
1733
- className,
1734
- ...props
1735
- }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
1736
- return (
1737
- <AvatarPrimitive.Image
1738
- data-slot="avatar-image"
1739
- className={cn("aspect-square size-full", className)}
1740
- {...props}
1741
- />
1742
- );
1743
- }
1744
-
1745
- function AvatarFallback({
1746
- className,
1747
- ...props
1748
- }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
1749
- return (
1750
- <AvatarPrimitive.Fallback
1751
- data-slot="avatar-fallback"
1752
- className={cn(
1753
- "flex size-full items-center justify-center rounded-full bg-muted",
1754
- className,
1755
- )}
1756
- {...props}
1757
- />
1758
- );
1759
- }
1760
-
1761
- export { Avatar, AvatarImage, AvatarFallback };
1762
-
1763
- ```
1764
-
1765
- ## components/ui/button.tsx
1766
-
1767
- ```tsx
1768
- import * as React from "react";
1769
- import { Slot } from "@radix-ui/react-slot";
1770
- import { cva, type VariantProps } from "class-variance-authority";
1771
-
1772
- import { cn } from "@/lib/utils";
1773
-
1774
- const buttonVariants = cva(
1775
- "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",
1776
- {
1777
- variants: {
1778
- variant: {
1779
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
1780
- destructive:
1781
- "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
1782
- outline:
1783
- "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
1784
- secondary:
1785
- "bg-secondary text-secondary-foreground hover:bg-secondary/80",
1786
- ghost:
1787
- "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
1788
- link: "text-primary underline-offset-4 hover:underline",
1789
- },
1790
- size: {
1791
- default: "h-9 px-4 py-2 has-[>svg]:px-3",
1792
- sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
1793
- lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
1794
- icon: "size-9",
1795
- "icon-sm": "size-8",
1796
- "icon-lg": "size-10",
1797
- },
1798
- },
1799
- defaultVariants: {
1800
- variant: "default",
1801
- size: "default",
1802
- },
1803
- },
1804
- );
1805
-
1806
- function Button({
1807
- className,
1808
- variant = "default",
1809
- size = "default",
1810
- asChild = false,
1811
- ...props
1812
- }: React.ComponentProps<"button"> &
1813
- VariantProps<typeof buttonVariants> & {
1814
- asChild?: boolean;
1815
- }) {
1816
- const Comp = asChild ? Slot : "button";
1817
-
1818
- return (
1819
- <Comp
1820
- data-slot="button"
1821
- data-variant={variant}
1822
- data-size={size}
1823
- className={cn(buttonVariants({ variant, size, className }))}
1824
- {...props}
1825
- />
1826
- );
1827
- }
1828
-
1829
- export { Button, buttonVariants };
1830
-
1831
- ```
1832
-
1833
- ## components/ui/collapsible.tsx
1834
-
1835
- ```tsx
1836
- "use client";
1837
-
1838
- import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
1839
-
1840
- function Collapsible({
1841
- ...props
1842
- }: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
1843
- return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />;
1844
- }
1845
-
1846
- function CollapsibleTrigger({
1847
- ...props
1848
- }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
1849
- return (
1850
- <CollapsiblePrimitive.CollapsibleTrigger
1851
- data-slot="collapsible-trigger"
1852
- {...props}
1853
- />
1854
- );
1855
- }
1856
-
1857
- function CollapsibleContent({
1858
- ...props
1859
- }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
1860
- return (
1861
- <CollapsiblePrimitive.CollapsibleContent
1862
- data-slot="collapsible-content"
1863
- {...props}
1864
- />
1865
- );
1866
- }
1867
-
1868
- export { Collapsible, CollapsibleTrigger, CollapsibleContent };
1869
-
1870
- ```
1871
-
1872
- ## components/ui/dialog.tsx
1873
-
1874
- ```tsx
1875
- "use client";
1876
-
1877
- import * as React from "react";
1878
- import * as DialogPrimitive from "@radix-ui/react-dialog";
1879
- import { XIcon } from "lucide-react";
1880
-
1881
- import { cn } from "@/lib/utils";
1882
-
1883
- function Dialog({
1884
- ...props
1885
- }: React.ComponentProps<typeof DialogPrimitive.Root>) {
1886
- return <DialogPrimitive.Root data-slot="dialog" {...props} />;
1887
- }
1888
-
1889
- function DialogTrigger({
1890
- ...props
1891
- }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
1892
- return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
1893
- }
1894
-
1895
- function DialogPortal({
1896
- ...props
1897
- }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
1898
- return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
1899
- }
1900
-
1901
- function DialogClose({
1902
- ...props
1903
- }: React.ComponentProps<typeof DialogPrimitive.Close>) {
1904
- return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
1905
- }
1906
-
1907
- function DialogOverlay({
1908
- className,
1909
- ...props
1910
- }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
1911
- return (
1912
- <DialogPrimitive.Overlay
1913
- data-slot="dialog-overlay"
1914
- className={cn(
1915
- "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=open]:animate-in",
1916
- className,
1917
- )}
1918
- {...props}
1919
- />
1920
- );
1921
- }
1922
-
1923
- function DialogContent({
1924
- className,
1925
- children,
1926
- showCloseButton = true,
1927
- ...props
1928
- }: React.ComponentProps<typeof DialogPrimitive.Content> & {
1929
- showCloseButton?: boolean;
1930
- }) {
1931
- return (
1932
- <DialogPortal data-slot="dialog-portal">
1933
- <DialogOverlay />
1934
- <DialogPrimitive.Content
1935
- data-slot="dialog-content"
1936
- className={cn(
1937
- "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 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 outline-none duration-200 data-[state=closed]:animate-out data-[state=open]:animate-in sm:max-w-lg",
1938
- className,
1939
- )}
1940
- {...props}
1941
- >
1942
- {children}
1943
- {showCloseButton && (
1944
- <DialogPrimitive.Close
1945
- data-slot="dialog-close"
1946
- 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"
1947
- >
1948
- <XIcon />
1949
- <span className="sr-only">Close</span>
1950
- </DialogPrimitive.Close>
1951
- )}
1952
- </DialogPrimitive.Content>
1953
- </DialogPortal>
1954
- );
1955
- }
1956
-
1957
- function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
1958
- return (
1959
- <div
1960
- data-slot="dialog-header"
1961
- className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
1962
- {...props}
1963
- />
1964
- );
1965
- }
1966
-
1967
- function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
1968
- return (
1969
- <div
1970
- data-slot="dialog-footer"
1971
- className={cn(
1972
- "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
1973
- className,
1974
- )}
1975
- {...props}
1976
- />
1977
- );
1978
- }
1979
-
1980
- function DialogTitle({
1981
- className,
1982
- ...props
1983
- }: React.ComponentProps<typeof DialogPrimitive.Title>) {
1984
- return (
1985
- <DialogPrimitive.Title
1986
- data-slot="dialog-title"
1987
- className={cn("font-semibold text-lg leading-none", className)}
1988
- {...props}
1989
- />
1990
- );
1991
- }
1992
-
1993
- function DialogDescription({
1994
- className,
1995
- ...props
1996
- }: React.ComponentProps<typeof DialogPrimitive.Description>) {
1997
- return (
1998
- <DialogPrimitive.Description
1999
- data-slot="dialog-description"
2000
- className={cn("text-muted-foreground text-sm", className)}
2001
- {...props}
2002
- />
2003
- );
2004
- }
2005
-
2006
- export {
2007
- Dialog,
2008
- DialogClose,
2009
- DialogContent,
2010
- DialogDescription,
2011
- DialogFooter,
2012
- DialogHeader,
2013
- DialogOverlay,
2014
- DialogPortal,
2015
- DialogTitle,
2016
- DialogTrigger,
2017
- };
2018
-
2019
- ```
2020
-
2021
- ## components/ui/tooltip.tsx
2022
-
2023
- ```tsx
2024
- "use client";
2025
-
2026
- import * as React from "react";
2027
- import * as TooltipPrimitive from "@radix-ui/react-tooltip";
2028
-
2029
- import { cn } from "@/lib/utils";
2030
-
2031
- function TooltipProvider({
2032
- delayDuration = 0,
2033
- ...props
2034
- }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
2035
- return (
2036
- <TooltipPrimitive.Provider
2037
- data-slot="tooltip-provider"
2038
- delayDuration={delayDuration}
2039
- {...props}
2040
- />
2041
- );
2042
- }
2043
-
2044
- function Tooltip({
2045
- ...props
2046
- }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
2047
- return (
2048
- <TooltipProvider>
2049
- <TooltipPrimitive.Root data-slot="tooltip" {...props} />
2050
- </TooltipProvider>
2051
- );
2052
- }
2053
-
2054
- function TooltipTrigger({
2055
- ...props
2056
- }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
2057
- return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
2058
- }
2059
-
2060
- function TooltipContent({
2061
- className,
2062
- sideOffset = 0,
2063
- children,
2064
- ...props
2065
- }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
2066
- return (
2067
- <TooltipPrimitive.Portal>
2068
- <TooltipPrimitive.Content
2069
- data-slot="tooltip-content"
2070
- sideOffset={sideOffset}
2071
- className={cn(
2072
- "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-foreground px-3 py-1.5 text-background text-xs data-[state=closed]:animate-out",
2073
- className,
2074
- )}
2075
- {...props}
2076
- >
2077
- {children}
2078
- <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" />
2079
- </TooltipPrimitive.Content>
2080
- </TooltipPrimitive.Portal>
2081
- );
471
+ "iconLibrary": "lucide",
472
+ "registries": {
473
+ "@assistant-ui": "https://r.assistant-ui.com/{name}.json"
474
+ }
2082
475
  }
2083
476
 
2084
- export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
2085
-
2086
477
  ```
2087
478
 
2088
479
  ## lib/utils.ts
@@ -2124,10 +515,11 @@ export default nextConfig;
2124
515
  "start": "next start"
2125
516
  },
2126
517
  "dependencies": {
2127
- "@ai-sdk/openai": "^3.0.13",
518
+ "@ai-sdk/openai": "^3.0.25",
2128
519
  "@assistant-ui/react": "workspace:*",
2129
520
  "@assistant-ui/react-ai-sdk": "workspace:*",
2130
521
  "@assistant-ui/react-markdown": "workspace:*",
522
+ "@assistant-ui/ui": "workspace:*",
2131
523
  "@ffmpeg/ffmpeg": "^0.12.15",
2132
524
  "@ffmpeg/util": "^0.12.2",
2133
525
  "@radix-ui/react-avatar": "^1.1.11",
@@ -2135,23 +527,21 @@ export default nextConfig;
2135
527
  "@radix-ui/react-dialog": "^1.1.15",
2136
528
  "@radix-ui/react-slot": "^1.2.4",
2137
529
  "@radix-ui/react-tooltip": "^1.2.8",
2138
- "ai": "^6.0.42",
530
+ "ai": "^6.0.69",
2139
531
  "class-variance-authority": "^0.7.1",
2140
532
  "clsx": "^2.1.1",
2141
- "lucide-react": "^0.562.0",
2142
- "next": "^16.1.4",
2143
- "react": "^19.2.3",
2144
- "react-dom": "^19.2.3",
2145
- "remark-gfm": "^4.0.1",
533
+ "lucide-react": "^0.563.0",
534
+ "next": "^16.1.6",
535
+ "react": "^19.2.4",
536
+ "react-dom": "^19.2.4",
2146
537
  "tailwind-merge": "^3.4.0",
2147
- "zod": "^4.3.5",
2148
- "zustand": "^5.0.10"
538
+ "zod": "^4.3.6"
2149
539
  },
2150
540
  "devDependencies": {
2151
541
  "@assistant-ui/x-buildutils": "workspace:*",
2152
542
  "@tailwindcss/postcss": "^4.1.18",
2153
- "@types/node": "^25.0.9",
2154
- "@types/react": "^19.2.9",
543
+ "@types/node": "^25.2.0",
544
+ "@types/react": "^19.2.10",
2155
545
  "@types/react-dom": "^19.2.3",
2156
546
  "postcss": "^8.5.6",
2157
547
  "tailwindcss": "^4.1.18",
@@ -2162,13 +552,65 @@ export default nextConfig;
2162
552
 
2163
553
  ```
2164
554
 
555
+ ## README.md
556
+
557
+ ```markdown
558
+ # FFmpeg Video Processing Tool
559
+
560
+ This example demonstrates how to create a file conversion assistant using FFmpeg WebAssembly with assistant-ui.
561
+
562
+ ## Quick Start
563
+
564
+ ### Using CLI (Recommended)
565
+
566
+ ```bash
567
+ npx assistant-ui@latest create my-app --example with-ffmpeg
568
+ cd my-app
569
+ ```
570
+
571
+ ### Environment Variables
572
+
573
+ Create `.env.local`:
574
+
575
+ ```
576
+ OPENAI_API_KEY=sk-...
577
+ ```
578
+
579
+ ### Run
580
+
581
+ ```bash
582
+ npm run dev
583
+ ```
584
+
585
+ ## Features
586
+
587
+ - FFmpeg WebAssembly integration
588
+ - File attachment handling
589
+ - Custom tool for running FFmpeg commands
590
+ - Download button for converted files
591
+ - Vercel AI SDK integration
592
+
593
+ ## Related Documentation
594
+
595
+ - [assistant-ui Documentation](https://www.assistant-ui.com/docs)
596
+ - [Tool UI Guide](https://www.assistant-ui.com/docs/tools)
597
+
598
+ ```
599
+
2165
600
  ## tsconfig.json
2166
601
 
2167
602
  ```json
2168
603
  {
2169
604
  "extends": "@assistant-ui/x-buildutils/ts/next",
2170
605
  "compilerOptions": {
2171
- "paths": { "@/*": ["./*"] }
606
+ "paths": {
607
+ "@/*": ["./*"],
608
+ "@/components/assistant-ui/*": [
609
+ "../../packages/ui/src/components/assistant-ui/*"
610
+ ],
611
+ "@/components/ui/*": ["../../packages/ui/src/components/ui/*"],
612
+ "@assistant-ui/ui/*": ["../../packages/ui/src/*"]
613
+ }
2172
614
  },
2173
615
  "include": ["**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
2174
616
  "exclude": ["node_modules"]