/** * HelpChat Component * Persistent side-panel chat with LLM-powered help * Context-aware and maintains conversation history */ import React, { useState, useEffect, useRef } from 'react'; const HelpChat = ({ isOpen = false, onClose, currentPage = 'dashboard', context = {} }) => { const [messages, setMessages] = useState([]); const [inputText, setInputText] = useState(''); const [isLoading, setIsLoading] = useState(false); const [sessionId] = useState(() => `session-${Date.now()}`); const messagesEndRef = useRef(null); const inputRef = useRef(null); useEffect(() => { if (isOpen && messages.length === 0) { // Add welcome message setMessages([{ role: 'assistant', content: `👋 Hi! I'm your AI assistant for StrikePackageGPT. I can help you with: • Understanding security tools and commands • Interpreting scan results • Writing nmap, nikto, and other tool commands • Navigating the platform • Security best practices What would you like help with?`, timestamp: new Date() }]); } }, [isOpen]); useEffect(() => { scrollToBottom(); }, [messages]); useEffect(() => { if (isOpen && inputRef.current) { inputRef.current.focus(); } }, [isOpen]); const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }; const handleSendMessage = async () => { if (!inputText.trim() || isLoading) return; const userMessage = { role: 'user', content: inputText, timestamp: new Date() }; setMessages(prev => [...prev, userMessage]); setInputText(''); setIsLoading(true); try { // Build context string const contextString = `User is on ${currentPage} page. ${JSON.stringify(context)}`; const response = await fetch('/api/llm/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: inputText, session_id: sessionId, context: contextString }) }); if (!response.ok) { throw new Error('Failed to get response'); } const data = await response.json(); const assistantMessage = { role: 'assistant', content: data.message || data.content || 'I apologize, I had trouble processing that request.', timestamp: new Date() }; setMessages(prev => [...prev, assistantMessage]); } catch (error) { console.error('Error sending message:', error); setMessages(prev => [...prev, { role: 'assistant', content: '❌ Sorry, I encountered an error. Please try again.', timestamp: new Date(), isError: true }]); } finally { setIsLoading(false); } }; const handleKeyPress = (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSendMessage(); } }; const copyToClipboard = (text) => { navigator.clipboard.writeText(text).then(() => { // Could show a toast notification here console.log('Copied to clipboard'); }); }; const clearChat = () => { if (window.confirm('Clear all chat history?')) { setMessages([{ role: 'assistant', content: 'Chat history cleared. How can I help you?', timestamp: new Date() }]); } }; const renderMessage = (message, index) => { const isUser = message.role === 'user'; const isError = message.isError; // Check if message contains code blocks const hasCode = message.content.includes('```'); let renderedContent; if (hasCode) { // Simple code block rendering const parts = message.content.split(/(```[\s\S]*?```)/g); renderedContent = parts.map((part, i) => { if (part.startsWith('```')) { const code = part.slice(3, -3).trim(); const [lang, ...codeLines] = code.split('\n'); const codeText = codeLines.join('\n'); return (
{codeText}