Web only, no migrations. dev CI is green on 918db27f (run 8814, image build included).
One track-list shape (M494). Album, playlist, Liked, History, Search and Search → Tracks draw rows with TrackListRow inside TrackList: title over "artist · album", like, radio, add, menu and duration, with striped rows.
Shared page shape. Album, playlist, Liked, History and Search → Tracks use the centred column. Album and playlist share CollectionHeader.
Play and Shuffle (PlayActions). Filled Play and outlined Shuffle on album, playlist and artist pages. The artist page's Play now plays in order; before, it shuffled.
Drag reorder snaps (#5393). A dropped row now lands in its slot. Playlist reorder is optimistic, and on playlists only the grip takes the touch gesture.
The playlist page no longer queues missing-file entries. It now uses the shared playlistTrackToRef.
Shelf headers lose the accent rule that Tailwind 4 started drawing (e16d1824).
Web only, no migrations. dev CI is green on 918db27f (run 8814, image build included).
- **One track-list shape (M494).** Album, playlist, Liked, History, Search and Search → Tracks draw rows with TrackListRow inside TrackList: title over "artist · album", like, radio, add, menu and duration, with striped rows.
- **Shared page shape.** Album, playlist, Liked, History and Search → Tracks use the centred column. Album and playlist share CollectionHeader.
- **Play and Shuffle (PlayActions).** Filled Play and outlined Shuffle on album, playlist and artist pages. The artist page's Play now plays in order; before, it shuffled.
- **Drag reorder snaps (#5393).** A dropped row now lands in its slot. Playlist reorder is optimistic, and on playlists only the grip takes the touch gesture.
- **The playlist page no longer queues missing-file entries.** It now uses the shared playlistTrackToRef.
- **Shelf headers lose the accent rule** that Tailwind 4 started drawing (e16d1824).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The rule after each shelf title used `after:bg-accent/60`. Tailwind 3
cannot put an opacity modifier on a `var()` colour, so it emitted nothing
for that class and the rule never showed. Tailwind 4 mixes the opacity
in with color-mix, so the rule appeared after the upgrade. Removed, so
the headers look as they did before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
HistoryRow now renders LikeButton, and both history test files imported
the component before the likes mock helper. LikeButton then loaded while
emptyLikesMock was still uninitialised, and the suites failed at import.
Every other suite imports components after its mocks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- CollectionHeader holds cover, title, facts, then Play and Shuffle, for
album and playlist. The playlist page gains Play and Shuffle; it had
neither. The cover uses the shared Cover component, so the playlist
header also gets the fallback image.
- PlayActions is the one Play and Shuffle pair: filled Play, outlined
Shuffle, on album, playlist and artist pages (operator, 2026-10-08).
The artist page's round icon button said Play but shuffled; Play now
keeps the server's order, and Shuffle shuffles.
- PageColumn is the playlist page's centred column, now used by album,
playlist, Liked, History and Search → Tracks.
- The playlist page builds its queue with playlistTrackToRef, the shared
converter, in place of its own copy. Its copy skipped only removed
tracks, so a missing file (#2527) could still be queued from there.
- shuffled() in lib/utils is the one shuffle, replacing three private
copies (album page, artist page, ArtistCard).
- The album's refetch error now uses text-error-fg. `text-oxblood` is
not a colour in the theme, so it never rendered.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Web only, no migrations. dev CI is green on
918db27f(run 8814, image build included).e16d1824).🤖 Generated with Claude Code