let debounceTimer; const DEBOUNCE_DELAY = 300; let isDebugMode = { chat: false, summary: false }; document.addEventListener('DOMContentLoaded', () => { setupTabNavigation(); setupChatPredictions(); setupSummaryPredictions(); setupImageUpload(); initializeChatInterface(); }); function setupTabNavigation() { const navItems = document.querySelectorAll('.nav-item'); navItems.forEach(item => { item.addEventListener('click', (e) => { e.preventDefault(); navItems.forEach(i => i.classList.remove('active')); item.classList.add('active'); const tabId = item.getAttribute('data-tab'); document.querySelectorAll('.tab-content').forEach(tab => { tab.classList.remove('active'); }); document.getElementById(tabId).classList.add('active'); }); }); } function initializeChatInterface() { const chatOutput = document.getElementById('chat-output'); chatOutput.innerHTML = `
Hello! I'm your AI assistant. How can I help you today?
`; } function setupChatPredictions() { const chatInput = document.getElementById('chat-input'); const toggleDebug = document.querySelector('#chat .toggle-debug'); const wordPredictions = document.querySelector('#chat .word-predictions'); toggleDebug.addEventListener('click', () => { isDebugMode.chat = !isDebugMode.chat; wordPredictions.style.display = isDebugMode.chat ? 'block' : 'none'; toggleDebug.classList.toggle('active'); }); chatInput.addEventListener('input', () => { clearTimeout(debounceTimer); debounceTimer = setTimeout(() => { if (isDebugMode.chat) { const words = chatInput.value.split(/\s+/); if (words.length > 0) { const lastWord = words[words.length - 1]; getPredictions(lastWord, 'chat'); } } }, DEBOUNCE_DELAY); }); } function setupSummaryPredictions() { const summaryInput = document.getElementById('summary-input'); const toggleDebug = document.querySelector('#summary .toggle-debug'); const wordPredictions = document.querySelector('#summary .word-predictions'); toggleDebug.addEventListener('click', () => { isDebugMode.summary = !isDebugMode.summary; wordPredictions.style.display = isDebugMode.summary ? 'block' : 'none'; toggleDebug.classList.toggle('active'); }); summaryInput.addEventListener('input', () => { clearTimeout(debounceTimer); debounceTimer = setTimeout(() => { if (isDebugMode.summary) { const words = summaryInput.value.split(/\s+/); if (words.length > 0) { const lastWord = words[words.length - 1]; getPredictions(lastWord, 'summary'); } } }, DEBOUNCE_DELAY); }); } async function getPredictions(word, section) { try { const response = await fetch('/predict_words', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ word: word }) }); const predictions = await response.json(); displayPredictions(predictions, section); } catch (error) { console.error('Error getting predictions:', error); } } function displayPredictions(predictions, section) { const predictionsContent = document.querySelector(`#${section} .predictions-content`); predictionsContent.innerHTML = ''; predictions.forEach((pred, index) => { const predictionItem = document.createElement('div'); predictionItem.className = 'prediction-item'; predictionItem.innerHTML = ` ${index + 1}. ${pred.word} ${(pred.probability * 100).toFixed(2)}% `; predictionsContent.appendChild(predictionItem); }); } async function sendMessage() { const input = document.getElementById('chat-input'); const message = input.value.trim(); if (!message) return; // Add user message to chat const chatOutput = document.getElementById('chat-output'); const userMessage = document.createElement('div'); userMessage.className = 'message user'; userMessage.textContent = message; chatOutput.appendChild(userMessage); // Show loading document.getElementById('loading').classList.add('show'); try { const response = await fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ message: message }) }); const data = await response.json(); const botMessage = document.createElement('div'); botMessage.className = 'message assistant'; // If the server returned per-token info, render tokens individually so we // can show alternative tokens on hover. Otherwise, fall back to plain text. if (data.tokens && Array.isArray(data.tokens) && data.tokens.length > 0) { const frag = document.createDocumentFragment(); const wrapper = document.createElement('div'); wrapper.className = 'generated-text'; data.tokens.forEach((t, idx) => { const span = document.createElement('span'); span.className = 'generated-token'; span.setAttribute('data-token-index', idx); span.textContent = t.token || ''; // store alternatives on the element for quick access span._alternatives = t.alternatives || []; wrapper.appendChild(span); }); frag.appendChild(wrapper); botMessage.appendChild(frag); chatOutput.appendChild(botMessage); // Tooltip element for showing alternatives let tooltip = document.getElementById('alt-tooltip'); if (!tooltip) { tooltip = document.createElement('div'); tooltip.id = 'alt-tooltip'; tooltip.className = 'alt-tooltip'; document.body.appendChild(tooltip); } // Attach hover listeners wrapper.querySelectorAll('.generated-token').forEach(el => { el.addEventListener('mouseenter', (ev) => { const alts = el._alternatives || []; if (!alts.length) return; // build tooltip html tooltip.innerHTML = ''; const title = document.createElement('div'); title.className = 'alt-title'; title.textContent = 'Alternatives'; tooltip.appendChild(title); alts.forEach(a => { const row = document.createElement('div'); row.className = 'alt-row'; const tok = document.createElement('span'); tok.className = 'alt-token'; tok.textContent = a.token || ''; const prob = document.createElement('span'); prob.className = 'alt-prob'; prob.textContent = `${(a.probability * 100).toFixed(2)}%`; row.appendChild(tok); row.appendChild(prob); // click to insert token into input (optional UX) row.addEventListener('click', () => { const chatInput = document.getElementById('chat-input'); insertAtCursor(chatInput, a.token || ''); }); tooltip.appendChild(row); }); // Position tooltip near the hovered token const rect = el.getBoundingClientRect(); tooltip.style.display = 'block'; tooltip.style.left = `${rect.left + window.scrollX}px`; tooltip.style.top = `${rect.bottom + window.scrollY + 6}px`; }); el.addEventListener('mouseleave', () => { const tooltip = document.getElementById('alt-tooltip'); if (tooltip) tooltip.style.display = 'none'; }); }); } else { botMessage.textContent = data.response || 'Sorry, I could not process your request.'; chatOutput.appendChild(botMessage); } // Clear input input.value = ''; chatOutput.scrollTop = chatOutput.scrollHeight; } catch (error) { console.error('Error:', error); const errorMessage = document.createElement('div'); errorMessage.className = 'message error'; errorMessage.textContent = 'Error: Could not send message.'; chatOutput.appendChild(errorMessage); } finally { document.getElementById('loading').classList.remove('show'); } } async function generateSummary() { const input = document.getElementById('summary-input'); const text = input.value.trim(); if (!text) { alert('Please enter some text to summarize'); return; } const outputDiv = document.getElementById('summary-output'); document.getElementById('loading').classList.add('show'); try { const response = await fetch('/api/summarize', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ text: text, max_length: 150, min_length: 30 }) }); const data = await response.json(); if (data.success) { outputDiv.innerHTML = `
${data.summary}
`; } else { throw new Error(data.error || 'Could not generate summary'); } } catch (error) { console.error('Error:', error); outputDiv.innerHTML = `
Error: ${error.message || 'Could not generate summary.'}
`; } finally { document.getElementById('loading').classList.remove('show'); } } function setupImageUpload() { const imageInput = document.getElementById('image-input'); const imagePreview = document.getElementById('image-preview'); const imageOutput = document.getElementById('image-output'); imageInput.addEventListener('change', (e) => { const file = e.target.files[0]; if (file) { // Show preview const reader = new FileReader(); reader.onload = (e) => { imagePreview.src = e.target.result; imagePreview.style.display = 'block'; }; reader.readAsDataURL(file); } }); } async function processImage() { const imageInput = document.getElementById('image-input'); const imageOutput = document.getElementById('image-output'); if (!imageInput.files[0]) { alert('Please select an image first'); return; } // Validate file type const file = imageInput.files[0]; if (!file.type.startsWith('image/')) { alert('Please select a valid image file'); return; } const formData = new FormData(); formData.append('image', file); document.getElementById('loading').classList.add('show'); try { // Make sure to use the correct endpoint const response = await fetch('/process_image', { method: 'POST', body: formData }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); if (result.success) { imageOutput.innerHTML = `

Image Analysis Results:

${result.description}

`; } else { throw new Error(result.error || 'Failed to analyze image'); } } catch (error) { console.error('Error processing image:', error); imageOutput.innerHTML = `
Error: ${error.message || 'Could not process image.'}
`; } finally { document.getElementById('loading').classList.remove('show'); } } function displayImageResults(results) { const output = document.getElementById('image-output'); if (results.success) { output.innerHTML = `

Recognition Results:

${results.description}

`; } else { output.innerHTML = `
Error: ${results.error || 'Could not analyze image.'}
`; } } // Add event listeners for Enter key document.getElementById('chat-input').addEventListener('keypress', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } }); document.getElementById('summary-input').addEventListener('keypress', (e) => { if (e.key === 'Enter' && e.ctrlKey) { e.preventDefault(); generateSummary(); } }); // Helper to insert text at the cursor position for input/textarea function insertAtCursor(el, text) { if (!el) return; const start = typeof el.selectionStart === 'number' ? el.selectionStart : el.value.length; const end = typeof el.selectionEnd === 'number' ? el.selectionEnd : start; const before = el.value.substring(0, start); const after = el.value.substring(end); el.value = before + text + after; const pos = before.length + text.length; el.selectionStart = el.selectionEnd = pos; el.focus(); }