'use client'; import React from 'react'; import { Bot, Loader2, Paperclip, User } from 'lucide-react'; import type { ChatMessage } from '@/types'; import { getAccessToken, getFileUrl } from '@/lib/api'; import { MarkdownContent } from '@/components/chat-workbench/MarkdownContent'; import { ScrollArea } from '@/components/ui/scroll-area'; 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 }: { message: ChatMessage }) { const isUser = message.role === 'user'; const textContent = typeof message.content === 'string' ? message.content : String(message.content || ''); 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 && (
)}
); } export function MessageList({ messages, isThinking, messagesEndRef, viewportRef, }: { messages: ChatMessage[]; isThinking: boolean; messagesEndRef: React.RefObject; viewportRef: React.RefObject; }) { return (
{messages.length === 0 && !isThinking && (

Boardware Agent Sandbox

发送消息开始对话

)} {messages.map((msg, i) => ( ))} {isThinking && (
思考中...
)}
); }