feat(search): context-scoped search with filter chips and group order

Add a search scope (all/music/shows/movies) resolved from the entry
route and adjustable via filter chips, threaded through the library
store's search() into includeItemTypes. Results group by type in a
user-configurable order, editable from settings.

TRACES: UR-049 | DR-063, DR-064, DR-065; UR-050 | DR-066, DR-067
This commit is contained in:
2026-07-23 20:02:15 +02:00
parent e083b53ee8
commit c175378f38
11 changed files with 1135 additions and 256 deletions
+19 -2
View File
@@ -4,6 +4,8 @@
import { writable, derived } from "svelte/store";
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
import type { Library, MediaItem, SearchResult, Genre } from "$lib/api/types";
import type { SearchOptions } from "$lib/api/bindings";
import { scopeItemTypes, type SearchScope } from "$lib/utils/searchScope";
import { auth } from "./auth";
/**
@@ -222,7 +224,17 @@ function createLibraryStore() {
}
}
async function search(query: string) {
/**
* Search the library, optionally narrowed to a scope.
*
* `scope` is additive and defaults to `all`, which sends no
* `includeItemTypes` at all — see scopeItemTypes() for why that differs from
* listing every type. Both the online and offline repository paths already
* honour the filter.
*
* TRACES: UR-049 | DR-065
*/
async function search(query: string, scope: SearchScope = "all") {
// Bump the request id for every call (including clears) so any in-flight
// backend update for a previous query is ignored when it arrives.
const requestId = ++searchRequestId;
@@ -247,8 +259,13 @@ function createLibraryStore() {
// Phase 1: the command resolves with instant local-cache results. The
// merged (cache + server) union arrives later via the `search-event`
// listener above, tagged with this same requestId.
const itemTypes = scopeItemTypes(scope);
const options: SearchOptions = { limit: 10000 };
// Omit the key entirely for the `all` scope rather than sending null.
if (itemTypes) options.includeItemTypes = itemTypes;
const result = await Promise.race([
repo.search(query, { limit: 10000 }, requestId),
repo.search(query, options, requestId),
timeoutPromise
]);