Reported directly: the music bar's queue popup looked broken, and the
listen-party card on the home page never showed an image. Put together a
mockup first (this app's own theme tokens, not a generic one) to confirm
the redesign direction before touching any code, since the queue side
was a real UI rework, not a one-line fix.
The layout bug: MusicBar.ts's queue rows were reusing views/album's
shared .trackRow (styles/trackList.css) - a CSS grid with 4 fixed
columns sized for THAT page's own row shape (track number, name,
duration, a button). The queue never had a track number, and rendered
completely different content in that same slot, so the track's own name
- the first child - landed in the 28px column meant for a one-or-two-
digit index, crushed to its first couple of characters, while the
artist/album text filled the wide column meant for the name instead.
Fixed with a dedicated .queueRow (flex, not grid): album art, a
truncating name+artist/album/added-by info column, duration on the
right.
The missing images: confirmed directly against the real Emby server
(three sampled tracks, Fields=AlbumId,ImageTags) that an individual
Audio item essentially never carries its own Primary image - every one
tested answered 500, not even a clean 404 - while the same tracks'
parent albums mostly did. Every place that built an image URL from a
raw EmbyItem was independently reconstructing /api/catalog/image/{id}
by hand (CatalogService.toItemDto, and three separate spots in
index.ts's party-creation routes bypassing toItemDto entirely) - all
now funnel through one new CatalogService.imageUrlFor(item), which
points a track at its AlbumId instead of its own id when one's known.
EmbyItem gained AlbumId; every Fields param listing Audio items now
requests it.
While fixing the now-working image, the home page's party card turned
out to be the wrong shape for music too: a fixed 48x72 portrait box,
correct for a movie/show poster, forcing a hard crop on an album's
square cover. Video parties are untouched; a music party's card now
gets a 56x56 square box instead, toggled from LobbyPartyDto.mediaType.
Tests: imageUrlFor (movie/episode unaffected, a track with an AlbumId
falls back to it, a track with neither still falls back to its own id
rather than a broken URL). Full typecheck/test/build clean on both
projects (118/118 server tests). Verified visually against the actual
compiled app.css (not just the mockup) - injected real markup using the
new classes into a running instance and confirmed the queue rows and
both card shapes render as designed.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
62 lines
1.4 KiB
CSS
62 lines
1.4 KiB
CSS
.section { margin-bottom: 32px; }
|
|
|
|
/* ---- Horizontal carousels ---- */
|
|
|
|
.carousel {
|
|
display: flex;
|
|
gap: 16px;
|
|
overflow-x: auto;
|
|
padding-bottom: 10px;
|
|
}
|
|
|
|
.carousel .tile { flex: 0 0 160px; }
|
|
.carousel .partyCard { flex: 0 0 280px; }
|
|
|
|
.partyCard {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
background: var(--bg-header);
|
|
border-radius: 10px;
|
|
padding: 10px;
|
|
}
|
|
|
|
.partyCard img {
|
|
width: 48px;
|
|
height: 72px;
|
|
object-fit: cover;
|
|
border-radius: 6px;
|
|
background: var(--panel);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* A music party's own cover art is square (an album's, not a movie/show
|
|
poster) - the portrait 48x72 box above was forcing a hard crop on it.
|
|
Video parties keep the portrait box unchanged. */
|
|
.partyCard img.partyCardArt--square {
|
|
width: 56px;
|
|
height: 56px;
|
|
}
|
|
|
|
.partyCard img.noImage { visibility: hidden; }
|
|
|
|
.partyCardInfo {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
}
|
|
|
|
.partyCardInfo strong { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
.partyCard button { flex-shrink: 0; padding: 8px 14px; font-size: 13px; }
|
|
|
|
/* Stands in for "Rejoindre" on the one card that's this browser tab's own
|
|
active listen party - see renderParties in Home.ts. Same footprint as
|
|
the button it replaces, so the row doesn't visibly reflow. */
|
|
.partyCardMine {
|
|
flex-shrink: 0;
|
|
padding: 8px 14px;
|
|
color: var(--accent-bright);
|
|
font-weight: 600;
|
|
}
|