'use client' import { Button } from '@/components/ui/button' import { cn } from '@/lib/utils' import type { UIMessage } from 'ai' import { ArrowDownIcon, DownloadIcon } from 'lucide-react' import type { ComponentProps } from 'react' import { useCallback } from 'react' import { StickToBottom, useStickToBottomContext } from 'use-stick-to-bottom' export type ConversationProps = ComponentProps export const Conversation = ({ className, ...props }: ConversationProps) => ( ) export type ConversationContentProps = ComponentProps export const ConversationContent = ({ className, ...props }: ConversationContentProps) => ( ) export type ConversationEmptyStateProps = ComponentProps<'div'> & { title?: string description?: string icon?: React.ReactNode } export const ConversationEmptyState = ({ className, title = 'No messages yet', description = 'Start a conversation to see messages here', icon, children, ...props }: ConversationEmptyStateProps) => (
{children ?? ( <> {icon &&
{icon}
}

{title}

{description &&

{description}

}
)}
) export type ConversationScrollButtonProps = ComponentProps export const ConversationScrollButton = ({ className, ...props }: ConversationScrollButtonProps) => { const { isAtBottom, scrollToBottom } = useStickToBottomContext() const handleScrollToBottom = useCallback(() => { scrollToBottom() }, [scrollToBottom]) return ( !isAtBottom && ( ) ) } const getMessageText = (message: UIMessage): string => message.parts .filter((part) => part.type === 'text') .map((part) => part.text) .join('') export type ConversationDownloadProps = Omit, 'onClick'> & { messages: UIMessage[] filename?: string formatMessage?: (message: UIMessage, index: number) => string } const defaultFormatMessage = (message: UIMessage): string => { const roleLabel = message.role.charAt(0).toUpperCase() + message.role.slice(1) return `**${roleLabel}:** ${getMessageText(message)}` } export const messagesToMarkdown = ( messages: UIMessage[], formatMessage: (message: UIMessage, index: number) => string = defaultFormatMessage ): string => messages.map((msg, i) => formatMessage(msg, i)).join('\n\n') export const ConversationDownload = ({ messages, filename = 'conversation.md', formatMessage = defaultFormatMessage, className, children, ...props }: ConversationDownloadProps) => { const handleDownload = useCallback(() => { const markdown = messagesToMarkdown(messages, formatMessage) const blob = new Blob([markdown], { type: 'text/markdown' }) const url = URL.createObjectURL(blob) const link = document.createElement('a') link.href = url link.download = filename document.body.append(link) link.click() link.remove() URL.revokeObjectURL(url) }, [messages, filename, formatMessage]) return ( ) }