diff --git a/web-player/CLAUDE.md b/web-player/CLAUDE.md index 444ea62..4b8700d 100644 --- a/web-player/CLAUDE.md +++ b/web-player/CLAUDE.md @@ -50,7 +50,7 @@ The design system lives in `src/routes/layout.css` (`@theme` tokens plus `@utili - **Radii:** always `rounded-tag/thumb/control/art/art-lg/panel/panel-lg` (or `rounded-full` for pills and circles). Never `rounded-sm/md/lg/xl/2xl/3xl` or a bare `rounded`. - **Square sizes:** use `size-*` instead of repeating `h-N w-N` (icons: `size-icon-xs/sm/md/lg/xl`; covers: `size-cover-xs` through `size-cover-hero`). -- **Typography:** use the recipes `text-display-1/2/3/4`, `text-eyebrow`, `text-body`, `text-count`, with tracking through `tracking-display`/`tracking-eyebrow` rather than a loose `tracking-[…]`. +- **Typography:** use the recipes `text-display-1/2/3/4`, `text-eyebrow`, `text-body`, with tracking through `tracking-display`/`tracking-eyebrow` rather than a loose `tracking-[…]`. - **Spacing:** stick to the Tailwind scale in `.5` steps and avoid quarters like `.25`/`.75`, unless the semantic token calls for it (for example `mb-4.5` in `SectionHeading`). - **Colors:** always use `--mf-*`/`text-*`/`bg-*`/`border-*` from `theme.css`/`tokens.ts`. Never a literal hex or `rgba()` inside a `.svelte` file; those literals only belong in `theme.css`/`tokens.ts` as the source of truth. - **Inline `style="…"` and arbitrary `-[…]` values:** only the ones whitelisted in `scripts/check-tokens.mjs` (the stagger variable `--i`, tile hue, menu position, progress width, `text-[clamp(…)]`, `leading-[…]` on login's display type, `grid-cols-[auto_1fr]`, `max-h-[85dvh]`, `my-`/`py-[Ndvh]` for vertical centering, `transition-[filter]` on genre tiles, and `bg-[image:var(--mf-*)]`). Everything else should become a token. diff --git a/web-player/src/lib/components/layout/Sidebar.svelte b/web-player/src/lib/components/layout/Sidebar.svelte index dfdc201..144970c 100644 --- a/web-player/src/lib/components/layout/Sidebar.svelte +++ b/web-player/src/lib/components/layout/Sidebar.svelte @@ -69,7 +69,7 @@ {#if visiblePlaylists.length > 0} createPlaylistModal.show()} > @@ -96,7 +96,7 @@ diff --git a/web-player/src/lib/player/player.svelte.ts b/web-player/src/lib/player/player.svelte.ts index bb71d35..8869725 100644 --- a/web-player/src/lib/player/player.svelte.ts +++ b/web-player/src/lib/player/player.svelte.ts @@ -1,6 +1,6 @@ import { browser } from '$app/environment'; import { DEFAULT_ACCENT } from '$lib/theme/color'; -import { extractAccent, type Accent } from '$lib/theme/palette'; +import { extractAccent } from '$lib/theme/palette'; import { shuffle } from '$lib/data/collections'; import { dialog } from '$lib/state/dialog.svelte'; import { ListenTracker } from './listenTracker'; @@ -17,12 +17,6 @@ export interface PlayerTrack { listensCount?: number | string; } -export interface Playlist { - id: string | number; - name: string; - trackIds: (string | number)[]; -} - export interface QueueItem { id: string | number; title: string; @@ -139,7 +133,6 @@ class PlayerState { muted = $state(browser ? readStorage(MUTED_KEY) === 'true' : false); loading = $state(false); recentlyPlayed = $state([]); - playlists = $state([]); shuffle = $state(false); repeat = $state(false); @@ -147,7 +140,7 @@ class PlayerState { #audio: HTMLAudioElement | null = null; #loadToken = 0; - #accentCache = new Map(); + #accentCache = new Map(); #rafId: number | null = null; #listen = new ListenTracker(); @@ -157,10 +150,6 @@ class PlayerState { this.current.duration > 0 ? Math.min(100, (this.progress / this.current.duration) * 100) : 0 ); - get isPlaying() { - return this.playing; - } - #audioEl(): HTMLAudioElement | null { if (!browser) return null; if (this.#audio) return this.#audio; @@ -301,14 +290,14 @@ class PlayerState { const key = String(id); const cached = this.#accentCache.get(key); if (cached) { - this.accentColor = cached.accent; + this.accentColor = cached; return; } const result = await extractAccent(`/api/tracks/${id}/cover?size=small`); if (this.currentId !== id) return; if (result) { this.#accentCache.set(key, result); - this.accentColor = result.accent; + this.accentColor = result; } else { this.accentColor = null; } @@ -395,12 +384,6 @@ class PlayerState { this.#syncMediaSessionMetadata(track); } - playlistTracks(ids: (string | number)[]): PlayerTrack[] { - return ids - .map((id) => this.tracks.find((t) => t.id === id)) - .filter((t): t is PlayerTrack => Boolean(t)); - } - playQueue(list: QueueItem[], startIndex = 0) { if (list.length === 0) return; this.tracks = list.map(toTrack); @@ -415,10 +398,6 @@ class PlayerState { else this.playQueue(list, index); } - playNextItem(item: QueueItem) { - this.playNext([item]); - } - playNext(items: QueueItem[]) { if (items.length === 0) return; if (this.currentId === null) { @@ -452,13 +431,6 @@ class PlayerState { this.#loadCurrent(); } - playPlaylist(id: string | number) { - const playlist = this.playlists.find((p) => p.id === id); - if (!playlist) return; - const items = this.playlistTracks(playlist.trackIds); - if (items.length > 0) this.playQueue(items, 0); - } - toggle() { const audio = this.#audioEl(); if (!audio || this.currentId === null) return; diff --git a/web-player/src/lib/server/auth.ts b/web-player/src/lib/server/auth.ts index 7df84c1..b17178e 100644 --- a/web-player/src/lib/server/auth.ts +++ b/web-player/src/lib/server/auth.ts @@ -51,7 +51,7 @@ function sessionKey(): Promise { return keyPromise; } -export function sessionCookieOptions(url: URL) { +function sessionCookieOptions(url: URL) { return { httpOnly: true, sameSite: 'lax' as const, diff --git a/web-player/src/lib/state/menus.svelte.ts b/web-player/src/lib/state/menus.svelte.ts index af9000f..5a34e31 100644 --- a/web-player/src/lib/state/menus.svelte.ts +++ b/web-player/src/lib/state/menus.svelte.ts @@ -21,7 +21,7 @@ export function createTrackMenu() { }, playNext() { if (!state) return; - player.playNextItem(toQueueItem(state.track)); + player.playNext([toQueueItem(state.track)]); state = null; } }; diff --git a/web-player/src/lib/theme/palette.ts b/web-player/src/lib/theme/palette.ts index b3a7381..39c1f00 100644 --- a/web-player/src/lib/theme/palette.ts +++ b/web-player/src/lib/theme/palette.ts @@ -1,11 +1,6 @@ import { browser } from '$app/environment'; import { ACCENT_LIGHTNESS } from './color'; -export interface Accent { - accent: string; - gradient: string; -} - function srgbToLinear(channel: number): number { const c = channel / 255; return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4); @@ -45,7 +40,7 @@ function loadImage(url: string): Promise { const BUCKETS = 36; -export async function extractAccent(url: string): Promise { +export async function extractAccent(url: string): Promise { if (!browser) return null; let img: HTMLImageElement; @@ -104,11 +99,6 @@ export async function extractAccent(url: string): Promise { const h = hue.toFixed(1); const c = chroma.toFixed(3); - const c2 = (chroma * 0.85).toFixed(3); - const h2 = ((hue + 25) % 360).toFixed(1); - return { - accent: `oklch(${ACCENT_LIGHTNESS}% ${c} ${h})`, - gradient: `linear-gradient(135deg, oklch(74% ${c} ${h}), oklch(56% ${c2} ${h2}))` - }; + return `oklch(${ACCENT_LIGHTNESS}% ${c} ${h})`; } diff --git a/web-player/src/routes/(app)/explore/+page.server.ts b/web-player/src/routes/(app)/explore/+page.server.ts index d7904c4..ee7609c 100644 --- a/web-player/src/routes/(app)/explore/+page.server.ts +++ b/web-player/src/routes/(app)/explore/+page.server.ts @@ -1,7 +1,6 @@ import type { PageServerLoad, Actions } from './$types'; import { createApiClient, unwrapOrError } from '$lib/server/api'; import { addAlbumToPlaylistAction, addTrackAction } from '$lib/server/playlistActions'; -import { followUserAction, unfollowUserAction } from '$lib/server/followActions'; import { EXPLORE_ALBUMS_PAGE_SIZE, EXPLORE_PAGE_SIZE as PAGE_SIZE, @@ -66,7 +65,5 @@ export const load: PageServerLoad = async ({ url, locals, fetch }) => { export const actions: Actions = { addTrack: addTrackAction, - addAlbumToPlaylist: addAlbumToPlaylistAction, - follow: followUserAction, - unfollow: unfollowUserAction + addAlbumToPlaylist: addAlbumToPlaylistAction }; diff --git a/web-player/src/routes/(app)/user/[id]/[list=followList]/+page.server.ts b/web-player/src/routes/(app)/user/[id]/[list=followList]/+page.server.ts index 66dd166..82c6eb7 100644 --- a/web-player/src/routes/(app)/user/[id]/[list=followList]/+page.server.ts +++ b/web-player/src/routes/(app)/user/[id]/[list=followList]/+page.server.ts @@ -1,7 +1,6 @@ -import type { PageServerLoad, Actions } from './$types'; +import type { PageServerLoad } from './$types'; import { createApiClient, optionalUser, unwrapOrError } from '$lib/server/api'; import { fetchFollowList } from '$lib/server/follows'; -import { followUserAction, unfollowUserAction } from '$lib/server/followActions'; import { FOLLOW_LIST_PAGE_SIZE } from '$lib/config'; export const load: PageServerLoad = async ({ params, locals, url, fetch, parent }) => { @@ -25,8 +24,3 @@ export const load: PageServerLoad = async ({ params, locals, url, fetch, parent viewerId: viewer?.sub ?? null }; }; - -export const actions: Actions = { - follow: followUserAction, - unfollow: unfollowUserAction -}; diff --git a/web-player/src/routes/(app)/user/[id]/[list=followList]/+page.svelte b/web-player/src/routes/(app)/user/[id]/[list=followList]/+page.svelte index 7dde364..505103b 100644 --- a/web-player/src/routes/(app)/user/[id]/[list=followList]/+page.svelte +++ b/web-player/src/routes/(app)/user/[id]/[list=followList]/+page.svelte @@ -5,12 +5,11 @@ import PageHeader from '$lib/components/ui/layout/PageHeader.svelte'; import EmptyState from '$lib/components/ui/primitives/EmptyState.svelte'; import InfiniteScroll from '$lib/components/ui/primitives/InfiniteScroll.svelte'; - import Alert from '$lib/components/ui/primitives/Alert.svelte'; import UserRow from '$lib/components/ui/media/UserRow.svelte'; import { appendUnique } from '$lib/data/collections'; import { plural } from '$lib/utils/format'; - let { data, form } = $props(); + let { data } = $props(); type ListUser = NonNullable['items'][number]; @@ -62,10 +61,6 @@ : plural(total, 'usuario', 'usuarios')} /> - {#if form?.message} - {form.message} - {/if} - {#if data.forbidden} { redirect(302, `/auth?returnTo=${encodeURIComponent(url.pathname + url.search)}`); } - const accountUrl = authConfig.issuer - ? `${authConfig.issuer.replace(/\/+$/, '')}/ui/console` - : null; + const accountUrl = `${authConfig.issuer.replace(/\/+$/, '')}/ui/console`; if (!locals.user) { return { diff --git a/web-player/src/routes/layout.css b/web-player/src/routes/layout.css index 23d1ddf..dc6de81 100644 --- a/web-player/src/routes/layout.css +++ b/web-player/src/routes/layout.css @@ -82,14 +82,6 @@ } } -@utility media-grid { - grid-template-columns: repeat(auto-fill, minmax(var(--mf-grid-min-mobile), 1fr)); - - @media (width >= 40rem) { - grid-template-columns: repeat(auto-fill, minmax(var(--mf-grid-min), 1fr)); - } -} - @utility track-grid { grid-template-columns: var(--mf-track-cols-mobile); @@ -116,10 +108,6 @@ @utility text-body { @apply text-sm leading-body; } -@utility text-count { - @apply text-xs tabular-nums; -} - @utility btn-transition { transition-property: color, background-color, border-color, outline-color, opacity, box-shadow, filter;