"use client";
import { cn } from "@/lib/utils";
import {
ActionBarPrimitive,
ActionBarMorePrimitive,
AuiIf,
AttachmentPrimitive,
BranchPickerPrimitive,
ComposerPrimitive,
MessagePrimitive,
ThreadPrimitive,
useAui,
useAuiState,
} from "@assistant-ui/react";
import { useEffect, useState, type FC } from "react";
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
import { useShallow } from "zustand/shallow";
import {
ArrowUpIcon,
AudioLines,
CheckIcon,
ChevronDownIcon,
ChevronLeftIcon,
ChevronRightIcon,
CopyIcon,
Download,
Mic,
MoreHorizontal,
PencilIcon,
PlusIcon,
RefreshCwIcon,
Share,
ThumbsDown,
ThumbsUp,
Volume2,
XIcon,
} from "lucide-react";
import { MarkdownText } from "@/components/assistant-ui/markdown-text";
import { ToolFallback } from "@/components/assistant-ui/tool-fallback";
export const ChatGPT: FC = () => {
return (
s.thread.isEmpty}>
!s.thread.isEmpty}>
{({ message }) => {
if (message.composer.isEditing) return ;
if (message.role === "user") return ;
return ;
}}
ChatGPT can make mistakes. Check important info.
);
};
const EmptyState: FC = () => {
return (
);
};
const Composer: FC<{ placeholder: string }> = ({ placeholder }) => {
return (
s.composer.attachments.length > 0}>
);
};
const ComposerPrimaryAction: FC = () => {
return (
s.thread.isRunning}>
!s.thread.isRunning && s.composer.dictation != null}
>
!s.thread.isRunning &&
s.composer.dictation == null &&
!s.composer.isEmpty
}
>
!s.thread.isRunning &&
s.composer.dictation == null &&
s.composer.isEmpty
}
>
);
};
const ThreadScrollToBottom: FC = () => {
return (
);
};
const UserMessage: FC = () => {
return (
s.message.isCopied}>
!s.message.isCopied}>
);
};
const EditComposer: FC = () => {
return (
Cancel
Send
);
};
const assistantActionClassName =
"flex size-8 items-center justify-center rounded-lg text-[#5d5d5d] transition-colors hover:bg-black/[0.07] hover:text-[#5d5d5d] dark:text-[#cdcdcd] dark:hover:bg-white/15 dark:hover:text-[#cdcdcd]";
const AssistantMessage: FC = () => {
return (
{({ part }) => {
if (part.type === "text") return ;
if (part.type === "tool-call")
return part.toolUI ?? ;
return null;
}}
s.message.isCopied}>
!s.message.isCopied}>
Export as Markdown
);
};
const BranchPicker: FC<{ className?: string }> = ({ className }) => {
return (
/
);
};
const useFileSrc = (file: File | undefined) => {
const [src, setSrc] = useState(undefined);
useEffect(() => {
if (!file) {
setSrc(undefined);
return;
}
const objectUrl = URL.createObjectURL(file);
setSrc(objectUrl);
return () => {
URL.revokeObjectURL(objectUrl);
};
}, [file]);
return src;
};
const useAttachmentSrc = () => {
const { file, src } = useAuiState(
useShallow((s): { file?: File; src?: string } => {
if (s.attachment.type !== "image") return {};
if (s.attachment.file) return { file: s.attachment.file };
const src = s.attachment.content?.filter((c) => c.type === "image")[0]
?.image;
if (!src) return {};
return { src };
}),
);
return useFileSrc(file) ?? src;
};
const ChatGPTAttachmentUI: FC = () => {
const aui = useAui();
const isComposer = aui.attachment.source !== "message";
const src = useAttachmentSrc();
return (
s.attachment.type === "image"}>
{src ? (
) : (
)}
s.attachment.type !== "image"}>
{isComposer && (
)}
);
};