From e1c8474271e40f0351ffc161cb4159704efa977e Mon Sep 17 00:00:00 2001 From: "alexei.dolgolyov" Date: Tue, 19 May 2026 01:17:47 +0300 Subject: [PATCH] fix(csp): wire display sliders and accent picker without inline on* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The display brightness/contrast sliders and the accent color picker rendered dynamic HTML with inline oninput/onchange/onclick attributes, which are blocked by the script-src 'self' CSP — so display settings were silently un-clickable from the WebUI. Replace the inline attributes with data-* markers, then attach proper event listeners after innerHTML (delegated on the container for the slider rows, direct for the accent dropdown). --- media_server/static/js/links.js | 39 +++++++++++++++++++++++++++----- media_server/static/js/player.js | 29 ++++++++++++++++++++---- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/media_server/static/js/links.js b/media_server/static/js/links.js index f1a0f78..d6cee1a 100644 --- a/media_server/static/js/links.js +++ b/media_server/static/js/links.js @@ -182,8 +182,7 @@ export async function loadDisplayMonitors() { ${t('display.contrast')} + data-display-slider="contrast" data-monitor-id="${monitor.id}"> ${contrastValue}% `; } @@ -296,8 +295,7 @@ export async function loadDisplayMonitors() { ${t('display.brightness')} + data-display-slider="brightness" data-monitor-id="${monitor.id}"> ${brightnessValue}% ${contrastRow} @@ -306,10 +304,15 @@ export async function loadDisplayMonitors() { container.appendChild(card); }); - // Bind a single delegated click handler for the power buttons. - // Avoids inline onclick="..." with interpolated monitor data. + // Bind a single delegated click handler for the power buttons, + // plus input/change handlers for the brightness & contrast sliders. + // Avoids inline on* attributes (blocked by script-src 'self' CSP). container.removeEventListener('click', _onPowerButtonClick); container.addEventListener('click', _onPowerButtonClick); + container.removeEventListener('input', _onDisplaySliderInput); + container.addEventListener('input', _onDisplaySliderInput); + container.removeEventListener('change', _onDisplaySliderChange); + container.addEventListener('change', _onDisplaySliderChange); // Enhance every tuning + `; dropdown.innerHTML = swatches + customRow; + + // Wire CSP-safe handlers (script-src 'self' blocks inline on* attributes). + dropdown.querySelectorAll('.accent-swatch[data-accent-color]').forEach(el => { + el.addEventListener('click', () => { + selectAccentColor(el.dataset.accentColor, el.dataset.accentHover); + }); + }); + const customRowEl = dropdown.querySelector('[data-accent-custom-row]'); + const customInput = dropdown.querySelector('#accentCustomInput'); + if (customRowEl && customInput) { + customRowEl.addEventListener('click', (e) => { + // The native color popup only opens from a user-initiated click on + // the . Forward clicks on the row to the input — except when + // the input itself was the source (avoids re-entry). + if (e.target !== customInput) customInput.click(); + }); + customInput.addEventListener('click', (e) => e.stopPropagation()); + customInput.addEventListener('change', () => { + selectAccentColor(customInput.value, lightenColor(customInput.value, 15)); + }); + } } export function selectAccentColor(color, hover) {