fix(08-02): revert repeat() inside lit-virtualizer, restore .renderItem + .keyFunction

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.
This commit is contained in:
2026-03-05 08:46:01 -05:00
parent 1303422e69
commit 72ef719ba7
5 changed files with 21 additions and 40 deletions
@@ -10,7 +10,6 @@ import type {
VisibilityChangedEvent, VisibilityChangedEvent,
} from '@lit-labs/virtualizer'; } from '@lit-labs/virtualizer';
import { grid } from '@lit-labs/virtualizer/layouts/grid.js'; import { grid } from '@lit-labs/virtualizer/layouts/grid.js';
import { repeat } from 'lit/directives/repeat.js';
import { import {
GetAlbumsByArtist, GetAlbumsByArtist,
GetAlbumTracks, GetAlbumTracks,
@@ -1217,13 +1216,11 @@ export class ArtistsView
> >
<lit-virtualizer <lit-virtualizer
.items=${entries} .items=${entries}
.renderItem=${(entry: ArtistEntry) => this.renderArtistCard(entry)}
.keyFunction=${(entry: ArtistEntry) => entry.artist.ID}
.layout=${this.gridLayout} .layout=${this.gridLayout}
@visibilityChanged=${this.onVisibilityChanged} @visibilityChanged=${this.onVisibilityChanged}
>${repeat( ></lit-virtualizer>
entries,
(entry) => entry.artist.ID,
(entry) => this.renderArtistCard(entry),
)}</lit-virtualizer>
</div> </div>
${this.renderContextMenu()} ${this.renderContextMenu()}
`; `;
@@ -11,7 +11,6 @@ import type {
VisibilityChangedEvent, VisibilityChangedEvent,
} from '@lit-labs/virtualizer'; } from '@lit-labs/virtualizer';
import { grid } from '@lit-labs/virtualizer/layouts/grid.js'; import { grid } from '@lit-labs/virtualizer/layouts/grid.js';
import { repeat } from 'lit/directives/repeat.js';
import { GetAlbumTracks } from '@go/library/Library'; import { GetAlbumTracks } from '@go/library/Library';
import { library } from '@go/models'; import { library } from '@go/models';
import { LibraryController } from '@store/controllers/library-controller'; import { LibraryController } from '@store/controllers/library-controller';
@@ -1848,17 +1847,15 @@ export class CoverGrid
<lit-virtualizer <lit-virtualizer
id="grid-single" id="grid-single"
.items=${this.buildGridEntries()} .items=${this.buildGridEntries()}
.renderItem=${this.renderGridEntry}
.keyFunction=${(entry: GridEntry) => entry.album.ID}
.layout=${this.gridLayout} .layout=${this.gridLayout}
@click=${this.onGridAlbumClick} @click=${this.onGridAlbumClick}
@dblclick=${this.onGridAlbumDblClick} @dblclick=${this.onGridAlbumDblClick}
@keydown=${this.onGridAlbumKeydown} @keydown=${this.onGridAlbumKeydown}
@contextmenu=${this.onGridAlbumContextMenu} @contextmenu=${this.onGridAlbumContextMenu}
@visibilityChanged=${this.onVisibilityChanged} @visibilityChanged=${this.onVisibilityChanged}
>${repeat( ></lit-virtualizer>
this.buildGridEntries(),
(entry) => entry.album.ID,
(entry) => this.renderGridEntry(entry),
)}</lit-virtualizer>
`; `;
} }
@@ -1877,17 +1874,15 @@ export class CoverGrid
<lit-virtualizer <lit-virtualizer
id="grid-before" id="grid-before"
.items=${this.getBeforeEntries()} .items=${this.getBeforeEntries()}
.renderItem=${this.renderGridEntry}
.keyFunction=${(entry: GridEntry) => entry.album.ID}
.layout=${this.gridLayout} .layout=${this.gridLayout}
@click=${this.onGridAlbumClick} @click=${this.onGridAlbumClick}
@dblclick=${this.onGridAlbumDblClick} @dblclick=${this.onGridAlbumDblClick}
@keydown=${this.onGridAlbumKeydown} @keydown=${this.onGridAlbumKeydown}
@contextmenu=${this.onGridAlbumContextMenu} @contextmenu=${this.onGridAlbumContextMenu}
@visibilityChanged=${this.onVisibilityChanged} @visibilityChanged=${this.onVisibilityChanged}
>${repeat( ></lit-virtualizer>
this.getBeforeEntries(),
(entry) => entry.album.ID,
(entry) => this.renderGridEntry(entry),
)}</lit-virtualizer>
<album-dropdown <album-dropdown
.tracks=${this.expandedTracks} .tracks=${this.expandedTracks}
@@ -1908,16 +1903,14 @@ export class CoverGrid
<lit-virtualizer <lit-virtualizer
id="grid-after" id="grid-after"
.items=${afterEntries} .items=${afterEntries}
.renderItem=${this.renderGridEntry}
.keyFunction=${(entry: GridEntry) => entry.album.ID}
.layout=${this.gridLayoutAfter} .layout=${this.gridLayoutAfter}
@click=${this.onGridAlbumClick} @click=${this.onGridAlbumClick}
@dblclick=${this.onGridAlbumDblClick} @dblclick=${this.onGridAlbumDblClick}
@keydown=${this.onGridAlbumKeydown} @keydown=${this.onGridAlbumKeydown}
@contextmenu=${this.onGridAlbumContextMenu} @contextmenu=${this.onGridAlbumContextMenu}
>${repeat( ></lit-virtualizer>
afterEntries,
(entry) => entry.album.ID,
(entry) => this.renderGridEntry(entry),
)}</lit-virtualizer>
` `
: nothing} : nothing}
`; `;
@@ -10,7 +10,6 @@ import type {
VisibilityChangedEvent, VisibilityChangedEvent,
} from '@lit-labs/virtualizer'; } from '@lit-labs/virtualizer';
import { grid } from '@lit-labs/virtualizer/layouts/grid.js'; import { grid } from '@lit-labs/virtualizer/layouts/grid.js';
import { repeat } from 'lit/directives/repeat.js';
import { GetTracksByGenre } from '@go/library/Library'; import { GetTracksByGenre } from '@go/library/Library';
import type { library } from '@go/models'; import type { library } from '@go/models';
import { LibraryController } from '@store/controllers/library-controller'; import { LibraryController } from '@store/controllers/library-controller';
@@ -1169,13 +1168,11 @@ export class GenresView
> >
<lit-virtualizer <lit-virtualizer
.items=${entries} .items=${entries}
.renderItem=${(entry: GenreEntry) => this.renderGenreCard(entry)}
.keyFunction=${(entry: GenreEntry) => entry.genre.name}
.layout=${this.gridLayout} .layout=${this.gridLayout}
@visibilityChanged=${this.onVisibilityChanged} @visibilityChanged=${this.onVisibilityChanged}
>${repeat( ></lit-virtualizer>
entries,
(entry) => entry.genre.name,
(entry) => this.renderGenreCard(entry),
)}</lit-virtualizer>
</div> </div>
${this.renderContextMenu()} ${this.renderContextMenu()}
`; `;
@@ -16,7 +16,6 @@ import type { PlaylistPicker } from '@components/playlist-picker/playlist-picker
import '@lit-labs/virtualizer'; import '@lit-labs/virtualizer';
import type { LitVirtualizer } from '@lit-labs/virtualizer'; import type { LitVirtualizer } from '@lit-labs/virtualizer';
import { flow } from '@lit-labs/virtualizer/layouts/flow.js'; import { flow } from '@lit-labs/virtualizer/layouts/flow.js';
import { repeat } from 'lit/directives/repeat.js';
import { classMap } from 'lit/directives/class-map.js'; import { classMap } from 'lit/directives/class-map.js';
import type { QueueTrack } from '@store/queue-store'; import type { QueueTrack } from '@store/queue-store';
import { SelectionController } from '@utils/selection-controller'; import { SelectionController } from '@utils/selection-controller';
@@ -1281,12 +1280,10 @@ export class QueuePanel
<lit-virtualizer <lit-virtualizer
scroller scroller
.items=${tracks} .items=${tracks}
.renderItem=${this.renderTrackItem}
.keyFunction=${(track: QueueTrack) => track.id}
.layout=${this.flowLayout} .layout=${this.flowLayout}
>${repeat( ></lit-virtualizer>
tracks,
(track) => track.id,
(track, index) => this.renderTrackItem(track, index),
)}</lit-virtualizer>
`} `}
</div> </div>
</div> </div>
@@ -26,7 +26,6 @@ import {
DEFAULT_COLUMN_IDS, DEFAULT_COLUMN_IDS,
} from './columns'; } from './columns';
import type { ColumnDef } from './columns'; import type { ColumnDef } from './columns';
import { repeat } from 'lit/directives/repeat.js';
import { classMap } from 'lit/directives/class-map.js'; import { classMap } from 'lit/directives/class-map.js';
import { import {
rankTracks, rankTracks,
@@ -1738,12 +1737,10 @@ export class TrackList extends LitElement implements SelectionHost, ContextMenuH
<lit-virtualizer <lit-virtualizer
scroller scroller
.items=${visibleTracks} .items=${visibleTracks}
.renderItem=${this.renderTrackRow}
.keyFunction=${(track: library.Track) => track.FilePath}
.layout=${this.flowLayout} .layout=${this.flowLayout}
>${repeat( ></lit-virtualizer>
visibleTracks,
(track) => track.FilePath,
(track, index) => this.renderTrackRow(track, index),
)}</lit-virtualizer>
`} `}
${this.searchCtrl.term && visibleTracks.length > 0 ${this.searchCtrl.term && visibleTracks.length > 0