'use client'; import React from 'react'; import Link from 'next/link'; import { Bot, CheckCircle2, ChevronRight, Loader2, Paperclip, RefreshCcw, ThumbsUp, User, XCircle } from 'lucide-react'; import type { ChatMessage, ProcessArtifact, ProcessEvent, ProcessRun } from '@/types'; import { getAccessToken, getFileUrl } from '@/lib/api'; import { AgentTeamBlock } from '@/components/chat-workbench/AgentTeamBlock'; import { MarkdownContent } from '@/components/chat-workbench/MarkdownContent'; import { ScrollArea } from '@/components/ui/scroll-area'; import { pickAppText } from '@/lib/i18n/core'; import { useAppI18n } from '@/lib/i18n/provider'; function AuthImage({ src, alt, className }: { src: string; alt: string; className?: string }) { const [blobUrl, setBlobUrl] = React.useState(null); React.useEffect(() => { const token = getAccessToken(); const headers: Record = {}; if (token) headers.Authorization = `Bearer ${token}`; let revoke: string | null = null; fetch(src, { headers }) .then((res) => res.blob()) .then((blob) => { revoke = URL.createObjectURL(blob); setBlobUrl(revoke); }) .catch(() => {}); return () => { if (revoke) URL.revokeObjectURL(revoke); }; }, [src]); if (!blobUrl) return
; return {alt}; } function MessageBubble({ message, canSendFeedback, onFeedback, }: { message: ChatMessage; canSendFeedback: boolean; onFeedback: (runId: string, feedbackType: 'satisfied' | 'revise' | 'abandon', comment?: string) => void; }) { const { locale } = useAppI18n(); const isUser = message.role === 'user'; const textContent = typeof message.content === 'string' ? message.content : String(message.content || ''); const [feedbackMode, setFeedbackMode] = React.useState<'satisfied' | 'revise' | null>(null); const [feedbackComment, setFeedbackComment] = React.useState(''); const validationFailed = message.validation_status === 'failed'; const validationDetails = validationFailed ? pickAppText(locale, '详细原因会在任务验证区展示;展开任务可查看验证报告。', 'Detailed reasons are shown in the task validation area. Open the task to inspect the validation report.') : ''; return (
{!isUser && (
)}
{message.attachments && message.attachments.length > 0 && (
{message.attachments.map((att) => { const fileUrl = getFileUrl(att.file_id); if (att.content_type.startsWith('image/')) { return ( ); } return ( {att.name} {att.size && ( {att.size > 1024 * 1024 ? `${(att.size / 1024 / 1024).toFixed(1)}MB` : `${(att.size / 1024).toFixed(0)}KB`} )} ); })}
)} {isUser ? (

{textContent}

) : ( )} {!isUser && message.task_id && (
Task
{pickAppText(locale, '已创建任务', 'Task created')}: {message.task_id}
{pickAppText(locale, '查看任务', 'Open task')}
)} {!isUser && validationFailed && (
{pickAppText(locale, '验证失败', 'Validation failed')}

{validationDetails}

)} {!isUser && canSendFeedback && message.run_id && (
{message.feedback_state ? (
{message.feedback_state === 'satisfied' ? pickAppText(locale, '已标记满意', 'Marked satisfied') : message.feedback_state === 'revise' ? pickAppText(locale, '已请求修改', 'Revision requested') : pickAppText(locale, '已放弃任务', 'Task abandoned')}
) : ( <>
{feedbackMode && (