import React, { useEffect, useRef, useState, useMemo } from 'react'; import { Box, Typography, Paper, Avatar, IconButton, List, ListItem, ListItemIcon, ListItemText, useTheme } from '@mui/material'; import ChevronRightIcon from '@mui/icons-material/ChevronRight'; import type { DocumentMetadata } from '../../../electron/types'; import ReactMarkdown from 'react-markdown'; import rehypeHighlight from 'rehype-highlight'; import 'highlight.js/styles/github.css'; import { Keyboard, Waves, Shield, DataObject, Folder, Lock } from '@mui/icons-material'; interface ChatMessage { id: string; text: string; isUser: boolean; timestamp: string; sources?: DocumentMetadata[]; avatar?: string; } interface MessageListProps { messages: ChatMessage[]; } export default function MessageList({ messages }: MessageListProps) { const messageListRef = useRef(null); useEffect(() => { if (messageListRef.current) { messageListRef.current.scrollTop = messageListRef.current.scrollHeight; } }, [messages]); return ( theme.palette.divider, borderRadius: '4px', '&:hover': { background: (theme) => theme.palette.action.hover, }, }, '&::-webkit-scrollbar-track': { background: 'transparent', }, }}> {messages.length>0 ? ( <> {messages.map((message) => ( {message.isUser ? 'User' : 'Data Identification Manager'} ') ? 0 : 1.5, // Reduce vertical padding pb: 1.5, flex: 1, bgcolor: message.isUser ? 'primary.main' : 'background.paper', color: message.isUser ? 'primary.contrastText' : 'text.primary', transition: 'all 0.2s ease-in-out', boxShadow: 1, borderRadius: 2, '&:hover': { boxShadow: 2, }, }} > theme.palette.mode === 'dark' ? '#1e1e1e' : '#f6f8fa', padding: 0, borderRadius: 1, overflow: 'auto' }, '& code': { backgroundColor: (theme) => theme.palette.mode === 'dark' ? '#1e1e1e' : '#f6f8fa', borderRadius: 1, fontSize: '85%' }, '& h1, & h2, & h3, & h4, & h5, & h6': { marginTop: '0px', // Reduced from 24px marginBottom: '0px', // Added margin bottom for headers fontWeight: 600, lineHeight: 1.25 }, '& p': { marginTop: '0', marginBottom: '0px' // Added margin bottom for paragraphs }, '& a': { color: (theme) => theme.palette.primary.main, textDecoration: 'none', '&:hover': { textDecoration: 'underline' } }, '& img': { maxWidth: '100%', height: 'auto' }, '& blockquote': { padding: '0 1em', color: (theme) => theme.palette.text.secondary, borderLeft: (theme) => `0.25em solid ${theme.palette.divider}`, margin: '0 0 0px 0' }, '& ul, & ol': { paddingLeft: '2em', marginTop: '0px', // Added margin top marginBottom: '0px' }, '& li': { // Added specific list item styling marginBottom: '0px' // Add space between list items }, '& li:last-child': { // Remove bottom margin from last list item marginBottom: '0' }, '& li > p': { // Adjust paragraph spacing within list items marginBottom: '0px' }, '& li > p:last-child': { marginBottom: '0' } } }}> {message.text.includes('') || message.text.length==0 ? ( message.text.split(/|<\/think>/).map((segment, index) => { if (index % 2 === 1) { // This is a thinking section // Calculate thinking time - assume 1 character = 0.1s const thinkingTime = (segment.length * 0.1).toFixed(1); return (
{ const content = document.getElementById(`thinking-content-${message.id}-${index}`); const indicator = document.getElementById(`thinking-indicator-${message.id}-${index}`); if (content && indicator) { content.classList.toggle('open'); indicator.classList.toggle('open'); } }} >
Reasoning
(thought for {thinkingTime}ms)
{segment}
); } else if (segment.includes('Sorry, there was an error processing your request.')) { return (
{segment}
); } return ( {segment} ); }) ) : ( {message.text} )}
{message.sources && message.sources.length > 0 && ( Sources: {message.sources.map((source, index) => ( {source.path} ))} )}
))} ):( <> )}
); } const WelcomeMessage = () => { const [text, setText] = useState(''); const fullText = 'Send a message...'; const [showCursor, setShowCursor] = useState(true); const theme = useTheme(); useEffect(() => { if (text.length < fullText.length) { const timeout = setTimeout(() => { setText(fullText.slice(0, text.length + 1)); }, 100); return () => clearTimeout(timeout); } }, [text]); useEffect(() => { const cursorInterval = setInterval(() => { setShowCursor(prev => !prev); }, 530); return () => clearInterval(cursorInterval); }, []); return ( {/* Animated Keyboard and Hands */} {/* Typing Animation */} {text} | {/* Example Questions */} {/* Footer Note */} Ask me anything about your data, security needs, or Data443 products ); };