File size: 3,968 Bytes
2accaeb |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
import React from 'react';
import type { ChatMessage } from '../types';
import { MarkdownRenderer } from './MarkdownRenderer';
import { UserIcon, DocumentTextIcon } from './icons';
// REELBOT_IMAGE_URL is not needed here anymore for the avatar itself
export const ChatMessageItem: React.FC<{ message: ChatMessage }> = React.memo(({ message }) => {
const isUser = message.sender === 'user';
const shouldDisplaySources = () => {
if (message.sender === 'model' && message.groundingChunks && message.groundingChunks.length > 0) {
const text = message.text.toLowerCase();
return text.includes("sources:") || text.includes("source:") || text.includes("fuentes:") || text.includes("fuente:");
}
return false;
};
const isImageFile = message.file && message.file.dataUrl && message.file.type.startsWith('image/');
const isDocumentFile = message.file && !isImageFile && (message.file.type.includes('pdf') || message.file.type.includes('word') || message.file.type.includes('document'));
return (
<div className={`flex ${isUser ? 'justify-end' : 'justify-start'} mb-4`}>
<div
className={`max-w-xl lg:max-w-2xl px-4 py-3 rounded-xl shadow ${
isUser
? 'bg-cyan-600 text-white rounded-br-none'
: 'bg-slate-700 text-slate-100 rounded-bl-none'
}`}
>
<div className="flex items-center space-x-2 mb-1"> {/* Adjusted items-start to items-center for better emoji alignment */}
{isUser ? (
<UserIcon className="w-5 h-5 text-cyan-200 mt-0.5"/>
) : (
<span role="img" aria-label="ReelBot avatar" className="text-xl self-center">🤖</span>
)}
<span className="font-semibold text-sm self-center">{isUser ? 'Tú' : 'ReelBot'}</span>
</div>
{/* File Preview */}
{message.file && (
<div className="mb-2 p-2 border border-slate-500/50 rounded-md">
{isImageFile ? (
<img
src={message.file.dataUrl}
alt={message.file.name}
className="max-w-xs max-h-48 rounded object-contain"
/>
) : isDocumentFile ? (
<div className="flex items-center space-x-2">
<DocumentTextIcon className="w-6 h-6 text-slate-400 flex-shrink-0" />
<span className="text-xs text-slate-300 truncate" title={message.file.name}>
{message.file.name}
</span>
</div>
) : null }
</div>
)}
{/* Message Text */}
{message.text && (
message.sender === 'model' ? (
<MarkdownRenderer markdownText={message.text} />
) : (
<p className="whitespace-pre-wrap break-words">{message.text}</p>
)
)}
{message.error && <p className="text-red-300 text-xs mt-1">Error: {message.error}</p>}
{shouldDisplaySources() && (
<div className="mt-3 pt-2 border-t border-slate-600">
<h4 className="text-xs font-semibold text-slate-400 mb-1">Sources:</h4>
<ul className="list-disc list-inside space-y-1">
{message.groundingChunks!.map((chunk, index) => (
<li key={index} className="text-xs">
<a
href={chunk.web.uri}
target="_blank"
rel="noopener noreferrer"
className="text-cyan-400 hover:text-cyan-300 hover:underline truncate block"
title={chunk.web.uri}
>
{chunk.web.title || chunk.web.uri}
</a>
</li>
))}
</ul>
</div>
)}
</div>
</div>
);
});
|