diff --git a/server/src/wled_controller/static/app.js b/server/src/wled_controller/static/app.js index 19b34d7..49956a2 100644 --- a/server/src/wled_controller/static/app.js +++ b/server/src/wled_controller/static/app.js @@ -164,8 +164,8 @@ document.addEventListener('DOMContentLoaded', async () => { // Show content now that translations are loaded document.body.style.visibility = 'visible'; - // Restore collapsible section states - initCollapsibleSections(); + // Restore active tab + initTabs(); // Load API key from localStorage apiKey = localStorage.getItem('wled_api_key'); @@ -298,7 +298,8 @@ async function loadDisplays() { return; } - // Render visual layout + // Cache and render visual layout + _cachedDisplays = data.displays; renderDisplayLayout(data.displays); } catch (error) { console.error('Failed to load displays:', error); @@ -309,25 +310,22 @@ 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'); +let _cachedDisplays = null; + +function switchTab(name) { + document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.toggle('active', btn.dataset.tab === name)); + document.querySelectorAll('.tab-panel').forEach(panel => panel.classList.toggle('active', panel.id === `tab-${name}`)); + localStorage.setItem('activeTab', name); + if (name === 'displays' && _cachedDisplays) { + requestAnimationFrame(() => renderDisplayLayout(_cachedDisplays)); + } } -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 initTabs() { + const saved = localStorage.getItem('activeTab'); + if (saved && document.getElementById(`tab-${saved}`)) { + switchTab(saved); + } } function renderDisplayLayout(displays) { @@ -409,7 +407,10 @@ async function loadDevices() { const container = document.getElementById('devices-list'); if (!devices || devices.length === 0) { - container.innerHTML = `
${t('devices.none')}
`; + container.innerHTML = `
+
+
+
${t('devices.add')}
+
`; return; } @@ -473,7 +474,11 @@ async function loadDevices() { }) ); - container.innerHTML = devicesWithState.map(device => createDeviceCard(device)).join(''); + container.innerHTML = devicesWithState.map(device => createDeviceCard(device)).join('') + + `
+
+
+
${t('devices.add')}
+
`; // Update footer WLED Web UI link with first device's URL const webuiLink = document.querySelector('.wled-webui-link'); diff --git a/server/src/wled_controller/static/index.html b/server/src/wled_controller/static/index.html index 12e81bd..7bc7f9f 100644 --- a/server/src/wled_controller/static/index.html +++ b/server/src/wled_controller/static/index.html @@ -32,12 +32,13 @@ -
-
-

💡 Devices

- +
+
+ +
-
+ +

WLED Configuration: Configure your WLED device (effects, segments, color order, power limits, etc.) using the official WLED app @@ -50,11 +51,8 @@

Loading devices...
-
-
-

🖥️ Displays

-
+
Loading layout...
@@ -62,7 +60,7 @@
-
+