The player dock used to sit fixed over the scrolling page, so it needed an
opaque background. The shell is now a fixed-height frame where only the
content column scrolls, letting the dock stay transparent like the sidebar
and queue. Scroll reset/restore on navigation moves to restoreScroll(), and
the stable scrollbar gutter moves from <html> to the content scroller.
Following lists are public; followers are visible to the user and to mutual followers.
Adds GET /users/{id}/followers and /following, exposes isFollowing and canViewFollowers
on the profile, and returns isFollowedByViewer from user search so the web player no
longer calls /is-following per user.
Discover used a hardcoded list of genre tiles that searched track titles,
and the upload form kept its own copy of the genre list and compatibility
rules. Both now come from the backend.
Backend:
- GET /genres returns genres with at least one track and their counts.
- GET /genres/available returns every assignable genre with the genres
it cannot be combined with, so the rules live only in
GenreCompatibility.
- GET /tracks accepts ?genre= (case-insensitive, 400 on unknown values).
web-player:
- Discover tiles are built from /genres; each opens /explore?genre=X, a
song list filtered by that tag. Unknown genres fall back to a label
derived from their name.
- The upload form loads /genres/available, disables incompatible genres
as you pick, and the server validates before reserving upload URLs.
- lib/data/trackGenres.ts is removed; its helpers merge into
lib/data/genres.ts.
- Regenerate the typed API client.
"Listening time this week" summed the full duration of every track whose
stream ticket was issued, so pressing play and skipping counted the whole
song. Playback is now measured on the client and reported to the API.
Backend:
- ListeningHistory gains PlayedSeconds, LastProgressAt and IsCounted
(migration TrackPlayedSeconds marks existing rows as counted).
- /tracks/{id}/stream returns a listenId; new
PUT /tracks/listens/{listenId}/progress stores the accumulated seconds,
capped by track duration and elapsed wall-clock time, monotonic and
owner-only.
- A listen counts once it reaches min(30s, 50% of the track). Weekly
stats, streak, recent history/albums, mixes and listensCount only use
counted listens; seconds sum PlayedSeconds.
- ListeningHistoryCleanupJob (daily) deletes uncounted listens older
than 8 days.
web-player:
- ListenTracker accumulates played time (seeks are ignored) and flushes
on pause, track change, ended, every 30s, and on pagehide.
- Proxy route /api/listens/[id]/progress; repeat starts a new listen per
loop.
0.65 was still too dark per feedback. The original concern that made
us drop accentL from 0.74 (accent surfaces stacking too bright over
the darkened spotlight/hero cards) is already fixed architecturally by
the earlier color-mix() conversion, which makes bg-accent-tint/
bg-accent-btn a solid precomputed value that no longer stacks with
whatever's underneath. So there's no reason to keep accentL dimmed for
that reason anymore.
0.55 (dropped from the original 0.74 to fix accent surfaces looking
too bright when stacked on the darkened spotlight/hero cards) made
bg-accent-tint on the plain page background barely visible. Split the
difference at 0.65, closer to how it looked originally.
--mf-accent-soft-bg/--mf-accent-btn-bg/-hover were translucent alpha
overlays, so an accent-tinted element (e.g. the active-row highlight,
or the accent Button) rendered differently depending on what was
already underneath it: fine over the plain page background, but over
an already-tinted surface like the spotlight card, the two translucent
layers stacked and the combined result read brighter/more saturated
than intended.
Same fix as bg-btn earlier: replace the alpha overlay with a solid
color-mix() against --mf-bg, computed once, so the token always
renders identically regardless of what it's placed on top of. Verified
the active-row highlight and the accent button now compute to the
exact same color whether they sit on the plain background or on the
spotlight card.
Per feedback, --mf-hero-bg/--mf-spotlight-bg in dark mode were still
too bright: drop their tc() base lightness from 42 to 38 and alpha
from 0.2 to 0.1, so the cards read as a subtle wash close to the page
background instead of a distinct bright block.
That made bg-accent-tint (Button's accent variant, ListRow/TrackList's
active-row highlight) look comparatively too light against the now
much darker cards, since accentL was still 0.74. Drop dark's accentL
to 0.55 so every accent-derived surface (rings, accent-tint, accent-btn
hover) darkens to match. Text (titleL, still 0.87) is untouched since
it needs to stay legible regardless of how dark the surface under it
gets.
Cleaned up the now-unused ACCENT_LIGHTNESS import/L constant.