"use client"; import type { UIMessage } from "ai"; import type { ComponentProps, HTMLAttributes, ReactElement, ReactNode, } from "react"; 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 { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"; import { createContext, memo, useCallback, useContext, useEffect, useMemo, useState, } 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-sm", ...props }: MessageActionProps) => { const button = ( ); if (tooltip) { return ( {button}

{tooltip}

); } return button; }; interface 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 = useCallback( (newBranch: number) => { setCurrentBranch(newBranch); onBranchChange?.(newBranch); }, [onBranchChange] ); const goToPrevious = useCallback(() => { const newBranch = currentBranch > 0 ? currentBranch - 1 : branches.length - 1; handleBranchChange(newBranch); }, [currentBranch, branches.length, handleBranchChange]); const goToNext = useCallback(() => { const newBranch = currentBranch < branches.length - 1 ? currentBranch + 1 : 0; handleBranchChange(newBranch); }, [currentBranch, branches.length, handleBranchChange]); const contextValue = useMemo( () => ({ branches, currentBranch, goToNext, goToPrevious, setBranches, totalBranches: branches.length, }), [currentBranch, branches, goToNext, goToPrevious] ); return (
); }; export type MessageBranchContentProps = HTMLAttributes; export const MessageBranchContent = ({ children, className, ...props }: MessageBranchContentProps) => { const { currentBranch, setBranches, branches } = useMessageBranch(); const childrenArray = useMemo( () => (Array.isArray(children) ? children : [children]).filter( Boolean ) as ReactElement[], [children] ); useEffect(() => { setBranches(childrenArray); }, [childrenArray, setBranches]); if (branches.length === 0) { return null; } return (
{branches[currentBranch]}
); }; export type MessageBranchSelectorProps = ComponentProps; export const MessageBranchSelector = ({ className, ...props }: MessageBranchSelectorProps) => ( ); export type MessageBranchPreviousProps = ComponentProps; export const MessageBranchPrevious = ({ className, ...props }: MessageBranchPreviousProps) => { const { goToPrevious, totalBranches } = useMessageBranch(); return ( ); }; export type MessageBranchNextProps = ComponentProps; export const MessageBranchNext = ({ 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<"div">; function extractText(children: ReactNode): string | null { if (children == null || typeof children === "boolean") { return ""; } if ( typeof children === "string" || typeof children === "number" || typeof children === "bigint" ) { return String(children); } if (Array.isArray(children)) { let result = ""; for (const child of children) { const part = extractText(child); if (part === null) { return null; } result += part; } return result; } return null; } export const MessageResponse = memo( ({ className, children, ...props }: MessageResponseProps) => { const text = extractText(children); return (
*:first-child]:mt-0 [&>*:last-child]:mb-0", className )} {...props} > {text !== null ? ( {text} ) : ( children )}
); }, (prevProps, nextProps) => prevProps.children === nextProps.children ); MessageResponse.displayName = "MessageResponse"; export type MessageToolbarProps = ComponentProps<"div">; export const MessageToolbar = ({ className, children, ...props }: MessageToolbarProps) => (
{children}
);