diff --git a/bun.lockb b/bun.lockb index b1b5288..abf32e4 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 5968557..ba9e5b0 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "@protobuf-ts/grpcweb-transport": "^2.9.4", "@protobuf-ts/plugin": "^2.9.4", "dayjs": "^1.11.13", + "fzf": "^0.5.2", "mode-watcher": "^0.5.0" } } diff --git a/src/lib/components/groove/Queue.svelte b/src/lib/components/groove/Queue.svelte index f2a0bc5..d9a277f 100644 --- a/src/lib/components/groove/Queue.svelte +++ b/src/lib/components/groove/Queue.svelte @@ -68,9 +68,8 @@ method="POST" use:enhance={submitPlayerAction} > - {#each player.queue as track, i (track.hash)} + {#each player.queue as track, i} diff --git a/src/lib/search.svelte.ts b/src/lib/search.svelte.ts new file mode 100644 index 0000000..72a62b4 --- /dev/null +++ b/src/lib/search.svelte.ts @@ -0,0 +1,25 @@ +import { Fzf } from 'fzf'; +import type { Track } from './proto/library'; +import { getContext, setContext } from 'svelte'; + +class SearchState { + input = $state(''); + + filterTracks(tracks: Track[]) { + const fzf = new Fzf(tracks, { + selector: (t) => `${t.name} ${t.artistName}`, + sort: true, + }); + return fzf.find(this.input); + } +} + +const SEARCH_KEY = Symbol('SEARCH'); + +export function setSearchState() { + return setContext(SEARCH_KEY, new SearchState()); +} + +export function getSearchState() { + return getContext>(SEARCH_KEY); +} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 3d076a4..1b88cfa 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -7,11 +7,17 @@ import { ScrollArea } from '$lib/components/ui/scroll-area'; import { getPlayerState, setPlayerState } from '$lib/player.svelte'; import { onMount } from 'svelte'; + import { Separator } from '$lib/components/ui/separator'; + import { Input } from '$lib/components/ui/input'; + import { Search } from 'lucide-svelte'; + import { setSearchState, getSearchState } from '$lib/search.svelte'; let { children } = $props(); setPlayerState(); + setSearchState(); const player = getPlayerState(); + const search = getSearchState(); onMount(() => { return () => player.abort(); @@ -24,8 +30,17 @@
-
+
+ +
+ + +
+
+
diff --git a/src/routes/tracks/+page.svelte b/src/routes/tracks/+page.svelte index c7cb406..7b01150 100644 --- a/src/routes/tracks/+page.svelte +++ b/src/routes/tracks/+page.svelte @@ -1,18 +1,61 @@
- {#each data.tracks as track} - + {#each search.input.length > 0 ? (search.filterTracks(data.tracks).map(r => r.item) ?? []) : data.tracks as track (track.hash)} + {/each}