search: the results page, the typeahead, and a session that survives a reload
The page is the signed-off prototype built out: floating rounded hero, inline top-tracks panel, filter chips, two-column Songs|Albums beside Artists, and all seven states — results, searching, no results, one-kind-only, library-empty, provider-unreachable, first visit. MusicBrainz records are merged in silently behind the Deezer rows. The top result is ranked by POPULARITY, not by kind: each kind puts up its most popular exact match and the winner is whichever holds the largest share of its own kind's leader. A raw fans-vs-rank comparison was tried first and handed "panda bear" to an Owl City song. Two provenance traps are paid for here. Deezer's /search/album returns no release_date at all, so sorting newest-first ordered by PROVENANCE rather than by date; dates are backfilled from the artist overview, resolved PER ALBUM ARTIST because Deezer's artist search answers nothing for "weezer blue album". And the typeahead holds its keyboard selection as the row's KEY rather than its index, because the MusicBrainz supplement lands ~600ms later and re-orders the album group under the cursor. The hero's middle third is hidden on the width of the PANE, not the window. The sidebar and the now-playing rail take ~490px between them, so a 1504px window can leave the hero 1012px — a window-width media query cannot see that, and the "latest release" card ended up sheared down to 26px of itself. The budget is written out at the @container rule. Session restore rounds it off: reload or restart returns to the same place with the same track, everything but the transport coming from localStorage. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7547fed56d
commit
b173f60085
6 files changed
+3315
-493
No files matched your search
@@ -77,6 +77,10 @@ export const playLocal = (path, duration) => invoke("play_local", { path, durati
|
||||
export const preloadLocal = (path, duration) => invoke("preload_local", { path, duration });
|
||||
export const playerControl = (action, value, rate) =>
|
||||
invoke("player_control", { action, value: value ?? null, rate: rate ?? null });
|
||||
// What the audio thread is doing right now. The webview only ever learns about
|
||||
// playback from events, so after a reload this is the only way to find out that
|
||||
// audio is still running — see `restoreSession`.
|
||||
export const playerState = () => invoke("player_state");
|
||||
export const waveform = (path, buckets) => invoke("waveform", { path, buckets });
|
||||
export const libraryAlbums = () => invoke("library_albums");
|
||||
export const albumTracks = (albumId) => invoke("album_tracks", { albumId });
|
||||
@@ -149,6 +153,15 @@ export const catalogArtistPopular = (artistId) =>
|
||||
export const pickArtistHero = (artistId) => invoke("pick_artist_hero", { artistId });
|
||||
export const clearArtistHero = (artistId) => invoke("clear_artist_hero", { artistId });
|
||||
export const catalogRelated = (artistId) => invoke("catalog_related", { artistId });
|
||||
// Deferred Last.fm pass for search results: `tracks` is [{ artistId, title }]
|
||||
// and the answer comes back in the same order. Kept OUT of catalog_search,
|
||||
// which the typeahead calls per keystroke and which must never wait on
|
||||
// MusicBrainz's 1 req/s gate.
|
||||
export const catalogTrackListeners = (tracks) =>
|
||||
invoke("catalog_track_listeners", { tracks });
|
||||
// Genres + Last.fm totals for the search hero's artist. Deferred, like the rest.
|
||||
export const catalogArtistProfile = (artistId) =>
|
||||
invoke("catalog_artist_profile", { artistId });
|
||||
export const catalogAlbum = (albumId) => checkedAlbumPayload(albumId);
|
||||
export const catalogAlbumInfo = (artistId, artist, title) =>
|
||||
invoke("catalog_album_info", { artistId: artistId ?? null, artist, title });
|
||||
|
||||
Reference in new issue
Block a user