Fix panel apps not rendering and Add button disappearing
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 <noreply@anthropic.com>
This commit is contained in:
parent
14f5ce7cf1
commit
e44ee2fe64
1 changed files with 81 additions and 63 deletions
|
|
@ -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 = '<div style="color:var(--text-muted);font-size:12px;font-family:var(--font-mono);padding:4px 0;">No apps configured</div>';
|
||||
} 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 = `
|
||||
<div class="panel-app-dot ${running ? 'active' : ''}"></div>
|
||||
<span class="panel-app-icon">${app.icon || '🖥'}</span>
|
||||
|
|
@ -375,13 +393,13 @@
|
|||
<button class="panel-app-action ${running ? 'stop' : 'start'}"
|
||||
data-command="${app.command}" data-app-id="${running ? running.app_id : ''}"
|
||||
data-gui-id="${app.id}" data-title="${app.name || app.command}"
|
||||
data-args="${(app.args || []).join(' ')}" data-fps="${app.target_fps || 10}"
|
||||
data-args="${args}" data-fps="${app.target_fps || 10}"
|
||||
title="${running ? 'Stop' : 'Launch'}">
|
||||
${running ? '■' : '▶'}
|
||||
</button>
|
||||
`;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
|
|
|
|||
Loading…
Reference in a new issue