From 65b513ca1742f3686837a1a97f7ba7871459a2ba Mon Sep 17 00:00:00 2001 From: "alexei.dolgolyov" Date: Mon, 23 Feb 2026 23:16:53 +0300 Subject: [PATCH] Update media-server: UI polish and bug fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Compact header and footer (reduced padding, margins, font sizes) - Remove separator borders from header and footer - Fix color contrast on hover states (white text on accent backgrounds) - Fix album art not updating on track change (composite cache key) - Slow vinyl spin animation (4s → 12s) - Replace Add buttons with dashed-border add-cards - Fix dialog header text color - Make theme toggle button transparent Co-Authored-By: Claude Sonnet 4.5 --- media_server/static/css/styles.css | 110 +++++++++++++++++------------ media_server/static/index.html | 22 +++--- media_server/static/js/app.js | 57 ++++++++------- 3 files changed, 108 insertions(+), 81 deletions(-) diff --git a/media_server/static/css/styles.css b/media_server/static/css/styles.css index bf285aa..34ec0db 100644 --- a/media_server/static/css/styles.css +++ b/media_server/static/css/styles.css @@ -108,16 +108,15 @@ .container { max-width: 800px; margin: 0 auto; - padding: 2rem; + padding: 2rem 2rem 0.5rem; } header { display: flex; justify-content: space-between; align-items: center; - margin-bottom: 2rem; - padding-bottom: 1rem; - border-bottom: 1px solid var(--border); + margin-bottom: 0.75rem; + padding-bottom: 0.5rem; } h1 { @@ -148,27 +147,27 @@ } .theme-toggle { - background: var(--bg-tertiary); - border: 1px solid var(--border); - border-radius: 8px; - padding: 0.5rem; + background: transparent; + border: none; + padding: 0.3rem; cursor: pointer; display: flex; align-items: center; justify-content: center; - transition: all 0.3s; - width: 40px; - height: 40px; + transition: opacity 0.3s; + width: 32px; + height: 32px; + opacity: 0.7; } .theme-toggle:hover { - background: var(--border); + opacity: 1; } .theme-toggle svg { - width: 20px; - height: 20px; - fill: var(--text-primary); + width: 16px; + height: 16px; + fill: currentColor; } #locale-select { @@ -176,9 +175,9 @@ border: 1px solid var(--border); color: var(--text-primary); border-radius: 6px; - padding: 6px 12px; + padding: 4px 8px; cursor: pointer; - font-size: 14px; + font-size: 12px; font-weight: 500; transition: all 0.3s ease; } @@ -343,11 +342,11 @@ } .album-art-container.vinyl.spinning #album-art { - animation: vinylSpin 4s linear infinite; + animation: vinylSpin 12s linear infinite; } .album-art-container.vinyl.paused #album-art { - animation: vinylSpin 4s linear infinite; + animation: vinylSpin 12s linear infinite; animation-play-state: paused; } @@ -471,6 +470,7 @@ button:hover:not(:disabled) { background: var(--accent); + color: #fff; transform: scale(1.05); } @@ -487,6 +487,7 @@ button.primary:hover:not(:disabled) { background: var(--accent-hover); + color: #fff; transform: scale(1.1); } @@ -640,6 +641,7 @@ .script-btn:hover:not(:disabled) { background: var(--accent); border-color: var(--accent); + color: #fff; transform: translateY(-2px); } @@ -657,6 +659,11 @@ font-size: 0.75rem; color: var(--text-secondary); text-align: center; + transition: color 0.2s; + } + + .script-btn:hover:not(:disabled) .script-description { + color: rgba(255, 255, 255, 0.85); } .script-btn.executing { @@ -679,28 +686,38 @@ color: var(--text-primary); } - .script-management-header { + .add-card { display: flex; - justify-content: space-between; align-items: center; - margin-bottom: 1.5rem; - } - - .add-script-btn { - padding: 0.5rem 1.5rem; - border-radius: 6px; - background: var(--accent); - border: none; - color: var(--text-primary); + justify-content: center; + border: 2px dashed var(--border); + border-radius: 8px; + padding: 1.5rem; + margin-top: 1rem; cursor: pointer; - font-size: 0.875rem; - font-weight: 600; - transition: background 0.2s; - min-width: 140px; + transition: border-color 0.2s, background 0.2s; } - .add-script-btn:hover { - background: var(--accent-hover); + .add-card:hover { + border-color: var(--text-muted); + background: var(--bg-tertiary); + } + + .add-card-icon { + font-size: 1.5rem; + color: var(--text-muted); + font-weight: 300; + line-height: 1; + } + + .add-card-grid { + border: 2px dashed var(--border); + background: transparent; + } + + .add-card-grid:hover:not(:disabled) { + border-color: var(--text-muted); + background: var(--bg-tertiary); } .scripts-table { @@ -762,12 +779,14 @@ .action-btn:hover { background: var(--accent); border-color: var(--accent); + color: #fff; transform: translateY(-1px); } .action-btn.delete:hover { background: var(--error); border-color: var(--error); + color: #fff; } .action-buttons { @@ -779,6 +798,7 @@ .action-btn.execute:hover { background: #3b82f6; border-color: #3b82f6; + color: #fff; } /* Execution Result Dialog */ @@ -862,6 +882,7 @@ /* Dialog Styles */ dialog { background: var(--bg-secondary); + color: var(--text-primary); border: 1px solid var(--border); border-radius: 12px; padding: 0; @@ -882,7 +903,7 @@ .dialog-header { padding: 1.5rem; - background: var(--bg-tertiary); + background: var(--bg-secondary); border-bottom: 1px solid var(--border); } @@ -1135,9 +1156,9 @@ .clear-token-btn { width: auto; height: auto; - padding: 6px 12px; + padding: 4px 10px; border-radius: 6px; - font-size: 14px; + font-size: 12px; font-weight: 500; background: var(--bg-tertiary); color: var(--text-secondary); @@ -1317,6 +1338,7 @@ .mini-control-btn:hover { background: var(--accent); border-color: var(--accent); + color: #fff; transform: scale(1.05); } @@ -1415,11 +1437,10 @@ /* Footer */ footer { text-align: center; - padding: 2rem 1rem; - margin-top: 3rem; - border-top: 1px solid var(--border); - color: var(--text-secondary); - font-size: 0.875rem; + padding: 0.75rem 1rem; + margin-top: 0.5rem; + color: var(--text-muted); + font-size: 0.75rem; transition: padding-bottom 0.3s ease-in-out; } @@ -2092,6 +2113,7 @@ .pagination button:hover:not(:disabled) { background: var(--accent); border-color: var(--accent); + color: #fff; transform: none; } diff --git a/media_server/static/index.html b/media_server/static/index.html index 80c5114..712398f 100644 --- a/media_server/static/index.html +++ b/media_server/static/index.html @@ -65,7 +65,7 @@ -
+
-
@@ -282,14 +278,13 @@
+
+ + +
-
-

Callback Management

- -

Callbacks are scripts triggered automatically by media control events (play, pause, stop, etc.)

@@ -313,6 +308,9 @@ +
+ + +
diff --git a/media_server/static/js/app.js b/media_server/static/js/app.js index 0e2b7d2..f4da807 100644 --- a/media_server/static/js/app.js +++ b/media_server/static/js/app.js @@ -364,7 +364,7 @@ let volumeUpdateTimer = null; // Timer for throttling volume updates let scripts = []; let lastStatus = null; // Store last status for locale switching - let lastArtworkSource = null; // Track artwork source to skip redundant loads + let lastArtworkKey = null; // Track artwork identity to skip redundant loads // Dialog dirty state tracking let scriptFormDirty = false; @@ -656,11 +656,12 @@ currentState = status.state; updatePlaybackState(status.state); - // Update album art (skip if same source to avoid redundant network requests) + // Update album art (skip if same track to avoid redundant network requests) const artworkSource = status.album_art_url || null; + const artworkKey = `${status.title || ''}|${status.artist || ''}|${artworkSource || ''}`; - if (artworkSource !== lastArtworkSource) { - lastArtworkSource = artworkSource; + if (artworkKey !== lastArtworkKey) { + lastArtworkKey = artworkKey; const artworkUrl = artworkSource ? `/api/media/artwork?token=${encodeURIComponent(localStorage.getItem('media_server_token'))}&_=${Date.now()}` : "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 300'%3E%3Crect fill='%23282828' width='300' height='300'/%3E%3Cpath fill='%236a6a6a' d='M150 80c-38.66 0-70 31.34-70 70s31.34 70 70 70 70-31.34 70-70-31.34-70-70-70zm0 20c27.614 0 50 22.386 50 50s-22.386 50-50 50-50-22.386-50-50 22.386-50 50-50zm0 30a20 20 0 100 40 20 20 0 000-40z'/%3E%3C/svg%3E"; @@ -886,33 +887,39 @@ const container = document.getElementById('scripts-container'); const grid = document.getElementById('scripts-grid'); - if (scripts.length === 0) { - grid.innerHTML = `

${t('scripts.no_scripts')}

`; - return; - } - grid.innerHTML = ''; - scripts.forEach(script => { - const button = document.createElement('button'); - button.className = 'script-btn'; - button.onclick = () => executeScript(script.name, button); + if (scripts.length === 0) { + grid.innerHTML = `

${t('scripts.no_scripts')}

`; + } else { + scripts.forEach(script => { + const button = document.createElement('button'); + button.className = 'script-btn'; + button.onclick = () => executeScript(script.name, button); - const label = document.createElement('div'); - label.className = 'script-label'; - label.textContent = script.label || script.name; + const label = document.createElement('div'); + label.className = 'script-label'; + label.textContent = script.label || script.name; - button.appendChild(label); + button.appendChild(label); - if (script.description) { - const description = document.createElement('div'); - description.className = 'script-description'; - description.textContent = script.description; - button.appendChild(description); - } + if (script.description) { + const description = document.createElement('div'); + description.className = 'script-description'; + description.textContent = script.description; + button.appendChild(description); + } - grid.appendChild(button); - }); + grid.appendChild(button); + }); + } + + // Add "+" card at the end + const addCard = document.createElement('div'); + addCard.className = 'script-btn add-card-grid'; + addCard.onclick = () => showAddScriptDialog(); + addCard.innerHTML = '+'; + grid.appendChild(addCard); } async function executeScript(scriptName, buttonElement) {