web-player: P7 — filas (ListRow, MediaIdentity, EqBars)

Crea MediaIdentity.svelte (renombra y generaliza TrackTitleCell,
puramente presentacional: arte + título + subtítulo, sin manejo de
click) y ListRow.svelte encima (href|onclick, active, size xs/sm/lg,
variant row|card, leading/trailing/overlay). Usa MediaIdentity también
en player/TrackInfo... (pendiente: TrackInfo se deja para P12, que ya
tiene tarea explícita de tocar ese archivo).

Migra a ListRow: SearchResultRow (Explorar, 4 grupos — se borra el
componente), Inicio (Continuar escuchando, Populares, spotlight, Tus
playlists), Queue (A continuación), Sidebar (playlists). Las 6 tablas
de pista (AlbumTrackTable, PlaylistTrackTable, library, liked, mixes,
LibraryPicker) migran a MediaIdentity con su propio use:pressable
local, ya que TrackList (P8) todavía no existe.

EqBars gana prop `overlay` (fondo scrim/45, color on-art) que
sustituye a NowPlaying.svelte (borrado). Corrige de paso 6 llamadas a
plural() que P5 había dejado sin migrar (liked, library, mixes,
playlists, PlaylistHeader, Inicio).

Deltas documentados en el registro del plan: fila activa de Sidebar
ahora usa el tinte de acento (antes gris neutro); tarjeta "Tus
playlists" de Inicio pierde la línea de autor redundante y su radio
pasa a rounded-art; filas de Queue pierden el opacity-80 del arte.

Verificado con npm run check/build; sin QA visual autenticada (Docker
no disponible en esta sesión).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-21 00:14:20 +02:00
co-authored by Claude Sonnet 5
parent a0c9c5fca9
commit 5a528e197e
19 changed files with 407 additions and 390 deletions
@@ -0,0 +1,90 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import MediaIdentity from './MediaIdentity.svelte';
import { pressable } from '$lib/actions/pressable';
interface Props {
href?: string;
onclick?: () => void;
oncontextmenu?: (event: MouseEvent) => void;
active?: boolean;
size?: 'xs' | 'sm' | 'lg';
variant?: 'row' | 'card';
title: string;
subtitle?: string | null;
subtitleHref?: string | number | null;
explicit?: boolean;
trackId?: string | number;
trackIds?: (string | number)[];
coverSrc?: string | null;
art?: Snippet;
overlay?: Snippet;
leading?: Snippet;
trailing?: Snippet;
class?: string;
style?: string;
}
let {
href,
onclick,
oncontextmenu,
active = false,
size = 'sm',
variant = 'row',
title,
subtitle,
subtitleHref,
explicit = false,
trackId,
trackIds,
coverSrc,
art,
overlay,
leading,
trailing,
class: klass = '',
style
}: Props = $props();
const classes = $derived(
`flex items-center gap-3 text-left transition-colors ${
variant === 'card' ? 'rounded-art p-2.25 pr-3.5' : 'rounded-control p-2'
} ${active ? 'bg-accent-tint' : 'hover:bg-hover'} ${klass}`
);
</script>
{#snippet content()}
{@render leading?.()}
<MediaIdentity
{title}
artist={subtitle}
ownerUserId={subtitleHref}
{trackId}
{trackIds}
{coverSrc}
{size}
{explicit}
{active}
{art}
{overlay}
/>
{@render trailing?.()}
{/snippet}
{#if href}
<a {href} {oncontextmenu} class={classes} {style}>
{@render content()}
</a>
{:else}
<div
role="button"
tabindex="0"
use:pressable={onclick ?? (() => {})}
{oncontextmenu}
class={classes}
{style}
>
{@render content()}
</div>
{/if}