document.addEventListener('DOMContentLoaded', () => { const queryInput = document.getElementById('queryInput'); const askButton = document.getElementById('askButton'); const chatMessages = document.getElementById('chatMessages'); const attachFileBtn = document.getElementById('attachFileBtn'); const fileInput = document.getElementById('fileInput'); const ocrModal = document.getElementById('ocrModal'); const closeOcrModal = document.getElementById('closeOcrModal'); const useOcrText = document.getElementById('useOcrText'); const queryWithOcr = document.getElementById('queryWithOcr'); const cancelOcr = document.getElementById('cancelOcr'); const ocrPreview = document.getElementById('ocrPreview'); const ocrResults = document.getElementById('ocrResults'); // 💬 Conversation state management let conversationHistory = []; let currentOcrText = ''; let currentOcrImage = null; function addUserMessage(content) { conversationHistory.push({ role: 'user', content: content, timestamp: Date.now() }); } function addAssistantMessage(content, source) { conversationHistory.push({ role: 'assistant', content: content, timestamp: Date.now(), source: source }); } function clearConversation() { conversationHistory = []; } function getHistory() { return conversationHistory; } // 💬 Message rendering functions function renderUserMessage(content, timestamp = null) { const ts = timestamp || Date.now(); return `
${escapeHtml(content)}
`; } function renderAssistantMessage(content, source) { const sourceBadge = source ? `${source}` : ''; return `
${sourceBadge}
${formatAnswer(content)}
`; } function renderLoadingMessage() { return `
Thinking...
`; } function renderWelcomeMessage() { return `

Welcome to Corex!

Ask me anything and I'll help you with accurate, document-backed answers.

`; } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } function displayAllMessages() { if (conversationHistory.length === 0) { chatMessages.innerHTML = renderWelcomeMessage(); return; } let html = ''; conversationHistory.forEach(msg => { if (msg.role === 'user') { html += renderUserMessage(msg.content); } else { html += renderAssistantMessage(msg.content, msg.source); } }); chatMessages.innerHTML = html; scrollToBottom(); } function scrollToBottom() { chatMessages.scrollTop = chatMessages.scrollHeight; } function formatAnswer(text) { if (typeof text !== "string") { text = String(text ?? "No response received."); } return text .split('\n') .filter(line => line.trim()) .map(line => `

${line}

`) .join(''); } // 📷 OCR Functions async function handleFileUpload(event) { const file = event.target.files[0]; if (!file) return; // Validate file type if (!file.type.startsWith('image/')) { alert('Please select an image file.'); return; } // Show modal ocrModal.style.display = 'block'; ocrPreview.innerHTML = `Preview`; ocrResults.innerHTML = '
Processing image...
'; try { // Extract text using OCR const formData = new FormData(); formData.append('file', file); const response = await fetch('/ocr/extract-text/', { method: 'POST', body: formData }); if (!response.ok) throw new Error(`Server returned ${response.status}`); const data = await response.json(); if (data.success) { currentOcrText = data.extracted_text; currentOcrImage = file; ocrResults.innerHTML = `

Extracted Text:

${escapeHtml(data.extracted_text)}
`; } else { ocrResults.innerHTML = `

Error:

${data.error || 'Failed to extract text'}

`; } } catch (error) { ocrResults.innerHTML = `

Error:

Failed to process image: ${error.message}

`; } } async function handleOcrQuery(query) { if (!currentOcrText) { alert('No OCR text available. Please upload an image first.'); return; } // Add user message addUserMessage(`[Image Query] ${query}`); displayAllMessages(); // Show loading const loadingMessage = renderLoadingMessage(); chatMessages.innerHTML += loadingMessage; scrollToBottom(); try { const response = await fetch('/ocr/query/', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ query: query, conversation_history: getHistory(), extracted_text: currentOcrText }) }); if (!response.ok) throw new Error(`Server returned ${response.status}`); const data = await response.json(); // Remove loading and add response chatMessages.innerHTML = chatMessages.innerHTML.replace(loadingMessage, ''); addAssistantMessage(data.response, data.source); displayAllMessages(); ocrModal.style.display = 'none'; } catch (error) { chatMessages.innerHTML = chatMessages.innerHTML.replace(loadingMessage, ''); addAssistantMessage(`Failed to get response: ${error.message}`, 'Error'); displayAllMessages(); } } // 🔍 Query handler async function handleQuery() { const query = queryInput.value.trim(); if (!query) return; // Add user message to conversation addUserMessage(query); displayAllMessages(); // Clear input queryInput.value = ''; // Show loading message const loadingMessage = renderLoadingMessage(); chatMessages.innerHTML += loadingMessage; scrollToBottom(); try { // Send conversation history to backend const response = await fetch('/query/', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ query: query, conversation_history: getHistory() }) }); if (!response.ok) throw new Error(`Server returned ${response.status}`); const data = await response.json(); // Remove loading message and add assistant response chatMessages.innerHTML = chatMessages.innerHTML.replace(loadingMessage, ''); addAssistantMessage(data.response, data.source); displayAllMessages(); } catch (err) { // Remove loading message and add error message chatMessages.innerHTML = chatMessages.innerHTML.replace(loadingMessage, ''); addAssistantMessage(`Failed to get response: ${err.message}`, 'Error'); displayAllMessages(); } } // 🔗 Event listeners askButton.addEventListener('click', handleQuery); queryInput.addEventListener('keypress', e => { if (e.key === 'Enter') handleQuery(); }); // OCR functionality attachFileBtn.addEventListener('click', () => { fileInput.click(); }); fileInput.addEventListener('change', handleFileUpload); closeOcrModal.addEventListener('click', () => { ocrModal.style.display = 'none'; }); useOcrText.addEventListener('click', () => { queryInput.value = currentOcrText; ocrModal.style.display = 'none'; queryInput.focus(); }); queryWithOcr.addEventListener('click', () => { const query = prompt('Enter your question about the image:'); if (query) { handleOcrQuery(query); } }); cancelOcr.addEventListener('click', () => { ocrModal.style.display = 'none'; }); // Close modal when clicking outside window.addEventListener('click', (e) => { if (e.target === ocrModal) { ocrModal.style.display = 'none'; } }); // Auto-resize input queryInput.addEventListener('input', () => { queryInput.style.height = 'auto'; queryInput.style.height = queryInput.scrollHeight + 'px'; }); // Dropdown menu functionality const optionsBtn = document.getElementById('optionsBtn'); const optionsMenu = document.getElementById('optionsMenu'); const downloadTxtBtn = document.getElementById('downloadTxt'); const downloadPdfBtn = document.getElementById('downloadPdf'); const clearChatBtn = document.getElementById('clearChat'); // Toggle dropdown menu optionsBtn.addEventListener('click', (e) => { e.stopPropagation(); optionsMenu.classList.toggle('show'); }); // Close dropdown when clicking outside document.addEventListener('click', (e) => { if (!optionsBtn.contains(e.target) && !optionsMenu.contains(e.target)) { optionsMenu.classList.remove('show'); } }); // Download as TXT downloadTxtBtn.addEventListener('click', () => { downloadChatAsTxt(); optionsMenu.classList.remove('show'); }); // Download as PDF downloadPdfBtn.addEventListener('click', () => { downloadChatAsPdf(); optionsMenu.classList.remove('show'); }); // Clear chat clearChatBtn.addEventListener('click', () => { clearConversation(); displayAllMessages(); optionsMenu.classList.remove('show'); }); // Download functions function downloadChatAsTxt() { if (conversationHistory.length === 0) { alert('No conversation to download'); return; } let content = 'Corex Chat History\n'; content += '='.repeat(50) + '\n\n'; conversationHistory.forEach((msg, index) => { const timestamp = new Date(msg.timestamp).toLocaleString(); const role = msg.role === 'user' ? 'You' : 'Corex'; const source = msg.source ? ` (${msg.source})` : ''; content += `[${timestamp}] ${role}${source}:\n`; content += msg.content + '\n\n'; }); const blob = new Blob([content], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `corex-chat-${new Date().toISOString().split('T')[0]}.txt`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } function downloadChatAsPdf() { if (conversationHistory.length === 0) { alert('No conversation to download'); return; } try { const { jsPDF } = window.jspdf; const doc = new jsPDF(); // Set up the document let yPosition = 20; const pageHeight = doc.internal.pageSize.height; const pageWidth = doc.internal.pageSize.width; const margin = 20; const maxWidth = pageWidth - (margin * 2); // Helper function to add text with word wrapping function addTextWithWrap(text, x, y, maxWidth, fontSize = 10) { doc.setFontSize(fontSize); const lines = doc.splitTextToSize(text, maxWidth); doc.text(lines, x, y); return y + (lines.length * (fontSize * 0.4)); } // Helper function to check if we need a new page function checkNewPage(requiredSpace) { if (yPosition + requiredSpace > pageHeight - 20) { doc.addPage(); yPosition = 20; return true; } return false; } // Title doc.setFontSize(16); doc.setFont(undefined, 'bold'); doc.text('Corex Chat History', pageWidth / 2, yPosition, { align: 'center' }); yPosition += 10; // Date doc.setFontSize(10); doc.setFont(undefined, 'normal'); doc.text(`Generated on: ${new Date().toLocaleString()}`, pageWidth / 2, yPosition, { align: 'center' }); yPosition += 15; // Add a line doc.line(margin, yPosition, pageWidth - margin, yPosition); yPosition += 10; // Process each message conversationHistory.forEach((msg, index) => { const timestamp = new Date(msg.timestamp).toLocaleString(); const role = msg.role === 'user' ? 'You' : 'Corex'; const source = msg.source ? ` (${msg.source})` : ''; // Check if we need a new page for this message const messageText = `[${timestamp}] ${role}${source}:\n${msg.content}`; const estimatedHeight = (messageText.split('\n').length * 4) + 10; if (checkNewPage(estimatedHeight)) { // Add a continuation marker doc.setFontSize(8); doc.text('...continued from previous page...', margin, yPosition); yPosition += 5; } // Message header doc.setFontSize(10); doc.setFont(undefined, 'bold'); yPosition = addTextWithWrap(`[${timestamp}] ${role}${source}:`, margin, yPosition, maxWidth, 10); // Message content doc.setFont(undefined, 'normal'); yPosition = addTextWithWrap(msg.content, margin + 5, yPosition, maxWidth - 5, 9); // Add some space between messages yPosition += 8; // Add a subtle line between messages (except for the last one) if (index < conversationHistory.length - 1) { doc.setDrawColor(200, 200, 200); doc.line(margin, yPosition, pageWidth - margin, yPosition); yPosition += 5; } }); // Save the PDF const fileName = `corex-chat-${new Date().toISOString().split('T')[0]}.pdf`; doc.save(fileName); } catch (error) { console.error('Error generating PDF:', error); alert('Error generating PDF. Please try downloading as TXT instead.'); } } // Scroll to bottom when new messages arrive const observer = new MutationObserver(() => { scrollToBottom(); }); observer.observe(chatMessages, { childList: true, subtree: true }); // Initialize displayAllMessages(); });