From d2771f0f2696a939111573350010013e03630129 Mon Sep 17 00:00:00 2001 From: BoulderBadgeDad Date: Sat, 6 Jun 2026 18:27:00 -0700 Subject: [PATCH] =?UTF-8?q?Download=20modal:=20live=20track-row=20states?= =?UTF-8?q?=20=E2=80=94=20pills=20that=20breathe=20only=20while=20working?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The status cells were the last plain-text corner of the revamped modal, and they're the most alive data in the app during a run. The renderer now stamps data-state on the download-status cell and toggles .row-working on the row (visual-only hooks; zero logic change). CSS turns both status columns into state-colored pills — accent while searching/downloading, amber processing, green completed, red failed, orange quarantined — and ONLY the actively- working states breathe (opacity, compositor-only). The working row carries the same accent edge treatment as hover, but earned by real work instead of the mouse. prefers-reduced-motion respected. --- webui/static/downloads.js | 6 ++ webui/static/style.css | 116 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) diff --git a/webui/static/downloads.js b/webui/static/downloads.js index 85c0f944..8a44b782 100644 --- a/webui/static/downloads.js +++ b/webui/static/downloads.js @@ -3679,6 +3679,12 @@ function processModalStatusUpdate(playlistId, data) { delete statusEl.dataset.quarantineTrack; delete statusEl.dataset.detailOpen; statusEl.textContent = statusText; + // Visual-only hooks: the cell carries its state for the badge + // styling, the row glows while a track is actively working. + statusEl.dataset.state = isQuarantinedTask ? 'quarantined' + : (isV2Task && uiState === 'cancelling' ? 'cancelling' : task.status); + row.classList.toggle('row-working', + ['searching', 'downloading', 'post_processing'].includes(task.status)); if ((task.status === 'failed' || task.status === 'cancelled' || task.status === 'not_found') && task.error_message) { statusEl.classList.add('has-error-tooltip'); diff --git a/webui/static/style.css b/webui/static/style.css index ed1a3bb9..fab8bd65 100644 --- a/webui/static/style.css +++ b/webui/static/style.css @@ -66323,3 +66323,119 @@ body.em-scroll-lock { overflow: hidden; } animation: none !important; } } + +/* ── Modal revamp: live track-row states (animation == gauge, in the table) ── + The download-status cell carries data-state (set by the renderer); the row + carries .row-working while a track is actively searching/downloading/ + processing. Badges are state-colored pills; only ACTIVE states breathe. */ + +.track-download-status[data-state] { + display: inline-flex; + align-items: center; + padding: 3px 11px; + border-radius: 999px; + font-size: 11px; + font-weight: 600; + letter-spacing: 0.02em; + border: 1px solid var(--row-state-border, rgba(255, 255, 255, 0.1)); + background: var(--row-state-bg, rgba(255, 255, 255, 0.04)); + color: var(--row-state-fg, rgba(255, 255, 255, 0.65)); + white-space: nowrap; +} + +.track-download-status[data-state="pending"], +.track-download-status[data-state="cancelled"], +.track-download-status[data-state="cancelling"] { + --row-state-fg: rgba(255, 255, 255, 0.5); +} + +.track-download-status[data-state="searching"], +.track-download-status[data-state="downloading"] { + --row-state-border: rgba(var(--accent-rgb), 0.4); + --row-state-bg: rgba(var(--accent-rgb), 0.12); + --row-state-fg: rgb(var(--accent-light-rgb)); +} + +.track-download-status[data-state="post_processing"] { + --row-state-border: rgba(251, 191, 36, 0.35); + --row-state-bg: rgba(251, 191, 36, 0.1); + --row-state-fg: #fbbf24; +} + +.track-download-status[data-state="completed"] { + --row-state-border: rgba(74, 222, 128, 0.35); + --row-state-bg: rgba(74, 222, 128, 0.1); + --row-state-fg: #4ade80; +} + +.track-download-status[data-state="failed"] { + --row-state-border: rgba(239, 68, 68, 0.35); + --row-state-bg: rgba(239, 68, 68, 0.1); + --row-state-fg: #f87171; +} + +.track-download-status[data-state="quarantined"] { + --row-state-border: rgba(251, 146, 60, 0.4); + --row-state-bg: rgba(251, 146, 60, 0.1); + --row-state-fg: #fb923c; +} + +.track-download-status[data-state="not_found"] { + --row-state-fg: rgba(255, 255, 255, 0.4); +} + +/* Only states that are actually WORKING breathe */ +.track-download-status[data-state="searching"], +.track-download-status[data-state="downloading"], +.track-download-status[data-state="post_processing"], +.track-download-status[data-state="cancelling"] { + animation: row-state-breathe 1.8s ease-in-out infinite; +} + +@keyframes row-state-breathe { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.55; } +} + +/* Match-status cells get the same pill language (classes already existed) */ +.track-match-status { + display: inline-flex; + align-items: center; + padding: 3px 11px; + border-radius: 999px; + font-size: 11px; + font-weight: 600; + white-space: nowrap; +} + +.track-match-status.match-found { + border: 1px solid rgba(74, 222, 128, 0.35); + background: rgba(74, 222, 128, 0.1); + color: #4ade80; +} + +.track-match-status.match-missing { + border: 1px solid rgba(251, 191, 36, 0.35); + background: rgba(251, 191, 36, 0.1); + color: #fbbf24; +} + +.track-match-status.match-checking { + border: 1px solid rgba(255, 255, 255, 0.1); + background: rgba(255, 255, 255, 0.04); + color: rgba(255, 255, 255, 0.5); +} + +/* The actively-working row carries a persistent accent presence — like the + hover treatment, but earned by real work instead of the mouse */ +.download-tracks-table tbody tr.row-working { + background: linear-gradient(90deg, + rgba(var(--accent-rgb), 0.08), + rgba(var(--accent-rgb), 0.025) 60%, + transparent); + box-shadow: inset 2.5px 0 0 rgba(var(--accent-rgb), 0.7); +} + +@media (prefers-reduced-motion: reduce) { + .track-download-status[data-state] { animation: none !important; } +}