@assistant-ui/mcp-docs-server 0.1.14 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.docs/organized/code-examples/store-example.md +554 -0
- package/.docs/organized/code-examples/with-ag-ui.md +582 -32
- package/.docs/organized/code-examples/with-ai-sdk-v5.md +549 -47
- package/.docs/organized/code-examples/with-assistant-transport.md +547 -46
- package/.docs/organized/code-examples/with-cloud.md +634 -39
- package/.docs/organized/code-examples/with-external-store.md +581 -31
- package/.docs/organized/code-examples/with-ffmpeg.md +581 -47
- package/.docs/organized/code-examples/with-langgraph.md +633 -50
- package/.docs/organized/code-examples/with-parent-id-grouping.md +581 -31
- package/.docs/organized/code-examples/with-react-hook-form.md +582 -70
- package/.docs/raw/blog/2024-07-29-hello/index.mdx +0 -1
- package/.docs/raw/docs/cli.mdx +396 -0
- package/.docs/raw/docs/getting-started.mdx +31 -37
- package/.docs/raw/docs/runtimes/assistant-transport.mdx +891 -0
- package/.docs/raw/docs/runtimes/langgraph/index.mdx +1 -1
- package/package.json +13 -5
|
@@ -426,7 +426,6 @@ export default function Home() {
|
|
|
426
426
|
setLastFile(lastAttachment.file!);
|
|
427
427
|
}, [attachments]);
|
|
428
428
|
|
|
429
|
-
console.log(lastFile);
|
|
430
429
|
return (
|
|
431
430
|
<div className="flex h-full flex-col">
|
|
432
431
|
<div className="border-b">
|
|
@@ -472,6 +471,247 @@ export default function Home() {
|
|
|
472
471
|
|
|
473
472
|
```
|
|
474
473
|
|
|
474
|
+
## components/assistant-ui/attachment.tsx
|
|
475
|
+
|
|
476
|
+
```tsx
|
|
477
|
+
"use client";
|
|
478
|
+
|
|
479
|
+
import { PropsWithChildren, useEffect, useState, type FC } from "react";
|
|
480
|
+
import Image from "next/image";
|
|
481
|
+
import { XIcon, PlusIcon, FileText } from "lucide-react";
|
|
482
|
+
import {
|
|
483
|
+
AttachmentPrimitive,
|
|
484
|
+
ComposerPrimitive,
|
|
485
|
+
MessagePrimitive,
|
|
486
|
+
useAssistantState,
|
|
487
|
+
useAssistantApi,
|
|
488
|
+
} from "@assistant-ui/react";
|
|
489
|
+
import { useShallow } from "zustand/shallow";
|
|
490
|
+
import {
|
|
491
|
+
Tooltip,
|
|
492
|
+
TooltipContent,
|
|
493
|
+
TooltipTrigger,
|
|
494
|
+
} from "@/components/ui/tooltip";
|
|
495
|
+
import {
|
|
496
|
+
Dialog,
|
|
497
|
+
DialogTitle,
|
|
498
|
+
DialogContent,
|
|
499
|
+
DialogTrigger,
|
|
500
|
+
} from "@/components/ui/dialog";
|
|
501
|
+
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
|
|
502
|
+
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
|
|
503
|
+
import { cn } from "@/lib/utils";
|
|
504
|
+
|
|
505
|
+
const useFileSrc = (file: File | undefined) => {
|
|
506
|
+
const [src, setSrc] = useState<string | undefined>(undefined);
|
|
507
|
+
|
|
508
|
+
useEffect(() => {
|
|
509
|
+
if (!file) {
|
|
510
|
+
setSrc(undefined);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const objectUrl = URL.createObjectURL(file);
|
|
515
|
+
setSrc(objectUrl);
|
|
516
|
+
|
|
517
|
+
return () => {
|
|
518
|
+
URL.revokeObjectURL(objectUrl);
|
|
519
|
+
};
|
|
520
|
+
}, [file]);
|
|
521
|
+
|
|
522
|
+
return src;
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
const useAttachmentSrc = () => {
|
|
526
|
+
const { file, src } = useAssistantState(
|
|
527
|
+
useShallow(({ attachment }): { file?: File; src?: string } => {
|
|
528
|
+
if (attachment.type !== "image") return {};
|
|
529
|
+
if (attachment.file) return { file: attachment.file };
|
|
530
|
+
const src = attachment.content?.filter((c) => c.type === "image")[0]
|
|
531
|
+
?.image;
|
|
532
|
+
if (!src) return {};
|
|
533
|
+
return { src };
|
|
534
|
+
}),
|
|
535
|
+
);
|
|
536
|
+
|
|
537
|
+
return useFileSrc(file) ?? src;
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
type AttachmentPreviewProps = {
|
|
541
|
+
src: string;
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
const AttachmentPreview: FC<AttachmentPreviewProps> = ({ src }) => {
|
|
545
|
+
const [isLoaded, setIsLoaded] = useState(false);
|
|
546
|
+
return (
|
|
547
|
+
<Image
|
|
548
|
+
src={src}
|
|
549
|
+
alt="Image Preview"
|
|
550
|
+
width={1}
|
|
551
|
+
height={1}
|
|
552
|
+
className={
|
|
553
|
+
isLoaded
|
|
554
|
+
? "aui-attachment-preview-image-loaded block h-auto max-h-[80vh] w-auto max-w-full object-contain"
|
|
555
|
+
: "aui-attachment-preview-image-loading hidden"
|
|
556
|
+
}
|
|
557
|
+
onLoadingComplete={() => setIsLoaded(true)}
|
|
558
|
+
priority={false}
|
|
559
|
+
/>
|
|
560
|
+
);
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
const AttachmentPreviewDialog: FC<PropsWithChildren> = ({ children }) => {
|
|
564
|
+
const src = useAttachmentSrc();
|
|
565
|
+
|
|
566
|
+
if (!src) return children;
|
|
567
|
+
|
|
568
|
+
return (
|
|
569
|
+
<Dialog>
|
|
570
|
+
<DialogTrigger
|
|
571
|
+
className="aui-attachment-preview-trigger hover:bg-accent/50 cursor-pointer transition-colors"
|
|
572
|
+
asChild
|
|
573
|
+
>
|
|
574
|
+
{children}
|
|
575
|
+
</DialogTrigger>
|
|
576
|
+
<DialogContent className="aui-attachment-preview-dialog-content [&_svg]:text-background [&>button]:bg-foreground/60 [&>button]:hover:[&_svg]:text-destructive p-2 sm:max-w-3xl [&>button]:rounded-full [&>button]:p-1 [&>button]:opacity-100 [&>button]:!ring-0">
|
|
577
|
+
<DialogTitle className="aui-sr-only sr-only">
|
|
578
|
+
Image Attachment Preview
|
|
579
|
+
</DialogTitle>
|
|
580
|
+
<div className="aui-attachment-preview bg-background relative mx-auto flex max-h-[80dvh] w-full items-center justify-center overflow-hidden">
|
|
581
|
+
<AttachmentPreview src={src} />
|
|
582
|
+
</div>
|
|
583
|
+
</DialogContent>
|
|
584
|
+
</Dialog>
|
|
585
|
+
);
|
|
586
|
+
};
|
|
587
|
+
|
|
588
|
+
const AttachmentThumb: FC = () => {
|
|
589
|
+
const isImage = useAssistantState(
|
|
590
|
+
({ attachment }) => attachment.type === "image",
|
|
591
|
+
);
|
|
592
|
+
const src = useAttachmentSrc();
|
|
593
|
+
|
|
594
|
+
return (
|
|
595
|
+
<Avatar className="aui-attachment-tile-avatar h-full w-full rounded-none">
|
|
596
|
+
<AvatarImage
|
|
597
|
+
src={src}
|
|
598
|
+
alt="Attachment preview"
|
|
599
|
+
className="aui-attachment-tile-image object-cover"
|
|
600
|
+
/>
|
|
601
|
+
<AvatarFallback delayMs={isImage ? 200 : 0}>
|
|
602
|
+
<FileText className="aui-attachment-tile-fallback-icon text-muted-foreground size-8" />
|
|
603
|
+
</AvatarFallback>
|
|
604
|
+
</Avatar>
|
|
605
|
+
);
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
const AttachmentUI: FC = () => {
|
|
609
|
+
const api = useAssistantApi();
|
|
610
|
+
const isComposer = api.attachment.source === "composer";
|
|
611
|
+
|
|
612
|
+
const isImage = useAssistantState(
|
|
613
|
+
({ attachment }) => attachment.type === "image",
|
|
614
|
+
);
|
|
615
|
+
const typeLabel = useAssistantState(({ attachment }) => {
|
|
616
|
+
const type = attachment.type;
|
|
617
|
+
switch (type) {
|
|
618
|
+
case "image":
|
|
619
|
+
return "Image";
|
|
620
|
+
case "document":
|
|
621
|
+
return "Document";
|
|
622
|
+
case "file":
|
|
623
|
+
return "File";
|
|
624
|
+
default:
|
|
625
|
+
const _exhaustiveCheck: never = type;
|
|
626
|
+
throw new Error(`Unknown attachment type: ${_exhaustiveCheck}`);
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
return (
|
|
631
|
+
<Tooltip>
|
|
632
|
+
<AttachmentPrimitive.Root
|
|
633
|
+
className={cn(
|
|
634
|
+
"aui-attachment-root relative",
|
|
635
|
+
isImage &&
|
|
636
|
+
"aui-attachment-root-composer only:[&>#attachment-tile]:size-24",
|
|
637
|
+
)}
|
|
638
|
+
>
|
|
639
|
+
<AttachmentPreviewDialog>
|
|
640
|
+
<TooltipTrigger asChild>
|
|
641
|
+
<div
|
|
642
|
+
className={cn(
|
|
643
|
+
"aui-attachment-tile bg-muted size-14 cursor-pointer overflow-hidden rounded-[14px] border transition-opacity hover:opacity-75",
|
|
644
|
+
isComposer &&
|
|
645
|
+
"aui-attachment-tile-composer border-foreground/20",
|
|
646
|
+
)}
|
|
647
|
+
role="button"
|
|
648
|
+
id="attachment-tile"
|
|
649
|
+
aria-label={`${typeLabel} attachment`}
|
|
650
|
+
>
|
|
651
|
+
<AttachmentThumb />
|
|
652
|
+
</div>
|
|
653
|
+
</TooltipTrigger>
|
|
654
|
+
</AttachmentPreviewDialog>
|
|
655
|
+
{isComposer && <AttachmentRemove />}
|
|
656
|
+
</AttachmentPrimitive.Root>
|
|
657
|
+
<TooltipContent side="top">
|
|
658
|
+
<AttachmentPrimitive.Name />
|
|
659
|
+
</TooltipContent>
|
|
660
|
+
</Tooltip>
|
|
661
|
+
);
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
const AttachmentRemove: FC = () => {
|
|
665
|
+
return (
|
|
666
|
+
<AttachmentPrimitive.Remove asChild>
|
|
667
|
+
<TooltipIconButton
|
|
668
|
+
tooltip="Remove file"
|
|
669
|
+
className="aui-attachment-tile-remove text-muted-foreground hover:[&_svg]:text-destructive absolute top-1.5 right-1.5 size-3.5 rounded-full bg-white opacity-100 shadow-sm hover:!bg-white [&_svg]:text-black"
|
|
670
|
+
side="top"
|
|
671
|
+
>
|
|
672
|
+
<XIcon className="aui-attachment-remove-icon size-3 dark:stroke-[2.5px]" />
|
|
673
|
+
</TooltipIconButton>
|
|
674
|
+
</AttachmentPrimitive.Remove>
|
|
675
|
+
);
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
export const UserMessageAttachments: FC = () => {
|
|
679
|
+
return (
|
|
680
|
+
<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">
|
|
681
|
+
<MessagePrimitive.Attachments components={{ Attachment: AttachmentUI }} />
|
|
682
|
+
</div>
|
|
683
|
+
);
|
|
684
|
+
};
|
|
685
|
+
|
|
686
|
+
export const ComposerAttachments: FC = () => {
|
|
687
|
+
return (
|
|
688
|
+
<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">
|
|
689
|
+
<ComposerPrimitive.Attachments
|
|
690
|
+
components={{ Attachment: AttachmentUI }}
|
|
691
|
+
/>
|
|
692
|
+
</div>
|
|
693
|
+
);
|
|
694
|
+
};
|
|
695
|
+
|
|
696
|
+
export const ComposerAddAttachment: FC = () => {
|
|
697
|
+
return (
|
|
698
|
+
<ComposerPrimitive.AddAttachment asChild>
|
|
699
|
+
<TooltipIconButton
|
|
700
|
+
tooltip="Add Attachment"
|
|
701
|
+
side="bottom"
|
|
702
|
+
variant="ghost"
|
|
703
|
+
size="icon"
|
|
704
|
+
className="aui-composer-add-attachment hover:bg-muted-foreground/15 dark:border-muted-foreground/15 dark:hover:bg-muted-foreground/30 size-[34px] rounded-full p-1 text-xs font-semibold"
|
|
705
|
+
aria-label="Add Attachment"
|
|
706
|
+
>
|
|
707
|
+
<PlusIcon className="aui-attachment-add-icon size-5 stroke-[1.5px]" />
|
|
708
|
+
</TooltipIconButton>
|
|
709
|
+
</ComposerPrimitive.AddAttachment>
|
|
710
|
+
);
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
```
|
|
714
|
+
|
|
475
715
|
## components/assistant-ui/markdown-text.tsx
|
|
476
716
|
|
|
477
717
|
```tsx
|
|
@@ -480,13 +720,13 @@ export default function Home() {
|
|
|
480
720
|
import "@assistant-ui/react-markdown/styles/dot.css";
|
|
481
721
|
|
|
482
722
|
import {
|
|
483
|
-
CodeHeaderProps,
|
|
723
|
+
type CodeHeaderProps,
|
|
484
724
|
MarkdownTextPrimitive,
|
|
485
725
|
unstable_memoizeMarkdownComponents as memoizeMarkdownComponents,
|
|
486
726
|
useIsMarkdownCodeBlock,
|
|
487
727
|
} from "@assistant-ui/react-markdown";
|
|
488
728
|
import remarkGfm from "remark-gfm";
|
|
489
|
-
import { FC, memo, useState } from "react";
|
|
729
|
+
import { type FC, memo, useState } from "react";
|
|
490
730
|
import { CheckIcon, CopyIcon } from "lucide-react";
|
|
491
731
|
|
|
492
732
|
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
|
|
@@ -512,8 +752,10 @@ const CodeHeader: FC<CodeHeaderProps> = ({ language, code }) => {
|
|
|
512
752
|
};
|
|
513
753
|
|
|
514
754
|
return (
|
|
515
|
-
<div className="flex items-center justify-between gap-4 rounded-t-lg
|
|
516
|
-
<span className="lowercase [&>span]:text-xs">
|
|
755
|
+
<div className="aui-code-header-root bg-muted-foreground/15 text-foreground dark:bg-muted-foreground/20 mt-4 flex items-center justify-between gap-4 rounded-t-lg px-4 py-2 text-sm font-semibold">
|
|
756
|
+
<span className="aui-code-header-language lowercase [&>span]:text-xs">
|
|
757
|
+
{language}
|
|
758
|
+
</span>
|
|
517
759
|
<TooltipIconButton tooltip="Copy" onClick={onCopy}>
|
|
518
760
|
{!isCopied && <CopyIcon />}
|
|
519
761
|
{isCopied && <CheckIcon />}
|
|
@@ -545,7 +787,7 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
545
787
|
h1: ({ className, ...props }) => (
|
|
546
788
|
<h1
|
|
547
789
|
className={cn(
|
|
548
|
-
"mb-8 scroll-m-20 text-4xl font-extrabold tracking-tight last:mb-0",
|
|
790
|
+
"aui-md-h1 mb-8 scroll-m-20 text-4xl font-extrabold tracking-tight last:mb-0",
|
|
549
791
|
className,
|
|
550
792
|
)}
|
|
551
793
|
{...props}
|
|
@@ -554,7 +796,7 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
554
796
|
h2: ({ className, ...props }) => (
|
|
555
797
|
<h2
|
|
556
798
|
className={cn(
|
|
557
|
-
"mt-8 mb-4 scroll-m-20 text-3xl font-semibold tracking-tight first:mt-0 last:mb-0",
|
|
799
|
+
"aui-md-h2 mt-8 mb-4 scroll-m-20 text-3xl font-semibold tracking-tight first:mt-0 last:mb-0",
|
|
558
800
|
className,
|
|
559
801
|
)}
|
|
560
802
|
{...props}
|
|
@@ -563,7 +805,7 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
563
805
|
h3: ({ className, ...props }) => (
|
|
564
806
|
<h3
|
|
565
807
|
className={cn(
|
|
566
|
-
"mt-6 mb-4 scroll-m-20 text-2xl font-semibold tracking-tight first:mt-0 last:mb-0",
|
|
808
|
+
"aui-md-h3 mt-6 mb-4 scroll-m-20 text-2xl font-semibold tracking-tight first:mt-0 last:mb-0",
|
|
567
809
|
className,
|
|
568
810
|
)}
|
|
569
811
|
{...props}
|
|
@@ -572,7 +814,7 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
572
814
|
h4: ({ className, ...props }) => (
|
|
573
815
|
<h4
|
|
574
816
|
className={cn(
|
|
575
|
-
"mt-6 mb-4 scroll-m-20 text-xl font-semibold tracking-tight first:mt-0 last:mb-0",
|
|
817
|
+
"aui-md-h4 mt-6 mb-4 scroll-m-20 text-xl font-semibold tracking-tight first:mt-0 last:mb-0",
|
|
576
818
|
className,
|
|
577
819
|
)}
|
|
578
820
|
{...props}
|
|
@@ -581,7 +823,7 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
581
823
|
h5: ({ className, ...props }) => (
|
|
582
824
|
<h5
|
|
583
825
|
className={cn(
|
|
584
|
-
"my-4 text-lg font-semibold first:mt-0 last:mb-0",
|
|
826
|
+
"aui-md-h5 my-4 text-lg font-semibold first:mt-0 last:mb-0",
|
|
585
827
|
className,
|
|
586
828
|
)}
|
|
587
829
|
{...props}
|
|
@@ -589,20 +831,26 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
589
831
|
),
|
|
590
832
|
h6: ({ className, ...props }) => (
|
|
591
833
|
<h6
|
|
592
|
-
className={cn(
|
|
834
|
+
className={cn(
|
|
835
|
+
"aui-md-h6 my-4 font-semibold first:mt-0 last:mb-0",
|
|
836
|
+
className,
|
|
837
|
+
)}
|
|
593
838
|
{...props}
|
|
594
839
|
/>
|
|
595
840
|
),
|
|
596
841
|
p: ({ className, ...props }) => (
|
|
597
842
|
<p
|
|
598
|
-
className={cn(
|
|
843
|
+
className={cn(
|
|
844
|
+
"aui-md-p mt-5 mb-5 leading-7 first:mt-0 last:mb-0",
|
|
845
|
+
className,
|
|
846
|
+
)}
|
|
599
847
|
{...props}
|
|
600
848
|
/>
|
|
601
849
|
),
|
|
602
850
|
a: ({ className, ...props }) => (
|
|
603
851
|
<a
|
|
604
852
|
className={cn(
|
|
605
|
-
"text-primary font-medium underline underline-offset-4",
|
|
853
|
+
"aui-md-a text-primary font-medium underline underline-offset-4",
|
|
606
854
|
className,
|
|
607
855
|
)}
|
|
608
856
|
{...props}
|
|
@@ -610,29 +858,29 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
610
858
|
),
|
|
611
859
|
blockquote: ({ className, ...props }) => (
|
|
612
860
|
<blockquote
|
|
613
|
-
className={cn("border-l-2 pl-6 italic", className)}
|
|
861
|
+
className={cn("aui-md-blockquote border-l-2 pl-6 italic", className)}
|
|
614
862
|
{...props}
|
|
615
863
|
/>
|
|
616
864
|
),
|
|
617
865
|
ul: ({ className, ...props }) => (
|
|
618
866
|
<ul
|
|
619
|
-
className={cn("my-5 ml-6 list-disc [&>li]:mt-2", className)}
|
|
867
|
+
className={cn("aui-md-ul my-5 ml-6 list-disc [&>li]:mt-2", className)}
|
|
620
868
|
{...props}
|
|
621
869
|
/>
|
|
622
870
|
),
|
|
623
871
|
ol: ({ className, ...props }) => (
|
|
624
872
|
<ol
|
|
625
|
-
className={cn("my-5 ml-6 list-decimal [&>li]:mt-2", className)}
|
|
873
|
+
className={cn("aui-md-ol my-5 ml-6 list-decimal [&>li]:mt-2", className)}
|
|
626
874
|
{...props}
|
|
627
875
|
/>
|
|
628
876
|
),
|
|
629
877
|
hr: ({ className, ...props }) => (
|
|
630
|
-
<hr className={cn("my-5 border-b", className)} {...props} />
|
|
878
|
+
<hr className={cn("aui-md-hr my-5 border-b", className)} {...props} />
|
|
631
879
|
),
|
|
632
880
|
table: ({ className, ...props }) => (
|
|
633
881
|
<table
|
|
634
882
|
className={cn(
|
|
635
|
-
"my-5 w-full border-separate border-spacing-0 overflow-y-auto",
|
|
883
|
+
"aui-md-table my-5 w-full border-separate border-spacing-0 overflow-y-auto",
|
|
636
884
|
className,
|
|
637
885
|
)}
|
|
638
886
|
{...props}
|
|
@@ -641,7 +889,7 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
641
889
|
th: ({ className, ...props }) => (
|
|
642
890
|
<th
|
|
643
891
|
className={cn(
|
|
644
|
-
"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",
|
|
892
|
+
"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",
|
|
645
893
|
className,
|
|
646
894
|
)}
|
|
647
895
|
{...props}
|
|
@@ -650,7 +898,7 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
650
898
|
td: ({ className, ...props }) => (
|
|
651
899
|
<td
|
|
652
900
|
className={cn(
|
|
653
|
-
"border-b border-l px-4 py-2 text-left last:border-r [&[align=center]]:text-center [&[align=right]]:text-right",
|
|
901
|
+
"aui-md-td border-b border-l px-4 py-2 text-left last:border-r [&[align=center]]:text-center [&[align=right]]:text-right",
|
|
654
902
|
className,
|
|
655
903
|
)}
|
|
656
904
|
{...props}
|
|
@@ -659,7 +907,7 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
659
907
|
tr: ({ className, ...props }) => (
|
|
660
908
|
<tr
|
|
661
909
|
className={cn(
|
|
662
|
-
"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",
|
|
910
|
+
"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",
|
|
663
911
|
className,
|
|
664
912
|
)}
|
|
665
913
|
{...props}
|
|
@@ -667,14 +915,14 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
667
915
|
),
|
|
668
916
|
sup: ({ className, ...props }) => (
|
|
669
917
|
<sup
|
|
670
|
-
className={cn("[&>a]:text-xs [&>a]:no-underline", className)}
|
|
918
|
+
className={cn("aui-md-sup [&>a]:text-xs [&>a]:no-underline", className)}
|
|
671
919
|
{...props}
|
|
672
920
|
/>
|
|
673
921
|
),
|
|
674
922
|
pre: ({ className, ...props }) => (
|
|
675
923
|
<pre
|
|
676
924
|
className={cn(
|
|
677
|
-
"overflow-x-auto rounded-b-lg bg-black p-4 text-white",
|
|
925
|
+
"aui-md-pre overflow-x-auto !rounded-t-none rounded-b-lg bg-black p-4 text-white",
|
|
678
926
|
className,
|
|
679
927
|
)}
|
|
680
928
|
{...props}
|
|
@@ -685,7 +933,8 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
685
933
|
return (
|
|
686
934
|
<code
|
|
687
935
|
className={cn(
|
|
688
|
-
!isCodeBlock &&
|
|
936
|
+
!isCodeBlock &&
|
|
937
|
+
"aui-md-inline-code bg-muted rounded border font-semibold",
|
|
689
938
|
className,
|
|
690
939
|
)}
|
|
691
940
|
{...props}
|
|
@@ -987,12 +1236,111 @@ const CircleStopIcon = () => {
|
|
|
987
1236
|
|
|
988
1237
|
```
|
|
989
1238
|
|
|
1239
|
+
## components/assistant-ui/tool-fallback.tsx
|
|
1240
|
+
|
|
1241
|
+
```tsx
|
|
1242
|
+
import type { ToolCallMessagePartComponent } from "@assistant-ui/react";
|
|
1243
|
+
import {
|
|
1244
|
+
CheckIcon,
|
|
1245
|
+
ChevronDownIcon,
|
|
1246
|
+
ChevronUpIcon,
|
|
1247
|
+
XCircleIcon,
|
|
1248
|
+
} from "lucide-react";
|
|
1249
|
+
import { useState } from "react";
|
|
1250
|
+
import { Button } from "@/components/ui/button";
|
|
1251
|
+
import { cn } from "@/lib/utils";
|
|
1252
|
+
|
|
1253
|
+
export const ToolFallback: ToolCallMessagePartComponent = ({
|
|
1254
|
+
toolName,
|
|
1255
|
+
argsText,
|
|
1256
|
+
result,
|
|
1257
|
+
status,
|
|
1258
|
+
}) => {
|
|
1259
|
+
const [isCollapsed, setIsCollapsed] = useState(true);
|
|
1260
|
+
|
|
1261
|
+
const isCancelled =
|
|
1262
|
+
status?.type === "incomplete" && status.reason === "cancelled";
|
|
1263
|
+
const cancelledReason =
|
|
1264
|
+
isCancelled && status.error
|
|
1265
|
+
? typeof status.error === "string"
|
|
1266
|
+
? status.error
|
|
1267
|
+
: JSON.stringify(status.error)
|
|
1268
|
+
: null;
|
|
1269
|
+
|
|
1270
|
+
return (
|
|
1271
|
+
<div
|
|
1272
|
+
className={cn(
|
|
1273
|
+
"aui-tool-fallback-root mb-4 flex w-full flex-col gap-3 rounded-lg border py-3",
|
|
1274
|
+
isCancelled && "border-muted-foreground/30 bg-muted/30",
|
|
1275
|
+
)}
|
|
1276
|
+
>
|
|
1277
|
+
<div className="aui-tool-fallback-header flex items-center gap-2 px-4">
|
|
1278
|
+
{isCancelled ? (
|
|
1279
|
+
<XCircleIcon className="aui-tool-fallback-icon text-muted-foreground size-4" />
|
|
1280
|
+
) : (
|
|
1281
|
+
<CheckIcon className="aui-tool-fallback-icon size-4" />
|
|
1282
|
+
)}
|
|
1283
|
+
<p
|
|
1284
|
+
className={cn(
|
|
1285
|
+
"aui-tool-fallback-title grow",
|
|
1286
|
+
isCancelled && "text-muted-foreground line-through",
|
|
1287
|
+
)}
|
|
1288
|
+
>
|
|
1289
|
+
{isCancelled ? "Cancelled tool: " : "Used tool: "}
|
|
1290
|
+
<b>{toolName}</b>
|
|
1291
|
+
</p>
|
|
1292
|
+
<Button onClick={() => setIsCollapsed(!isCollapsed)}>
|
|
1293
|
+
{isCollapsed ? <ChevronUpIcon /> : <ChevronDownIcon />}
|
|
1294
|
+
</Button>
|
|
1295
|
+
</div>
|
|
1296
|
+
{!isCollapsed && (
|
|
1297
|
+
<div className="aui-tool-fallback-content flex flex-col gap-2 border-t pt-2">
|
|
1298
|
+
{cancelledReason && (
|
|
1299
|
+
<div className="aui-tool-fallback-cancelled-root px-4">
|
|
1300
|
+
<p className="aui-tool-fallback-cancelled-header text-muted-foreground font-semibold">
|
|
1301
|
+
Cancelled reason:
|
|
1302
|
+
</p>
|
|
1303
|
+
<p className="aui-tool-fallback-cancelled-reason text-muted-foreground">
|
|
1304
|
+
{cancelledReason}
|
|
1305
|
+
</p>
|
|
1306
|
+
</div>
|
|
1307
|
+
)}
|
|
1308
|
+
<div
|
|
1309
|
+
className={cn(
|
|
1310
|
+
"aui-tool-fallback-args-root px-4",
|
|
1311
|
+
isCancelled && "opacity-60",
|
|
1312
|
+
)}
|
|
1313
|
+
>
|
|
1314
|
+
<pre className="aui-tool-fallback-args-value whitespace-pre-wrap">
|
|
1315
|
+
{argsText}
|
|
1316
|
+
</pre>
|
|
1317
|
+
</div>
|
|
1318
|
+
{!isCancelled && result !== undefined && (
|
|
1319
|
+
<div className="aui-tool-fallback-result-root border-t border-dashed px-4 pt-2">
|
|
1320
|
+
<p className="aui-tool-fallback-result-header font-semibold">
|
|
1321
|
+
Result:
|
|
1322
|
+
</p>
|
|
1323
|
+
<pre className="aui-tool-fallback-result-content whitespace-pre-wrap">
|
|
1324
|
+
{typeof result === "string"
|
|
1325
|
+
? result
|
|
1326
|
+
: JSON.stringify(result, null, 2)}
|
|
1327
|
+
</pre>
|
|
1328
|
+
</div>
|
|
1329
|
+
)}
|
|
1330
|
+
</div>
|
|
1331
|
+
)}
|
|
1332
|
+
</div>
|
|
1333
|
+
);
|
|
1334
|
+
};
|
|
1335
|
+
|
|
1336
|
+
```
|
|
1337
|
+
|
|
990
1338
|
## components/assistant-ui/tooltip-icon-button.tsx
|
|
991
1339
|
|
|
992
1340
|
```tsx
|
|
993
1341
|
"use client";
|
|
994
1342
|
|
|
995
|
-
import {
|
|
1343
|
+
import { ComponentPropsWithRef, forwardRef } from "react";
|
|
996
1344
|
import { Slottable } from "@radix-ui/react-slot";
|
|
997
1345
|
|
|
998
1346
|
import {
|
|
@@ -1003,7 +1351,7 @@ import {
|
|
|
1003
1351
|
import { Button } from "@/components/ui/button";
|
|
1004
1352
|
import { cn } from "@/lib/utils";
|
|
1005
1353
|
|
|
1006
|
-
export type TooltipIconButtonProps =
|
|
1354
|
+
export type TooltipIconButtonProps = ComponentPropsWithRef<typeof Button> & {
|
|
1007
1355
|
tooltip: string;
|
|
1008
1356
|
side?: "top" | "bottom" | "left" | "right";
|
|
1009
1357
|
};
|
|
@@ -1019,11 +1367,11 @@ export const TooltipIconButton = forwardRef<
|
|
|
1019
1367
|
variant="ghost"
|
|
1020
1368
|
size="icon"
|
|
1021
1369
|
{...rest}
|
|
1022
|
-
className={cn("size-6 p-1", className)}
|
|
1370
|
+
className={cn("aui-button-icon size-6 p-1", className)}
|
|
1023
1371
|
ref={ref}
|
|
1024
1372
|
>
|
|
1025
1373
|
<Slottable>{children}</Slottable>
|
|
1026
|
-
<span className="sr-only">{tooltip}</span>
|
|
1374
|
+
<span className="aui-sr-only sr-only">{tooltip}</span>
|
|
1027
1375
|
</Button>
|
|
1028
1376
|
</TooltipTrigger>
|
|
1029
1377
|
<TooltipContent side={side}>{tooltip}</TooltipContent>
|
|
@@ -1035,6 +1383,62 @@ TooltipIconButton.displayName = "TooltipIconButton";
|
|
|
1035
1383
|
|
|
1036
1384
|
```
|
|
1037
1385
|
|
|
1386
|
+
## components/ui/avatar.tsx
|
|
1387
|
+
|
|
1388
|
+
```tsx
|
|
1389
|
+
"use client";
|
|
1390
|
+
|
|
1391
|
+
import * as React from "react";
|
|
1392
|
+
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
1393
|
+
|
|
1394
|
+
import { cn } from "@/lib/utils";
|
|
1395
|
+
|
|
1396
|
+
const Avatar = React.forwardRef<
|
|
1397
|
+
React.ElementRef<typeof AvatarPrimitive.Root>,
|
|
1398
|
+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
|
|
1399
|
+
>(({ className, ...props }, ref) => (
|
|
1400
|
+
<AvatarPrimitive.Root
|
|
1401
|
+
ref={ref}
|
|
1402
|
+
className={cn(
|
|
1403
|
+
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
|
|
1404
|
+
className,
|
|
1405
|
+
)}
|
|
1406
|
+
{...props}
|
|
1407
|
+
/>
|
|
1408
|
+
));
|
|
1409
|
+
Avatar.displayName = AvatarPrimitive.Root.displayName;
|
|
1410
|
+
|
|
1411
|
+
const AvatarImage = React.forwardRef<
|
|
1412
|
+
React.ElementRef<typeof AvatarPrimitive.Image>,
|
|
1413
|
+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
|
|
1414
|
+
>(({ className, ...props }, ref) => (
|
|
1415
|
+
<AvatarPrimitive.Image
|
|
1416
|
+
ref={ref}
|
|
1417
|
+
className={cn("aspect-square h-full w-full", className)}
|
|
1418
|
+
{...props}
|
|
1419
|
+
/>
|
|
1420
|
+
));
|
|
1421
|
+
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
|
|
1422
|
+
|
|
1423
|
+
const AvatarFallback = React.forwardRef<
|
|
1424
|
+
React.ElementRef<typeof AvatarPrimitive.Fallback>,
|
|
1425
|
+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
|
|
1426
|
+
>(({ className, ...props }, ref) => (
|
|
1427
|
+
<AvatarPrimitive.Fallback
|
|
1428
|
+
ref={ref}
|
|
1429
|
+
className={cn(
|
|
1430
|
+
"bg-muted flex h-full w-full items-center justify-center rounded-full",
|
|
1431
|
+
className,
|
|
1432
|
+
)}
|
|
1433
|
+
{...props}
|
|
1434
|
+
/>
|
|
1435
|
+
));
|
|
1436
|
+
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
|
|
1437
|
+
|
|
1438
|
+
export { Avatar, AvatarImage, AvatarFallback };
|
|
1439
|
+
|
|
1440
|
+
```
|
|
1441
|
+
|
|
1038
1442
|
## components/ui/button.tsx
|
|
1039
1443
|
|
|
1040
1444
|
```tsx
|
|
@@ -1102,6 +1506,147 @@ export { Button, buttonVariants };
|
|
|
1102
1506
|
|
|
1103
1507
|
```
|
|
1104
1508
|
|
|
1509
|
+
## components/ui/dialog.tsx
|
|
1510
|
+
|
|
1511
|
+
```tsx
|
|
1512
|
+
"use client";
|
|
1513
|
+
|
|
1514
|
+
import * as React from "react";
|
|
1515
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
1516
|
+
import { XIcon } from "lucide-react";
|
|
1517
|
+
|
|
1518
|
+
import { cn } from "@/lib/utils";
|
|
1519
|
+
|
|
1520
|
+
function Dialog({
|
|
1521
|
+
...props
|
|
1522
|
+
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
|
1523
|
+
return <DialogPrimitive.Root data-slot="dialog" {...props} />;
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
function DialogTrigger({
|
|
1527
|
+
...props
|
|
1528
|
+
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
|
1529
|
+
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
function DialogPortal({
|
|
1533
|
+
...props
|
|
1534
|
+
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
|
1535
|
+
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
function DialogClose({
|
|
1539
|
+
...props
|
|
1540
|
+
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
|
|
1541
|
+
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
function DialogOverlay({
|
|
1545
|
+
className,
|
|
1546
|
+
...props
|
|
1547
|
+
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
|
1548
|
+
return (
|
|
1549
|
+
<DialogPrimitive.Overlay
|
|
1550
|
+
data-slot="dialog-overlay"
|
|
1551
|
+
className={cn(
|
|
1552
|
+
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80",
|
|
1553
|
+
className,
|
|
1554
|
+
)}
|
|
1555
|
+
{...props}
|
|
1556
|
+
/>
|
|
1557
|
+
);
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
function DialogContent({
|
|
1561
|
+
className,
|
|
1562
|
+
children,
|
|
1563
|
+
...props
|
|
1564
|
+
}: React.ComponentProps<typeof DialogPrimitive.Content>) {
|
|
1565
|
+
return (
|
|
1566
|
+
<DialogPortal data-slot="dialog-portal">
|
|
1567
|
+
<DialogOverlay />
|
|
1568
|
+
<DialogPrimitive.Content
|
|
1569
|
+
data-slot="dialog-content"
|
|
1570
|
+
className={cn(
|
|
1571
|
+
"bg-background data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
|
1572
|
+
className,
|
|
1573
|
+
)}
|
|
1574
|
+
{...props}
|
|
1575
|
+
>
|
|
1576
|
+
{children}
|
|
1577
|
+
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4">
|
|
1578
|
+
<XIcon />
|
|
1579
|
+
<span className="sr-only">Close</span>
|
|
1580
|
+
</DialogPrimitive.Close>
|
|
1581
|
+
</DialogPrimitive.Content>
|
|
1582
|
+
</DialogPortal>
|
|
1583
|
+
);
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
1587
|
+
return (
|
|
1588
|
+
<div
|
|
1589
|
+
data-slot="dialog-header"
|
|
1590
|
+
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
|
1591
|
+
{...props}
|
|
1592
|
+
/>
|
|
1593
|
+
);
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
1597
|
+
return (
|
|
1598
|
+
<div
|
|
1599
|
+
data-slot="dialog-footer"
|
|
1600
|
+
className={cn(
|
|
1601
|
+
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
|
1602
|
+
className,
|
|
1603
|
+
)}
|
|
1604
|
+
{...props}
|
|
1605
|
+
/>
|
|
1606
|
+
);
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
function DialogTitle({
|
|
1610
|
+
className,
|
|
1611
|
+
...props
|
|
1612
|
+
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
|
1613
|
+
return (
|
|
1614
|
+
<DialogPrimitive.Title
|
|
1615
|
+
data-slot="dialog-title"
|
|
1616
|
+
className={cn("text-lg leading-none font-semibold", className)}
|
|
1617
|
+
{...props}
|
|
1618
|
+
/>
|
|
1619
|
+
);
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
function DialogDescription({
|
|
1623
|
+
className,
|
|
1624
|
+
...props
|
|
1625
|
+
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
|
1626
|
+
return (
|
|
1627
|
+
<DialogPrimitive.Description
|
|
1628
|
+
data-slot="dialog-description"
|
|
1629
|
+
className={cn("text-muted-foreground text-sm", className)}
|
|
1630
|
+
{...props}
|
|
1631
|
+
/>
|
|
1632
|
+
);
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
export {
|
|
1636
|
+
Dialog,
|
|
1637
|
+
DialogClose,
|
|
1638
|
+
DialogContent,
|
|
1639
|
+
DialogDescription,
|
|
1640
|
+
DialogFooter,
|
|
1641
|
+
DialogHeader,
|
|
1642
|
+
DialogOverlay,
|
|
1643
|
+
DialogPortal,
|
|
1644
|
+
DialogTitle,
|
|
1645
|
+
DialogTrigger,
|
|
1646
|
+
};
|
|
1647
|
+
|
|
1648
|
+
```
|
|
1649
|
+
|
|
1105
1650
|
## components/ui/tooltip.tsx
|
|
1106
1651
|
|
|
1107
1652
|
```tsx
|
|
@@ -1181,18 +1726,6 @@ export function cn(...inputs: ClassValue[]) {
|
|
|
1181
1726
|
|
|
1182
1727
|
```
|
|
1183
1728
|
|
|
1184
|
-
## next-env.d.ts
|
|
1185
|
-
|
|
1186
|
-
```typescript
|
|
1187
|
-
/// <reference types="next" />
|
|
1188
|
-
/// <reference types="next/image-types/global" />
|
|
1189
|
-
import "./.next/types/routes.d.ts";
|
|
1190
|
-
|
|
1191
|
-
// NOTE: This file should not be edited
|
|
1192
|
-
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
|
1193
|
-
|
|
1194
|
-
```
|
|
1195
|
-
|
|
1196
1729
|
## next.config.ts
|
|
1197
1730
|
|
|
1198
1731
|
```typescript
|
|
@@ -1220,7 +1753,7 @@ export default nextConfig;
|
|
|
1220
1753
|
"lint": "eslint ."
|
|
1221
1754
|
},
|
|
1222
1755
|
"dependencies": {
|
|
1223
|
-
"@ai-sdk/openai": "^2.0.
|
|
1756
|
+
"@ai-sdk/openai": "^2.0.73",
|
|
1224
1757
|
"@assistant-ui/react": "workspace:*",
|
|
1225
1758
|
"@assistant-ui/react-ai-sdk": "workspace:*",
|
|
1226
1759
|
"@assistant-ui/react-hook-form": "workspace:*",
|
|
@@ -1228,19 +1761,20 @@ export default nextConfig;
|
|
|
1228
1761
|
"@ffmpeg/ffmpeg": "^0.12.15",
|
|
1229
1762
|
"@ffmpeg/util": "^0.12.2",
|
|
1230
1763
|
"@hookform/resolvers": "^5.2.2",
|
|
1231
|
-
"@radix-ui/react-avatar": "^1.1.
|
|
1764
|
+
"@radix-ui/react-avatar": "^1.1.4",
|
|
1765
|
+
"@radix-ui/react-dialog": "^1.1.7",
|
|
1232
1766
|
"@radix-ui/react-icons": "^1.3.2",
|
|
1233
1767
|
"@radix-ui/react-label": "^2.1.8",
|
|
1234
1768
|
"@radix-ui/react-slot": "^1.2.4",
|
|
1235
1769
|
"@radix-ui/react-tabs": "^1.1.13",
|
|
1236
1770
|
"@radix-ui/react-tooltip": "^1.2.8",
|
|
1237
1771
|
"@react-hook/media-query": "^1.1.1",
|
|
1238
|
-
"ai": "^5.0.
|
|
1772
|
+
"ai": "^5.0.102",
|
|
1239
1773
|
"class-variance-authority": "^0.7.1",
|
|
1240
1774
|
"clsx": "^2.1.1",
|
|
1241
|
-
"
|
|
1242
|
-
"
|
|
1243
|
-
"next": "16.0.
|
|
1775
|
+
"lucide-react": "^0.555.0",
|
|
1776
|
+
"motion": "^11.18.2",
|
|
1777
|
+
"next": "16.0.4",
|
|
1244
1778
|
"react": "19.2.0",
|
|
1245
1779
|
"react-dom": "19.2.0",
|
|
1246
1780
|
"react-hook-form": "^7.66.1",
|
|
@@ -1248,7 +1782,7 @@ export default nextConfig;
|
|
|
1248
1782
|
"remark-gfm": "^4.0.1",
|
|
1249
1783
|
"tailwind-merge": "^3.4.0",
|
|
1250
1784
|
"tw-animate-css": "^1.4.0",
|
|
1251
|
-
"zod": "^4.1.
|
|
1785
|
+
"zod": "^4.1.13",
|
|
1252
1786
|
"zustand": "^5.0.8"
|
|
1253
1787
|
},
|
|
1254
1788
|
"devDependencies": {
|