diff --git a/media_server/static/css/styles.css b/media_server/static/css/styles.css index 8ad31cb..3fa38d6 100644 --- a/media_server/static/css/styles.css +++ b/media_server/static/css/styles.css @@ -6867,21 +6867,20 @@ body.visualizer-active .now-playing .spectrogram-canvas { /* ─── Spectrum bars (JS-injected; real audio from backend FFT) ── */ .now-playing .spectrum { - /* Grid with explicit equal-width columns guarantees each bar - claims its share of the full container width, regardless of - the bar count. */ - display: grid; + /* Explicit equal-width columns. The CSS variable --spectrum-bars + is set by JS so adding/removing bars stays in sync. */ + display: grid !important; + grid-template-columns: repeat(var(--spectrum-bars, 40), minmax(0, 1fr)) !important; grid-auto-flow: column; - grid-auto-columns: 1fr; align-items: end; column-gap: 4px; height: 70px; margin: 36px 0 24px; - width: 100%; + width: 100% !important; box-sizing: border-box; min-width: 0; } -.now-playing .spectrum span { +.now-playing .spectrum > span { display: block; width: 100%; min-width: 0; diff --git a/media_server/static/js/app.js b/media_server/static/js/app.js index 1b82c81..d9e31ce 100644 --- a/media_server/static/js/app.js +++ b/media_server/static/js/app.js @@ -172,6 +172,9 @@ window.addEventListener('DOMContentLoaded', async () => { const spectrumRoot = document.getElementById('player-spectrum'); if (spectrumRoot && !spectrumRoot.children.length) { const SPECTRUM_BARS = 40; + // Sync the grid column count to the bar count so the row truly + // fills the column even if the bar count changes later. + spectrumRoot.style.setProperty('--spectrum-bars', SPECTRUM_BARS); const frag = document.createDocumentFragment(); for (let i = 0; i < SPECTRUM_BARS; i++) { const s = document.createElement('span'); diff --git a/media_server/static/js/player.js b/media_server/static/js/player.js index 910b194..80ad74f 100644 --- a/media_server/static/js/player.js +++ b/media_server/static/js/player.js @@ -618,7 +618,12 @@ export async function onAudioDeviceChanged() { const result = await resp.json(); updateAudioDeviceStatus({ available: result.success, ...result }); await checkVisualizerAvailability(); - if (visualizerEnabled) applyVisualizerMode(); + // Picking a device is an explicit signal the user wants + // capture: auto-enable the visualizer if it isn't already on. + if (!visualizerEnabled && visualizerAvailable) { + setVisualizerEnabled(true); + } + applyVisualizerMode(); showToast(t('settings.audio.device_changed'), 'success'); } else { showToast(t('settings.audio.device_change_failed'), 'error');