Update panel: always show version status (up-to-date or update available)

Replace dismissable toast with persistent status row showing a green
dot + "Up to date" with commit hash, or amber dot + "Update available"
with an inline Install button. One or the other is always visible.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
roberts 2026-03-14 21:49:37 -05:00
parent eb1aa9d434
commit 436a009059
2 changed files with 65 additions and 60 deletions

View file

@ -213,74 +213,72 @@
color: var(--accent);
}
/* Update toast */
/* Update status */
.panel-updates {
transition: all var(--transition-base);
}
.update-toast {
position: relative;
padding: 12px;
background: var(--accent-dim);
border-left: 3px solid var(--accent);
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}
.update-dismiss {
position: absolute;
top: 4px;
right: 4px;
width: 24px;
height: 24px;
background: none;
border: none;
color: var(--text-muted);
cursor: pointer;
font-size: 14px;
.update-status {
display: flex;
align-items: center;
justify-content: center;
border-radius: var(--radius-sm);
gap: 10px;
}
.update-dismiss:hover {
background: var(--accent-hover);
color: var(--text-primary);
.update-status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
flex-shrink: 0;
}
.update-toast-title {
.update-status-dot.current {
background: var(--status-green);
}
.update-status-dot.available {
background: var(--status-amber);
}
.update-status-title {
font-family: var(--font-ui);
font-size: 13px;
font-size: 12px;
font-weight: 500;
color: var(--accent);
margin-bottom: 4px;
color: var(--text-primary);
line-height: 1.2;
}
.update-toast-info {
.update-status-hash {
font-family: var(--font-mono);
font-size: 11px;
color: var(--text-secondary);
margin-bottom: 10px;
font-size: 10px;
color: var(--text-muted);
line-height: 1.3;
}
.update-toast-btn {
width: 100%;
height: 30px;
.update-status-text {
flex: 1;
min-width: 0;
}
.update-install-btn {
flex-shrink: 0;
height: 26px;
padding: 0 12px;
background: var(--accent);
border: none;
border-radius: var(--radius-sm);
color: #fff;
font-family: var(--font-ui);
font-size: 12px;
font-size: 11px;
font-weight: 500;
cursor: pointer;
white-space: nowrap;
}
.update-toast-btn:hover {
.update-install-btn:hover {
opacity: 0.9;
}
.update-toast-btn:disabled {
.update-install-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}

View file

@ -391,42 +391,50 @@
// =====================================================================
// Panel — Update checker
// =====================================================================
let updateDismissed = false;
async function checkForUpdates() {
if (updateDismissed) return;
const panel = $('#panelUpdates');
if (!panel) return;
try {
const res = await Atlus.apiFetch('/api/updates/check');
if (!res || !res.ok) return;
const data = await res.json();
const panel = $('#panelUpdates');
if (!panel) return;
panel.classList.remove('hidden');
if (data.available && data.behind_count > 0) {
showUpdateToast(panel, data);
showUpdateAvailable(panel, data);
} else {
panel.classList.add('hidden');
showUpToDate(panel, data);
}
} catch (e) { /* ignore */ }
}
function showUpdateToast(panel, data) {
panel.classList.remove('hidden');
function showUpToDate(panel, data) {
panel.innerHTML = `
<div class="update-toast">
<button class="update-dismiss" title="Dismiss">&times;</button>
<div class="update-toast-title">Update available</div>
<div class="update-toast-info">${data.behind_count} commit${data.behind_count !== 1 ? 's' : ''} behind (${data.remote_hash})</div>
<button class="update-toast-btn">Install Update</button>
<div class="update-status update-current">
<span class="update-status-dot current"></span>
<div class="update-status-text">
<div class="update-status-title">Up to date</div>
<div class="update-status-hash">${data.local_hash}</div>
</div>
</div>
`;
}
function showUpdateAvailable(panel, data) {
panel.innerHTML = `
<div class="update-status update-available">
<span class="update-status-dot available"></span>
<div class="update-status-text">
<div class="update-status-title">Update available</div>
<div class="update-status-hash">${data.behind_count} commit${data.behind_count !== 1 ? 's' : ''} behind (${data.remote_hash})</div>
</div>
<button class="update-install-btn" title="Install update">Update</button>
</div>
`;
panel.querySelector('.update-dismiss').addEventListener('click', () => {
panel.classList.add('hidden');
updateDismissed = true;
});
panel.querySelector('.update-toast-btn').addEventListener('click', async (e) => {
panel.querySelector('.update-install-btn').addEventListener('click', async (e) => {
const btn = e.target;
btn.disabled = true;
btn.textContent = 'Updating…';
@ -435,10 +443,9 @@
const res = await Atlus.apiFetch('/api/updates/apply', { method: 'POST' });
if (res.ok) {
btn.textContent = 'Restarting…';
// Server will restart — try to reload after a delay
setTimeout(() => attemptReload(0), 4000);
} else {
btn.textContent = 'Update failed';
btn.textContent = 'Failed';
btn.disabled = false;
}
} catch (e) {