From 8cea298b2a7261321dcdabeae37d6c5658e5f93d Mon Sep 17 00:00:00 2001 From: XShine555 Date: Mon, 21 Sep 2026 00:21:15 +0200 Subject: [PATCH] web-player: track tables (TrackList) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add TrackList.svelte, replacing TrackTable+TrackRow+TrackIndexCell+ TrackMeta and the 6 track tables (library, liked, mix, album, playlist, album's library picker): declarative columns (added/uploaded/plays) with fixed widths as module constants, an index with an EQ indicator for the active track (or an overlay on the art when index={false}, for the library picker), and a rowAction that builds the form+IconButton (removeTrack/addTrack/deleteTrack) or a direct onclick (Like). AlbumTrackTable, PlaylistTrackTable and LibraryPicker are inlined into their pages and removed, along with the 4 components TrackList replaces. Decision (recommended by the plan itself, not blocking): remove the "Album" column, which in all 3 tables that had it always showed "—" (a placeholder with no real data). Verified with npm run check/build; no authenticated visual QA (Docker unavailable this session). --- web-player/docs/frontend-refactor-plan.md | 2 +- .../lib/components/ui/TrackIndexCell.svelte | 27 --- .../src/lib/components/ui/TrackList.svelte | 200 ++++++++++++++++++ .../src/lib/components/ui/TrackMeta.svelte | 14 -- .../src/lib/components/ui/TrackRow.svelte | 27 --- .../src/lib/components/ui/TrackTable.svelte | 79 ------- .../src/routes/(app)/albums/[id]/+page.svelte | 33 ++- .../[id]/components/AlbumTrackTable.svelte | 73 ------- .../[id]/components/LibraryPicker.svelte | 74 ------- .../src/routes/(app)/library/+page.svelte | 65 +----- .../src/routes/(app)/liked/+page.svelte | 71 ++----- .../src/routes/(app)/mixes/[id]/+page.svelte | 59 ++---- .../routes/(app)/playlists/[id]/+page.svelte | 16 +- .../[id]/components/PlaylistTrackTable.svelte | 80 ------- 14 files changed, 285 insertions(+), 535 deletions(-) delete mode 100644 web-player/src/lib/components/ui/TrackIndexCell.svelte create mode 100644 web-player/src/lib/components/ui/TrackList.svelte delete mode 100644 web-player/src/lib/components/ui/TrackMeta.svelte delete mode 100644 web-player/src/lib/components/ui/TrackRow.svelte delete mode 100644 web-player/src/lib/components/ui/TrackTable.svelte delete mode 100644 web-player/src/routes/(app)/albums/[id]/components/AlbumTrackTable.svelte delete mode 100644 web-player/src/routes/(app)/albums/[id]/components/LibraryPicker.svelte delete mode 100644 web-player/src/routes/(app)/playlists/[id]/components/PlaylistTrackTable.svelte diff --git a/web-player/docs/frontend-refactor-plan.md b/web-player/docs/frontend-refactor-plan.md index 02f9b8f..ef080f5 100644 --- a/web-player/docs/frontend-refactor-plan.md +++ b/web-player/docs/frontend-refactor-plan.md @@ -780,7 +780,7 @@ Tareas: | P5 | 🟡 | (pendiente) | Hecho: pressable/clickOutside, toggle.svelte.ts, plural() (+minúsculas, Δ visible), helpers de player.svelte.ts (toQueueItem/isQueueCurrent/playAllOrToggle/playShuffled, addToQueue→playNextItem), navigation.svelte.ts (searchHref/appNavLinks — appNavLinks creado pero su migración a Sidebar/TabsBar se deja para P12 tal como indica esa parte), menus.svelte.ts (createTrackMenu, migrado en Inicio/Explorar/Mezcla). **Pendiente a propósito**: la tarea 5 (eliminar el envoltorio `TrackTarget` de tracks.ts y sus 8 accesores) se difiere — es el cambio de mayor riesgo del plan (toca tipos en Inicio/Explorar/Mezcla/menús de forma transversal) y no se pudo verificar visualmente sin Docker; abordarlo junto con P13 cuando se reescriban esas páginas | | P6 | ✅ | (pendiente) | z-index de AccountMenu unificado a z-(--z-menu) (antes z-20, ahora al mismo nivel que ContextMenu — Δ menor, mejora consistencia); sin QA visual autenticada (Docker no disponible) | | P7 | ✅ | (pendiente) | MediaIdentity queda "puro" (sin onClick) según spec; las 6 tablas de pista (P8) envuelven su propio `use:pressable` hasta que P8 las sustituya por TrackList. Deltas documentados: fila activa de Sidebar ahora usa bg-accent-tint/text-accent-soft (antes gris neutro, ahora consistente con el resto de filas activas); tarjeta "Tus playlists" de Inicio pierde la línea "por {tu nombre}" (redundante, siempre el propio usuario) y su radio pasa de rounded-panel a rounded-art (consistencia con la tarjeta "Continuar"); filas de Queue "A continuación" pierden el opacity-80 del arte (ListRow no expone esa personalización). Sin QA visual autenticada (Docker no disponible) | -| P8 | ⬜ | | | +| P8 | ✅ | (pendiente) | Decisión tomada (recomendada por el propio plan, sin bloquear): se elimina la columna "Álbum" que siempre mostraba "—". TrackList unifica TrackTable+TrackRow+TrackIndexCell+TrackMeta; AlbumTrackTable/PlaylistTrackTable/LibraryPicker se inlinean en sus páginas y se borran. Sin QA visual autenticada (Docker no disponible) | | P9 | ⬜ | | | | P10 | ⬜ | | | | P11 | ⬜ | | | diff --git a/web-player/src/lib/components/ui/TrackIndexCell.svelte b/web-player/src/lib/components/ui/TrackIndexCell.svelte deleted file mode 100644 index f2bca7e..0000000 --- a/web-player/src/lib/components/ui/TrackIndexCell.svelte +++ /dev/null @@ -1,27 +0,0 @@ - - - diff --git a/web-player/src/lib/components/ui/TrackList.svelte b/web-player/src/lib/components/ui/TrackList.svelte new file mode 100644 index 0000000..1f99ffb --- /dev/null +++ b/web-player/src/lib/components/ui/TrackList.svelte @@ -0,0 +1,200 @@ + + + + +
+
+ {#if index} + + {/if} + Título + {#each columns as column (column)} + + {/each} + + {#if rowAction} + + {/if} +
+
+ {#each tracks as track, i (track.id)} + {@const active = player.current.id === track.id} + +
oncontextmenu(event, track, i) : undefined} + > + {#if index} + + {/if} + +
onPlay(i)} + onclick={(event) => event.stopPropagation()} + class="flex min-w-0 flex-1 text-left" + > + + {#snippet overlay()} + {#if !index && active} + + {/if} + {/snippet} + +
+ {#each columns as column (column)} + + {/each} + + {fmtTime(Number(track.duration))} + + {#if rowAction} + {#if rowAction.action} +
+ async ({ update }) => + update()} + > + + + + +
+ {:else} + rowAction.onclick?.(track)} + > + + + {/if} + {/if} +
+ {/each} +
+
diff --git a/web-player/src/lib/components/ui/TrackMeta.svelte b/web-player/src/lib/components/ui/TrackMeta.svelte deleted file mode 100644 index f330e5d..0000000 --- a/web-player/src/lib/components/ui/TrackMeta.svelte +++ /dev/null @@ -1,14 +0,0 @@ - - - - {@render children()} - diff --git a/web-player/src/lib/components/ui/TrackRow.svelte b/web-player/src/lib/components/ui/TrackRow.svelte deleted file mode 100644 index 444fa9d..0000000 --- a/web-player/src/lib/components/ui/TrackRow.svelte +++ /dev/null @@ -1,27 +0,0 @@ - - - -
- {@render children()} -
diff --git a/web-player/src/lib/components/ui/TrackTable.svelte b/web-player/src/lib/components/ui/TrackTable.svelte deleted file mode 100644 index a693b3d..0000000 --- a/web-player/src/lib/components/ui/TrackTable.svelte +++ /dev/null @@ -1,79 +0,0 @@ - - - - -
-
- {#if index} - - {/if} - Título - {#each meta as column (column.label)} - - {/each} - - {#if action} - - {/if} -
-
- {@render children()} -
-
diff --git a/web-player/src/routes/(app)/albums/[id]/+page.svelte b/web-player/src/routes/(app)/albums/[id]/+page.svelte index 52b0713..ced8b86 100644 --- a/web-player/src/routes/(app)/albums/[id]/+page.svelte +++ b/web-player/src/routes/(app)/albums/[id]/+page.svelte @@ -1,13 +1,15 @@ @@ -50,11 +60,26 @@ {/if} {#if tracks.length > 0} - + {/if} {#if isOwner && library.length > 0} - +
+ + +
{/if} diff --git a/web-player/src/routes/(app)/albums/[id]/components/AlbumTrackTable.svelte b/web-player/src/routes/(app)/albums/[id]/components/AlbumTrackTable.svelte deleted file mode 100644 index 437d332..0000000 --- a/web-player/src/routes/(app)/albums/[id]/components/AlbumTrackTable.svelte +++ /dev/null @@ -1,73 +0,0 @@ - - - - {#each tracks as track, i (track.id)} - {@const active = player.current.id === track.id} - - playFrom(i)} /> - -
playFrom(i)} - onclick={(event) => event.stopPropagation()} - class="flex min-w-0 flex-1 text-left" - > - -
- - {fmtTime(Number(track.duration))} - {#if isOwner} -
- async ({ update }) => - update()} - > - - - - -
- {/if} -
- {/each} -
diff --git a/web-player/src/routes/(app)/albums/[id]/components/LibraryPicker.svelte b/web-player/src/routes/(app)/albums/[id]/components/LibraryPicker.svelte deleted file mode 100644 index b35a965..0000000 --- a/web-player/src/routes/(app)/albums/[id]/components/LibraryPicker.svelte +++ /dev/null @@ -1,74 +0,0 @@ - - -
- - - {#each library as track, i (track.id)} - {@const active = player.current.id === track.id} - - -
playFrom(i)} - onclick={(event) => event.stopPropagation()} - class="flex min-w-0 flex-1 text-left" - > - - {#snippet overlay()} - {#if player.current.id === track.id} - - {/if} - {/snippet} - -
- - {fmtTime(Number(track.duration))} -
- async ({ update }) => - update()} - > - - - - -
-
- {/each} -
-
diff --git a/web-player/src/routes/(app)/library/+page.svelte b/web-player/src/routes/(app)/library/+page.svelte index df7866d..6c437e2 100644 --- a/web-player/src/routes/(app)/library/+page.svelte +++ b/web-player/src/routes/(app)/library/+page.svelte @@ -1,26 +1,20 @@ @@ -44,7 +50,13 @@ /> {#if tracks.length > 0} - + {:else} - import { enhance } from '$app/forms'; - import X from '@lucide/svelte/icons/x'; - import { player, toQueueItems, type ApiTrackLike } from '$lib/player/player.svelte'; - import { fmtTime, fmtDate } from '$lib/format'; - import IconButton from '$lib/components/ui/IconButton.svelte'; - import TrackTable from '$lib/components/ui/TrackTable.svelte'; - 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 MediaIdentity from '$lib/components/ui/MediaIdentity.svelte'; - import { pressable } from '$lib/actions/pressable'; - - type PlaylistTrack = ApiTrackLike & { - createdAt: string; - duration: number | string; - listensCount: number | string; - }; - - interface Props { - tracks: PlaylistTrack[]; - } - - let { tracks }: Props = $props(); - - function playFrom(index: number) { - player.playOrToggle(toQueueItems(tracks), index); - } - - - - {#each tracks as track, i (track.id)} - {@const active = player.current.id === track.id} - - playFrom(i)} {active} /> - -
playFrom(i)} - onclick={(event) => event.stopPropagation()} - class="flex min-w-0 flex-1 text-left" - > - -
- - - - {fmtTime(Number(track.duration))} -
- async ({ update }) => - update()} - > - - - - -
-
- {/each} -