From 84de4ad16b3cc7d0e68f1d62370d4d4422c50db0 Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Tue, 3 Mar 2026 20:26:21 -0800 Subject: [PATCH] Redesign watchlist modal with enriched artist detail view --- web_server.py | 66 +++++- webui/static/mobile.css | 52 ++++- webui/static/script.js | 264 ++++++++++++++++++++--- webui/static/style.css | 466 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 811 insertions(+), 37 deletions(-) diff --git a/web_server.py b/web_server.py index 64568752..0cb5fe08 100644 --- a/web_server.py +++ b/web_server.py @@ -21527,7 +21527,14 @@ def get_watchlist_artists(): "created_at": artist.created_at.isoformat() if artist.created_at else None, "updated_at": artist.updated_at.isoformat() if artist.updated_at else None, "image_url": artist.image_url, # Cached during watchlist scans - "itunes_artist_id": artist.itunes_artist_id # For iTunes-only artists + "itunes_artist_id": artist.itunes_artist_id, # For iTunes-only artists + "include_albums": artist.include_albums, + "include_eps": artist.include_eps, + "include_singles": artist.include_singles, + "include_live": artist.include_live, + "include_remixes": artist.include_remixes, + "include_acoustic": artist.include_acoustic, + "include_compilations": artist.include_compilations, }) return jsonify({"success": True, "artists": artists_data}) @@ -22362,7 +22369,8 @@ def watchlist_artist_config(artist_id): cursor.execute(""" SELECT include_albums, include_eps, include_singles, include_live, include_remixes, include_acoustic, include_compilations, - artist_name, image_url, spotify_artist_id, itunes_artist_id + artist_name, image_url, spotify_artist_id, itunes_artist_id, + last_scan_timestamp, date_added FROM watchlist_artists WHERE spotify_artist_id = ? OR itunes_artist_id = ? """, (artist_id, artist_id)) @@ -22405,6 +22413,53 @@ def watchlist_artist_config(artist_id): 'genres': [] } + # Enrich with library artist data (banner, bio, style, mood, label) + try: + conn2 = sqlite3.connect(str(database.database_path)) + cur2 = conn2.cursor() + cur2.execute(""" + SELECT banner_url, summary, style, mood, label, genres + FROM artists + WHERE spotify_artist_id = ? OR itunes_artist_id = ? + LIMIT 1 + """, (artist_id, artist_id)) + lib_row = cur2.fetchone() + if lib_row: + artist_info['banner_url'] = lib_row[0] + artist_info['summary'] = lib_row[1] + artist_info['style'] = lib_row[2] + artist_info['mood'] = lib_row[3] + artist_info['label'] = lib_row[4] + # Backfill genres from library if Spotify didn't provide any + if not artist_info.get('genres') and lib_row[5]: + try: + artist_info['genres'] = json.loads(lib_row[5]) + except (json.JSONDecodeError, TypeError): + pass + + # Get recent releases for this watchlist artist + cur2.execute(""" + SELECT rr.album_name, rr.release_date, rr.album_cover_url, rr.track_count + FROM recent_releases rr + JOIN watchlist_artists wa ON rr.watchlist_artist_id = wa.id + WHERE wa.spotify_artist_id = ? OR wa.itunes_artist_id = ? + ORDER BY rr.release_date DESC + LIMIT 6 + """, (artist_id, artist_id)) + releases = [ + { + 'album_name': r[0], + 'release_date': r[1], + 'album_cover_url': r[2], + 'track_count': r[3], + } + for r in cur2.fetchall() + ] + conn2.close() + except Exception as e: + print(f"Warning: Could not enrich artist from library: {e}") + releases = [] + config = { 'include_albums': bool(result[0]), # Convert INTEGER to boolean 'include_eps': bool(result[1]), @@ -22412,13 +22467,16 @@ def watchlist_artist_config(artist_id): 'include_live': bool(result[3]), 'include_remixes': bool(result[4]), 'include_acoustic': bool(result[5]), - 'include_compilations': bool(result[6]) + 'include_compilations': bool(result[6]), + 'last_scan_timestamp': result[11], + 'date_added': result[12], } return jsonify({ "success": True, "config": config, - "artist": artist_info + "artist": artist_info, + "recent_releases": releases }) else: # POST diff --git a/webui/static/mobile.css b/webui/static/mobile.css index 8116056b..1fde95fe 100644 --- a/webui/static/mobile.css +++ b/webui/static/mobile.css @@ -1486,12 +1486,34 @@ font-size: 10px; } - /* Show checkbox and remove always on mobile (no hover) */ + /* Show checkbox, remove, and gear always on mobile (no hover) */ .watchlist-card-checkbox, - .watchlist-artist-card .watchlist-card-remove { + .watchlist-artist-card .watchlist-card-remove, + .watchlist-artist-card .watchlist-card-gear { opacity: 1; } + .watchlist-detail-hero { + flex-direction: column; + text-align: center; + } + + .watchlist-detail-hero-stats { + justify-content: center; + } + + .watchlist-detail-hero-genres { + justify-content: center; + } + + .watchlist-detail-actions { + flex-direction: column; + } + + .watchlist-detail-actions button { + width: 100%; + } + .watchlist-search-container { padding: 0 16px; } @@ -1904,6 +1926,32 @@ padding: 8px 10px; } + .watchlist-pill { + font-size: 8px; + padding: 1px 4px; + } + + .watchlist-card-pills { + padding: 0 10px 6px; + } + + .watchlist-detail-content { + padding: 20px 16px; + } + + .watchlist-detail-hero img { + width: 80px; + height: 80px; + } + + .watchlist-detail-hero-name { + font-size: 22px; + } + + .watchlist-detail-releases { + grid-template-columns: 1fr; + } + /* Download modal - even more compact */ .download-missing-modal-hero { padding: 10px 12px; diff --git a/webui/static/script.js b/webui/static/script.js index f424f8e6..da4beebd 100644 --- a/webui/static/script.js +++ b/webui/static/script.js @@ -26757,25 +26757,25 @@ async function showWatchlistModal() {
-
+
-
- Artist -
Waiting...
+
+ Artist +
Waiting...
-
- Album -
Waiting...
+
+ Album +
Waiting...
-
-
Current Track:
-
Waiting...
+
+
Current Track:
+
Waiting...
-
✨ Recently Added:
+
✨ Recently Added:
@@ -26845,7 +26845,16 @@ async function showWatchlistModal() {
- ${artistsData.artists.map(artist => ` + ${artistsData.artists.map(artist => { + const pills = []; + if (artist.include_albums) pills.push('Albums'); + if (artist.include_eps) pills.push('EPs'); + if (artist.include_singles) pills.push('Singles'); + if (artist.include_live) pills.push('Live'); + if (artist.include_remixes) pills.push('Remixes'); + if (artist.include_acoustic) pills.push('Acoustic'); + if (artist.include_compilations) pills.push('Compilations'); + return `
@@ -26858,11 +26867,13 @@ async function showWatchlistModal() { - + title="Artist settings"> + +
${artist.image_url ? ` @@ -26877,29 +26888,29 @@ async function showWatchlistModal() {
${escapeHtml(artist.artist_name)} - Added ${new Date(artist.date_added).toLocaleDateString()} + ${formatRelativeScanTime(artist.last_scan_timestamp)}
+ ${pills.length > 0 ? `
${pills.join('')}
` : ''}
- `).join('')} + `}).join('')}
`; - // Add event listeners for remove buttons - modal.querySelectorAll('.watchlist-card-remove').forEach(button => { + // Add event listeners for gear buttons + modal.querySelectorAll('.watchlist-card-gear').forEach(button => { button.addEventListener('click', () => { const artistId = button.getAttribute('data-artist-id'); const artistName = button.getAttribute('data-artist-name'); - removeFromWatchlistModal(artistId, artistName); + openWatchlistArtistConfigModal(artistId, artistName); }); }); - // Add click handlers to artist cards (except for remove button or checkbox) + // Add click handlers to artist cards (except for gear button or checkbox) modal.querySelectorAll('.watchlist-artist-card').forEach(item => { item.addEventListener('click', (e) => { - // Don't trigger if clicking the remove button or checkbox - if (e.target.closest('.watchlist-card-remove') || e.target.closest('.watchlist-card-checkbox')) { + if (e.target.closest('.watchlist-card-gear') || e.target.closest('.watchlist-card-checkbox')) { return; } @@ -26907,7 +26918,7 @@ async function showWatchlistModal() { const artistName = item.querySelector('.watchlist-card-name').textContent; console.log(`🎵 Artist card clicked: ${artistName} (${artistId})`); - openWatchlistArtistConfigModal(artistId, artistName); + openWatchlistArtistDetailView(artistId, artistName); }); }); @@ -27113,6 +27124,177 @@ function closeWatchlistArtistConfigModal() { } } +/** + * Open watchlist artist detail view (slides in from right) + */ +async function openWatchlistArtistDetailView(artistId, artistName) { + try { + const response = await fetch(`/api/watchlist/artist/${artistId}/config`); + const data = await response.json(); + + if (!data.success) { + showToast(`Error loading artist info: ${data.error}`, 'error'); + return; + } + + const { config, artist, recent_releases } = data; + + // Remove existing overlay if any + const existing = document.querySelector('.watchlist-artist-detail-overlay'); + if (existing) existing.remove(); + + const overlay = document.createElement('div'); + overlay.className = 'watchlist-artist-detail-overlay'; + + // Build pills + const pills = []; + if (config.include_albums) pills.push('Albums'); + if (config.include_eps) pills.push('EPs'); + if (config.include_singles) pills.push('Singles'); + if (config.include_live) pills.push('Live'); + if (config.include_remixes) pills.push('Remixes'); + if (config.include_acoustic) pills.push('Acoustic'); + if (config.include_compilations) pills.push('Compilations'); + + // Build scan info + const scanTimeText = config.last_scan_timestamp ? formatRelativeScanTime(config.last_scan_timestamp) : 'Never scanned'; + const dateAddedText = config.date_added ? `Added ${new Date(config.date_added).toLocaleDateString()}` : ''; + + // Build metadata tags (style, mood, label) + const metaTags = []; + if (artist.style) metaTags.push(`${escapeHtml(artist.style)}`); + if (artist.mood) metaTags.push(`${escapeHtml(artist.mood)}`); + if (artist.label) metaTags.push(`${escapeHtml(artist.label)}`); + + overlay.innerHTML = ` + ${artist.banner_url ? ` +
+ +
+
+ ` : ''} + +
+ + +
+ ${artist.image_url ? `${escapeHtml(artist.name)}` : ''} +
+

${escapeHtml(artist.name)}

+ ${artist.followers || artist.popularity ? ` +
+ ${artist.followers ? ` +
+ ${formatNumber(artist.followers)} + Followers +
` : ''} + ${artist.popularity ? ` +
+ ${artist.popularity}/100 + Popularity +
` : ''} +
+ ` : ''} + ${artist.genres && artist.genres.length > 0 ? ` +
+ ${artist.genres.map(g => `${escapeHtml(g)}`).join('')} +
+ ` : ''} +
+
+ + ${artist.summary ? ` +
+
About
+

${escapeHtml(artist.summary)}

+
+ ` : ''} + + ${metaTags.length > 0 ? ` +
+
Info
+
${metaTags.join('')}
+
+ ` : ''} + + ${recent_releases && recent_releases.length > 0 ? ` +
+
Recent Releases
+
+ ${recent_releases.map(r => ` +
+ ${r.album_cover_url ? `` : ''} +
+ ${escapeHtml(r.album_name)} + ${r.release_date}${r.track_count ? ` · ${r.track_count} tracks` : ''} +
+
+ `).join('')} +
+
+ ` : ''} + +
+
Watchlist
+
+ ${scanTimeText} + ${dateAddedText ? `·${dateAddedText}` : ''} +
+
+ ${pills.length > 0 ? pills.join('') : 'No release types enabled'} +
+
+ +
+ + +
+
+ `; + + // Wire up event listeners (avoids inline onclick escaping issues) + overlay.querySelector('.watchlist-detail-back-btn').addEventListener('click', () => { + closeWatchlistArtistDetailView(); + }); + + overlay.querySelector('.watchlist-detail-settings-action').addEventListener('click', () => { + // Remove overlay immediately so it doesn't block the config modal + const detailOverlay = document.querySelector('.watchlist-artist-detail-overlay'); + if (detailOverlay) detailOverlay.remove(); + openWatchlistArtistConfigModal(artistId, artistName); + }); + + overlay.querySelector('.watchlist-detail-remove-action').addEventListener('click', () => { + removeFromWatchlistModal(artistId, artistName); + }); + + // Append to the modal container + const container = document.querySelector('.watchlist-fullscreen'); + if (container) { + container.appendChild(overlay); + // Trigger slide-in animation + requestAnimationFrame(() => overlay.classList.add('visible')); + } + + } catch (error) { + console.error('Error opening artist detail view:', error); + showToast(`Error: ${error.message}`, 'error'); + } +} + +/** + * Close watchlist artist detail view (slides out) + */ +function closeWatchlistArtistDetailView() { + const overlay = document.querySelector('.watchlist-artist-detail-overlay'); + if (overlay) { + overlay.classList.remove('visible'); + overlay.addEventListener('transitionend', () => overlay.remove(), { once: true }); + } +} + /** * Open global watchlist settings modal */ @@ -27365,6 +27547,23 @@ function formatNumber(num) { return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); } +/** + * Format last scan timestamp as relative time + */ +function formatRelativeScanTime(isoString) { + if (!isoString) return 'Never scanned'; + const diff = Date.now() - new Date(isoString).getTime(); + const mins = Math.floor(diff / 60000); + if (mins < 1) return 'Scanned just now'; + if (mins < 60) return `Scanned ${mins}m ago`; + const hrs = Math.floor(mins / 60); + if (hrs < 24) return `Scanned ${hrs}h ago`; + const days = Math.floor(hrs / 24); + if (days < 30) return `Scanned ${days}d ago`; + const months = Math.floor(days / 30); + return `Scanned ${months}mo ago`; +} + /** * Filter watchlist artists based on search input */ @@ -27476,16 +27675,16 @@ function handleWatchlistScanData(data) { if (additionsFeed) { if (data.recent_wishlist_additions && data.recent_wishlist_additions.length > 0) { additionsFeed.innerHTML = data.recent_wishlist_additions.map(item => ` -
- -
-
${item.track_name}
-
${item.artist_name}
+
+ +
+
${item.track_name}
+
${item.artist_name}
`).join(''); } else { - additionsFeed.innerHTML = '
No tracks added yet...
'; + additionsFeed.innerHTML = '
No tracks added yet...
'; } } } else if (liveActivity && data.status !== 'scanning') { @@ -27523,8 +27722,8 @@ function handleWatchlistScanData(data) { // Update the scan status display with completion message and summary statusDiv.innerHTML = ` -
-
${completionMessage}
+
+
${completionMessage}
Artists: ${totalArtists} @@ -27694,6 +27893,9 @@ async function removeFromWatchlistModal(artistId, artistName) { console.log(`❌ Removed ${artistName} from watchlist`); + // Close detail view if open + closeWatchlistArtistDetailView(); + // Refresh the modal showWatchlistModal(); diff --git a/webui/static/style.css b/webui/static/style.css index c5b29d48..fe445a44 100644 --- a/webui/static/style.css +++ b/webui/static/style.css @@ -8714,6 +8714,8 @@ body { max-width: 95vw; width: 95vw; max-height: 95vh; + position: relative; + overflow: hidden; } /* Override playlist-modal-body within watchlist to allow grid scrolling */ @@ -8807,6 +8809,34 @@ body { font-weight: 500; } +/* Card release-type pills */ +.watchlist-card-pills { + display: flex; + flex-wrap: wrap; + gap: 4px; + padding: 0 16px 10px; +} + +.watchlist-pill { + font-size: 9px; + font-weight: 600; + text-transform: uppercase; + padding: 2px 6px; + border-radius: 6px; + line-height: 1.3; + letter-spacing: 0.3px; +} + +.watchlist-pill-active { + background: rgba(var(--accent-rgb), 0.15); + color: rgb(var(--accent-rgb)); +} + +.watchlist-pill-filter { + background: rgba(255, 152, 0, 0.15); + color: #ff9800; +} + /* Checkbox — top-left, hidden until hover or checked */ .watchlist-card-checkbox { position: absolute; @@ -8877,6 +8907,442 @@ body { color: #ffffff; } +/* Gear icon — top-right, visible on hover */ +.watchlist-card-gear { + position: absolute; + top: 10px; + right: 10px; + z-index: 3; + opacity: 0; + transition: opacity 0.15s ease, background 0.15s ease; + width: 30px; + height: 30px; + border-radius: 50%; + border: none; + background: rgba(0, 0, 0, 0.55); + color: rgba(255, 255, 255, 0.7); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + padding: 0; +} + +.watchlist-artist-card:hover .watchlist-card-gear { + opacity: 1; +} + +.watchlist-card-gear:hover { + background: rgba(var(--accent-rgb), 0.7); + color: #ffffff; +} + +.watchlist-card-gear svg { + width: 16px; + height: 16px; + fill: currentColor; +} + +/* Watchlist Artist Detail Overlay */ +.watchlist-artist-detail-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(18, 18, 18, 0.98); + z-index: 10; + transform: translateX(100%); + transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); + overflow-y: auto; +} + +.watchlist-artist-detail-overlay.visible { + transform: translateX(0); +} + +/* Banner at top of detail view */ +.watchlist-detail-banner { + position: relative; + width: 100%; +} + +.watchlist-detail-banner img { + width: 100%; + height: auto; + display: block; +} + +.watchlist-detail-banner-fade { + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 80px; + background: linear-gradient(to bottom, transparent, rgba(18, 18, 18, 0.98)); +} + +.watchlist-detail-content { + padding: 32px; + max-width: 640px; + margin: 0 auto; +} + +.watchlist-detail-content.has-banner { + padding-top: 16px; +} + +/* Bio text */ +.watchlist-detail-bio { + font-size: 13px; + line-height: 1.6; + color: rgba(255, 255, 255, 0.6); + margin: 0; + display: -webkit-box; + -webkit-line-clamp: 4; + -webkit-box-orient: vertical; + overflow: hidden; +} + +/* Watchlist info row */ +.watchlist-detail-watchlist-info { + display: flex; + align-items: center; + gap: 8px; +} + +.watchlist-detail-info-sep { + color: rgba(255, 255, 255, 0.3); + font-size: 11px; +} + +/* Recent releases grid */ +.watchlist-detail-releases { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); + gap: 10px; +} + +.watchlist-detail-release { + display: flex; + align-items: center; + gap: 10px; + padding: 8px; + border-radius: 8px; + background: rgba(255, 255, 255, 0.04); + transition: background 0.15s ease; +} + +.watchlist-detail-release:hover { + background: rgba(255, 255, 255, 0.08); +} + +.watchlist-detail-release img { + width: 48px; + height: 48px; + border-radius: 4px; + object-fit: cover; + flex-shrink: 0; +} + +.watchlist-detail-release-info { + display: flex; + flex-direction: column; + gap: 2px; + min-width: 0; +} + +.watchlist-detail-release-name { + font-size: 13px; + font-weight: 600; + color: #ffffff; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.watchlist-detail-release-meta { + font-size: 11px; + color: rgba(255, 255, 255, 0.4); +} + +.watchlist-detail-back { + display: inline-flex; + align-items: center; + gap: 8px; + background: none; + border: none; + color: rgba(255, 255, 255, 0.7); + font-size: 14px; + cursor: pointer; + padding: 8px 0; + margin-bottom: 24px; + transition: color 0.15s ease; +} + +.watchlist-detail-back:hover { + color: rgb(var(--accent-rgb)); +} + +.watchlist-detail-hero { + display: flex; + align-items: center; + gap: 24px; + margin-bottom: 32px; +} + +.watchlist-detail-hero img { + width: 120px; + height: 120px; + border-radius: 50%; + object-fit: cover; + border: 3px solid rgba(var(--accent-rgb), 0.3); + background: #1a1a1a; + flex-shrink: 0; +} + +.watchlist-detail-hero-info { + display: flex; + flex-direction: column; + gap: 8px; + min-width: 0; +} + +.watchlist-detail-hero-name { + font-size: 28px; + font-weight: 700; + color: #ffffff; + margin: 0; + line-height: 1.2; +} + +.watchlist-detail-hero-stats { + display: flex; + gap: 20px; +} + +.watchlist-detail-stat { + display: flex; + flex-direction: column; + gap: 2px; +} + +.watchlist-detail-stat-value { + font-size: 16px; + font-weight: 700; + color: rgb(var(--accent-rgb)); +} + +.watchlist-detail-stat-label { + font-size: 10px; + text-transform: uppercase; + color: rgba(255, 255, 255, 0.4); + font-weight: 600; + letter-spacing: 0.5px; +} + +.watchlist-detail-hero-genres { + display: flex; + flex-wrap: wrap; + gap: 6px; + margin-top: 4px; +} + +.watchlist-detail-genre-tag { + font-size: 10px; + padding: 3px 8px; + border-radius: 12px; + background: rgba(255, 255, 255, 0.08); + color: rgba(255, 255, 255, 0.6); + text-transform: capitalize; +} + +.watchlist-detail-section { + margin-bottom: 24px; +} + +.watchlist-detail-section-title { + font-size: 11px; + text-transform: uppercase; + color: rgba(255, 255, 255, 0.4); + font-weight: 600; + letter-spacing: 0.5px; + margin-bottom: 12px; +} + +.watchlist-detail-actions { + display: flex; + gap: 12px; + margin-top: 32px; +} + +.watchlist-detail-actions button { + flex: 1; + padding: 12px 20px; + border-radius: 8px; + border: none; + font-size: 14px; + font-weight: 600; + cursor: pointer; + transition: background 0.15s ease, transform 0.1s ease; +} + +.watchlist-detail-actions button:active { + transform: scale(0.98); +} + +.watchlist-detail-settings-btn { + background: rgba(var(--accent-rgb), 0.15); + color: rgb(var(--accent-rgb)); +} + +.watchlist-detail-settings-btn:hover { + background: rgba(var(--accent-rgb), 0.25); +} + +.watchlist-detail-remove-btn { + background: rgba(255, 255, 255, 0.06); + color: rgba(255, 255, 255, 0.7); +} + +.watchlist-detail-remove-btn:hover { + background: rgba(255, 59, 48, 0.2); + color: #ff3b30; +} + +/* Live activity classes (replaces inline styles) */ +.watchlist-live-activity { + display: flex; + gap: 15px; + margin-top: 15px; + padding: 15px; + background: #2a2a2a; + border-radius: 8px; + border: 1px solid #444; + justify-content: center; + align-items: flex-start; +} + +.watchlist-live-activity-col { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; +} + +.watchlist-live-activity-artist-img { + width: 80px; + height: 80px; + border-radius: 50%; + border: 2px solid rgb(var(--accent-rgb)); + object-fit: cover; + background: #1a1a1a; +} + +.watchlist-live-activity-album-img { + width: 80px; + height: 80px; + border-radius: 6px; + border: 2px solid #ffc107; + object-fit: cover; + background: #1a1a1a; +} + +.watchlist-live-activity-label { + font-size: 11px; + font-weight: bold; + color: #fff; + text-align: center; + max-width: 100px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.watchlist-live-activity-feed { + display: flex; + flex-direction: column; + gap: 6px; + min-width: 250px; + max-width: 300px; +} + +.watchlist-live-activity-feed-label { + font-size: 10px; + color: #b3b3b3; + text-transform: uppercase; +} + +.watchlist-live-activity-feed-label-orange { + font-size: 10px; + color: #ff9800; + text-transform: uppercase; +} + +.watchlist-live-activity-track { + font-size: 11px; + font-weight: bold; + color: rgb(var(--accent-light-rgb)); + margin-bottom: 8px; +} + +.watchlist-live-addition-item { + display: flex; + gap: 6px; + align-items: center; + padding: 3px; + background: #1a1a1a; + border-radius: 4px; +} + +.watchlist-live-addition-item img { + width: 24px; + height: 24px; + border-radius: 3px; + object-fit: cover; +} + +.watchlist-live-addition-item-info { + flex: 1; + overflow: hidden; +} + +.watchlist-live-addition-item-track { + font-weight: bold; + font-size: 10px; + color: rgb(var(--accent-light-rgb)); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.watchlist-live-addition-item-artist { + font-size: 9px; + color: #b3b3b3; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.watchlist-live-addition-empty { + color: #666; + font-size: 10px; +} + +.watchlist-scan-completion { + text-align: center; + padding: 15px; + background: #2a2a2a; + border-radius: 8px; + border: 1px solid #444; +} + +.watchlist-scan-completion-message { + font-size: 14px; + color: rgb(var(--accent-light-rgb)); + margin-bottom: 10px; +} + /* Watchlist Search */ .watchlist-search-container { padding: 0 32px;