// DocStrange Document Extraction - Frontend JavaScript class DocStrangeApp { constructor() { this.selectedFile = null; this.extractionResults = null; this.initializeApp(); } async initializeApp() { await this.loadSystemInfo(); this.initializeEventListeners(); } async loadSystemInfo() { try { const response = await fetch('/api/system-info'); if (response.ok) { const systemInfo = await response.json(); this.updateProcessingModeOptions(systemInfo); } } catch (error) { console.warn('Could not load system info:', error); } } updateProcessingModeOptions(systemInfo) { // Processing mode is now handled automatically - cloud by default, GPU if available and selected // No UI changes needed as processing mode selection has been removed } initializeEventListeners() { // File input change document.getElementById('fileInput').addEventListener('change', (e) => { this.handleFileSelect(e.target.files[0]); }); // Drag and drop events const uploadArea = document.getElementById('fileUploadArea'); uploadArea.addEventListener('dragover', (e) => { e.preventDefault(); uploadArea.classList.add('dragover'); }); uploadArea.addEventListener('dragleave', (e) => { e.preventDefault(); uploadArea.classList.remove('dragover'); }); uploadArea.addEventListener('drop', (e) => { e.preventDefault(); uploadArea.classList.remove('dragover'); const files = e.dataTransfer.files; if (files.length > 0) { this.handleFileSelect(files[0]); } }); // Click on upload area to trigger file input uploadArea.addEventListener('click', () => { document.getElementById('fileInput').click(); }); // Form submission document.getElementById('uploadForm').addEventListener('submit', (e) => { e.preventDefault(); this.handleFormSubmission(); }); // Tab switching document.querySelectorAll('.tab-btn').forEach(btn => { btn.addEventListener('click', (e) => { this.switchTab(e.target.dataset.tab); }); }); } handleFileSelect(file) { if (!file) return; // Validate file type const allowedTypes = [ '.pdf', '.docx', '.doc', '.xlsx', '.xls', '.csv', '.txt', '.png', '.jpg', '.jpeg', '.tiff', '.bmp', '.ppt', '.pptx', '.html', '.htm' ]; const fileExtension = '.' + file.name.split('.').pop().toLowerCase(); if (!allowedTypes.includes(fileExtension)) { this.showError(`Unsupported file type: ${fileExtension}`); return; } // Validate file size (100MB limit) const maxSize = 100 * 1024 * 1024; // 100MB if (file.size > maxSize) { this.showError('File too large. Maximum size is 100MB.'); return; } this.selectedFile = file; this.displayFileInfo(file); this.enableSubmitButton(); } displayFileInfo(file) { const fileInfo = document.getElementById('fileInfo'); const fileName = document.getElementById('fileName'); const fileSize = document.getElementById('fileSize'); const uploadArea = document.getElementById('fileUploadArea'); fileName.textContent = file.name; fileSize.textContent = this.formatFileSize(file.size); fileInfo.style.display = 'flex'; // Hide the drag and drop area when file is selected uploadArea.style.display = 'none'; } formatFileSize(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } removeFile() { this.selectedFile = null; const fileInfo = document.getElementById('fileInfo'); const uploadArea = document.getElementById('fileUploadArea'); fileInfo.style.display = 'none'; // Show the drag and drop area again when file is removed uploadArea.style.display = 'block'; document.getElementById('fileInput').value = ''; this.disableSubmitButton(); this.hideResults(); } enableSubmitButton() { const submitBtn = document.getElementById('submitBtn'); submitBtn.disabled = false; } disableSubmitButton() { const submitBtn = document.getElementById('submitBtn'); submitBtn.disabled = true; } async handleFormSubmission() { if (!this.selectedFile) { this.showError('Please select a file first.'); return; } this.showLoading(); this.hideResults(); try { const formData = new FormData(); formData.append('file', this.selectedFile); // Get selected output format const outputFormat = document.querySelector('input[name="outputFormat"]:checked').value; formData.append('output_format', outputFormat); // Use cloud processing mode by default formData.append('processing_mode', 'cloud'); const response = await fetch('/api/extract', { method: 'POST', body: formData }); const result = await response.json(); if (response.ok && result.success) { this.displayResults(result); } else { const errorMessage = result.error || 'Extraction failed'; // Handle specific GPU errors if (errorMessage.includes('GPU') && errorMessage.includes('not available')) { this.showError('GPU mode is not available. Please install PyTorch with CUDA support or use cloud processing.'); } else { this.showError(errorMessage); } } } catch (error) { console.error('Error during extraction:', error); this.showError('Network error. Please try again.'); } finally { this.hideLoading(); } } displayResults(result) { this.extractionResults = result; // Update metadata document.getElementById('fileType').textContent = result.metadata.file_type.toUpperCase(); document.getElementById('pagesProcessed').textContent = `${result.metadata.pages_processed} pages`; document.getElementById('processingTime').textContent = `${result.metadata.processing_time.toFixed(2)}s`; // Add processing mode to metadata if available if (result.metadata.processing_mode) { const processingModeElement = document.getElementById('processingMode'); if (processingModeElement) { processingModeElement.textContent = result.metadata.processing_mode.toUpperCase(); } } // Display content this.updatePreviewContent(result.content); this.updateRawContent(result.content); // Show results section document.getElementById('resultsSection').style.display = 'block'; // Scroll to results document.getElementById('resultsSection').scrollIntoView({ behavior: 'smooth', block: 'start' }); } updatePreviewContent(content) { const previewContent = document.getElementById('previewContent'); // Format content based on output type const outputFormat = document.querySelector('input[name="outputFormat"]:checked').value; if (outputFormat === 'json' || outputFormat === 'flat-json') { try { const formatted = JSON.stringify(JSON.parse(content), null, 2); previewContent.innerHTML = `
${this.escapeHtml(formatted)}
`; } catch (e) { previewContent.textContent = content; } } else if (outputFormat === 'html') { previewContent.innerHTML = content; } else { previewContent.textContent = content; } } updateRawContent(content) { const rawContent = document.getElementById('rawContent'); rawContent.textContent = content; } escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } switchTab(tabName) { // Update tab buttons document.querySelectorAll('.tab-btn').forEach(btn => { btn.classList.remove('active'); }); document.querySelector(`[data-tab="${tabName}"]`).classList.add('active'); // Update tab content document.querySelectorAll('.tab-pane').forEach(pane => { pane.classList.remove('active'); }); document.getElementById(tabName).classList.add('active'); } showLoading() { const submitBtn = document.getElementById('submitBtn'); const btnText = submitBtn.querySelector('.btn-text'); const spinner = submitBtn.querySelector('.spinner'); btnText.textContent = 'Processing...'; spinner.style.display = 'block'; submitBtn.disabled = true; } hideLoading() { const submitBtn = document.getElementById('submitBtn'); const btnText = submitBtn.querySelector('.btn-text'); const spinner = submitBtn.querySelector('.spinner'); btnText.textContent = 'Extract Content'; spinner.style.display = 'none'; submitBtn.disabled = false; } showError(message) { // Create error notification const notification = document.createElement('div'); notification.className = 'error-notification'; notification.innerHTML = `
⚠️ ${message}
`; // Add styles notification.style.cssText = ` position: fixed; top: 20px; right: 20px; background: #D02B2B; color: white; padding: 16px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 1000; max-width: 400px; `; notification.querySelector('.error-content').style.cssText = ` display: flex; align-items: center; gap: 12px; `; notification.querySelector('.error-close').style.cssText = ` background: none; border: none; color: white; font-size: 20px; cursor: pointer; margin-left: auto; `; document.body.appendChild(notification); // Auto-remove after 5 seconds setTimeout(() => { if (notification.parentElement) { notification.remove(); } }, 5000); } hideResults() { document.getElementById('resultsSection').style.display = 'none'; } } // Global functions for HTML onclick handlers function removeFile() { if (window.docStrangeApp) { window.docStrangeApp.removeFile(); } } function downloadAsText() { if (window.docStrangeApp && window.docStrangeApp.extractionResults) { const content = window.docStrangeApp.extractionResults.content; const fileName = window.docStrangeApp.selectedFile.name; const outputFormat = document.querySelector('input[name="outputFormat"]:checked').value; const extension = outputFormat === 'json' ? 'json' : outputFormat === 'html' ? 'html' : 'txt'; const blob = new Blob([content], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `${fileName.split('.')[0]}_extracted.${extension}`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } } function downloadAsJson() { if (window.docStrangeApp && window.docStrangeApp.extractionResults) { const result = { content: window.docStrangeApp.extractionResults.content, metadata: window.docStrangeApp.extractionResults.metadata, original_file: window.docStrangeApp.selectedFile.name }; const fileName = window.docStrangeApp.selectedFile.name; const blob = new Blob([JSON.stringify(result, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `${fileName.split('.')[0]}_extraction_result.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } } // Initialize app when DOM is loaded document.addEventListener('DOMContentLoaded', () => { window.docStrangeApp = new DocStrangeApp(); });