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:
parent
eb1aa9d434
commit
436a009059
2 changed files with 65 additions and 60 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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">×</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) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue