From 521cafc5c41b7f2e5e9fa1ebce2840d33b1881ae Mon Sep 17 00:00:00 2001 From: XShine555 Date: Sun, 20 Sep 2026 22:39:05 +0200 Subject: [PATCH] =?UTF-8?q?web-player:=20P3=20=E2=80=94=20Artwork=20y=20Av?= =?UTF-8?q?atar=20unificados?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Crea src/lib/components/ui/Artwork.svelte y Avatar.svelte (B.3) y migra todos los usos de Cover, PlaylistArt, MixArt y ArtistAvatar (Sidebar, Queue, TrackInfo, TrackContextMenu, AlbumContextMenu, TrackTitleCell, AlbumCard, PlaylistCard, MixTile, AlbumHeader, PlaylistHeader, Home, Explorar, mezcla, perfil de usuario, TopBar, MobileHeader). Borra los 4 componentes viejos. Artwork: tamaño decide dimensión y radio automáticamente (tokens cover-*/radius de P1); cascada src → mosaico (≥4 trackIds) → primera carátula → fallback, con caída en cascada si cada paso falla (antes repartida en 3 componentes con lógica ligeramente distinta). Añade un tamaño 'fill' (aspect-square w-full) no descrito en la spec, necesario para las tarjetas de grid fluidas hasta que P9 introduzca MediaCard. TrackTitleCell cambia su prop `coverSize` (clases Tailwind libres) por `size: 'sm'|'lg'` delegando en Artwork. SearchResultRow pierde su prop `kind` y el radio por tipo (ahora lo decide Artwork/Avatar). Decisión de producto (P3, sin bloquear en pregunta): se quita el nombre bajo el avatar de perfil en /u/[id] por ser redundante con el título de CollectionHeader. Small deltas de radio/tamaño documentados en README del plan (auto- radio por tamaño reemplaza clases ad-hoc; ver tabla B.1.1/B.1.2). Verificado con npm run check/build; sin QA visual autenticada (Docker no disponible en esta sesión). --- web-player/docs/frontend-refactor-plan.md | 2 +- .../lib/components/AlbumContextMenu.svelte | 11 +- .../src/lib/components/MobileHeader.svelte | 11 +- web-player/src/lib/components/Sidebar.svelte | 13 +- web-player/src/lib/components/TopBar.svelte | 18 ++- .../lib/components/TrackContextMenu.svelte | 11 +- .../src/lib/components/player/Queue.svelte | 21 +-- .../lib/components/player/TrackInfo.svelte | 12 +- .../src/lib/components/ui/AlbumCard.svelte | 9 +- .../src/lib/components/ui/ArtistAvatar.svelte | 36 ----- .../src/lib/components/ui/Artwork.svelte | 133 ++++++++++++++++++ .../src/lib/components/ui/Avatar.svelte | 60 ++++++++ web-player/src/lib/components/ui/Cover.svelte | 47 ------- .../src/lib/components/ui/MixArt.svelte | 25 ---- .../src/lib/components/ui/MixTile.svelte | 23 +-- .../src/lib/components/ui/PlaylistArt.svelte | 61 -------- .../src/lib/components/ui/PlaylistCard.svelte | 10 +- .../lib/components/ui/SearchResultRow.svelte | 15 +- .../lib/components/ui/TrackTitleCell.svelte | 16 +-- .../albums/[id]/components/AlbumHeader.svelte | 9 +- .../src/routes/(app)/explore/+page.svelte | 76 ++++------ .../src/routes/(app)/mixes/[id]/+page.svelte | 9 +- .../[id]/components/PlaylistHeader.svelte | 12 +- .../src/routes/(app)/u/[id]/+page.svelte | 4 +- web-player/src/routes/+page.svelte | 41 +++--- 25 files changed, 326 insertions(+), 359 deletions(-) delete mode 100644 web-player/src/lib/components/ui/ArtistAvatar.svelte create mode 100644 web-player/src/lib/components/ui/Artwork.svelte create mode 100644 web-player/src/lib/components/ui/Avatar.svelte delete mode 100644 web-player/src/lib/components/ui/Cover.svelte delete mode 100644 web-player/src/lib/components/ui/MixArt.svelte delete mode 100644 web-player/src/lib/components/ui/PlaylistArt.svelte diff --git a/web-player/docs/frontend-refactor-plan.md b/web-player/docs/frontend-refactor-plan.md index 8dc5a0c..502a994 100644 --- a/web-player/docs/frontend-refactor-plan.md +++ b/web-player/docs/frontend-refactor-plan.md @@ -775,7 +775,7 @@ Tareas: | P0 | ✅ | (pendiente) | Button primary/secondary se mantienen iguales (decisión del usuario) | | P1 | ✅ | (pendiente) | Verificado con `build` (Tailwind compila `@apply sm:` dentro de `@utility`); sin QA visual autenticada (Docker no disponible en esta sesión) | | P2 | ✅ | (pendiente) | `` no interpola en Svelte (se trata como CSS del componente); solución: `{@html \`\`}` | -| P3 | ⬜ | | | +| P3 | ✅ | (pendiente) | Se quitó el nombre bajo el avatar de perfil (decisión tomada: redundante con el título de CollectionHeader). Se añadió `size="fill"` a Artwork (no está en la spec) para las tarjetas de grid fluidas (AlbumCard/PlaylistCard/MixTile), ya que P9 (MediaCard) aún no existe. SearchResultRow perdió su prop `kind`/radio-por-tipo (ahora el radio lo decide Artwork/Avatar). Sin QA visual autenticada (Docker no disponible) | | P4 | ⬜ | | | | P5 | ⬜ | | | | P6 | ⬜ | | | diff --git a/web-player/src/lib/components/AlbumContextMenu.svelte b/web-player/src/lib/components/AlbumContextMenu.svelte index d55609e..d0a8bd1 100644 --- a/web-player/src/lib/components/AlbumContextMenu.svelte +++ b/web-player/src/lib/components/AlbumContextMenu.svelte @@ -20,7 +20,7 @@ import ChevronRight from '@lucide/svelte/icons/chevron-right'; import ListPlus from '@lucide/svelte/icons/list-plus'; import ListEnd from '@lucide/svelte/icons/list-end'; - import PlaylistArt from '$lib/components/ui/PlaylistArt.svelte'; + import Artwork from '$lib/components/ui/Artwork.svelte'; import GlassMenu from '$lib/components/ui/GlassMenu.svelte'; interface Props { @@ -99,11 +99,10 @@ type="submit" class="flex w-full items-center gap-3 rounded-control px-2 py-2.5 text-left text-sm font-medium text-fg transition hover:bg-hover" > - {playlist.name} diff --git a/web-player/src/lib/components/MobileHeader.svelte b/web-player/src/lib/components/MobileHeader.svelte index 24aef1d..ae1c0c5 100644 --- a/web-player/src/lib/components/MobileHeader.svelte +++ b/web-player/src/lib/components/MobileHeader.svelte @@ -5,6 +5,7 @@ import LogIn from '@lucide/svelte/icons/log-in'; import MenuItem from './ui/MenuItem.svelte'; import AccountMenu from './ui/AccountMenu.svelte'; + import Avatar from './ui/Avatar.svelte'; interface Props { user: SessionUser | null; @@ -40,15 +41,7 @@ aria-expanded={open} class="flex items-center rounded-full" > - {#if user.picture} - - {:else} - - {user.name.charAt(0)} - - {/if} + {/snippet} {#snippet extraItems({ close })} diff --git a/web-player/src/lib/components/Sidebar.svelte b/web-player/src/lib/components/Sidebar.svelte index 1e2a797..8824a7b 100644 --- a/web-player/src/lib/components/Sidebar.svelte +++ b/web-player/src/lib/components/Sidebar.svelte @@ -4,7 +4,7 @@ import { liked } from '$lib/player/liked.svelte'; import { createPlaylistModal } from '$lib/playlists.svelte'; import type { SessionUser } from '$lib/types'; - import PlaylistArt from './ui/PlaylistArt.svelte'; + import Artwork from './ui/Artwork.svelte'; import Home from '@lucide/svelte/icons/house'; import Compass from '@lucide/svelte/icons/compass'; import ListMusic from '@lucide/svelte/icons/list-music'; @@ -97,12 +97,13 @@ ? 'bg-surface-hover' : 'hover:bg-hover'}" > -
diff --git a/web-player/src/lib/components/TopBar.svelte b/web-player/src/lib/components/TopBar.svelte index b8bc201..12566e6 100644 --- a/web-player/src/lib/components/TopBar.svelte +++ b/web-player/src/lib/components/TopBar.svelte @@ -8,6 +8,7 @@ import ChevronLeft from '@lucide/svelte/icons/chevron-left'; import ChevronRight from '@lucide/svelte/icons/chevron-right'; import AccountMenu from './ui/AccountMenu.svelte'; + import Avatar from './ui/Avatar.svelte'; interface Props { user: SessionUser | null; @@ -113,17 +114,14 @@ onclick={toggle} aria-label="Tu cuenta" aria-expanded={open} - class="block h-8.25 w-8.25 shrink-0 overflow-hidden rounded-full border border-line-strong transition active:scale-95" + class="block shrink-0 rounded-full transition active:scale-95" > - {#if user.picture} - - {:else} - - {user.name.charAt(0)} - - {/if} + {/snippet} diff --git a/web-player/src/lib/components/TrackContextMenu.svelte b/web-player/src/lib/components/TrackContextMenu.svelte index f8a03e5..b793914 100644 --- a/web-player/src/lib/components/TrackContextMenu.svelte +++ b/web-player/src/lib/components/TrackContextMenu.svelte @@ -21,7 +21,7 @@ import { enhance } from '$app/forms'; import ListPlus from '@lucide/svelte/icons/list-plus'; import ChevronRight from '@lucide/svelte/icons/chevron-right'; - import PlaylistArt from '$lib/components/ui/PlaylistArt.svelte'; + import Artwork from '$lib/components/ui/Artwork.svelte'; import GlassMenu from '$lib/components/ui/GlassMenu.svelte'; interface Props { @@ -91,11 +91,10 @@ type="submit" class="flex w-full items-center gap-3 rounded-control px-2 py-2.5 text-left text-sm font-medium text-fg transition hover:bg-hover" > - {playlist.name} diff --git a/web-player/src/lib/components/player/Queue.svelte b/web-player/src/lib/components/player/Queue.svelte index da41e33..9194270 100644 --- a/web-player/src/lib/components/player/Queue.svelte +++ b/web-player/src/lib/components/player/Queue.svelte @@ -1,7 +1,7 @@ - -
-
- {#if imageUrl} - - {:else} -
- {initials} - {/if} -
-
{name}
-
diff --git a/web-player/src/lib/components/ui/Artwork.svelte b/web-player/src/lib/components/ui/Artwork.svelte new file mode 100644 index 0000000..471b578 --- /dev/null +++ b/web-player/src/lib/components/ui/Artwork.svelte @@ -0,0 +1,133 @@ + + +
+ {#if showSrc} + (srcFailed = true)} + class="h-full w-full object-cover" + /> + {:else if showMosaic} +
+ {#each trackIds.slice(0, 4) as id (id)} + ((e.currentTarget as HTMLImageElement).style.visibility = 'hidden')} + class="h-full w-full object-cover" + /> + {/each} +
+ {:else if showSingle} + (singleFailed = true)} + class="h-full w-full object-cover" + /> + {:else if fallback === 'music'} +
+ + + +
+ {:else if fallback === 'gradient'} +
+ {:else} +
+ {/if} + {@render children?.()} +
diff --git a/web-player/src/lib/components/ui/Avatar.svelte b/web-player/src/lib/components/ui/Avatar.svelte new file mode 100644 index 0000000..d26b0b5 --- /dev/null +++ b/web-player/src/lib/components/ui/Avatar.svelte @@ -0,0 +1,60 @@ + + +
+ {#if src && !failed} + (failed = true)} + class="absolute inset-0 h-full w-full object-cover" + /> + {:else} + {initials} + {/if} +
diff --git a/web-player/src/lib/components/ui/Cover.svelte b/web-player/src/lib/components/ui/Cover.svelte deleted file mode 100644 index 8c97aa9..0000000 --- a/web-player/src/lib/components/ui/Cover.svelte +++ /dev/null @@ -1,47 +0,0 @@ - - - diff --git a/web-player/src/lib/components/ui/MixArt.svelte b/web-player/src/lib/components/ui/MixArt.svelte deleted file mode 100644 index f931d93..0000000 --- a/web-player/src/lib/components/ui/MixArt.svelte +++ /dev/null @@ -1,25 +0,0 @@ - - -{#if tiles.length >= 4} -
- {#each tiles as item (mixItemKey(item))} - - {/each} -
-{:else if tiles.length > 0} - -{:else} -
-{/if} diff --git a/web-player/src/lib/components/ui/MixTile.svelte b/web-player/src/lib/components/ui/MixTile.svelte index 5f7c540..591d6b7 100644 --- a/web-player/src/lib/components/ui/MixTile.svelte +++ b/web-player/src/lib/components/ui/MixTile.svelte @@ -1,6 +1,6 @@ - -{#if src && !coverFailed} -
- (coverFailed = true)} - class="h-full w-full object-cover" - /> -
-{:else if mosaic} -
- {#each trackIds.slice(0, 4) as id (id)} - - {/each} -
-{:else if single !== null} - -{:else} -
- - - -
-{/if} diff --git a/web-player/src/lib/components/ui/PlaylistCard.svelte b/web-player/src/lib/components/ui/PlaylistCard.svelte index bf00bf7..6950690 100644 --- a/web-player/src/lib/components/ui/PlaylistCard.svelte +++ b/web-player/src/lib/components/ui/PlaylistCard.svelte @@ -1,6 +1,6 @@ - {#snippet content()} diff --git a/web-player/src/lib/components/ui/TrackTitleCell.svelte b/web-player/src/lib/components/ui/TrackTitleCell.svelte index 8c6f0f8..14dd3d8 100644 --- a/web-player/src/lib/components/ui/TrackTitleCell.svelte +++ b/web-player/src/lib/components/ui/TrackTitleCell.svelte @@ -1,6 +1,6 @@