web-player: rows (ListRow, MediaIdentity, EqBars)

Add MediaIdentity.svelte (renames and generalizes TrackTitleCell,
purely presentational: art + title + subtitle, no click handling)
and ListRow.svelte on top of it (href|onclick, active, size xs/sm/lg,
variant row|card, leading/trailing/overlay). Also used by
player/TrackInfo... (TrackInfo itself is left for a later pass, which
already has an explicit task to touch that file).

Migrated to ListRow: SearchResultRow (Explore, 4 groups — component
removed), Home (Continue listening, Popular, spotlight, Your
playlists), Queue (Up next), Sidebar (playlists). The 6 track tables
(AlbumTrackTable, PlaylistTrackTable, library, liked, mixes,
LibraryPicker) migrate to MediaIdentity with their own local
use:pressable, since TrackList doesn't exist yet.

EqBars gains an `overlay` prop (scrim/45 background, on-art color)
replacing NowPlaying.svelte (removed). Along the way, fixes 6
plural() calls that a previous pass had left unmigrated (liked,
library, mixes, playlists, PlaylistHeader, Home).

Deltas documented in the plan's log: Sidebar's active row now uses
the accent tint (previously neutral gray); Home's "Your playlists"
card loses its redundant author line and its radius becomes
rounded-art; Queue rows lose the art's opacity-80.

Verified with npm run check/build; no authenticated visual QA (Docker
unavailable this session).
This commit is contained in:
2026-09-21 00:14:20 +02:00
parent 14ee1dc45d
commit fb4a8ccc0e
19 changed files with 407 additions and 390 deletions
+21 -12
View File
@@ -3,7 +3,7 @@
import X from '@lucide/svelte/icons/x';
import { player, isQueueCurrent, playAllOrToggle } from '$lib/player/player.svelte';
import { liked } from '$lib/player/liked.svelte';
import { fmtDate, fmtTime } from '$lib/format';
import { fmtDate, fmtTime, plural } from '$lib/format';
import Page from '$lib/components/ui/Page.svelte';
import CollectionHeader from '$lib/components/ui/CollectionHeader.svelte';
import Button from '$lib/components/ui/Button.svelte';
@@ -13,7 +13,8 @@
import TrackRow from '$lib/components/ui/TrackRow.svelte';
import TrackMeta from '$lib/components/ui/TrackMeta.svelte';
import TrackIndexCell from '$lib/components/ui/TrackIndexCell.svelte';
import TrackTitleCell from '$lib/components/ui/TrackTitleCell.svelte';
import MediaIdentity from '$lib/components/ui/MediaIdentity.svelte';
import { pressable } from '$lib/actions/pressable';
const tracks = $derived(liked.list);
const isCurrentQueue = $derived(isQueueCurrent(tracks));
@@ -36,7 +37,7 @@
<CollectionHeader
eyebrow="Colección"
title="Me gusta"
meta="{tracks.length} {tracks.length === 1 ? 'Canción' : 'Canciones'}"
meta={plural(tracks.length, 'canción', 'canciones')}
align="center"
>
{#snippet cover()}
@@ -73,15 +74,23 @@
playing={player.playing}
onToggle={() => playFrom(i)}
/>
<TrackTitleCell
trackId={track.id}
title={track.title}
artist={track.artist}
ownerUserId={track.ownerUserId}
explicit={track.explicit}
{active}
onClick={() => playFrom(i)}
/>
<!-- svelte-ignore a11y_click_events_have_key_events -->
<div
role="button"
tabindex="0"
use:pressable={() => playFrom(i)}
onclick={(event) => event.stopPropagation()}
class="flex min-w-0 flex-1 text-left"
>
<MediaIdentity
trackId={track.id}
title={track.title}
artist={track.artist}
ownerUserId={track.ownerUserId}
explicit={track.explicit}
{active}
/>
</div>
<TrackMeta class="hidden sm:block">—</TrackMeta>
<TrackMeta class="hidden sm:block">{fmtDate(track.likedAt)}</TrackMeta>
<TrackMeta class="hidden sm:block">{Number(track.listensCount ?? 0)}</TrackMeta>