document.addEventListener('DOMContentLoaded', () => { const editor = document.getElementById('editor'); const suggestionOverlay = document.getElementById('suggestion-overlay'); const status = document.getElementById('status'); const statusIndicator = document.querySelector('.status-indicator'); // Controls const nGramSelect = document.getElementById('n-gram'); const nValDisplay = document.getElementById('n-val'); const tempInput = document.getElementById('temperature'); const tempValDisplay = document.getElementById('temp-val'); const lengthInput = document.getElementById('length'); const lengthValDisplay = document.getElementById('length-val'); const generateBtn = document.getElementById('generate-more-btn'); const sidebarToggle = document.getElementById('sidebar-toggle'); const sidebar = document.getElementById('sidebar'); const acceptSuggestionBtn = document.getElementById('accept-suggestion-btn'); let currentSuggestion = ''; let isFetching = false; let debounceTimer; // --- UI Logic --- const updateUI = () => { nValDisplay.textContent = nGramSelect.value; tempValDisplay.textContent = tempInput.value; lengthValDisplay.textContent = lengthInput.value; }; sidebarToggle.addEventListener('click', () => { sidebar.classList.toggle('open'); }); const closeSidebarOnMobile = () => { if (window.innerWidth <= 768) { sidebar.classList.remove('open'); } }; tempInput.addEventListener('input', updateUI); lengthInput.addEventListener('input', updateUI); nGramSelect.addEventListener('change', () => { updateUI(); triggerUpdate(); }); const triggerUpdate = () => { currentSuggestion = ''; updateSuggestion(); const prompt = editor.value; if (prompt.trim().length > 0) fetchPrediction(prompt); }; tempInput.addEventListener('change', () => { triggerUpdate(); // Optional: close sidebar on change if on mobile // closeSidebarOnMobile(); }); lengthInput.addEventListener('change', () => { triggerUpdate(); }); // --- Core Functions --- const fetchPrediction = async (prompt, customLength = null) => { if (isFetching) return; isFetching = true; status.textContent = 'Thinking...'; statusIndicator.classList.add('fetching'); const n = parseInt(nGramSelect.value); const temperature = parseFloat(tempInput.value); const length = customLength || parseInt(lengthInput.value); try { const response = await fetch('/api/predict', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt, n, temperature, length }), }); if (!response.ok) throw new Error('Network response failed'); const data = await response.json(); if (customLength) { insertText(data.prediction || ''); } else { currentSuggestion = data.prediction || ''; updateSuggestion(); } } catch (error) { console.error('Prediction failed:', error); status.textContent = 'Error'; } finally { isFetching = false; status.textContent = 'Idle'; statusIndicator.classList.remove('fetching'); } }; const updateSuggestion = () => { const editorText = editor.value; const space = (editorText.length > 0 && !/\s$/.test(editorText)) ? ' ' : ''; suggestionOverlay.textContent = editorText + space + currentSuggestion; // Show/hide accept button if (currentSuggestion) { acceptSuggestionBtn.classList.add('visible'); } else { acceptSuggestionBtn.classList.remove('visible'); } }; const insertText = (text) => { if (!text) return; const space = (editor.value.length > 0 && !/\s$/.test(editor.value)) ? ' ' : ''; editor.value += space + text; currentSuggestion = ''; updateSuggestion(); // Ensure the editor scrolls with content editor.scrollTop = editor.scrollHeight; }; // --- Event Handlers --- editor.addEventListener('input', () => { clearTimeout(debounceTimer); currentSuggestion = ''; updateSuggestion(); const prompt = editor.value; if (prompt.trim().length === 0) return; debounceTimer = setTimeout(() => fetchPrediction(prompt), 300); }); editor.addEventListener('keydown', (e) => { if (e.key === 'Tab' && currentSuggestion) { e.preventDefault(); insertText(currentSuggestion); fetchPrediction(editor.value); } }); acceptSuggestionBtn.addEventListener('click', () => { if (currentSuggestion) { insertText(currentSuggestion); fetchPrediction(editor.value); editor.focus(); } }); generateBtn.addEventListener('click', () => { fetchPrediction(editor.value, 50); closeSidebarOnMobile(); }); // Sync scroll editor.addEventListener('scroll', () => { suggestionOverlay.scrollTop = editor.scrollTop; }); // Initialize UI badges updateUI(); });