feat: Library Only mode — toggle, search, artist page, similar artists

Backend:
- Migration 17: similar_artist_map table stores per-artist similar
  artist relationships (source_mbid → similar_mbid + name + score)
- Tier 4 index build now persists similar artists to this table
- GetLibrarySimilarArtists(mbid) queries similar artists filtered
  by JOIN with the artists table (library-only, no API calls)
- Added db field to explore.Service for direct queries

Frontend:
- ExploreSettingsStore with libraryOnly toggle, persisted to
  localStorage
- Top bar toggle button with active/inactive styling
- Explore search: skips full MB/LB pipeline when library-only,
  uses only searchLibraryCache (pure JS, instant)
- Artist detail page: in library-only mode, skips all API calls
  (no top tracks, no top releases, no LB play count, no MB
  artist lookup). Uses library store for discography, calls
  GetLibrarySimilarArtists for similar artists.
- Similar artists section: changed from horizontal scroll to
  wrapping flex layout with collapsible toggle (Show all N)
- Removed debug artist ranking log
This commit is contained in:
2026-03-30 15:36:37 -04:00
parent 6440b0333c
commit d73226b173
11 changed files with 278 additions and 24 deletions
+22
View File
@@ -32,6 +32,7 @@ import '@store/theme-store';
// Importing the keyboard shortcut service triggers initialization:
// registers the document keydown listener for global shortcuts.
import './src/services/keyboard-shortcut-service';
import { exploreSettings } from '@store/explore-settings';
import {
hasTrackPayload,
getDragPayload,
@@ -268,3 +269,24 @@ if (queueButton && queuePanel) {
// or timing assumptions needed.
void Player.EmitCurrentState();
void Queue.EmitCurrentState();
// ---------------------------------------------------------------------------
// Library Only toggle
// ---------------------------------------------------------------------------
const libraryOnlyToggle = document.getElementById('library-only-toggle');
if (libraryOnlyToggle) {
// Sync initial state.
if (exploreSettings.libraryOnly) {
libraryOnlyToggle.classList.add('active');
}
libraryOnlyToggle.addEventListener('click', () => {
exploreSettings.toggle();
libraryOnlyToggle.classList.toggle('active', exploreSettings.libraryOnly);
});
exploreSettings.subscribe(() => {
libraryOnlyToggle.classList.toggle('active', exploreSettings.libraryOnly);
});
}