// DOM Elements 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 downloadScriptBtn = document.getElementById('downloadScriptBtn'); 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); }); uploadZone.addEventListener('click', () => fileInput.click()); } if (fileInput) { fileInput.addEventListener('change', (e) => handleFiles(e.target.files)); fileInput.addEventListener('click', (e) => e.stopPropagation()); // Prevent bubbling to uploadZone } 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.className = 'file-item'; fileItem.innerHTML = ` ${file.name}`; fileList.appendChild(fileItem); } try { const res = await fetch('/api/upload', { method: 'POST', body: formData }); if (res.ok) { console.log('Upload success'); } else { alert('Upload failed'); } } catch (e) { console.error(e); alert('Upload failed'); } } // --- Analysis Logic --- if (startBtn) { startBtn.addEventListener('click', startAnalysis); } async function startAnalysis() { 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; 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; if (running) { startBtn.innerHTML = ' Analysis in Progress...'; statusDot.className = 'status-dot running'; statusText.innerText = 'Analyzing'; statusText.style.color = 'var(--primary-color)'; // Hide follow-up and download during run const followUpSection = document.getElementById('followUpSection'); if (followUpSection) followUpSection.classList.add('hidden'); if (downloadScriptBtn) downloadScriptBtn.classList.add('hidden'); } else { startBtn.innerHTML = ' Start Analysis'; statusDot.className = 'status-dot'; statusText.innerText = 'Completed'; statusText.style.color = 'var(--text-secondary)'; const followUpSection = document.getElementById('followUpSection'); if (currentSessionId && followUpSection) { followUpSection.classList.remove('hidden'); } } } 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 output..."; // Auto scroll const logTab = document.getElementById('logsTab'); if (logTab) logTab.scrollTop = logTab.scrollHeight; if (!data.is_running && isRunning) { // Finished setRunningState(false); clearInterval(pollingInterval); if (data.has_report) { await loadReport(); // 强制跳转到 Report Tab switchTab('report'); console.log("Analysis done, switched to report tab"); } // Check for script if (data.script_path) { if (downloadScriptBtn) { downloadScriptBtn.classList.remove('hidden'); downloadScriptBtn.style.display = 'inline-flex'; } } } } catch (e) { console.error('Polling error', e); } }, 2000); } // --- Report Logic --- async function loadReport() { if (!currentSessionId) return; try { const res = await fetch(`/api/report?session_id=${currentSessionId}`); const data = await res.json(); if (!data.content || data.content === "Report not ready.") { reportContainer.innerHTML = '

Analysis in progress or no report generated yet.

'; } else { reportContainer.innerHTML = marked.parse(data.content); } } catch (e) { reportContainer.innerHTML = '

Failed to load report.

'; } } // --- Gallery Logic --- let galleryImages = []; let currentImageIndex = 0; async function loadGallery() { if (!currentSessionId) return; try { const res = await fetch(`/api/figures?session_id=${currentSessionId}`); const data = await res.json(); galleryImages = data.figures || []; currentImageIndex = 0; renderGalleryImage(); } catch (e) { console.error("Gallery load failed", e); document.getElementById('carouselSlide').innerHTML = '

Failed to load images.

'; } } function renderGalleryImage() { const slide = document.getElementById('carouselSlide'); const info = document.getElementById('imageInfo'); if (galleryImages.length === 0) { slide.innerHTML = '

No images generated in this session.

'; info.innerHTML = ''; return; } const img = galleryImages[currentImageIndex]; // Image slide.innerHTML = `${img.filename}`; // Info info.innerHTML = `
${img.filename} (${currentImageIndex + 1}/${galleryImages.length})
${img.description || 'No description available.'}
${img.analysis ? `
${img.analysis}
` : ''} `; } window.prevImage = function () { if (galleryImages.length === 0) return; currentImageIndex = (currentImageIndex - 1 + galleryImages.length) % galleryImages.length; renderGalleryImage(); } window.nextImage = function () { if (galleryImages.length === 0) return; currentImageIndex = (currentImageIndex + 1) % galleryImages.length; renderGalleryImage(); } // --- Download Script --- window.downloadScript = async function () { if (!currentSessionId) return; const link = document.createElement('a'); link.href = `/api/download_script?session_id=${currentSessionId}`; link.download = ''; document.body.appendChild(link); link.click(); document.body.removeChild(link); } // --- Export Report --- window.triggerExport = async function () { if (!currentSessionId) { alert("No active session to export."); return; } const btn = document.getElementById('exportBtn'); const originalContent = btn.innerHTML; btn.innerHTML = ' Zipping...'; btn.disabled = true; try { const url = `/api/export?session_id=${currentSessionId}`; window.open(url, '_blank'); } catch (e) { alert("Export failed: " + e.message); } finally { setTimeout(() => { btn.innerHTML = originalContent; btn.disabled = false; }, 2000); } } // --- Follow-up Chat --- window.sendFollowUp = async function () { if (!currentSessionId || isRunning) return; const input = document.getElementById('followUpInput'); const message = input.value.trim(); if (!message) return; input.disabled = true; try { const res = await fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ session_id: currentSessionId, message: message }) }); if (res.ok) { input.value = ''; setRunningState(true); startPolling(); switchTab('logs'); } else { alert('Failed to send request'); } } catch (e) { console.error(e); } finally { input.disabled = false; } } // --- History Logic --- async function loadHistory() { const list = document.getElementById('historyList'); if (!list) return; try { const res = await fetch('/api/history'); const data = await res.json(); if (data.history.length === 0) { list.innerHTML = '
No history yet
'; return; } let html = ''; data.history.forEach(item => { // item: {id, timestamp, name} const timeStr = item.timestamp.split(' ')[0]; // Just date for compactness html += `
${item.id}
`; }); list.innerHTML = html; } catch (e) { console.error("Failed to load history", e); } } window.loadSession = async function (sessionId) { if (isRunning) { alert("Analysis in progress, please wait."); return; } currentSessionId = sessionId; // Update active class document.querySelectorAll('.history-item').forEach(el => el.classList.remove('active')); const activeItem = document.getElementById(`hist-${sessionId}`); if (activeItem) activeItem.classList.add('active'); // Reset UI logOutput.innerText = "Loading session data..."; reportContainer.innerHTML = ""; if (downloadScriptBtn) downloadScriptBtn.classList.add('hidden'); // Fetch Status to get logs and check report try { const res = await fetch(`/api/status?session_id=${sessionId}`); if (res.ok) { const data = await res.json(); logOutput.innerText = data.log || "No logs available."; // Auto scroll log const logTab = document.getElementById('logsTab'); if (logTab) logTab.scrollTop = logTab.scrollHeight; if (data.has_report) { await loadReport(); // Check if script exists if (data.script_path && downloadScriptBtn) { downloadScriptBtn.classList.remove('hidden'); downloadScriptBtn.style.display = 'inline-flex'; } switchTab('report'); } else { switchTab('logs'); } } } catch (e) { logOutput.innerText = "Error loading session."; } } // Initialize document.addEventListener('DOMContentLoaded', () => { loadHistory(); }); // --- Navigation --- // No-op for switchView as sidebar is simplified window.switchView = function (viewName) { console.log("View switch requested:", viewName); } window.switchTab = function (tabName) { // Buttons document.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); // Content ['logs', 'report', 'gallery'].forEach(name => { const content = document.getElementById(`${name}Tab`); if (content) content.classList.add('hidden'); // 找到对应的 Tab 按钮并激活 // 这里假设 Tab 按钮的 onclick 包含 tabName document.querySelectorAll('.tab').forEach(btn => { if (btn.getAttribute('onclick') && btn.getAttribute('onclick').includes(`'${tabName}'`)) { btn.classList.add('active'); } }); }); // Valid tabs logic if (tabName === 'logs') { document.getElementById('logsTab').classList.remove('hidden'); } else if (tabName === 'report') { document.getElementById('reportTab').classList.remove('hidden'); } else if (tabName === 'gallery') { document.getElementById('galleryTab').classList.remove('hidden'); if (currentSessionId) loadGallery(); } }