From 831ddc97d81907857587996861c95a5d8bbaaf61 Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Wed, 13 May 2026 18:38:02 -0700 Subject: [PATCH] Polish Download Missing modal tracklist Pure CSS tune-up scoped to .download-missing-modal-content. Column layout, table semantics, and every JS hook (#match-*, #download-*, .track-*, .download-tracks-tbody-*) untouched. Adds: - Hairline row dividers + cleaner cell padding - Hover gets accent gradient sweep + 3px inset edge bar - Monospace track numbers (glow accent on row hover) + monospace tabular duration - Status cells in both columns get uppercase micro-caps with a leading colored dot + soft glow halo (green/amber/blue/orange/red), pulses while checking + downloading - Artist column centered - Soft scrollbar --- webui/static/helper.js | 1 + webui/static/style.css | 180 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+) diff --git a/webui/static/helper.js b/webui/static/helper.js index 7580c52a..736220dc 100644 --- a/webui/static/helper.js +++ b/webui/static/helper.js @@ -3416,6 +3416,7 @@ const WHATS_NEW = { '2.5.2': [ // --- May 13, 2026 — 2.5.2 release --- { date: 'May 13, 2026 — 2.5.2 release' }, + { title: 'Download Missing Modal: Tracklist Got A Polish Pass', desc: 'visual tune-up only — column layout untouched. hairline row dividers, accent gradient + edge bar on hover, monospace track numbers (glow accent on row hover), monospace tabular duration. status text in both library-match + download-status columns picks up a leading colored dot with a soft halo (green found / amber missing / blue checking / orange downloading / red failed) and pulses while in-flight. artist column centered. soft scrollbar.', page: 'downloads' }, { title: 'Search Source Picker: Fix Default Always Sticking To Spotify', desc: 'enhanced search + global search source picker always defaulted to spotify even when the user\'s primary metadata source was deezer / itunes / discogs / etc. trace: `shared-helpers.js:createSearchController` reads `/status.metadata_source` to pick the initial active icon, then checks `SOURCE_LABELS[src]` to validate. backend was returning `metadata_source` as a dict (`{source, connected, response_time, ...}` — used elsewhere for connection-state display), so `SOURCE_LABELS[]` was always undefined, the `if` guard never fired, and `state.activeSource` silently stayed at the hardcoded `\'spotify\'` default. fix: read `.source` off the dict (with forward-compat fallback to plain-string in case any older /status response shape predates the dict change). other consumers (core.js sidebar tile, helper.js status checker, search.js display) already used `?.source` correctly — this was the only stale call site.', page: 'search' }, { title: 'Download Discography: No Longer Caps Prolific Artists At 50 Releases', desc: 'discord report: clicking "download discography" on an artist with a deep catalogue (bach, beatles complete box, dance / electronic artists with hundreds of remixes) only showed ~50 albums in the modal. trace: `MetadataLookupOptions(limit=50, max_pages=0)` was hardcoded at the discography endpoint and the artist-detail discography view. spotify\'s `max_pages=0` already paginates through everything (per-page is clamped to 10 internally) so spotify-primary users were unaffected. but deezer / itunes / discogs / hydrabase all honor the outer `limit` as a hard cap. fix: bump `limit` from 50 to 200 at all three call sites (`web_server.py` discography endpoint + artist-detail view + `core/artist_source_detail.py`). 200 matches iTunes\'s and Discogs\'s own internal caps and covers near-everyone\'s full catalogue. spotify behavior unchanged.', page: 'library' }, { title: 'Artist Page: "Write Artist Image" Button (Real Artist Photos For Navidrome)', desc: 'github issue #572 (rhwc): navidrome shows album-art-derived thumbnails as artist photos because navidrome has no api for setting an artist image — it only reads `artist.jpg` from the artist folder during library scans. soulsync\'s `update_artist_poster` for navidrome was a no-op. new button on the artist detail page header writes `artist.jpg` to the artist\'s folder on disk: looks up any album track, resolves it through the path resolver (handles docker mount translation like #558 settled on), goes up one level to the artist folder, fetches the artist photo from the configured metadata source priority chain (spotify primary, fallback to deezer / discogs / etc), downloads with content-type validation + atomic write via `.tmp + os.replace`. when active server is navidrome, triggers a library scan immediately so the new file gets indexed. respects existing `artist.jpg` files (asks before overwriting) so user-supplied photos aren\'t clobbered. works for plex / jellyfin too as a fallback layer — both servers also read `artist.jpg` from disk. 26 tests pin the pure helpers in `core/library/artist_image.py`: folder derivation (trailing slash / backslash / empty / non-string), image url picking (missing attr / whitespace strip / non-string), download (non-image content-type / 404 / timeout / empty body), and write (atomic replace / temp-cleanup-on-failure / overwrite guard / missing folder).', page: 'library' }, diff --git a/webui/static/style.css b/webui/static/style.css index 225b2c66..7503bde2 100644 --- a/webui/static/style.css +++ b/webui/static/style.css @@ -20007,6 +20007,186 @@ body.helper-mode-active #dashboard-activity-feed:hover { box-shadow: none; } +/* ===================================================================== + Download-Missing tracklist polish — pure visual tune-up. + Keeps column layout intact (no grid restructure, no cell reflow). + Refines: header chrome, row hairlines, hover states, typography + (mono numbers/duration, weight tightening), status pill polish, + pulse on checking/downloading, soft scrollbar. + All overrides scoped to `.download-missing-modal-content` so they + only affect this modal — never the standalone Albums page tables. + ===================================================================== */ + +/* Header chrome — refined uppercase micro-caps for column labels */ +.download-missing-modal-content .download-tracks-table thead th { + background: linear-gradient(180deg, rgba(28,28,28,0.95), rgba(22,22,22,0.95)); + color: rgba(255, 255, 255, 0.5); + font-weight: 600; + font-size: 10.5px; + letter-spacing: 0.08em; + text-transform: uppercase; + padding: 11px 14px; + border-bottom: 1px solid rgba(255, 255, 255, 0.08); + text-align: center; +} + +/* Cell baseline — tighter padding, hairline divider */ +.download-missing-modal-content .download-tracks-table td { + padding: 11px 14px; + border-bottom: 1px solid rgba(255, 255, 255, 0.045); + color: rgba(255, 255, 255, 0.85); + font-size: 13px; + transition: color 0.15s ease; +} +.download-missing-modal-content .download-tracks-table tbody tr:last-child td { + border-bottom: none; +} + +/* Row hover — subtle accent gradient + left-edge accent bar via box-shadow inset */ +.download-missing-modal-content .download-tracks-table tbody tr { + transition: background 0.18s ease, box-shadow 0.18s ease; + position: relative; +} +.download-missing-modal-content .download-tracks-table tbody tr:hover { + background: linear-gradient(90deg, + rgba(var(--accent-rgb), 0.07) 0%, + rgba(255, 255, 255, 0.03) 60%, + transparent 100%); + box-shadow: inset 3px 0 0 rgb(var(--accent-rgb)); +} + +/* Track number — mono, dim, tabular */ +.download-missing-modal-content .download-tracks-table .track-number { + color: rgba(255, 255, 255, 0.32); + font-weight: 500; + font-size: 12px; + font-variant-numeric: tabular-nums; + font-family: 'SF Mono', Menlo, Consolas, ui-monospace, monospace; +} +.download-missing-modal-content .download-tracks-table tbody tr:hover .track-number { + color: rgb(var(--accent-rgb)); +} + +/* Title — bold white, slight negative tracking */ +.download-missing-modal-content .download-tracks-table .track-name { + font-weight: 600; + color: #fff; + letter-spacing: -0.005em; +} + +/* Artist — dim, centered */ +.download-missing-modal-content .download-tracks-table .track-artist { + color: rgba(255, 255, 255, 0.55); + font-weight: 400; + text-align: center; +} + +/* Duration — mono dim tabular */ +.download-missing-modal-content .download-tracks-table .track-duration { + color: rgba(255, 255, 255, 0.4); + font-size: 12px; + font-variant-numeric: tabular-nums; + font-family: 'SF Mono', Menlo, Consolas, ui-monospace, monospace; +} + +/* Status cells — uppercase micro-caps with leading colored dot */ +.download-missing-modal-content .download-tracks-table .track-match-status, +.download-missing-modal-content .download-tracks-table .track-download-status { + font-weight: 700; + font-size: 10.5px; + letter-spacing: 0.08em; + text-transform: uppercase; + text-align: center; +} + +/* Leading colored dot before status text — uses currentColor so it matches state */ +.download-missing-modal-content .download-tracks-table .match-found::before, +.download-missing-modal-content .download-tracks-table .match-missing::before, +.download-missing-modal-content .download-tracks-table .match-checking::before, +.download-missing-modal-content .download-tracks-table .download-searching::before, +.download-missing-modal-content .download-tracks-table .download-downloading::before, +.download-missing-modal-content .download-tracks-table .download-complete::before, +.download-missing-modal-content .download-tracks-table .download-failed::before { + content: ''; + display: inline-block; + width: 6px; + height: 6px; + border-radius: 50%; + background: currentColor; + margin-right: 7px; + vertical-align: 1px; + box-shadow: 0 0 8px currentColor, 0 0 2px currentColor; +} +.download-missing-modal-content .download-tracks-table .match-found { + color: #4ade80; + text-shadow: 0 0 12px rgba(74, 222, 128, 0.35); +} +.download-missing-modal-content .download-tracks-table .match-missing { + color: #facc15; + text-shadow: 0 0 12px rgba(250, 204, 21, 0.35); +} +.download-missing-modal-content .download-tracks-table .match-checking { + color: #60a5fa; + text-shadow: 0 0 12px rgba(96, 165, 250, 0.35); + animation: dms-status-pulse 1.5s ease-in-out infinite; +} +.download-missing-modal-content .download-tracks-table .download-searching { + color: #60a5fa; + text-shadow: 0 0 12px rgba(96, 165, 250, 0.35); + animation: dms-status-pulse 1.5s ease-in-out infinite; +} +.download-missing-modal-content .download-tracks-table .download-downloading { + color: #fb923c; + text-shadow: 0 0 12px rgba(251, 146, 60, 0.4); + animation: dms-status-pulse 1s ease-in-out infinite; +} +.download-missing-modal-content .download-tracks-table .download-complete { + color: #4ade80; + text-shadow: 0 0 12px rgba(74, 222, 128, 0.4); +} +.download-missing-modal-content .download-tracks-table .download-failed { + color: #f87171; + text-shadow: 0 0 12px rgba(248, 113, 113, 0.4); +} + +@keyframes dms-status-pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.55; } +} + +/* Checkbox column — accent + hover scale */ +.download-missing-modal-content .download-tracks-table .track-select-cb { + accent-color: rgb(var(--accent-rgb)); + transition: transform 0.12s ease; +} +.download-missing-modal-content .download-tracks-table tbody tr:hover .track-select-cb { + transform: scale(1.08); +} + +/* Deselected — fade title/artist, keep status visible */ +.download-missing-modal-content .download-tracks-table .track-deselected .track-name, +.download-missing-modal-content .download-tracks-table .track-deselected .track-artist { + text-decoration: line-through; + text-decoration-color: rgba(255, 255, 255, 0.25); + text-decoration-thickness: 1px; +} + +/* Soft scrollbar for the track scroll area */ +.download-missing-modal-content .download-tracks-table-container::-webkit-scrollbar { + width: 8px; +} +.download-missing-modal-content .download-tracks-table-container::-webkit-scrollbar-track { + background: transparent; +} +.download-missing-modal-content .download-tracks-table-container::-webkit-scrollbar-thumb { + background: rgba(255, 255, 255, 0.08); + border-radius: 999px; + transition: background 0.2s ease; +} +.download-missing-modal-content .download-tracks-table-container::-webkit-scrollbar-thumb:hover { + background: rgba(255, 255, 255, 0.18); +} + /* Force Download Toggle Styling */ .force-download-toggle-container { display: flex;