"use client"; import { Button } from "@/components/ui/button"; import { ButtonGroup, ButtonGroupText, } from "@/components/ui/button-group"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; import type { FileUIPart, UIMessage } from "ai"; import { ChevronLeftIcon, ChevronRightIcon, PaperclipIcon, XIcon, } from "lucide-react"; import type { ComponentProps, HTMLAttributes, ReactElement } from "react"; import { createContext, memo, useContext, useEffect, useState, useMemo } from "react"; import { Streamdown } from "streamdown"; export type MessageProps = HTMLAttributes & { from: UIMessage["role"]; }; export const Message = ({ className, from, ...props }: MessageProps) => (
); export type MessageContentProps = HTMLAttributes; export const MessageContent = ({ children, className, ...props }: MessageContentProps) => (
{children}
); export type MessageActionsProps = ComponentProps<"div">; export const MessageActions = ({ className, children, ...props }: MessageActionsProps) => (
{children}
); export type MessageActionProps = ComponentProps & { tooltip?: string; label?: string; }; export const MessageAction = ({ tooltip, children, label, variant = "ghost", size = "icon", ...props }: MessageActionProps) => { const button = ( ); if (tooltip) { return ( {button}

{tooltip}

); } return button; }; type MessageBranchContextType = { currentBranch: number; totalBranches: number; goToPrevious: () => void; goToNext: () => void; branches: ReactElement[]; setBranches: (branches: ReactElement[]) => void; }; const MessageBranchContext = createContext( null ); const useMessageBranch = () => { const context = useContext(MessageBranchContext); if (!context) { throw new Error( "MessageBranch components must be used within MessageBranch" ); } return context; }; export type MessageBranchProps = HTMLAttributes & { defaultBranch?: number; onBranchChange?: (branchIndex: number) => void; }; export const MessageBranch = ({ defaultBranch = 0, onBranchChange, className, ...props }: MessageBranchProps) => { const [currentBranch, setCurrentBranch] = useState(defaultBranch); const [branches, setBranches] = useState([]); const handleBranchChange = (newBranch: number) => { setCurrentBranch(newBranch); onBranchChange?.(newBranch); }; const goToPrevious = () => { const newBranch = currentBranch > 0 ? currentBranch - 1 : branches.length - 1; handleBranchChange(newBranch); }; const goToNext = () => { const newBranch = currentBranch < branches.length - 1 ? currentBranch + 1 : 0; handleBranchChange(newBranch); }; const contextValue: MessageBranchContextType = { currentBranch, totalBranches: branches.length, goToPrevious, goToNext, branches, setBranches, }; return (
div]:pb-0", className)} {...props} /> ); }; export type MessageBranchContentProps = HTMLAttributes; export const MessageBranchContent = ({ children, ...props }: MessageBranchContentProps) => { const { currentBranch, setBranches, branches } = useMessageBranch(); const childrenArray = useMemo( () => (Array.isArray(children) ? children : [children]), [children] ); // Use useEffect to update branches when they change useEffect(() => { if (branches.length !== childrenArray.length) { setBranches(childrenArray); } }, [childrenArray, branches, setBranches]); return childrenArray.map((branch, index) => (
div]:pb-0", index === currentBranch ? "block" : "hidden" )} key={branch.key} {...props} > {branch}
)); }; export type MessageBranchSelectorProps = HTMLAttributes & { from: UIMessage["role"]; }; export const MessageBranchSelector = ({ className, from, ...props }: MessageBranchSelectorProps) => { const { totalBranches } = useMessageBranch(); // Don't render if there's only one branch if (totalBranches <= 1) { return null; } return ( ); }; export type MessageBranchPreviousProps = ComponentProps; export const MessageBranchPrevious = ({ children, ...props }: MessageBranchPreviousProps) => { const { goToPrevious, totalBranches } = useMessageBranch(); return ( ); }; export type MessageBranchNextProps = ComponentProps; export const MessageBranchNext = ({ children, className, ...props }: MessageBranchNextProps) => { const { goToNext, totalBranches } = useMessageBranch(); return ( ); }; export type MessageBranchPageProps = HTMLAttributes; export const MessageBranchPage = ({ className, ...props }: MessageBranchPageProps) => { const { currentBranch, totalBranches } = useMessageBranch(); return ( {currentBranch + 1} of {totalBranches} ); }; export type MessageResponseProps = ComponentProps; export const MessageResponse = memo( ({ className, ...props }: MessageResponseProps) => ( *:first-child]:mt-0 [&>*:last-child]:mb-0", className )} {...props} /> ), (prevProps, nextProps) => prevProps.children === nextProps.children ); MessageResponse.displayName = "MessageResponse"; export type MessageAttachmentProps = HTMLAttributes & { data: FileUIPart; className?: string; onRemove?: () => void; }; export function MessageAttachment({ data, className, onRemove, ...props }: MessageAttachmentProps) { const filename = data.filename || ""; const mediaType = data.mediaType?.startsWith("image/") && data.url ? "image" : "file"; const isImage = mediaType === "image"; const attachmentLabel = filename || (isImage ? "Image" : "Attachment"); return (
{isImage ? ( <> {filename {onRemove && ( )} ) : ( <>

{attachmentLabel}

{onRemove && ( )} )}
); } export type MessageAttachmentsProps = ComponentProps<"div">; export function MessageAttachments({ children, className, ...props }: MessageAttachmentsProps) { if (!children) { return null; } return (
{children}
); } export type MessageToolbarProps = ComponentProps<"div">; export const MessageToolbar = ({ className, children, ...props }: MessageToolbarProps) => (
{children}
);