Spaces:
Sleeping
Sleeping
| 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 = ` | |
| <div class="message system"> | |
| Hello! I'm your AI assistant. How can I help you today? | |
| </div> | |
| `; | |
| } | |
| 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 = ` | |
| <span>${index + 1}. ${pred.word}</span> | |
| <span>${(pred.probability * 100).toFixed(2)}%</span> | |
| `; | |
| 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 = ` | |
| <div class="output-content"> | |
| ${data.summary} | |
| </div> | |
| `; | |
| } else { | |
| throw new Error(data.error || 'Could not generate summary'); | |
| } | |
| } catch (error) { | |
| console.error('Error:', error); | |
| outputDiv.innerHTML = ` | |
| <div class="output-content error"> | |
| Error: ${error.message || 'Could not generate summary.'} | |
| </div> | |
| `; | |
| } 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 = ` | |
| <div class="output-content"> | |
| <h3>Image Analysis Results:</h3> | |
| <p>${result.description}</p> | |
| </div> | |
| `; | |
| } else { | |
| throw new Error(result.error || 'Failed to analyze image'); | |
| } | |
| } catch (error) { | |
| console.error('Error processing image:', error); | |
| imageOutput.innerHTML = ` | |
| <div class="output-content error"> | |
| Error: ${error.message || 'Could not process image.'} | |
| </div> | |
| `; | |
| } finally { | |
| document.getElementById('loading').classList.remove('show'); | |
| } | |
| } | |
| function displayImageResults(results) { | |
| const output = document.getElementById('image-output'); | |
| if (results.success) { | |
| output.innerHTML = ` | |
| <div class="output-content"> | |
| <h3>Recognition Results:</h3> | |
| <p>${results.description}</p> | |
| </div> | |
| `; | |
| } else { | |
| output.innerHTML = ` | |
| <div class="output-content error"> | |
| Error: ${results.error || 'Could not analyze image.'} | |
| </div> | |
| `; | |
| } | |
| } | |
| // 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(); | |
| } |