perf(frontend): fix resource leaks and disable deep reactivity on media index

- useMediaWebSocket: shallowRef mediaIndex/tasks/loading/error/connected
  to eliminate Proxy overhead on the entire library data structure.
- useMediaWebSocket: replace per-task setTimeout leak with single 3s
  sweep interval for completed task cleanup.
- App.vue: remove {deep:true} watcher on mediaIndex; shallow ref change
  is sufficient to trigger search worker sync.
- useGamepadNavigation: rAF only when gamepads are connected; idle
  fallback to 500ms setTimeout to stop permanent 60fps CPU drain.
- useKeyboardNavigation: deduplicate synced scroll rAF requests to
  prevent overlapping animation frames.
- MediaDetail/SeriesFullView/CollageHero: pause, clear src, and load()
  video elements on unmount and before ref replacement to release
  decoder/memory resources.
- MediaDetail/SeriesFullView: clear all volume fade intervals on
  unmount to stop interval timer leaks.
This commit is contained in:
2026-05-27 20:28:51 +00:00
parent 79d3f40162
commit 96c2d52ee0
7 changed files with 149 additions and 28 deletions
@@ -177,9 +177,12 @@ function resetSyncedRows(immediate: boolean = false) {
return
}
if (syncedRowsFrame === null) {
syncedRowsFrame = window.requestAnimationFrame(animateSyncedRows)
}
startSyncedRowAnimation()
}
function startSyncedRowAnimation() {
if (syncedRowsFrame !== null) return
syncedRowsFrame = window.requestAnimationFrame(animateSyncedRows)
}
function stopSyncedRowAnimation() {
@@ -281,9 +284,7 @@ function updateSyncedRowTarget(anchorCol: number, anchorRow: HTMLElement | null
return
}
if (syncedRowsFrame === null) {
syncedRowsFrame = window.requestAnimationFrame(animateSyncedRows)
}
startSyncedRowAnimation()
}
function getLocalSyncedRowCol(