const uploadZone = document.getElementById('uploadZone'); const fileInput = document.getElementById('fileInput'); const fileList = document.getElementById('fileList'); const startBtn = document.getElementById('startBtn'); const requirementInput = document.getElementById('requirementInput'); const statusDot = document.getElementById('statusDot'); const statusText = document.getElementById('statusText'); const logOutput = document.getElementById('logOutput'); const reportContainer = document.getElementById('reportContainer'); const galleryContainer = document.getElementById('galleryContainer'); let isRunning = false; let pollingInterval = null; let currentSessionId = null; // --- Upload Logic --- if (uploadZone) { uploadZone.addEventListener('dragover', (e) => { e.preventDefault(); uploadZone.classList.add('dragover'); }); uploadZone.addEventListener('dragleave', () => uploadZone.classList.remove('dragover')); uploadZone.addEventListener('drop', (e) => { e.preventDefault(); uploadZone.classList.remove('dragover'); handleFiles(e.dataTransfer.files); }); } if (fileInput) { fileInput.addEventListener('change', (e) => handleFiles(e.target.files)); } async function handleFiles(files) { if (files.length === 0) return; fileList.innerHTML = ''; const formData = new FormData(); for (const file of files) { formData.append('files', file); const fileItem = document.createElement('div'); fileItem.innerText = `📄 ${file.name}`; fileItem.style.fontSize = '0.8rem'; fileItem.style.color = '#fff'; fileList.appendChild(fileItem); } try { const res = await fetch('/api/upload', { method: 'POST', body: formData }); if (res.ok) { const data = await res.json(); console.log('Upload success:', data); } else { alert('Upload failed'); } } catch (e) { console.error(e); alert('Upload failed'); } } // --- Start Analysis --- if (startBtn) { startBtn.addEventListener('click', async () => { if (isRunning) return; const requirement = requirementInput.value.trim(); if (!requirement) { alert('Please enter analysis requirement'); return; } setRunningState(true); try { const res = await fetch('/api/start', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ requirement }) }); if (res.ok) { const data = await res.json(); currentSessionId = data.session_id; // Store Session ID console.log("Started Session:", currentSessionId); startPolling(); switchTab('logs'); } else { const err = await res.json(); alert('Failed to start: ' + err.detail); setRunningState(false); } } catch (e) { console.error(e); alert('Error starting analysis'); setRunningState(false); } }); } function setRunningState(running) { isRunning = running; startBtn.disabled = running; startBtn.innerHTML = running ? ' Running...' : ' Start Analysis'; if (running) { statusDot.className = 'dot running'; statusText.innerText = 'Analysis in Progress'; } else { statusDot.className = 'dot done'; statusText.innerText = 'Completed'; } } // --- Status Polling --- function startPolling() { if (pollingInterval) clearInterval(pollingInterval); if (!currentSessionId) return; pollingInterval = setInterval(async () => { try { const res = await fetch(`/api/status?session_id=${currentSessionId}`); if (!res.ok) return; const data = await res.json(); // Update Logs logOutput.innerText = data.log || "Waiting for logs..."; // Auto scroll to bottom const term = document.querySelector('.terminal-window'); if (term) term.scrollTop = term.scrollHeight; if (!data.is_running && isRunning) { // Just finished setRunningState(false); clearInterval(pollingInterval); if (data.has_report) { loadReport(); loadGallery(); // Load images when done switchTab('report'); } } } catch (e) { console.error('Polling error', e); } }, 2000); } async function loadReport() { if (!currentSessionId) return; try { const res = await fetch(`/api/report?session_id=${currentSessionId}`); const data = await res.json(); // Render Markdown reportContainer.innerHTML = marked.parse(data.content); // Fix images relative path for display if needed // The backend should return a base_path or we handle it here if (data.base_path) { // Backend already handled replacement? // The backend returns content with updated paths. } } catch (e) { reportContainer.innerHTML = '

Failed to load report.

'; console.error(e); } } async function loadGallery() { if (!currentSessionId) return; // Switch to gallery view logic if we were already there // But this is just data loading try { const res = await fetch(`/api/figures?session_id=${currentSessionId}`); const data = await res.json(); const galleryGrid = document.getElementById('galleryContainer'); if (!data.figures || data.figures.length === 0) { galleryGrid.innerHTML = `

No images generated in this session.

`; return; } let html = ''; data.figures.forEach(fig => { html += ` `; }); galleryGrid.innerHTML = html; } catch (e) { console.error("Gallery load failed", e); } } // --- Export Report --- window.triggerExport = async function () { if (!currentSessionId) { alert("No active session to export."); return; } const btn = document.getElementById('exportBtn'); const originalText = btn.innerHTML; btn.innerHTML = ' Zipping...'; btn.disabled = true; try { // Trigger download // We can't use fetch for file download easily if we want browser to handle save dialog // So we create a link approach or check status first // Check if download is possible const url = `/api/export?session_id=${currentSessionId}`; const res = await fetch(url, { method: 'HEAD' }); if (res.ok) { window.location.href = url; } else { alert("Export failed: No data available."); } } catch (e) { alert("Export failed: " + e.message); } finally { btn.innerHTML = originalText; btn.disabled = false; } } // --- Tabs (Inner) --- window.switchTab = function (tabName) { document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active')); const btn = document.querySelector(`button[onclick="switchTab('${tabName}')"]`); if (btn) btn.classList.add('active'); const tab = document.getElementById(`${tabName}Tab`); if (tab) tab.classList.add('active'); } // --- View Navigation (Outer Sidebar) --- window.switchView = function (viewName) { // 1. Update Sidebar Buttons document.querySelectorAll('.nav-btn').forEach(b => b.classList.remove('active')); const navBtn = document.querySelector(`button[onclick="switchView('${viewName}')"]`); if (navBtn) navBtn.classList.add('active'); // 2. Switch Main Content Sections const viewId = 'view' + viewName.charAt(0).toUpperCase() + viewName.slice(1); document.querySelectorAll('.view-section').forEach(v => { // Hide all v.style.display = 'none'; v.classList.remove('active'); }); const activeView = document.getElementById(viewId); if (activeView) { // Analysis view uses flex for layout (logs scrolling), others use block if (viewName === 'analysis') { activeView.style.display = 'flex'; activeView.style.flexDirection = 'column'; } else { activeView.style.display = 'block'; // If switching to gallery, reload it if session exists if (viewName === 'gallery' && currentSessionId) { loadGallery(); } } activeView.classList.add('active'); } // 3. Toggle Sidebar Controls (only show Analysis Controls in analysis view) const analysisControls = document.getElementById('analysisControls'); if (analysisControls) { analysisControls.style.display = (viewName === 'analysis') ? 'block' : 'none'; } } // --- Tool Functions --- window.triggerMerge = async function () { const sourceDir = document.getElementById('mergeSource').value; const resultDiv = document.getElementById('mergeResult'); resultDiv.innerHTML = ' Merging...'; try { const res = await fetch('/api/tools/merge', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ source_dir: sourceDir, output_filename: 'merged_output.csv' }) }); const data = await res.json(); if (data.status === 'success') { resultDiv.innerHTML = ` ${data.message}`; } else { resultDiv.innerHTML = `${data.message}`; } } catch (e) { resultDiv.innerHTML = `Error: ${e.message}`; } } window.triggerSort = async function () { const targetFile = document.getElementById('sortTarget').value; const resultDiv = document.getElementById('sortResult'); resultDiv.innerHTML = ' Sorting...'; try { const res = await fetch('/api/tools/sort', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ target_file: targetFile }) }); const data = await res.json(); if (data.status === 'success') { resultDiv.innerHTML = ` ${data.message}`; } else { resultDiv.innerHTML = `${data.message}`; } } catch (e) { resultDiv.innerHTML = `Error: ${e.message}`; } } // --- Help Functions --- window.fetchHelp = async function () { const container = document.getElementById('helpContainer'); container.innerHTML = 'Loading...'; try { const res = await fetch('/api/help/troubleshooting'); const data = await res.json(); container.innerHTML = marked.parse(data.content); } catch (e) { container.innerHTML = 'Failed to load guide.'; } }