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
-
-
-
-
-
-
-
-