diff --git a/backend/explore/explore.go b/backend/explore/explore.go index f085e67..f84c95f 100644 --- a/backend/explore/explore.go +++ b/backend/explore/explore.go @@ -112,6 +112,23 @@ func (e *Service) SearchRecordings(query string) ([]MBRecording, error) { return e.mb.SearchRecordings(e.ctx, query, mbSearchLimit) } +// SearchLocal queries only the local FTS5 index and returns results +// instantly with no network calls. Returns nil if the index isn't +// ready. The frontend calls this in parallel with Search() to show +// instant results while the full pipeline runs. +func (e *Service) SearchLocal(query string) *MBSearchResult { + indexHits := e.index.Search(query, 30) //nolint:mnd + if len(indexHits) == 0 { + return nil + } + + var result MBSearchResult + mergeIndexHits(&result, indexHits) + filterAndCap(&result) + + return &result +} + // --------------------------------------------------------------------------- // MusicBrainz lookup // --------------------------------------------------------------------------- diff --git a/frontend/src/components/explore-view/explore-view.ts b/frontend/src/components/explore-view/explore-view.ts index 571cda8..037b3bd 100644 --- a/frontend/src/components/explore-view/explore-view.ts +++ b/frontend/src/components/explore-view/explore-view.ts @@ -1,7 +1,7 @@ import { LitElement, html, css, nothing } from 'lit'; import { customElement, state, query as litQuery } from 'lit/decorators.js'; import { designTokens } from '../../styles/tokens.css'; -import { Search, GetThumbnails, GetArtistImageURL, CheckLibraryMBIDs } from '@go/explore/Service'; +import { Search, SearchLocal, GetThumbnails, GetArtistImageURL, CheckLibraryMBIDs } from '@go/explore/Service'; import type { ThumbnailRequest } from '@go/explore/Service'; import type { MBSearchResult, @@ -520,6 +520,29 @@ export class ExploreView extends LitElement { const startTime = performance.now(); console.log(`[explore] search started: "${query}"`); + // Phase 1: show local index hits instantly (no network). + try { + const local = await SearchLocal(query); + if (version !== this.searchVersion) return; + if (local && (local.artists?.length || local.releaseGroups?.length || local.recordings?.length)) { + this.results = local; + this.loading = false; + this.loadThumbnails(); + this.loadArtistImages(); + this.checkLibrary(); + const elapsed = (performance.now() - startTime).toFixed(0); + console.log( + `[explore] local results: "${query}" in ${elapsed}ms — ` + + `artists=${local.artists?.length ?? 0}, ` + + `albums=${local.releaseGroups?.length ?? 0}, ` + + `tracks=${local.recordings?.length ?? 0}`, + ); + } + } catch { + // Local search failed — continue to full search. + } + + // Phase 2: full pipeline (MB + LB + reranking). try { const result = await Search(query); diff --git a/frontend/wailsjs/go/explore/Service.d.ts b/frontend/wailsjs/go/explore/Service.d.ts index 1ba3e4e..028dc17 100755 --- a/frontend/wailsjs/go/explore/Service.d.ts +++ b/frontend/wailsjs/go/explore/Service.d.ts @@ -35,6 +35,8 @@ export function Search(arg1:string):Promise; export function SearchArtists(arg1:string):Promise>; +export function SearchLocal(arg1:string):Promise; + export function SearchRecordings(arg1:string):Promise>; export function SearchReleaseGroups(arg1:string):Promise>; diff --git a/frontend/wailsjs/go/explore/Service.js b/frontend/wailsjs/go/explore/Service.js index 217a451..f1a1f46 100755 --- a/frontend/wailsjs/go/explore/Service.js +++ b/frontend/wailsjs/go/explore/Service.js @@ -66,6 +66,10 @@ export function SearchArtists(arg1) { return window['go']['explore']['Service']['SearchArtists'](arg1); } +export function SearchLocal(arg1) { + return window['go']['explore']['Service']['SearchLocal'](arg1); +} + export function SearchRecordings(arg1) { return window['go']['explore']['Service']['SearchRecordings'](arg1); }