Replace all emoji icons with Lucide SVGs, add accent color picker
- Replace all emoji characters across WebUI with inline Lucide SVG icons for cross-platform consistency (icon paths in icon-paths.js) - Add accent color picker popover with 9 preset colors + custom picker, persisted to localStorage, updates all CSS custom properties - Remove subtab separator line for cleaner look - Color badge icons with accent color for visual pop - Remove processing badge from target cards - Fix hardcoded #4CAF50 in FPS labels and active badges to use CSS vars - Replace CSS content emoji (▶) with pure CSS triangle Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -29,16 +29,39 @@
|
||||
<div class="server-info">
|
||||
<a href="/docs" target="_blank" class="header-link" data-i18n-title="app.api_docs" title="API Docs">API</a>
|
||||
<button class="search-toggle" id="tour-restart-btn" onclick="startGettingStartedTutorial()" data-i18n-title="tour.restart" title="Restart tutorial">
|
||||
?
|
||||
<svg class="icon" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><path d="M12 17h.01"/></svg>
|
||||
</button>
|
||||
<button class="search-toggle" onclick="openCommandPalette()" data-i18n-title="search.open" title="Search (Ctrl+K)">
|
||||
🔍
|
||||
<svg class="icon" viewBox="0 0 24 24"><path d="m21 21-4.34-4.34"/><circle cx="11" cy="11" r="8"/></svg>
|
||||
</button>
|
||||
<button class="theme-toggle" onclick="toggleTheme()" data-i18n-title="theme.toggle" title="Toggle theme">
|
||||
<span id="theme-icon">🌙</span>
|
||||
<span id="theme-icon"><svg class="icon" viewBox="0 0 24 24"><circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/></svg></span>
|
||||
</button>
|
||||
<div class="accent-wrapper">
|
||||
<button class="search-toggle" onclick="toggleAccentPicker()" data-i18n-title="accent.title" title="Accent color">
|
||||
<span id="accent-swatch" class="accent-swatch" style="background: var(--primary-color)"></span>
|
||||
</button>
|
||||
<div id="accent-popover" class="accent-popover" style="display:none">
|
||||
<div class="accent-grid">
|
||||
<button class="accent-dot" style="background:#4CAF50" onclick="pickAccent('#4CAF50')"></button>
|
||||
<button class="accent-dot" style="background:#7C4DFF" onclick="pickAccent('#7C4DFF')"></button>
|
||||
<button class="accent-dot" style="background:#FF6D00" onclick="pickAccent('#FF6D00')"></button>
|
||||
<button class="accent-dot" style="background:#E91E63" onclick="pickAccent('#E91E63')"></button>
|
||||
<button class="accent-dot" style="background:#00BCD4" onclick="pickAccent('#00BCD4')"></button>
|
||||
<button class="accent-dot" style="background:#FF5252" onclick="pickAccent('#FF5252')"></button>
|
||||
<button class="accent-dot" style="background:#26A69A" onclick="pickAccent('#26A69A')"></button>
|
||||
<button class="accent-dot" style="background:#2196F3" onclick="pickAccent('#2196F3')"></button>
|
||||
<button class="accent-dot" style="background:#FFC107" onclick="pickAccent('#FFC107')"></button>
|
||||
</div>
|
||||
<div class="accent-custom">
|
||||
<input type="color" id="accent-picker" value="#4CAF50"
|
||||
oninput="pickAccent(this.value)" onchange="pickAccent(this.value)">
|
||||
<span data-i18n="accent.custom">Custom</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="search-toggle" onclick="openSettingsModal()" data-i18n-title="settings.title" title="Settings">
|
||||
⚙️
|
||||
<svg class="icon" viewBox="0 0 24 24"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915"/><circle cx="12" cy="12" r="3"/></svg>
|
||||
</button>
|
||||
<select id="locale-select" onchange="changeLocale()" data-i18n-title="locale.change" title="Change language" style="padding: 4px 8px; border: 1px solid var(--border-color); border-radius: 4px; background: var(--bg-color); color: var(--text-color); font-size: 0.8rem; cursor: pointer;">
|
||||
<option value="en">English</option>
|
||||
@@ -46,20 +69,20 @@
|
||||
<option value="zh">中文</option>
|
||||
</select>
|
||||
<button id="login-btn" class="btn btn-primary" onclick="showLogin()" style="display: none; padding: 4px 12px; font-size: 0.8rem;">
|
||||
🔑 <span data-i18n="auth.login">Login</span>
|
||||
<svg class="icon" viewBox="0 0 24 24"><path d="M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z"/><circle cx="16.5" cy="7.5" r=".5" fill="currentColor"/></svg> <span data-i18n="auth.login">Login</span>
|
||||
</button>
|
||||
<button id="logout-btn" class="btn btn-danger" onclick="logout()" style="display: none; padding: 4px 12px; font-size: 0.8rem;">
|
||||
🚪
|
||||
<svg class="icon" viewBox="0 0 24 24"><path d="m16 17 5-5-5-5"/><path d="M21 12H9"/><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="tabs">
|
||||
<div class="tab-bar" role="tablist">
|
||||
<button class="tab-btn" data-tab="dashboard" onclick="switchTab('dashboard')" role="tab" aria-selected="true" aria-controls="tab-dashboard" id="tab-btn-dashboard" title="Ctrl+1"><span data-i18n="dashboard.title">📊 Dashboard</span></button>
|
||||
<button class="tab-btn" data-tab="profiles" onclick="switchTab('profiles')" role="tab" aria-selected="false" aria-controls="tab-profiles" id="tab-btn-profiles" title="Ctrl+2"><span data-i18n="profiles.title">📋 Profiles</span><span class="tab-badge" id="tab-badge-profiles" style="display:none"></span></button>
|
||||
<button class="tab-btn" data-tab="targets" onclick="switchTab('targets')" role="tab" aria-selected="false" aria-controls="tab-targets" id="tab-btn-targets" title="Ctrl+3"><span data-i18n="targets.title">⚡ Targets</span><span class="tab-badge" id="tab-badge-targets" style="display:none"></span></button>
|
||||
<button class="tab-btn" data-tab="streams" onclick="switchTab('streams')" role="tab" aria-selected="false" aria-controls="tab-streams" id="tab-btn-streams" title="Ctrl+4"><span data-i18n="streams.title">📺 Sources</span></button>
|
||||
<button class="tab-btn" data-tab="dashboard" onclick="switchTab('dashboard')" role="tab" aria-selected="true" aria-controls="tab-dashboard" id="tab-btn-dashboard" title="Ctrl+1"><svg class="icon" viewBox="0 0 24 24"><rect width="7" height="9" x="3" y="3" rx="1"/><rect width="7" height="5" x="14" y="3" rx="1"/><rect width="7" height="9" x="14" y="12" rx="1"/><rect width="7" height="5" x="3" y="16" rx="1"/></svg> <span data-i18n="dashboard.title">Dashboard</span></button>
|
||||
<button class="tab-btn" data-tab="profiles" onclick="switchTab('profiles')" role="tab" aria-selected="false" aria-controls="tab-profiles" id="tab-btn-profiles" title="Ctrl+2"><svg class="icon" viewBox="0 0 24 24"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><path d="M12 11h4"/><path d="M12 16h4"/><path d="M8 11h.01"/><path d="M8 16h.01"/></svg> <span data-i18n="profiles.title">Profiles</span><span class="tab-badge" id="tab-badge-profiles" style="display:none"></span></button>
|
||||
<button class="tab-btn" data-tab="targets" onclick="switchTab('targets')" role="tab" aria-selected="false" aria-controls="tab-targets" id="tab-btn-targets" title="Ctrl+3"><svg class="icon" viewBox="0 0 24 24"><path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"/></svg> <span data-i18n="targets.title">Targets</span><span class="tab-badge" id="tab-badge-targets" style="display:none"></span></button>
|
||||
<button class="tab-btn" data-tab="streams" onclick="switchTab('streams')" role="tab" aria-selected="false" aria-controls="tab-streams" id="tab-btn-streams" title="Ctrl+4"><svg class="icon" viewBox="0 0 24 24"><path d="m17 2-5 5-5-5"/><rect width="20" height="15" x="2" y="7" rx="2"/></svg> <span data-i18n="streams.title">Sources</span></button>
|
||||
</div>
|
||||
|
||||
<div class="tab-panel" id="tab-dashboard" role="tabpanel" aria-labelledby="tab-btn-dashboard">
|
||||
@@ -158,7 +181,9 @@
|
||||
|
||||
function updateThemeIcon(theme) {
|
||||
const icon = document.getElementById('theme-icon');
|
||||
icon.textContent = theme === 'dark' ? '☀️' : '🌙';
|
||||
icon.innerHTML = theme === 'dark'
|
||||
? '<svg class="icon" viewBox="0 0 24 24"><circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/></svg>'
|
||||
: '<svg class="icon" viewBox="0 0 24 24"><path d="M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401"/></svg>';
|
||||
}
|
||||
|
||||
function toggleTheme() {
|
||||
@@ -168,9 +193,90 @@
|
||||
document.documentElement.setAttribute('data-theme', newTheme);
|
||||
localStorage.setItem('theme', newTheme);
|
||||
updateThemeIcon(newTheme);
|
||||
// Re-derive accent text variant for the new theme
|
||||
const accent = localStorage.getItem('accentColor');
|
||||
if (accent) applyAccentColor(accent, true);
|
||||
showToast(`Switched to ${newTheme} theme`, 'info');
|
||||
}
|
||||
|
||||
// Initialize accent color
|
||||
function adjustLightness(hex, amount) {
|
||||
const r = parseInt(hex.slice(1,3),16)/255;
|
||||
const g = parseInt(hex.slice(3,5),16)/255;
|
||||
const b = parseInt(hex.slice(5,7),16)/255;
|
||||
const max = Math.max(r,g,b), min = Math.min(r,g,b);
|
||||
let h, s, l = (max+min)/2;
|
||||
if (max===min) { h=s=0; } else {
|
||||
const d = max-min;
|
||||
s = l>0.5 ? d/(2-max-min) : d/(max+min);
|
||||
if (max===r) h=((g-b)/d+(g<b?6:0))/6;
|
||||
else if (max===g) h=((b-r)/d+2)/6;
|
||||
else h=((r-g)/d+4)/6;
|
||||
}
|
||||
l = Math.max(0, Math.min(1, l + amount/100));
|
||||
const hue2rgb = (p,q,t) => { if(t<0)t+=1; if(t>1)t-=1; if(t<1/6)return p+(q-p)*6*t; if(t<1/2)return q; if(t<2/3)return p+(q-p)*(2/3-t)*6; return p; };
|
||||
let rr,gg,bb;
|
||||
if (s===0) { rr=gg=bb=l; } else {
|
||||
const q = l<0.5 ? l*(1+s) : l+s-l*s, p = 2*l-q;
|
||||
rr=hue2rgb(p,q,h+1/3); gg=hue2rgb(p,q,h); bb=hue2rgb(p,q,h-1/3);
|
||||
}
|
||||
return '#'+[rr,gg,bb].map(x=>Math.round(x*255).toString(16).padStart(2,'0')).join('');
|
||||
}
|
||||
|
||||
function applyAccentColor(hex, silent) {
|
||||
const root = document.documentElement;
|
||||
root.style.setProperty('--primary-color', hex);
|
||||
const theme = root.getAttribute('data-theme');
|
||||
root.style.setProperty('--primary-text-color', adjustLightness(hex, theme === 'dark' ? 15 : -15));
|
||||
root.style.setProperty('--primary-hover', adjustLightness(hex, 8));
|
||||
document.getElementById('accent-swatch').style.background = hex;
|
||||
document.getElementById('accent-picker').value = hex;
|
||||
// Mark the active preset dot
|
||||
document.querySelectorAll('.accent-dot').forEach(d => {
|
||||
d.classList.toggle('active', d.style.background === hex || d.style.backgroundColor === hex
|
||||
|| d.style.background.toLowerCase() === hex.toLowerCase());
|
||||
});
|
||||
localStorage.setItem('accentColor', hex);
|
||||
if (!silent) showToast('Accent color updated', 'info');
|
||||
}
|
||||
|
||||
function toggleAccentPicker() {
|
||||
const pop = document.getElementById('accent-popover');
|
||||
const show = pop.style.display === 'none';
|
||||
pop.style.display = show ? '' : 'none';
|
||||
if (show) {
|
||||
// Mark active dot on open
|
||||
const cur = localStorage.getItem('accentColor') || '#4CAF50';
|
||||
document.querySelectorAll('.accent-dot').forEach(d => {
|
||||
const dColor = d.style.backgroundColor || d.style.background;
|
||||
d.classList.toggle('active', rgbToHex(dColor) === cur.toUpperCase());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function rgbToHex(rgb) {
|
||||
if (rgb.startsWith('#')) return rgb.toUpperCase();
|
||||
const m = rgb.match(/\d+/g);
|
||||
if (!m) return rgb;
|
||||
return '#' + m.slice(0,3).map(n => parseInt(n).toString(16).padStart(2,'0')).join('').toUpperCase();
|
||||
}
|
||||
|
||||
function pickAccent(hex) {
|
||||
applyAccentColor(hex);
|
||||
document.getElementById('accent-popover').style.display = 'none';
|
||||
}
|
||||
|
||||
// Close popover on outside click
|
||||
document.addEventListener('click', function(e) {
|
||||
const wrapper = document.querySelector('.accent-wrapper');
|
||||
if (wrapper && !wrapper.contains(e.target)) {
|
||||
document.getElementById('accent-popover').style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
const savedAccent = localStorage.getItem('accentColor');
|
||||
if (savedAccent) applyAccentColor(savedAccent, true);
|
||||
|
||||
// Initialize auth state
|
||||
function updateAuthUI() {
|
||||
const apiKey = localStorage.getItem('wled_api_key');
|
||||
@@ -228,10 +334,10 @@
|
||||
const button = document.querySelector('.password-toggle');
|
||||
if (input.type === 'password') {
|
||||
input.type = 'text';
|
||||
button.textContent = '🙈';
|
||||
button.innerHTML = '<svg class="icon" viewBox="0 0 24 24"><path d="M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49"/><path d="M14.084 14.158a3 3 0 0 1-4.242-4.242"/><path d="M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143"/><path d="m2 2 20 20"/></svg>';
|
||||
} else {
|
||||
input.type = 'password';
|
||||
button.textContent = '👁️';
|
||||
button.innerHTML = '<svg class="icon" viewBox="0 0 24 24"><path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0"/><circle cx="12" cy="12" r="3"/></svg>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="modal-header">
|
||||
<h2 id="add-device-modal-title" data-i18n="devices.add">Add New Device</h2>
|
||||
<div class="modal-header-actions">
|
||||
<button type="button" class="modal-header-btn" id="scan-network-btn" onclick="scanForDevices()" data-i18n-title="device.scan" title="Auto Discovery">🔍</button>
|
||||
<button type="button" class="modal-header-btn" id="scan-network-btn" onclick="scanForDevices()" data-i18n-title="device.scan" title="Auto Discovery"><svg class="icon" viewBox="0 0 24 24"><path d="m21 21-4.34-4.34"/><circle cx="11" cy="11" r="8"/></svg></button>
|
||||
<button class="modal-close-btn" onclick="closeAddDeviceModal()" title="Close" data-i18n-aria-label="aria.close">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div id="api-key-modal" class="modal" role="dialog" aria-modal="true" aria-labelledby="api-key-modal-title">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2 id="api-key-modal-title" data-i18n="auth.title">🔑 Login to LED Grab</h2>
|
||||
<h2 id="api-key-modal-title"><svg class="icon" viewBox="0 0 24 24"><path d="M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z"/><circle cx="16.5" cy="7.5" r=".5" fill="currentColor"/></svg> <span data-i18n="auth.title">Login to LED Grab</span></h2>
|
||||
<button class="modal-close-btn" id="modal-close-x-btn" onclick="closeApiKeyModal()" title="Close" data-i18n-aria-label="aria.close">✕</button>
|
||||
</div>
|
||||
<form id="api-key-form" onsubmit="submitApiKey(event)">
|
||||
@@ -25,7 +25,7 @@
|
||||
autocomplete="off"
|
||||
>
|
||||
<button type="button" class="password-toggle" onclick="togglePasswordVisibility()">
|
||||
👁️
|
||||
<svg class="icon" viewBox="0 0 24 24"><path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0"/><circle cx="12" cy="12" r="3"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div id="calibration-modal" class="modal" role="dialog" aria-modal="true" aria-labelledby="calibration-modal-title">
|
||||
<div class="modal-content" style="max-width: 700px;">
|
||||
<div class="modal-header">
|
||||
<h2 id="calibration-modal-title" data-i18n="calibration.title">📐 LED Calibration</h2>
|
||||
<h2 id="calibration-modal-title"><svg class="icon" viewBox="0 0 24 24"><path d="M21.3 15.3a2.4 2.4 0 0 1 0 3.4l-2.6 2.6a2.4 2.4 0 0 1-3.4 0L2.7 8.7a2.41 2.41 0 0 1 0-3.4l2.6-2.6a2.41 2.41 0 0 1 3.4 0Z"/><path d="m14.5 12.5 2-2"/><path d="m11.5 9.5 2-2"/><path d="m8.5 6.5 2-2"/><path d="m17.5 15.5 2-2"/></svg> <span data-i18n="calibration.title">LED Calibration</span></h2>
|
||||
<button id="calibration-tutorial-btn" class="tutorial-trigger-btn" onclick="startCalibrationTutorial()" data-i18n-title="calibration.tutorial.start" title="Start tutorial">?</button>
|
||||
<button class="modal-close-btn" onclick="closeCalibrationModal()" title="Close" data-i18n-aria-label="aria.close">✕</button>
|
||||
</div>
|
||||
@@ -34,7 +34,7 @@
|
||||
<!-- Screen with direction toggle, total LEDs, and offset -->
|
||||
<div class="preview-screen">
|
||||
<button type="button" class="direction-toggle" onclick="toggleDirection()" title="Toggle direction">
|
||||
<span id="direction-icon">↻</span> <span id="direction-label">CW</span>
|
||||
<span id="direction-icon"><svg class="icon" viewBox="0 0 24 24"><path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/></svg></span> <span id="direction-label">CW</span>
|
||||
</button>
|
||||
<div class="preview-screen-total" onclick="toggleEdgeInputs()" title="Toggle edge LED inputs"><span id="cal-total-leds-inline">0</span> / <span id="cal-device-led-count-inline">0</span></div>
|
||||
<div class="preview-screen-border-width">
|
||||
@@ -42,7 +42,7 @@
|
||||
<input type="number" id="cal-border-width" min="1" max="100" value="10">
|
||||
</div>
|
||||
<button id="calibration-overlay-btn" class="calibration-overlay-toggle" onclick="toggleCalibrationOverlay()" data-i18n-title="overlay.button.show" title="Show overlay visualization" style="display:none">
|
||||
💡 <span data-i18n="calibration.overlay_toggle">Overlay</span>
|
||||
<svg class="icon" viewBox="0 0 24 24"><path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5"/><path d="M9 18h6"/><path d="M10 22h4"/></svg> <span data-i18n="calibration.overlay_toggle">Overlay</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div id="css-editor-modal" class="modal" role="dialog" aria-modal="true" aria-labelledby="css-editor-title">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2 id="css-editor-title" data-i18n="color_strip.add">🎞️ Add Color Strip Source</h2>
|
||||
<h2 id="css-editor-title"><svg class="icon" viewBox="0 0 24 24"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M7 3v18"/><path d="M3 7.5h4"/><path d="M3 12h18"/><path d="M3 16.5h4"/><path d="M17 3v18"/><path d="M17 7.5h4"/><path d="M17 16.5h4"/></svg> <span data-i18n="color_strip.add">Add Color Strip Source</span></h2>
|
||||
<button class="modal-close-btn" onclick="closeCSSEditorModal()" title="Close" data-i18n-aria-label="aria.close">✕</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div id="device-settings-modal" class="modal" role="dialog" aria-modal="true" aria-labelledby="device-settings-modal-title">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2 id="device-settings-modal-title" data-i18n="settings.general.title">⚙️ General Settings</h2>
|
||||
<h2 id="device-settings-modal-title"><svg class="icon" viewBox="0 0 24 24"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915"/><circle cx="12" cy="12" r="3"/></svg> <span data-i18n="settings.general.title">General Settings</span></h2>
|
||||
<button class="modal-close-btn" onclick="closeDeviceSettingsModal()" title="Close" data-i18n-aria-label="aria.close">✕</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div id="kc-editor-modal" class="modal" role="dialog" aria-modal="true" aria-labelledby="kc-editor-title">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2 id="kc-editor-title" data-i18n="kc.add">🎨 Add Key Colors Target</h2>
|
||||
<h2 id="kc-editor-title"><svg class="icon" viewBox="0 0 24 24"><path d="M12 22a1 1 0 0 1 0-20 10 9 0 0 1 10 9 5 5 0 0 1-5 5h-2.25a1.75 1.75 0 0 0-1.4 2.8l.3.4a1.75 1.75 0 0 1-1.4 2.8z"/><circle cx="13.5" cy="6.5" r=".5" fill="currentColor"/><circle cx="17.5" cy="10.5" r=".5" fill="currentColor"/><circle cx="6.5" cy="12.5" r=".5" fill="currentColor"/><circle cx="8.5" cy="7.5" r=".5" fill="currentColor"/></svg> <span data-i18n="kc.add">Add Key Colors Target</span></h2>
|
||||
<button class="modal-close-btn" onclick="closeKCEditorModal()" title="Close" data-i18n-aria-label="aria.close">✕</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div id="pattern-template-modal" class="modal" role="dialog" aria-modal="true" aria-labelledby="pattern-template-modal-title">
|
||||
<div class="modal-content modal-content-wide">
|
||||
<div class="modal-header">
|
||||
<h2 id="pattern-template-modal-title" data-i18n="pattern.add">📄 Add Pattern Template</h2>
|
||||
<h2 id="pattern-template-modal-title"><svg class="icon" viewBox="0 0 24 24"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"/><path d="M14 2v5a1 1 0 0 0 1 1h5"/><path d="M10 9H8"/><path d="M16 13H8"/><path d="M16 17H8"/></svg> <span data-i18n="pattern.add">Add Pattern Template</span></h2>
|
||||
<button class="modal-close-btn" onclick="closePatternTemplateModal()" title="Close" data-i18n-aria-label="aria.close">✕</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
@@ -36,7 +36,7 @@
|
||||
<small class="input-hint" style="display:none" data-i18n="pattern.visual_editor.hint">Click + buttons to add rectangles. Drag edges to resize, drag inside to move.</small>
|
||||
<div class="pattern-bg-row">
|
||||
<select id="pattern-bg-source"></select>
|
||||
<button type="button" class="btn btn-icon btn-secondary pattern-capture-btn" onclick="capturePatternBackground()" title="Capture Background" data-i18n-title="pattern.capture_bg">📷</button>
|
||||
<button type="button" class="btn btn-icon btn-secondary pattern-capture-btn" onclick="capturePatternBackground()" title="Capture Background" data-i18n-title="pattern.capture_bg"><svg class="icon" viewBox="0 0 24 24"><path d="M13.997 4a2 2 0 0 1 1.76 1.05l.486.9A2 2 0 0 0 18.003 7H20a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h1.997a2 2 0 0 0 1.759-1.048l.489-.904A2 2 0 0 1 10.004 4z"/><circle cx="12" cy="13" r="3"/></svg></button>
|
||||
</div>
|
||||
<div class="pattern-canvas-container">
|
||||
<canvas id="pattern-canvas"></canvas>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div id="profile-editor-modal" class="modal" role="dialog" aria-modal="true" aria-labelledby="profile-editor-title">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2 id="profile-editor-title" data-i18n="profiles.add">📋 Add Profile</h2>
|
||||
<h2 id="profile-editor-title"><svg class="icon" viewBox="0 0 24 24"><rect width="8" height="4" x="8" y="2" rx="1" ry="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><path d="M12 11h4"/><path d="M12 16h4"/><path d="M8 11h.01"/><path d="M8 16h.01"/></svg> <span data-i18n="profiles.add">Add Profile</span></h2>
|
||||
<button class="modal-close-btn" onclick="closeProfileEditorModal()" title="Close" data-i18n-aria-label="aria.close">✕</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div id="target-editor-modal" class="modal" role="dialog" aria-modal="true" aria-labelledby="target-editor-title">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2 id="target-editor-title" data-i18n="targets.add">🎯 Add Target</h2>
|
||||
<h2 id="target-editor-title"><svg class="icon" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/></svg> <span data-i18n="targets.add">Add Target</span></h2>
|
||||
<button class="modal-close-btn" onclick="closeTargetEditorModal()" title="Close" data-i18n-aria-label="aria.close">✕</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
</div>
|
||||
|
||||
<button type="button" id="test-audio-template-start-btn" class="btn btn-primary" onclick="startAudioTemplateTest()" style="margin-top: 8px;">
|
||||
<span data-i18n="audio_template.test.run">🧪 Run</span>
|
||||
<svg class="icon" viewBox="0 0 24 24"><path d="M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2"/><path d="M6.453 15h11.094"/><path d="M8.5 2h7"/></svg> <span data-i18n="audio_template.test.run">Run</span>
|
||||
</button>
|
||||
|
||||
<canvas id="audio-template-test-canvas" class="audio-test-canvas" style="display:none; margin-top: 12px;"></canvas>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-primary" onclick="runPPTemplateTest()" style="margin-top: 16px;">
|
||||
<span data-i18n="streams.test.run">🧪 Run</span>
|
||||
<svg class="icon" viewBox="0 0 24 24"><path d="M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2"/><path d="M6.453 15h11.094"/><path d="M8.5 2h7"/></svg> <span data-i18n="streams.test.run">Run</span>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-primary" onclick="runStreamTest()" style="margin-top: 16px;">
|
||||
<span data-i18n="streams.test.run">🧪 Run</span>
|
||||
<svg class="icon" viewBox="0 0 24 24"><path d="M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2"/><path d="M6.453 15h11.094"/><path d="M8.5 2h7"/></svg> <span data-i18n="streams.test.run">Run</span>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-primary" onclick="runTemplateTest()" style="margin-top: 16px;">
|
||||
<span data-i18n="templates.test.run">🧪 Run</span>
|
||||
<svg class="icon" viewBox="0 0 24 24"><path d="M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2"/><path d="M6.453 15h11.094"/><path d="M8.5 2h7"/></svg> <span data-i18n="templates.test.run">Run</span>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user