diff --git a/bun.lockb b/bun.lockb index 44fe1c3..3d79beb 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 71d7de2..5f3d177 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "@protobuf-ts/grpcweb-transport": "^2.9.4", "@protobuf-ts/plugin": "^2.9.4", "dayjs": "^1.11.13", - "fzf": "^0.5.2", + "fuse.js": "^7.0.0", "mode-watcher": "^0.5.0" } } diff --git a/src/lib/components/groove/TrackListing.svelte b/src/lib/components/groove/TrackListing.svelte index 7fdd6c7..8caceb8 100644 --- a/src/lib/components/groove/TrackListing.svelte +++ b/src/lib/components/groove/TrackListing.svelte @@ -1,7 +1,7 @@ -
oncontextmenu?.(e, track.hash)} -> +
-
+

{track.name}

diff --git a/src/lib/search.svelte.ts b/src/lib/search.svelte.ts index 35d0a25..52e921d 100644 --- a/src/lib/search.svelte.ts +++ b/src/lib/search.svelte.ts @@ -1,16 +1,29 @@ -import { Fzf } from 'fzf'; +import Fuse from 'fuse.js'; import type { Track } from './proto/library'; import { getContext, setContext } from 'svelte'; class SearchState { input = $state(''); + #fuse: Fuse | null = null; - filterTracks(tracks: Track[]) { - const fzf = new Fzf(tracks, { - selector: (t) => `${t.name} ${t.artistName}`, - sort: true + setTracks(tracks: Track[]) { + this.#fuse = new Fuse(tracks, { + keys: ['name', 'artistName'], + shouldSort: true, + includeScore: true, + threshold: 0.2, + findAllMatches: true }); - return fzf.find(this.input); + } + + filterTracks() { + if (this.input.length < 1 || !this.#fuse) { + return; + } + + const results = this.#fuse.search(this.input); + + return results.map((r) => r.item); } } diff --git a/src/routes/tracks/+page.svelte b/src/routes/tracks/+page.svelte index 1002c37..b2cf2f3 100644 --- a/src/routes/tracks/+page.svelte +++ b/src/routes/tracks/+page.svelte @@ -8,15 +8,22 @@ import Music2 from 'virtual:icons/lucide/music-2'; import ListMusic from 'virtual:icons/lucide/list-music'; import ListEnd from 'virtual:icons/lucide/list-end'; + import { getSearchState } from '$lib/search.svelte'; interface Props { data: PageServerData; } - const player = getPlayerState(); - let { data }: Props = $props(); + const player = getPlayerState(); + const search = getSearchState(); + + $effect(() => { + console.log('set tracks'); + search.setTracks(data.tracks); + }); + let contextMenuTarget = $state(null); const submitPlayTrack: SubmitFunction = async () => { @@ -53,6 +60,8 @@ function contextMenuItemClicked(e: MouseEvent) { e.stopPropagation(); } + + let filteredTracks = $derived(search.filterTracks());
- {#each data.tracks as track} + {#each filteredTracks ?? data.tracks as track}