- LibraryStore now listens for TrackMetadataChanged and invalidates all caches
- Track Details context menu item visible for any right-clicked track in all 4 views
- Removed selectionCount === 1 gate from track-list, queue-panel, cover-grid, playlist-details
- Event delegation handlers were attached in firstUpdated(), but the
virtualizer is conditionally rendered (hidden when tracks empty/loading)
- On first render, tracks are [] so virtualizer doesn't exist, and
firstUpdated() never fires again — delegation was never attached
- Move delegation to a guarded helper called from both firstUpdated()
and updated(), so it attaches as soon as the virtualizer appears
- Fixes click, multi-select, context menu, double-click, and drag in
both track-list and queue-panel components
Targeted optimizations for the DMABuf-disabled rendering path where
every frame is software-composited:
- Replace infinite CSS scroll-text animation with transition-based
cycle that only repaints during active scroll, not during pauses
- Remove CSS mask-image on scrolling text (mask + animation was the
single most expensive continuous repaint)
- Replace wa-icon in track rows with inline SVG — eliminates 30-50
shadow DOM trees (each with SVG fetch/parse) during scroll
- Remove hover transitions on album cards, artist cards, genre cards,
fav icons, queue remove buttons — each transition was causing
per-frame software repaints
- Use visibility:hidden instead of opacity:0 for queue remove button
(binary switch vs per-frame alpha blend)
- Add decoding=async to now-playing cover art images (prevents
main-thread blocking during image decode on track change)
- Add contain:strict to fixed-height track rows (33px) and queue
items (49px) — browser skips size contribution calculations
Root causes addressed:
- track-list had no _itemSize hint for flow layout — virtualizer
defaulted to 100px, measured actual ~33px rows, then called
_correctScrollError/scrollTo on every scroll causing visible jumps
- will-change:transform on virtualizer elements caused nested GPU
layers (virtualizer positions children with transforms internally)
adding compositor overhead instead of helping
- content-visibility:auto on album cards conflicted with virtualizer's
own DOM recycling, causing redundant layout recalculation
- track-list visibilityChanged handler wrote to store synchronously
on every event (per-item during scroll) without any throttling
- IIFE closure in renderTrackRow created a new function per row per render
Fixes applied:
- Add _itemSize:{height:33} + fixed height:33px on .track-row (matches
queue-panel pattern that already worked smoothly)
- Add overflow-anchor:none on track-list virtualizer
- Remove will-change:transform from all 6 scroll containers
- Remove content-visibility:auto from album cards
- RAF-throttle visibilityChanged scroll position saves
- Replace IIFE with direct cols.map() in template
- Replace inline arrow closures in renderTrackRow with event delegation via data-index
- Replace inline arrow closures in renderTrackItem with event delegation via data-index
- Add delegated click/dblclick/contextmenu/dragstart handlers on virtualizer elements
- Remove button click in queue panel also delegated via closest('.remove-button')
- Zero new function objects created per renderItem call during scroll
- contain: layout style on :host of all 6 scroll-heavy components
- contain: paint + will-change: transform on all scroll containers for GPU compositing
- content-visibility: auto + contain-intrinsic-size on .album-card for off-screen skip
- cover-grid, track-list, queue-panel, artists-view, genres-view, playlist-view
- Add handleSelectAll bound handler calling selection.selectAll() in all three components
- Register/unregister event listeners in connectedCallback/disconnectedCallback
Move search indicator from absolute-positioned overlay into sort toolbar
(or dedicated search-bar-row for artists/genres views). Shows indicator
on empty-state screens. Fix cover art not displaying for expanded album
tracks by checking expandedAlbumId before the albumName guard. Add
ScanWarning model bindings.
repeat() as child content of lit-virtualizer bypasses virtualization,
rendering ALL items instead of only visible ones. This caused 2+ minute
loading times and UI freezing with large libraries.
The correct approach: use lit-virtualizer's native .renderItem and
.keyFunction properties which integrate with its scroll-based viewport
management.
- cover-grid dynamic text tiers use --yj-text-xs/sm/md/lg tokens
- track-list sort toolbar, row text, and fav icon use design tokens
- queue-panel header and track text use type scale tokens
- track-details, track-info, artist-details, genre-details use tokens
- designTokens imported and prepended to static styles in all 8 files
- Hoist searchCtrl.term lookup outside per-column loop in renderTrackRow
- Search highlighting already short-circuits when search term is empty
- Import classMap in queue-panel.ts
- Replace array filter/join class construction with classMap in renderTrackItem
- Eliminates per-row array allocation in queue panel render hot path
- Import classMap from lit/directives/class-map.js
- Replace array filter/join class construction with classMap for track-row
- Convert fav-icon conditional class to classMap
- Convert cell alignment classes to classMap
- Eliminates per-row array allocation and string join in render hot path
album artist gets populated with artist as fallback (frontend also uses
this as display name fallback for albums). added scroll position
persistence when switching main views. also added frontend cache for
faster switching.