cover-grid album dropdown behavior fixes

This commit is contained in:
2026-02-18 11:34:10 -05:00
parent 026ab1c333
commit c9e78c491c
17 changed files with 934 additions and 424 deletions
@@ -78,4 +78,16 @@ export class LibraryController implements ReactiveController {
setScrollPosition(view: ViewName, offset: number): void {
libraryStore.setScrollPosition(view, offset);
}
// ===================================================================
// COVER SIZE
// ===================================================================
get coverSize(): number {
return libraryStore.getCoverSize();
}
set coverSize(size: number) {
libraryStore.setCoverSize(size);
}
}
+66
View File
@@ -7,6 +7,18 @@ type ViewName = 'tracks' | 'albums';
type Subscriber = () => void;
/** Minimum album card width in CSS pixels. */
const COVER_SIZE_MIN = 100;
/** Maximum album card width in CSS pixels. */
const COVER_SIZE_MAX = 350;
/** Default album card width in CSS pixels. */
const COVER_SIZE_DEFAULT = 176;
/** localStorage key for persisted cover size. */
const COVER_SIZE_KEY = 'cover-grid-size';
class LibraryStore {
private tracks: library.Track[] | null = null;
private albums: library.Album[] | null = null;
@@ -14,6 +26,8 @@ class LibraryStore {
private tracksLoading = false;
private albumsLoading = false;
private coverSizeValue: number = COVER_SIZE_DEFAULT;
private scrollPositions: Record<ViewName, number> = {
tracks: 0,
albums: 0,
@@ -25,6 +39,8 @@ class LibraryStore {
EventsOn(Events.LibraryScanComplete, () => {
this.invalidate();
});
this.loadCoverSize();
}
// ===================================================================
@@ -111,6 +127,56 @@ class LibraryStore {
this.scrollPositions[view] = offset;
}
// ===================================================================
// COVER SIZE
// ===================================================================
getCoverSize(): number {
return this.coverSizeValue;
}
setCoverSize(size: number): void {
const clamped = Math.round(
Math.max(COVER_SIZE_MIN, Math.min(COVER_SIZE_MAX, size)),
);
if (clamped === this.coverSizeValue) return;
this.coverSizeValue = clamped;
this.saveCoverSize();
this.notify();
}
private loadCoverSize(): void {
try {
const stored = localStorage.getItem(COVER_SIZE_KEY);
if (stored !== null) {
const parsed = parseInt(stored, 10);
if (!Number.isNaN(parsed)) {
this.coverSizeValue = Math.max(
COVER_SIZE_MIN,
Math.min(COVER_SIZE_MAX, parsed),
);
}
}
} catch {
// localStorage may be unavailable.
}
}
private saveCoverSize(): void {
try {
localStorage.setItem(
COVER_SIZE_KEY,
String(this.coverSizeValue),
);
} catch {
// localStorage may be unavailable.
}
}
// ===================================================================
// INVALIDATION
// ===================================================================
+4 -2
View File
@@ -11,8 +11,10 @@ export interface TrackInfo {
title: string; // track title (falls back to fileName)
artist: string; // artist name
album: string; // album name
coverArt: string; // URL path to cover art (e.g., "/covers/abc.jpg") or empty string
coverArtThumbnail: string; // URL path to thumbnail (e.g., "/covers/abc_thumb.jpg") or empty string
coverArt: string; // URL path to full-size cover art or empty string
coverArtSmall: string; // URL path to small variant (100px max) or empty string
coverArtMedium: string; // URL path to medium variant (200px max) or empty string
coverArtLarge: string; // URL path to large variant (400px max) or empty string
trackChangeId: number; // monotonic counter to detect track changes even when the same file plays consecutively
}