// Application State const AppState = { currentPdf: null, pdfs: [], deviceInfo: null, }; // Initialize on page load document.addEventListener('DOMContentLoaded', function() { initializeTheme(); loadDeviceInfo(); initializeEventListeners(); loadPdfList(); }); // Theme Toggle function initializeTheme() { const savedTheme = localStorage.getItem('theme') || 'light'; document.body.setAttribute('data-theme', savedTheme); updateThemeIcon(savedTheme); document.getElementById('themeToggle').addEventListener('click', function() { const currentTheme = document.body.getAttribute('data-theme'); const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; document.body.setAttribute('data-theme', newTheme); localStorage.setItem('theme', newTheme); updateThemeIcon(newTheme); }); } function updateThemeIcon(theme) { const icon = document.getElementById('themeIcon'); if (icon) { icon.className = theme === 'dark' ? 'fas fa-sun' : 'fas fa-moon'; } } // Load Device Info async function loadDeviceInfo() { try { const response = await fetch('/api/device-info'); const data = await response.json(); AppState.deviceInfo = data; updateDeviceStatus(data); } catch (error) { console.error('Error loading device info:', error); updateDeviceStatus({ device: 'unknown', cuda_available: false }); } } function updateDeviceStatus(info) { const badge = document.getElementById('deviceBadge'); const deviceName = document.getElementById('deviceName'); if (info.cuda_available) { badge.textContent = 'GPU'; badge.className = 'badge bg-success'; deviceName.textContent = info.device_name || 'CUDA Device'; } else { badge.textContent = 'CPU'; badge.className = 'badge bg-secondary'; deviceName.textContent = 'CPU Processing'; } } // Event Listeners function initializeEventListeners() { const uploadForm = document.getElementById('uploadForm'); uploadForm.addEventListener('submit', handleUpload); } // Handle File Upload async function handleUpload(e) { e.preventDefault(); const fileInput = document.getElementById('fileInput'); const files = fileInput.files; if (files.length === 0) { alert('Please select at least one PDF file'); return; } const extractionMode = document.querySelector('input[name="extractionMode"]:checked').value; // Show processing section const processingSection = document.getElementById('processingSection'); const processingStatus = document.getElementById('processingStatus'); const progressBar = document.getElementById('progressBar'); const progressBarFill = document.getElementById('progressBarFill'); processingSection.style.display = 'block'; document.getElementById('resultsSection').style.display = 'none'; document.getElementById('emptyState').style.display = 'none'; // Update processing UI processingStatus.textContent = 'Uploading files...'; if (progressBarFill) { progressBarFill.style.width = '0%'; } const formData = new FormData(); for (let i = 0; i < files.length; i++) { formData.append('files[]', files[i]); } formData.append('extraction_mode', extractionMode); try { const response = await fetch('/api/upload', { method: 'POST', body: formData }); const data = await response.json(); if (data.error) { throw new Error(data.error); } // Start polling for progress if (data.task_id) { await pollProgress(data.task_id, processingStatus, progressBarFill); } else { // Fallback for old API processingSection.style.display = 'none'; await loadPdfList(); } // Reset form fileInput.value = ''; } catch (error) { console.error('Upload error:', error); alert('Error processing files: ' + error.message); processingSection.style.display = 'none'; } } // Poll for progress updates async function pollProgress(taskId, statusElement, progressBarFill) { const maxAttempts = 600; // 5 minutes max (600 * 0.5s) let attempts = 0; const poll = async () => { try { const response = await fetch(`/api/progress/${taskId}`); const data = await response.json(); if (data.error) { throw new Error(data.error); } // Update progress bar if (progressBarFill) { const progress = data.progress || 0; progressBarFill.style.width = `${progress}%`; progressBarFill.setAttribute('aria-valuenow', progress); const progressText = document.getElementById('progressBarText'); if (progressText) { progressText.textContent = `${Math.round(progress)}%`; } } // Update status message if (statusElement) { statusElement.textContent = data.message || 'Processing...'; } // Check if completed if (data.status === 'completed') { // Hide processing section document.getElementById('processingSection').style.display = 'none'; // Reload PDF list and show results await loadPdfList(); // Show first PDF details if available if (data.results && data.results.length > 0) { const firstPdf = data.results[0]; if (!firstPdf.error) { showPdfDetails(firstPdf.stem); } } return; } // Check if error if (data.status === 'error') { throw new Error(data.message || 'Processing failed'); } // Continue polling attempts++; if (attempts < maxAttempts) { setTimeout(poll, 500); // Poll every 500ms } else { throw new Error('Processing timeout - please try again'); } } catch (error) { console.error('Progress polling error:', error); document.getElementById('processingSection').style.display = 'none'; alert('Error: ' + error.message); } }; // Start polling poll(); } // Load PDF List async function loadPdfList() { try { const response = await fetch('/api/pdf-list'); const data = await response.json(); AppState.pdfs = data.pdfs || []; renderPdfList(); if (AppState.pdfs.length > 0) { document.getElementById('resultsSection').style.display = 'block'; document.getElementById('emptyState').style.display = 'none'; } else { document.getElementById('resultsSection').style.display = 'none'; document.getElementById('emptyState').style.display = 'block'; } } catch (error) { console.error('Error loading PDF list:', error); } } // Render PDF List function renderPdfList() { const pdfList = document.getElementById('pdfList'); pdfList.innerHTML = ''; if (AppState.pdfs.length === 0) { pdfList.innerHTML = '