import { useState, useCallback } from 'react'; import { cn } from '@/lib/utils'; import { renderMarkdown } from '@/lib/markdown'; import { Bot, User, Copy, Trash2, ChevronDown, ChevronRight, RefreshCw, ArrowRight, Quote, Brain } from 'lucide-react'; import { ConfirmDialog } from '@/components/ui/confirm'; import { useConfirm } from '@/hooks/useConfirm'; import { useToast } from '@/hooks/useToast'; import type { Message } from '@/types'; type MessageState = 'streaming' | 'thinking' | 'done'; interface Props { message: Message; isLast?: boolean; isThinking?: boolean; onRegenerate?: (msgId: string) => void; onContinue?: () => void; onQuote?: (content: string) => void; onDelete?: (msgId: string) => void; } function getState(msg: Message, isLast: boolean, isThinking: boolean): MessageState { if (msg.role === 'user') return 'done'; if (isThinking && isLast) return 'thinking'; if (!msg.content && !msg.reasoningContent) return 'streaming'; return 'done'; } export function ChatMessage({ message, isLast, isThinking, onRegenerate, onContinue, onQuote, onDelete }: Props) { const isUser = message.role === 'user'; const isTool = message.role === 'tool'; const [reasoningOpen, setReasoningOpen] = useState(false); const { toast } = useToast(); const { confirmState, confirm, handleConfirm, handleCancel } = useConfirm(); const state = getState(message, !!isLast, !!isThinking); const handleCopy = useCallback(() => { navigator.clipboard.writeText(message.content).then(() => { toast('已复制到剪贴板', 'success'); }); }, [message.content, toast]); const handleDeleteClick = useCallback(async () => { const ok = await confirm({ title: '删除消息', description: '确定要删除这条消息吗?', confirmText: '删除', variant: 'danger', }); if (ok) onDelete?.(message.id); }, [message.id, onDelete, confirm]); // Tool messages if (isTool) { let toolName = ''; try { const p = JSON.parse(message.content); toolName = p.tool || ''; } catch {} return (
{message.content}
) : ( )}