diff --git a/webui/backend/data/tasks.db b/webui/backend/data/tasks.db index 7e5cf42..e356ce3 100644 Binary files a/webui/backend/data/tasks.db and b/webui/backend/data/tasks.db differ diff --git a/webui/backend/tests/golden/__pycache__/test_ui_smoke_golden.cpython-313.pyc b/webui/backend/tests/golden/__pycache__/test_ui_smoke_golden.cpython-313.pyc index 2633015..cf7f37e 100644 Binary files a/webui/backend/tests/golden/__pycache__/test_ui_smoke_golden.cpython-313.pyc and b/webui/backend/tests/golden/__pycache__/test_ui_smoke_golden.cpython-313.pyc differ diff --git a/webui/backend/tests/golden/test_ui_smoke_golden.py b/webui/backend/tests/golden/test_ui_smoke_golden.py index ae633a0..9e27de0 100644 --- a/webui/backend/tests/golden/test_ui_smoke_golden.py +++ b/webui/backend/tests/golden/test_ui_smoke_golden.py @@ -74,6 +74,7 @@ class UiSmokeGoldenTest(unittest.TestCase): self.assertIn('id="download-modal-progress-bar"', body) self.assertIn('id="download-modal-count"', body) self.assertIn('id="download-modal-status"', body) + self.assertIn('id="download-modal-logs-btn"', body) self.assertIn('id="download-modal-cancel-btn"', body) self.assertIn('id="download-modal-close-btn"', body) self.assertIn('id="context-menu"', body) @@ -282,6 +283,7 @@ class UiSmokeGoldenTest(unittest.TestCase): self.assertIn('countText: "Archive task cancelled"', app_js) self.assertIn('statusText: "Cancelling download..."', app_js) self.assertIn('statusText: `Failed: ${err.message || "Archive download failed"}`', app_js) + self.assertIn('logsVisible: true,', app_js) self.assertIn('return `${task.done_items}/${task.total_items} top-level items`;', app_js) self.assertIn('return `Current: ${task.current_item}`;', app_js) self.assertIn('downloadProgressState.requestKey === requestKey', app_js) @@ -298,6 +300,8 @@ class UiSmokeGoldenTest(unittest.TestCase): self.assertIn('return "Multi-item ZIP";', app_js) self.assertIn('details.push(`Current: ${task.current_item}`);', app_js) self.assertIn('details.push(`${task.done_items}/${task.total_items} items`);', app_js) + self.assertIn('downloadModal.logsButton.onclick = () => {', app_js) + self.assertIn('openSettings("logs");', app_js) self.assertIn('function applyContextMenuSelection()', app_js) self.assertIn('function startContextMenuOpen()', app_js) self.assertIn('function startContextMenuEdit()', app_js) diff --git a/webui/html/app.js b/webui/html/app.js index 6d2d0e6..750c628 100644 --- a/webui/html/app.js +++ b/webui/html/app.js @@ -342,6 +342,7 @@ function downloadModalElements() { count: document.getElementById("download-modal-count"), progressBar: document.getElementById("download-modal-progress-bar"), status: document.getElementById("download-modal-status"), + logsButton: document.getElementById("download-modal-logs-btn"), cancelButton: document.getElementById("download-modal-cancel-btn"), closeButton: document.getElementById("download-modal-close-btn"), }; @@ -464,6 +465,7 @@ function updateDownloadModalDisplay(info) { elements.count.textContent = info.countText || ""; elements.status.textContent = info.statusText || ""; elements.progressBar.style.width = `${Math.max(0, Math.min(100, info.percent || 0))}%`; + elements.logsButton.classList.toggle("hidden", !info.logsVisible); elements.cancelButton.disabled = !!info.cancelDisabled; elements.cancelButton.classList.toggle("hidden", !info.cancelVisible); elements.closeButton.disabled = !!info.active; @@ -487,6 +489,7 @@ function openZipDownloadModal(selectedItems) { countText: "Waiting for archive task", statusText: "Requested", percent: 0, + logsVisible: true, cancelVisible: true, cancelDisabled: true, }); @@ -508,6 +511,7 @@ function openSingleFileDownloadModal(selectedItem) { countText: "Direct file download", statusText: "Requesting download...", percent: 0, + logsVisible: true, cancelVisible: false, }); } @@ -523,6 +527,7 @@ function markZipDownloadReady(fileName) { countText: "Browser download requested", statusText: "Ready", percent: 100, + logsVisible: true, cancelVisible: false, }); window.setTimeout(closeDownloadModal, 480); @@ -538,6 +543,7 @@ function markZipDownloadFailed(err) { countText: "Archive task failed", statusText: `Failed: ${err.message || "Archive download failed"}`, percent: 0, + logsVisible: true, cancelVisible: false, }); } @@ -552,6 +558,7 @@ function markZipDownloadCancelled() { countText: "Archive task cancelled", statusText: "Cancelled", percent: 0, + logsVisible: true, cancelVisible: false, }); } @@ -565,6 +572,7 @@ function markSingleFileDownloadRequested(fileName, path) { countText: "Browser download requested", statusText: "Download requested", percent: 0, + logsVisible: true, cancelVisible: false, }); window.setTimeout(closeDownloadModal, 480); @@ -579,6 +587,7 @@ function markSingleFileDownloadFailed(err, selectedItem) { countText: "Direct file download", statusText: `Failed: ${err.message || "Download failed"}`, percent: 0, + logsVisible: true, cancelVisible: false, }); } @@ -594,6 +603,7 @@ function updateZipDownloadTaskProgress(task) { countText: archiveTaskCountText(task), statusText: downloadProgressState.cancelRequested ? "Cancelling download..." : archiveTaskStatusLabel(task.status), percent: archiveTaskProgressPercent(task), + logsVisible: true, cancelVisible: true, cancelDisabled: !downloadProgressState.taskId || downloadProgressState.cancelRequested, }); @@ -641,6 +651,7 @@ function closeDownloadModal() { countText: "", statusText: "", percent: 0, + logsVisible: false, cancelVisible: false, }); setDownloadModalVisible(false); @@ -4564,6 +4575,11 @@ function setupEvents() { }; } const downloadModal = downloadModalElements(); + if (downloadModal.logsButton) { + downloadModal.logsButton.onclick = () => { + openSettings("logs"); + }; + } if (downloadModal.cancelButton) { downloadModal.cancelButton.onclick = () => { requestArchiveDownloadCancel().catch((err) => { diff --git a/webui/html/index.html b/webui/html/index.html index 504dd5e..74f30e8 100644 --- a/webui/html/index.html +++ b/webui/html/index.html @@ -129,6 +129,7 @@