now playing: the transfer moves into the bar, and the panel speaks the app's headings

The hover popover is deleted. It showed the peer, the rate, a percentage and
a second progress bar — every one of which the status line and the hairline
beside it already carry — so it was a duplicate you had to hover for. The two
things unique to it move up: the save action into the bar, and nothing else.
Deleted with it, the reveal action, its 250ms close grace and the
`.np-statuswrap` hit area, all of which existed only to make a popover
reachable across the 25px gap between the status line and itself.

Release sections were the one heading in the app that was 13px, Title Case,
full white on an opaque sticky bar; every other one — `.lab` here,
`.disc-head` and `.rail-lab` on the catalog pages — is 11px uppercase muted.
They now match, with the hairline rule the catalog pages use, and they no
longer pin: the bar existed only to hide rows sliding under a sticky heading.

Those headings sit on the blurred record, not a flat page, so `--text-muted`
and #5c5c5c were chosen against a background that is not there. Each carries
a dark plate now, years get the album page's over-artwork treatment, and the
hairline goes 0.07 -> 0.14, which over a bright cover was the difference
between a faint line and no line.

"From the album" rendered `player.queue`, which the album fill grows ONE
TRACK AT A TIME as each Soulseek source resolves — a nine-track record read
"5 tracks" while you watched the rest arrive. It renders the record's own
tracklist now, matched to queue items by provider id, and a row with no
source yet resolves on demand when clicked.

The About pane gains a record head, a lede, stat tiles, pills and a source
line, all re-laid-out from data already in hand. The artist page's expander
gets the same lede and pills — no record head or tiles there, because its
rail already carries them.
This commit is contained in:
KeelrLeek committed 2026-07-29 18:46:31 -05:00
1 parent a6b585303e
commit 3f4f5c305a
2 files changed
+459 -185

No files matched your search

+84 -8
View File
@@ -38,6 +38,7 @@
editionLabel,
isEdition,
releaseFamilies,
splitDescription,
} from "../lib/editions.js";
import { listenerHeat, listenerPeak, listenerShare } from "../lib/listenerBars.js";
import { buildArtistManifest } from "../lib/preview/manifest.js";
@@ -2450,6 +2451,27 @@
return sized;
}
/** Lede + body for the open record, from the shared splitter. */
const railDesc = $derived(splitDescription(openInfo?.description));
/** What the expander is already showing, handed to the album page so its
* skeleton opens holding this record's artwork instead of a grey block.
* `railCover` on purpose: that exact URL is the one already decoded and in
* memory for the rail, so the seeded cover costs no request and cannot
* flash on its way in. */
function albumSeed(album) {
if (!album?.cover || artBroken[album.cover]) return null;
return {
cover: railCover(album.cover),
title: album.title ?? null,
artist: album.artist ?? page?.artist?.name ?? null,
kind: album.record_type ?? null,
// The open expander has already fetched the real tracks; its count
// beats the card's metadata, which is missing on some rows.
tracks: openTracks.length || album.track_count || null,
};
}
function handleCoverError(node, originalCover, title = "") {
const fallback = coverLoadError(
originalCover,
@@ -4746,7 +4768,10 @@
<button
class="rail-title-link"
title="Open album"
onclick={() => openAlbumId != null && openCatalogAlbum(openAlbumId)}
onclick={() => openAlbumId != null
&& openCatalogAlbum(openAlbumId, {
seed: albumSeed(railArtAlbum ?? openAlbum),
})}
>{openAlbum?.title ?? openFamily?.baseTitle ?? ""}</button>
</h3>
<p class="rail-meta">
@@ -4787,13 +4812,31 @@
</div>
{/if}
{#if openInfo?.description}
<p class="rail-desc" class:clamped={!descExpanded} in:partIn={{ delay: 60 }}>{openInfo.description}</p>
<button
class="rail-more"
class:open={descExpanded}
in:partIn={{ delay: 85 }}
onclick={() => (descExpanded = !descExpanded)}
>{descExpanded ? "Show less" : "Show more"} <Icon name="chevron-down" size={13} /></button>
<!-- The description's own first
sentence, promoted. Same
splitter the album page and
the Now Playing panel use;
nothing is written. The body
keeps its 4-line clamp and
its mask fade. -->
{#if railDesc.lede}
<p class="rail-lede" in:partIn={{ delay: 55 }}>{railDesc.lede}</p>
{/if}
{#if railDesc.rest}
<p class="rail-desc" class:clamped={!descExpanded} in:partIn={{ delay: 60 }}>{railDesc.rest}</p>
<button
class="rail-more"
class:open={descExpanded}
in:partIn={{ delay: 85 }}
onclick={() => (descExpanded = !descExpanded)}
>{descExpanded ? "Show less" : "Show more"} <Icon name="chevron-down" size={13} /></button>
{/if}
{#if openInfo.genre || openInfo.label}
<div class="rail-pills" in:partIn={{ delay: 100 }}>
{#if openInfo.genre}<span class="gpill accent">{openInfo.genre}</span>{/if}
{#if openInfo.label}<span class="gpill">{openInfo.label}</span>{/if}
</div>
{/if}
{/if}
{#if openFamily && openFamily.editions.length > 1}
<div class="rail-eds">
@@ -7904,6 +7947,39 @@
/* Clamped description dissolves at its bottom edge; the quiet text button
below expands it, its chevron rotating. */
/* The promoted first sentence. Only a little larger than the body: the rail
is 250px wide and the title above it is already 17px, so a bigger lede
would compete with the record's own name. */
.rail-lede {
margin: 0;
font-size: 14px;
font-weight: 600;
line-height: 1.5;
color: rgba(255, 255, 255, 0.9);
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
}
/* Genre and label only — the date is already in `.rail-meta` directly
above, and repeating it cost a second wrapped row in a 210px column. */
.rail-pills {
display: flex;
flex-wrap: wrap;
gap: 5px;
}
.gpill {
font-size: 10.5px;
font-weight: 700;
letter-spacing: 0.04em;
padding: 3px 9px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.07);
color: rgba(255, 255, 255, 0.82);
border: 1px solid rgba(255, 255, 255, 0.07);
}
.gpill.accent {
background: color-mix(in srgb, var(--accent) 30%, rgba(10, 10, 12, 0.4));
border-color: color-mix(in srgb, var(--accent) 50%, transparent);
color: #fff;
}
.rail-desc {
margin: 0;
font-size: 12.5px;