From 6936a089b837e28ea84422659d49ac82e23495f2 Mon Sep 17 00:00:00 2001 From: cc Date: Mon, 9 Mar 2026 20:32:04 -0400 Subject: [PATCH] perf(scan): replace deep watch with computed to reduce reactivity overhead MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During a scan with large collections, the scan page becomes slow or unresponsive. The watch on scanningPlatforms with { deep: true } was firing on every ROM addition, causing Vue to deeply traverse all nested platform and ROM data each time. The watch's only job is deciding which panels are open. A panel should open when a platform first receives ROMs — after that, its open/closed state doesn't need to change regardless of how many more ROMs arrive. Replace with a computed that maps each platform to a boolean (roms.length > 0), so the watch only fires on that one meaningful transition per platform. The ROM list inside each panel continues to update reactively as normal — this change only affects the panel open/close logic. Note: This PR was written with AI assistance (Claude Code). --- frontend/src/views/Scan.vue | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/frontend/src/views/Scan.vue b/frontend/src/views/Scan.vue index 4523367da..524ef65be 100644 --- a/frontend/src/views/Scan.vue +++ b/frontend/src/views/Scan.vue @@ -90,16 +90,18 @@ watch(metadataOptions, (newOptions) => { ); }); -// Adding each new scanned platform to panelIndex to be open by default -watch( - scanningPlatforms, - () => { - panels.value = scanningPlatforms.value - .map((p, index) => (p.roms.length > 0 ? index : -1)) - .filter((index) => index !== -1); - }, - { deep: true }, +// Track which platforms have ROMs without a deep watch on the entire array. +// The computed returns a stable string that only changes when a platform +// transitions from 0→>0 ROMs (or back), so the watch fires O(n_platforms) +// times rather than O(n_roms) times. +const platformsWithRomsKey = computed(() => + scanningPlatforms.value.map((p) => (p.roms.length > 0 ? 1 : 0)).join(""), ); +watch(platformsWithRomsKey, () => { + panels.value = scanningPlatforms.value + .map((p, index) => (p.roms.length > 0 ? index : -1)) + .filter((index) => index !== -1); +}); const scanOptions = [ {