From e44ee2fe647b4ca0d5e63665bbc0167cab4f1792 Mon Sep 17 00:00:00 2001 From: roberts Date: Sun, 15 Mar 2026 00:24:16 -0500 Subject: [PATCH] Fix panel apps not rendering and Add button disappearing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restructured loadPanelApps() to use per-operation try/catch instead of one giant try/catch that silently swallowed errors. The Add button is now appended unconditionally after the app list, so it always remains visible. Each app row renders independently — a single bad app config won't break the entire panel. Co-Authored-By: Claude Opus 4.6 --- frontend/js/atlus.js | 144 ++++++++++++++++++++++++------------------- 1 file changed, 81 insertions(+), 63 deletions(-) diff --git a/frontend/js/atlus.js b/frontend/js/atlus.js index 0eef45d..1804782 100644 --- a/frontend/js/atlus.js +++ b/frontend/js/atlus.js @@ -338,36 +338,54 @@ const container = $('#panelApps'); if (!container) return; + let guiApps = []; + let runningApps = []; + try { const cfgRes = await Atlus.apiFetch('/api/settings'); - if (!cfgRes.ok) return; - const cfg = await cfgRes.json(); - const guiApps = cfg.gui_apps || []; + if (cfgRes && cfgRes.ok) { + const cfg = await cfgRes.json(); + guiApps = cfg.gui_apps || []; + } + } catch (e) { + console.warn('Panel apps: failed to load config', e); + } - // Get running apps (may 503 if display deps unavailable) - let runningApps = []; + try { + const runRes = await Atlus.apiFetch('/api/display/apps'); + if (runRes && runRes.ok) runningApps = await runRes.json(); + } catch (e) { /* display may be unavailable */ } + + // Ensure all GUI apps are registered as Atlus app modules + for (const app of guiApps) { try { - const runRes = await Atlus.apiFetch('/api/display/apps'); - if (runRes.ok) runningApps = await runRes.json(); - } catch (e) { /* ignore */ } - - // Ensure all GUI apps are registered as Atlus app modules - for (const app of guiApps) { const appId = 'gui-' + app.id; if (!Atlus.apps[appId] && window._atlusRegisterGuiApp) { window._atlusRegisterGuiApp(app); } + } catch (e) { + console.warn('Panel apps: failed to register', app.id, e); } + } - container.innerHTML = ''; + // Clear and rebuild — always append Add button at the end + container.innerHTML = ''; - if (guiApps.length === 0) { - container.innerHTML = '
No apps configured
'; - } else { - for (const app of guiApps) { - const running = runningApps.find(r => r.command === app.command && r.alive); + if (guiApps.length === 0) { + const empty = document.createElement('div'); + empty.style.cssText = 'color:var(--text-muted);font-size:12px;font-family:var(--font-mono);padding:4px 0;'; + empty.textContent = 'No apps configured'; + container.appendChild(empty); + } else { + for (const app of guiApps) { + try { + const running = Array.isArray(runningApps) + ? runningApps.find(r => r.command === app.command && r.alive) + : null; const row = document.createElement('div'); row.className = 'panel-app-row'; + + const args = Array.isArray(app.args) ? app.args.join(' ') : ''; row.innerHTML = `
${app.icon || '🖥'} @@ -375,13 +393,13 @@ `; - // Click name to open in tab + // Click name/icon to open in tab row.querySelector('.panel-app-name').addEventListener('click', () => { openApp('gui-' + app.id); }); @@ -389,54 +407,54 @@ openApp('gui-' + app.id); }); + // Action button (launch/stop) + const actionBtn = row.querySelector('.panel-app-action'); + actionBtn.addEventListener('click', async (e) => { + e.stopPropagation(); + const isRunning = actionBtn.classList.contains('stop'); + actionBtn.disabled = true; + + try { + if (isRunning) { + const id = actionBtn.dataset.appId; + if (id) await Atlus.apiFetch(`/api/display/apps/${id}`, { method: 'DELETE' }); + } else { + const a = actionBtn.dataset.args ? actionBtn.dataset.args.split(' ').filter(Boolean) : []; + await Atlus.apiFetch('/api/display/apps', { + method: 'POST', + body: { + command: actionBtn.dataset.command, + title: actionBtn.dataset.title, + args: a, + target_fps: parseInt(actionBtn.dataset.fps) || 10, + }, + }); + } + } catch (err) { + console.warn('Panel app action failed', err); + } + setTimeout(loadPanelApps, 1500); + }); + container.appendChild(row); + } catch (e) { + console.warn('Panel apps: failed to render', app.id, e); } } + } - // Add App button - const addBtn = document.createElement('button'); - addBtn.className = 'panel-app-add'; - addBtn.textContent = '+ Add'; - addBtn.addEventListener('click', () => { - openApp('settings'); - // Navigate to Applications section after a brief delay - setTimeout(() => { - const navItem = document.querySelector('.settings-nav-item[data-section="applications"]'); - if (navItem) navItem.click(); - }, 200); - }); - container.appendChild(addBtn); - - // Action button handlers (launch/stop) - container.querySelectorAll('.panel-app-action').forEach(btn => { - btn.addEventListener('click', async (e) => { - e.stopPropagation(); - const isRunning = btn.classList.contains('stop'); - btn.disabled = true; - - if (isRunning) { - // Stop the app - const appId = btn.dataset.appId; - if (appId) { - await Atlus.apiFetch(`/api/display/apps/${appId}`, { method: 'DELETE' }); - } - } else { - // Launch the app - const args = btn.dataset.args ? btn.dataset.args.split(' ').filter(Boolean) : []; - await Atlus.apiFetch('/api/display/apps', { - method: 'POST', - body: { - command: btn.dataset.command, - title: btn.dataset.title, - args: args, - target_fps: parseInt(btn.dataset.fps) || 10, - }, - }); - } - setTimeout(loadPanelApps, 1500); - }); - }); - } catch (e) { /* ignore */ } + // Add App button — ALWAYS appended, regardless of errors above + const addBtn = document.createElement('button'); + addBtn.className = 'panel-app-add'; + addBtn.textContent = '+ Add'; + addBtn.addEventListener('click', () => { + openApp('settings'); + setTimeout(() => { + const navItem = document.querySelector('.settings-nav-item[data-section="applications"]'); + if (navItem) navItem.click(); + }, 200); + }); + container.appendChild(addBtn); } // =====================================================================