fix: harden UI actions and daemon calls for reliable control (v1.3.2)

Retry and serialize pineapd/hak5 calls, queue virtual-pager keys, and grey out buttons until the pager finishes. Deploy now installs python3-light after factory firmware. Bump version to 1.3.2.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-20 15:49:10 -05:00
co-authored by Cursor
parent cd26553d21
commit 7d48b7ad06
25 changed files with 3625 additions and 782 deletions
@@ -241,7 +241,7 @@ const App = (() => {
});
}
function handleMenuAction(action) {
function handleMenuAction(action, button) {
closeToolbarMenus();
if (action === 'help') {
location.hash = '#/settings/help';
@@ -252,14 +252,18 @@ const App = (() => {
if (typeof views.openClientModeModal === 'function') views.openClientModeModal();
else checkInternet(true);
} else if (action === 'logout') {
if (button) { button.disabled = true; button.classList.add('busy'); }
PagerAPI.post('/api/logout')
.then(() => showLogin())
.catch((error) => toast(error.message || 'Logout failed', 'error'));
.catch((error) => toast(error.message || 'Logout failed', 'error'))
.finally(() => { if (button) { button.disabled = false; button.classList.remove('busy'); } });
} else if (action === 'reboot') {
if (!window.confirm('Reboot Mark VIII now?')) return;
if (button) { button.disabled = true; button.classList.add('busy'); }
PagerAPI.post('/api/settings/reboot')
.then(() => toast('Reboot requested. Mark VIII will disconnect shortly.'))
.catch((error) => toast(error.message || 'Reboot failed', 'error'));
.catch((error) => toast(error.message || 'Reboot failed', 'error'))
.finally(() => { if (button) { button.disabled = false; button.classList.remove('busy'); } });
}
}
@@ -332,7 +336,7 @@ const App = (() => {
renderNotifications();
});
Array.prototype.forEach.call(els.overflowMenu.querySelectorAll('[data-menu-action]'), (item) => {
item.addEventListener('click', () => handleMenuAction(item.getAttribute('data-menu-action')));
item.addEventListener('click', () => handleMenuAction(item.getAttribute('data-menu-action'), item));
});
document.addEventListener('click', closeToolbarMenus);
@@ -342,13 +346,14 @@ const App = (() => {
const pw = document.getElementById('login-password').value;
document.getElementById('login-error').textContent = '';
btn.disabled = true;
btn.classList.add('busy');
PagerAPI.login('root', pw)
.then(() => { document.getElementById('login-password').value = ''; showApp(); toast('Logged in'); })
.catch((err) => {
document.getElementById('login-error').textContent = (err && err.message && err.message !== 'unauthorized')
? 'Login failed.' : 'Invalid credentials.';
})
.finally(() => { btn.disabled = false; });
.finally(() => { btn.disabled = false; btn.classList.remove('busy'); });
});
document.getElementById('terminal-btn').addEventListener('click', () => {