diff --git a/server/src/wled_controller/static/js/core/color-picker.js b/server/src/wled_controller/static/js/core/color-picker.js
index 0688583..89c5711 100644
--- a/server/src/wled_controller/static/js/core/color-picker.js
+++ b/server/src/wled_controller/static/js/core/color-picker.js
@@ -73,29 +73,56 @@ window._cpToggle = function (id) {
// Close all other pickers first (and drop their card elevation)
document.querySelectorAll('.color-picker-popover').forEach(p => {
if (p.id !== `cp-pop-${id}`) {
- p.style.display = 'none';
- const card = p.closest('.card, .template-card');
- if (card) card.classList.remove('cp-elevated');
+ _cpClosePopover(p);
}
});
const pop = document.getElementById(`cp-pop-${id}`);
if (!pop) return;
const show = pop.style.display === 'none';
- pop.style.display = show ? '' : 'none';
+ if (!show) {
+ _cpClosePopover(pop);
+ return;
+ }
+ pop.style.display = '';
// Elevate the card so the popover isn't clipped by sibling cards
const card = pop.closest('.card, .template-card');
- if (card) card.classList.toggle('cp-elevated', show);
- if (show) {
- // Mark active dot
- const swatch = document.getElementById(`cp-swatch-${id}`);
- const cur = swatch ? (_rgbToHex(swatch.style.backgroundColor) || swatch.style.background) : '';
- pop.querySelectorAll('.color-picker-dot').forEach(d => {
- const dHex = _rgbToHex(d.style.backgroundColor || d.style.background);
- d.classList.toggle('active', dHex.toLowerCase() === cur.toLowerCase());
- });
+ if (card) card.classList.toggle('cp-elevated', true);
+
+ // On small screens, if inside an overflow container (e.g. header toolbar),
+ // switch to fixed positioning so the popover isn't clipped.
+ const wrapper = pop.closest('.color-picker-wrapper');
+ if (wrapper && window.innerWidth <= 600) {
+ const rect = wrapper.getBoundingClientRect();
+ pop.style.position = 'fixed';
+ pop.style.top = (rect.bottom + 4) + 'px';
+ pop.style.right = Math.max(4, window.innerWidth - rect.right) + 'px';
+ pop.style.left = 'auto';
+ pop.classList.add('cp-fixed');
}
+
+ // Mark active dot
+ const swatch = document.getElementById(`cp-swatch-${id}`);
+ const cur = swatch ? (_rgbToHex(swatch.style.backgroundColor) || swatch.style.background) : '';
+ pop.querySelectorAll('.color-picker-dot').forEach(d => {
+ const dHex = _rgbToHex(d.style.backgroundColor || d.style.background);
+ d.classList.toggle('active', dHex.toLowerCase() === cur.toLowerCase());
+ });
};
+/** Reset popover positioning and close. */
+function _cpClosePopover(pop) {
+ pop.style.display = 'none';
+ if (pop.classList.contains('cp-fixed')) {
+ pop.classList.remove('cp-fixed');
+ pop.style.position = '';
+ pop.style.top = '';
+ pop.style.right = '';
+ pop.style.left = '';
+ }
+ const card = pop.closest('.card, .template-card');
+ if (card) card.classList.remove('cp-elevated');
+}
+
window._cpPick = function (id, hex) {
// Update swatch
const swatch = document.getElementById(`cp-swatch-${id}`);
@@ -103,16 +130,14 @@ window._cpPick = function (id, hex) {
// Update native input
const native = document.getElementById(`cp-native-${id}`);
if (native) native.value = hex;
- // Mark active dot
+ // Mark active dot and close
const pop = document.getElementById(`cp-pop-${id}`);
if (pop) {
pop.querySelectorAll('.color-picker-dot').forEach(d => {
const dHex = _rgbToHex(d.style.backgroundColor || d.style.background);
d.classList.toggle('active', dHex.toLowerCase() === hex.toLowerCase());
});
- pop.style.display = 'none';
- const card = pop.closest('.card, .template-card');
- if (card) card.classList.remove('cp-elevated');
+ _cpClosePopover(pop);
}
// Fire callback
if (_callbacks[id]) _callbacks[id](hex);
@@ -126,20 +151,14 @@ window._cpReset = function (id, resetColor) {
const pop = document.getElementById(`cp-pop-${id}`);
if (pop) {
pop.querySelectorAll('.color-picker-dot').forEach(d => d.classList.remove('active'));
- pop.style.display = 'none';
- const card = pop.closest('.card, .template-card');
- if (card) card.classList.remove('cp-elevated');
+ _cpClosePopover(pop);
}
// Fire callback with empty string to signal removal
if (_callbacks[id]) _callbacks[id]('');
};
export function closeAllColorPickers() {
- document.querySelectorAll('.color-picker-popover').forEach(p => {
- p.style.display = 'none';
- const card = p.closest('.card, .template-card');
- if (card) card.classList.remove('cp-elevated');
- });
+ document.querySelectorAll('.color-picker-popover').forEach(p => _cpClosePopover(p));
}
// Close on outside click
diff --git a/server/src/wled_controller/static/js/features/displays.js b/server/src/wled_controller/static/js/features/displays.js
index 4fbcada..4f50ffb 100644
--- a/server/src/wled_controller/static/js/features/displays.js
+++ b/server/src/wled_controller/static/js/features/displays.js
@@ -25,6 +25,12 @@ export function openDisplayPicker(callback, selectedIndex, engineType = null) {
_pickerEngineType = engineType || null;
const lightbox = document.getElementById('display-picker-lightbox');
+ // Use "Select a Device" title for engines with own display lists (camera, scrcpy, etc.)
+ const titleEl = lightbox.querySelector('.display-picker-title');
+ if (titleEl) {
+ titleEl.textContent = t(_pickerEngineType ? 'displays.picker.title.device' : 'displays.picker.title');
+ }
+
lightbox.classList.add('active');
requestAnimationFrame(() => {
diff --git a/server/src/wled_controller/static/js/features/streams.js b/server/src/wled_controller/static/js/features/streams.js
index 539e8d1..ea38de5 100644
--- a/server/src/wled_controller/static/js/features/streams.js
+++ b/server/src/wled_controller/static/js/features/streams.js
@@ -1295,8 +1295,8 @@ function renderPictureSourcesList(streams) {
];
const tabBar = `
${tabs.map(tab =>
- ``
- ).join('')}
`;
+ `
`
+ ).join('')}
`;
const renderAudioSourceCard = (src) => {
const isMono = src.source_type === 'mono';
diff --git a/server/src/wled_controller/static/js/features/targets.js b/server/src/wled_controller/static/js/features/targets.js
index 801cf5d..4dddb80 100644
--- a/server/src/wled_controller/static/js/features/targets.js
+++ b/server/src/wled_controller/static/js/features/targets.js
@@ -569,8 +569,8 @@ export async function loadTargetsTab() {
];
const tabBar = `