import React, { useRef, useEffect } from 'react'; import CodeBlock from "./CodeBlock"; import { Box, Paper, CircularProgress, Typography } from "@mui/material"; import "./ChatBox.css"; // Add dot animation CSS export default function ChatBox({ messages, loading }) { const bottomRef = useRef(null); useEffect(() => { if (!bottomRef.current) return; if (loading) { bottomRef.current.scrollIntoView({ behavior: 'auto' }); } else { // Smooth scroll after streaming ends with slight delay setTimeout(() => { bottomRef.current?.scrollIntoView({ behavior: 'smooth' }); }, 80); // tweak timing based on UX } }, [messages, loading]); return ( {messages.map((msg, i) => msg.content?.trim() ? (
) : null )} {loading && ( )} ); } const formatText = (text) => { // ✅ Handle base64 images const imageRegex = /\[IMAGE_START\](.*?)\[IMAGE_END\]/gs; text = text.replace(imageRegex, (match, base64) => { const src = `data:image/png;base64,${base64.trim()}`; return `Generated Image`; }); // ✅ Normalize line endings and remove excessive blank lines text = text.replace(/\r\n|\r/g, '\n'); text = text.replace(/\n{3,}/g, '\n\n'); // ✅ Parse fenced code blocks (```code```) text = text.replace(/```(\w+)?\n([\s\S]*?)```/g, (match, lang, code) => { const language = lang ? ` class="language-${lang}"` : ''; return `
${code.trim().replace(//g, '>')}
`; }); // ✅ Parse blockquotes text = text.replace(/^> (.*)$/gm, '
$1
'); // ✅ Headings text = text.replace(/^### (.*)$/gm, '

$1

'); // ✅ Horizontal rules text = text.replace(/^---$/gm, '
'); // ✅ Bold (**text**) and italic (*text*) text = text.replace(/\*\*(.*?)\*\*/g, '$1'); text = text.replace(/\*(.*?)\*/g, '$1'); // ✅ Emoji rendering using colon syntax (:smile:) const emojiMap = { smile: "😄", sad: "😢", heart: "❤️", thumbs_up: "👍", fire: "🔥", check: "✅", x: "❌", star: "⭐", rocket: "🚀", warning: "⚠️", }; text = text.replace(/:([a-z0-9_+-]+):/g, (match, name) => emojiMap[name] || match); // ✅ Unordered list (bullets) const listify = (lines, tag) => `<${tag}>` + lines.map(item => `
  • ${item.replace(/^(\-|\d+\.)\s*/, '').trim()}
  • `).join('') + ``; text = text.replace( /((?:^[-*] .+(?:\n|$))+)/gm, (match) => listify(match.trim().split('\n'), 'ul') ); // ✅ Ordered list (fix separate `1.` items issue) text = text.replace(/^(\d+\. .+)$/gm, '__ORDERED__START__$1__ORDERED__END__'); text = text.replace( /__ORDERED__START__(\d+\. .+?)__ORDERED__END__/gs, (_, line) => `
    1. ${line.replace(/^\d+\.\s*/, '')}
    ` ); text = text.replace(/<\/ol>\s*
      /g, ''); // ✅ Markdown-style tables text = text.replace( /^\|(.+?)\|\n\|([-:| ]+)\|\n((?:\|.*\|\n?)*)/gm, (_, headerRow, dividerRow, bodyRows) => { const headers = headerRow.split('|').map(h => `${h.trim()}`).join(''); const rows = bodyRows.trim().split('\n').map(r => '' + r.split('|').map(cell => `${cell.trim()}`).join('') + '' ).join(''); return `${headers}${rows}
      `; } ); // ✅ Paragraphs and line breaks inside paragraphs const blocks = text.split(/\n{2,}/).map(block => { if ( block.startsWith('

      ') || block.startsWith('
      ') || block.startsWith('
        ') || block.startsWith('
          ') || block.startsWith('') || block.startsWith('
          ') ||
                block.startsWith('
          ') || block.startsWith('${block.trim().replace(/\n/g, '
          ')}

          `; } }); return blocks.join('\n'); }; function Message({ content }) { const parts = content.split(/```(?:[a-z]*)\n([\s\S]*?)```/g); return ( <> {parts.map((part, i) => i % 2 === 1 ? ( ) : (
          ) )} ); }