feat(web): one track-list row shape, and drag reorder that snaps (M494, #5393)
release / govulncheck (push) Successful in 34s
release / web (push) Failing after 1m20s
release / go (push) Successful in 1m37s
release / Attach APK to the Release (tag releases only) (push) Canceled after 0s
release / Build + push container image (push) Canceled after 0s
release / Verify release artifacts (tag releases only) (push) Canceled after 0s
release / integration (push) Canceled after 4m38s
release / android (push) Canceled after 4m39s
release / Build signed APK (releases and dev) (push) Canceled after 3m49s

Every track list (album, Liked, Search, Search → Tracks, playlist,
History) now draws its rows with TrackListRow inside a TrackList box,
in place of four row components that had drifted apart:

- TrackListRow owns the look: lead column, title over "artist · album",
  like, radio, add to queue, menu, duration, then a trailing slot. It
  also owns the stripe, hover, the now-playing band and the
  unavailable state. The stripe is dropped on now-playing and selected
  rows, where Tailwind would otherwise let `odd:` win.
- TrackRow, PlaylistTrackRow and HistoryRow are wrappers that keep only
  their behaviour: track number and multi-select; grip, remove and
  dead-entry states; cover and timestamp. Playlist and History rows
  gain radio, add and the menu, so every list offers the same actions.
- The radio control is Lucide's Radio icon, not the 📻 emoji.
- Enter on a control inside a row now belongs to that control. The old
  TrackRow played the track when Enter was pressed on "add to queue".
- TrackList has no fill, so the stripe shows on playlists too.

Drag reorder (#5393): neodrag kept the drop offset, so after the list
re-rendered the dragged row sat between two slots. reorderDrag, shared
by the playlist and queue rows:
- snaps the row to whole rows while dragging;
- resets it to its slot on drop;
- on playlists, gives the touch gesture to the grip only, so the list
  still scrolls by touch.
The playlist page reorders optimistically and rolls back on error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 14:14:06 -04:00
co-authored by Claude Opus 5.5
parent e16d182417
commit b7f50c42a9
20 changed files with 666 additions and 241 deletions
+3 -2
View File
@@ -5,6 +5,7 @@
import ArtistCard from '#lib/components/ArtistCard.svelte';
import AlbumCard from '#lib/components/AlbumCard.svelte';
import TrackRow from '#lib/components/TrackRow.svelte';
import TrackList from '#lib/components/TrackList.svelte';
import SearchSkeleton from '#lib/components/SearchSkeleton.svelte';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import { useDelayed } from '#lib/utils/useDelayed.svelte.js';
@@ -104,11 +105,11 @@
</a>
{/if}
</header>
<div class="overflow-hidden rounded border border-border">
<TrackList>
{#each tracks.items as t, i (t.id)}
<TrackRow tracks={tracks.items} index={i} onPlay={onTrackPlay} />
{/each}
</div>
</TrackList>
</section>
{/if}
{/if}