From c9929e3b7fa5d232f417527847b24d1112ca589f Mon Sep 17 00:00:00 2001 From: "alexei.dolgolyov" Date: Mon, 9 Feb 2026 01:57:43 +0300 Subject: [PATCH] Restyle main page: collapsible sections, theme-aware ticks, UI polish - Make Devices and Displays sections collapsible with persistent state - Move WLED config tip from footer to under Devices heading - Add theme-aware colors for calibration canvas ticks and chevrons - Rename sections to "Devices" and "Displays" with emoji prefix icons - Fix display layout scaling to fill available space - Remove unused footer-tip and modal code Co-Authored-By: Claude Opus 4.6 --- server/src/wled_controller/static/app.js | 43 ++++++++++-- server/src/wled_controller/static/index.html | 44 ++++++------ .../wled_controller/static/locales/en.json | 4 +- .../wled_controller/static/locales/ru.json | 4 +- server/src/wled_controller/static/style.css | 67 +++++++++++++++---- 5 files changed, 116 insertions(+), 46 deletions(-) diff --git a/server/src/wled_controller/static/app.js b/server/src/wled_controller/static/app.js index 9f1a5a2..19b34d7 100644 --- a/server/src/wled_controller/static/app.js +++ b/server/src/wled_controller/static/app.js @@ -164,6 +164,9 @@ document.addEventListener('DOMContentLoaded', async () => { // Show content now that translations are loaded document.body.style.visibility = 'visible'; + // Restore collapsible section states + initCollapsibleSections(); + // Load API key from localStorage apiKey = localStorage.getItem('wled_api_key'); @@ -306,6 +309,27 @@ async function loadDisplays() { } } +function toggleSection(name) { + const header = document.querySelector(`.collapsible-header[data-section="${name}"]`); + const content = document.getElementById(`${name}-content`); + if (!header || !content) return; + const collapsed = !header.classList.contains('collapsed'); + header.classList.toggle('collapsed', collapsed); + content.classList.toggle('collapsed', collapsed); + localStorage.setItem(`section_${name}_collapsed`, collapsed ? '1' : '0'); +} + +function initCollapsibleSections() { + document.querySelectorAll('.collapsible-header[data-section]').forEach(header => { + const name = header.getAttribute('data-section'); + const content = document.getElementById(`${name}-content`); + if (localStorage.getItem(`section_${name}_collapsed`) === '1' && content) { + header.classList.add('collapsed'); + content.classList.add('collapsed'); + } + }); +} + function renderDisplayLayout(displays) { const canvas = document.getElementById('display-layout-canvas'); @@ -329,11 +353,10 @@ function renderDisplayLayout(displays) { // Scale factor to fit in canvas (respect available width, maintain aspect ratio) const availableWidth = canvas.clientWidth - 60; // account for padding - const maxCanvasWidth = Math.min(600, availableWidth); - const maxCanvasHeight = 450; - const scaleX = maxCanvasWidth / totalWidth; + const maxCanvasHeight = 350; + const scaleX = availableWidth / totalWidth; const scaleY = maxCanvasHeight / totalHeight; - const scale = Math.min(scaleX, scaleY, 0.3); // Max 0.3 scale to keep monitors reasonably sized + const scale = Math.min(scaleX, scaleY); const canvasWidth = totalWidth * scale; const canvasHeight = totalHeight * scale; @@ -1199,6 +1222,12 @@ function renderCalibrationCanvas() { const totalLeds = calibration.leds_top + calibration.leds_right + calibration.leds_bottom + calibration.leds_left; + // Theme-aware colors + const isDark = document.documentElement.getAttribute('data-theme') !== 'light'; + const tickStroke = isDark ? 'rgba(255, 255, 255, 0.4)' : 'rgba(0, 0, 0, 0.3)'; + const tickFill = isDark ? 'rgba(255, 255, 255, 0.65)' : 'rgba(0, 0, 0, 0.6)'; + const chevronStroke = isDark ? 'rgba(255, 255, 255, 0.6)' : 'rgba(0, 0, 0, 0.4)'; + // Edge bar geometry (matches CSS: corner zones 56px × 36px fixed) const cw = 56; const ch = 36; @@ -1287,9 +1316,9 @@ function renderCalibrationCanvas() { // Tick styling const tickLen = 5; - ctx.strokeStyle = 'rgba(255, 255, 255, 0.4)'; + ctx.strokeStyle = tickStroke; ctx.lineWidth = 1; - ctx.fillStyle = 'rgba(255, 255, 255, 0.65)'; + ctx.fillStyle = tickFill; ctx.font = '12px -apple-system, BlinkMacSystemFont, sans-serif'; labelsToShow.forEach(i => { @@ -1347,7 +1376,7 @@ function renderCalibrationCanvas() { ctx.translate(mx, my); ctx.rotate(angle); ctx.fillStyle = 'rgba(76, 175, 80, 0.85)'; - ctx.strokeStyle = 'rgba(255, 255, 255, 0.6)'; + ctx.strokeStyle = chevronStroke; ctx.lineWidth = 1; ctx.lineCap = 'round'; ctx.lineJoin = 'round'; diff --git a/server/src/wled_controller/static/index.html b/server/src/wled_controller/static/index.html index d579ad3..12e81bd 100644 --- a/server/src/wled_controller/static/index.html +++ b/server/src/wled_controller/static/index.html @@ -32,31 +32,13 @@ -
-

Display Layout

- -
-
-
Loading layout...
-
-
- - -
-
-

WLED Devices

+

💡 Devices

-
-
Loading devices...
-
-
- -