- 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>
Merges Renovate's kit 3 (PR #149) and adapter-static 4 (PR #148) bumps,
plus the migration they need. The mechanical part is `sv migrate
sveltekit-3`, run one task at a time and reviewed:
- svelte.config.js is gone. Its options move into sveltekit() in
vite.config.ts, exported as kitOptions so vitest.config.ts runs the
same kit setup, including the $test-utils alias the tests import.
- $lib becomes #lib through package.json "imports". There is no
src/lib/index, so only the "#lib/*" entry is kept.
- tsconfig extends $app/tsconfig.
- Peer floors raised to kit 3's requirements: svelte ^5.57.1, vite
^8.0.12, svelte-check ^4.7.5.
By hand, from the codemod's list of non-automated tasks:
- goto's replaceState option is now replace; keepFocus becomes
reset: false. For the search typeahead, reset: false also stops the
scroll-to-top, which is wanted while typing.
- The test setup mocks drop pushState/replaceState and $app/paths
base/assets, which kit 3 removed, and mock refreshAll in place of
invalidateAll.
- The other flagged files only read page.url or goto internal routes,
so they needed no change.
TypeScript goes to ^6, not the ^7 Renovate offers: kit 3 declares
typescript ^6 as a peer and svelte-check 4.7 accepts ^5 || ^6. Move to
7 once both accept it.
With Tailwind 4 and kit 3 in, `npm audit` on the whole tree reports 0,
so the web lane now audits every dependency rather than only what
ships to browsers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cover tiles (worst in the "You might like" home row, which surfaces
unplayed items whose art is often not yet backfilled) sat empty while
loading and stayed blank on a 404. The server returns a fast 404; the
gap was missing client-side loading/fallback states.
Web: new shared Cover.svelte owns the loading placeholder + onerror
fallback (static cover, or Disc3 for artists). AlbumCard, ArtistCard and
CompactTrackCard now reuse it instead of three hand-rolled <img> tags
that disagreed on fallback handling — notably ArtistCard had no onerror.
Android: ServerImage tracks Coil's load state so the per-caller fallback
doubles as a placeholder (loading) and an error state (404 / unreachable),
instead of only guarding the null-URL case. All five call sites pass an
explicit size modifier, so the new Box wrapper is layout-safe.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
First wave of Home visual polish (UI tasks 78/79/81/85):
- CardActionCluster like/queue/menu cluster is now opacity-0 and fades
in on group-hover or focus-within. The group class moves from the
inner anchor to the outer card wrapper so the cluster (positioned
outside the anchor) participates in the same hover scope. PlaylistCard
refresh kebab gets the same treatment.
- AlbumCard drops the year line. Title plus artist is enough on Home
tiles; the album detail page still shows the year.
- HorizontalScrollRow h2 picks up a flex-baseline layout with a trailing
12-wide accent rule (after:bg-accent/60), so section headers read as
chapter breaks instead of identical plain text.
- AlbumCard art-wrap is shadow-sm by default and lifts to shadow-lg
plus ring-accent/40 on hover. Pairs with the existing
group-hover:scale-1.03 for a clean lift-on-mouseover feel.
The MediaCard consolidation in the discovery doc would have needed
~14 props to cover the differentiating bits (wrapping element a vs
button, art-shape, art-fallback strategy, click semantics, title
align/size, subtitle count). That's a god-prop blob that's harder to
reason about than three explicit cards.
Narrowed to the actually-drifted bit: the action cluster
(LikeButton + Plus + bottom-right menu slot, including the
stopPropagation defensive wrappers). One small component covers
AlbumCard / ArtistCard / CompactTrackCard so the focus-ring and
button-styling drift can't recur.
Each card keeps its own data-fetching, art container, and wrapping
element since those have load-bearing differences. The consolidation
addresses drift, not LOC for its own sake.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Disc3 fallback was rendered at fixed 32px regardless of card size,
so on a 144px (or larger) circular ArtistCard it floated in mostly-empty
space. Now sized via h-4/5 w-4/5 so it tracks the parent — ~80%
fill, much more recognisable as a record/disc. Stroke-width drops from
1.5 to 1 so the heavier line work doesn't overwhelm at the larger size.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>