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 cf7f37e..73f90cf 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 9e27de0..03d3b30 100644 --- a/webui/backend/tests/golden/test_ui_smoke_golden.py +++ b/webui/backend/tests/golden/test_ui_smoke_golden.py @@ -246,6 +246,8 @@ class UiSmokeGoldenTest(unittest.TestCase): self.assertIn('async function loadTasksForSettings()', app_js) self.assertIn('async function loadLogsAndTasksForSettings()', app_js) self.assertIn('function scheduleSettingsLogsPolling()', app_js) + self.assertIn('lastHistoryRenderKey: ""', app_js) + self.assertIn('lastTasksRenderKey: ""', app_js) self.assertIn('function openZipDownloadModal(selectedItems)', app_js) self.assertIn('function openSingleFileDownloadModal(selectedItem)', app_js) self.assertIn('function markZipDownloadReady(fileName)', app_js) @@ -300,6 +302,14 @@ 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('if (settingsState.lastHistoryRenderKey === renderKey) {', app_js) + self.assertIn('if (settingsState.lastTasksRenderKey === renderKey) {', app_js) + self.assertIn('const scrollTop = elements.logsList.scrollTop;', app_js) + self.assertIn('const scrollTop = elements.tasksList.scrollTop;', app_js) + self.assertIn('elements.logsList.scrollTop = scrollTop;', app_js) + self.assertIn('elements.tasksList.scrollTop = scrollTop;', app_js) + self.assertIn('if (!settingsState.tasksLoaded) {', app_js) + self.assertIn('if (!settingsState.logsLoaded) {', app_js) self.assertIn('downloadModal.logsButton.onclick = () => {', app_js) self.assertIn('openSettings("logs");', app_js) self.assertIn('function applyContextMenuSelection()', app_js) diff --git a/webui/html/app.js b/webui/html/app.js index 750c628..6ba4795 100644 --- a/webui/html/app.js +++ b/webui/html/app.js @@ -103,6 +103,8 @@ let settingsState = { logsLoaded: false, tasksLoaded: false, logsPollTimer: null, + lastHistoryRenderKey: "", + lastTasksRenderKey: "", showThumbnails: false, preferredStartupPathLeft: null, preferredStartupPathRight: null, @@ -3804,12 +3806,18 @@ function formatTaskLine(task) { function renderHistoryItems(items) { const elements = settingsElements(); + const renderKey = JSON.stringify(Array.isArray(items) ? items : []); + if (settingsState.lastHistoryRenderKey === renderKey) { + return; + } + const scrollTop = elements.logsList.scrollTop; elements.logsList.innerHTML = ""; if (!Array.isArray(items) || items.length === 0) { const empty = document.createElement("div"); empty.className = "popup-meta"; empty.textContent = "No history entries yet."; elements.logsList.append(empty); + settingsState.lastHistoryRenderKey = renderKey; return; } for (const item of items) { @@ -3834,16 +3842,24 @@ function renderHistoryItems(items) { } elements.logsList.append(row); } + settingsState.lastHistoryRenderKey = renderKey; + elements.logsList.scrollTop = scrollTop; } function renderTaskItems(items) { const elements = settingsElements(); + const renderKey = JSON.stringify(Array.isArray(items) ? items : []); + if (settingsState.lastTasksRenderKey === renderKey) { + return; + } + const scrollTop = elements.tasksList.scrollTop; elements.tasksList.innerHTML = ""; if (!Array.isArray(items) || items.length === 0) { const empty = document.createElement("div"); empty.className = "popup-meta"; empty.textContent = "No tasks yet."; elements.tasksList.append(empty); + settingsState.lastTasksRenderKey = renderKey; return; } for (const task of items) { @@ -3868,6 +3884,8 @@ function renderTaskItems(items) { } elements.tasksList.append(row); } + settingsState.lastTasksRenderKey = renderKey; + elements.tasksList.scrollTop = scrollTop; } async function loadHistoryForSettings() { @@ -3885,8 +3903,12 @@ async function loadTasksForSettings() { async function loadLogsAndTasksForSettings() { const elements = settingsElements(); elements.logsError.textContent = ""; - elements.tasksList.innerHTML = ''; - elements.logsList.innerHTML = ''; + if (!settingsState.tasksLoaded) { + elements.tasksList.innerHTML = ''; + } + if (!settingsState.logsLoaded) { + elements.logsList.innerHTML = ''; + } try { await Promise.all([loadTasksForSettings(), loadHistoryForSettings()]); } catch (err) {