perf(webui): flatten scroll-container background + scope explorer wheel listener

Two measured, universally-beneficial fixes (kept after determining the rest of
the earlier perf work was chasing a Bitwarden extension that pegged the main
thread, not real app bugs):

- .main-content had a linear-gradient background. A gradient on the scroll
  container is re-rastered across the whole scrolled area every scroll frame
  (the compositor can't translate a cached tile): ~25% dropped frames -> <1%
  once flattened to a solid color (visually identical, was rgb 10->15->11).

- The explorer wheel-zoom listener was a non-passive listener on `document`,
  which disables compositor (async) scrolling app-wide so every wheel/trackpad
  scroll runs through the main thread. Scoped it to the explorer viewport.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
dev 2026-06-04 20:35:21 +02:00
parent 1c1d2eb884
commit 89fe7703fa
2 changed files with 23 additions and 15 deletions

View file

@ -1066,18 +1066,24 @@ function explorerFitToView() {
});
}
// Scroll wheel zoom (no modifier needed inside viewport)
document.addEventListener('wheel', (e) => {
// Scroll wheel zoom (no modifier needed inside viewport).
// IMPORTANT: attach to the viewport element, NOT document. A non-passive wheel
// listener on document disables the browser's compositor (async) scrolling for
// the ENTIRE app — every wheel/trackpad scroll then runs through the main thread.
// Scoping it to the viewport keeps zoom working while the rest of the app keeps
// smooth compositor scrolling.
(function attachExplorerWheelZoom() {
const viewport = document.getElementById('explorer-viewport');
if (!viewport || !viewport.contains(e.target)) return;
// Check if we're on the explorer page
const page = document.getElementById('playlist-explorer-page');
if (!page || !page.classList.contains('active')) return;
if (!viewport) return;
viewport.addEventListener('wheel', (e) => {
const page = document.getElementById('playlist-explorer-page');
if (!page || !page.classList.contains('active')) return;
e.preventDefault();
const step = e.deltaY > 0 ? -0.08 : 0.08;
explorerZoom(step);
}, { passive: false });
e.preventDefault();
const step = e.deltaY > 0 ? -0.08 : 0.08;
explorerZoom(step);
}, { passive: false });
})();
// Middle-click / right-click drag to pan
document.addEventListener('mousedown', (e) => {

View file

@ -2970,11 +2970,13 @@ body.helper-mode-active #dashboard-activity-feed:hover {
.main-content {
flex: 1;
/* Opaque dark background (GPU-optimized: no backdrop-filter needed on solid dark body) */
background: linear-gradient(135deg,
rgba(10, 10, 10, 1) 0%,
rgba(15, 15, 15, 1) 50%,
rgba(11, 11, 11, 1) 100%);
/* Flat solid background NOT a gradient. A gradient on the scroll container
has to be re-rastered across the whole scrolled area on every scroll frame
(the compositor can't just translate a cached tile), which caused the
dominant scroll jank (~25% dropped frames -> <1% once flattened, measured).
A solid color is drawn as a single compositor quad, no per-frame raster.
The gradient was rgb 10->15->11, visually indistinguishable from this. */
background: rgb(12, 12, 12);
overflow: auto;
position: relative;
}