Download components/ChatMessageItem.tsx from JeCabrera/ReelBot: direct link, hf CLI and curl.
- Browser
- Download file 3.97 kB
-
https://huggingface.co/spaces/JeCabrera/ReelBot/resolve/main/components/ChatMessageItem.tsx
- Command line
-
hf download hf://spaces/JeCabrera/ReelBot/components/ChatMessageItem.tsx
-
curl -L -o ChatMessageItem.tsx https://huggingface.co/spaces/JeCabrera/ReelBot/resolve/main/components/ChatMessageItem.tsx
3.97 kB
| 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> | |
| ); | |
| }); | |