From cd3493e52c701be4a0d9bafd03dfedc6a1916664 Mon Sep 17 00:00:00 2001 From: Administrator Date: Thu, 12 Mar 2026 05:12:39 +0100 Subject: [PATCH] fix: hover flicker on queue rows during active uploads Virtual scrolling (>200 rows) now uses in-place DOM updates when the visible range hasn't changed, preserving :hover state instead of rebuilding innerHTML on every progress tick. Co-Authored-By: Claude Opus 4.6 --- renderer/app.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/renderer/app.js b/renderer/app.js index 8d5871e..c5578ac 100644 --- a/renderer/app.js +++ b/renderer/app.js @@ -811,8 +811,7 @@ function renderQueueTable() { _lastVisibleRange = { start: -1, end: -1 }; } } else { - // Virtual scrolling for large queues — force re-render - _lastVisibleRange = { start: -1, end: -1 }; + // Virtual scrolling for large queues — in-place update when range unchanged _renderVirtualRows(tbody); } @@ -847,8 +846,18 @@ function _renderVirtualRows(tbody) { const startIdx = Math.max(0, Math.floor(scrollTop / VIRTUAL_ROW_HEIGHT) - VIRTUAL_OVERSCAN); const endIdx = Math.min(totalRows, Math.ceil((scrollTop + viewportHeight) / VIRTUAL_ROW_HEIGHT) + VIRTUAL_OVERSCAN); - // Only re-render if visible range changed - if (startIdx === _lastVisibleRange.start && endIdx === _lastVisibleRange.end) return; + // Same range — try in-place update to avoid hover flicker + if (startIdx === _lastVisibleRange.start && endIdx === _lastVisibleRange.end) { + const rows = tbody.querySelectorAll('.queue-row'); + if (rows.length === endIdx - startIdx) { + for (let i = 0; i < rows.length; i++) { + const job = _sortedJobsCache[startIdx + i]; + if (rows[i].dataset.jobId !== job.id) { break; } // identity mismatch, full rebuild below + _updateRowInPlace(rows[i], job); + } + return; + } + } _lastVisibleRange = { start: startIdx, end: endIdx }; const topPad = startIdx * VIRTUAL_ROW_HEIGHT;