feat: add manual 'Check for Updates' button in System settings (#785)

Add a 'Check now' button next to the version badge in the System
settings section, allowing users to manually trigger an update check
at any time without waiting for the automatic periodic check.

Changes:
- index.html: add button with spinner and status text inline with version badge
- panels.js: add checkUpdatesNow() calling /api/updates/check?force=1
  with immediate feedback (checking... / up to date / X updates available)
- style.css: style the button block and spinner
- i18n.js: add 5 new keys (settings_check_now, settings_checking,
  settings_up_to_date, settings_updates_available, settings_updates_disabled)
  in all 6 locales (en, ru, es, de, zh, zh-Hant)
This commit is contained in:
bergeouss
2026-04-25 16:44:13 +00:00
parent 12a8c051fb
commit bc85efe01a
4 changed files with 77 additions and 1 deletions
+30
View File
@@ -231,6 +231,11 @@ const LOCALES = {
settings_section_preferences_meta: 'Defaults and UI behavior for Hermes Web UI.',
settings_section_system_title: 'System',
settings_section_system_meta: 'Instance version and access controls.',
settings_check_now: 'Check now',
settings_checking: 'Checking\u2026',
settings_up_to_date: 'Up to date \u2713',
settings_updates_available: '{count} update(s) available',
settings_updates_disabled: 'Update checks disabled',
settings_dropdown_conversation: 'Conversation',
settings_dropdown_appearance: 'Appearance',
settings_dropdown_preferences: 'Preferences',
@@ -1174,6 +1179,11 @@ const LOCALES = {
settings_section_preferences_meta: 'Defaults and UI behavior for Hermes Web UI.',
settings_section_preferences_title: 'Preferences',
settings_section_system_meta: 'Instance version and access controls.',
settings_check_now: 'Проверить',
settings_checking: 'Проверка\u2026',
settings_up_to_date: 'Актуально \u2713',
settings_updates_available: 'Доступно обновлений: {count}',
settings_updates_disabled: 'Проверка обновлений отключена',
settings_section_system_title: 'System',
settings_tab_appearance: 'Appearance',
settings_tab_conversation: 'Conversation',
@@ -1714,6 +1724,11 @@ const LOCALES = {
settings_section_preferences_meta: 'Defaults and UI behavior for Hermes Web UI.',
settings_section_preferences_title: 'Preferences',
settings_section_system_meta: 'Instance version and access controls.',
settings_check_now: 'Comprobar ahora',
settings_checking: 'Comprobando\u2026',
settings_up_to_date: 'Actualizado \u2713',
settings_updates_available: '{count} actualización(es) disponible(s)',
settings_updates_disabled: 'Comprobación de actualizaciones desactivada',
settings_section_system_title: 'System',
settings_tab_appearance: 'Appearance',
settings_tab_conversation: 'Conversation',
@@ -2038,6 +2053,11 @@ const LOCALES = {
settings_section_preferences_meta: 'Defaults and UI behavior for Hermes Web UI.',
settings_section_preferences_title: 'Preferences',
settings_section_system_meta: 'Instance version and access controls.',
settings_check_now: 'Jetzt prüfen',
settings_checking: 'Prüfung\u2026',
settings_up_to_date: 'Aktuell \u2713',
settings_updates_available: '{count} Update(s) verfügbar',
settings_updates_disabled: 'Update-Prüfung deaktiviert',
settings_section_system_title: 'System',
settings_tab_appearance: 'Appearance',
settings_tab_conversation: 'Conversation',
@@ -2575,6 +2595,11 @@ const LOCALES = {
settings_section_preferences_meta: 'Defaults and UI behavior for Hermes Web UI.',
settings_section_preferences_title: 'Preferences',
settings_section_system_meta: 'Instance version and access controls.',
settings_check_now: '立即检查',
settings_checking: '检查中\u2026',
settings_up_to_date: '已是最新 \u2713',
settings_updates_available: '有 {count} 个更新可用',
settings_updates_disabled: '更新检查已禁用',
settings_section_system_title: 'System',
settings_tab_appearance: 'Appearance',
settings_tab_conversation: 'Conversation',
@@ -2742,6 +2767,11 @@ const LOCALES = {
settings_section_preferences_meta: 'Hermes Web UI 的預設值與介面行為。',
settings_section_system_title: '系統',
settings_section_system_meta: '實例版本與存取控制。',
settings_check_now: '立即檢查',
settings_checking: '檢查中\u2026',
settings_up_to_date: '已是最新 \u2713',
settings_updates_available: '有 {count} 個更新可用',
settings_updates_disabled: '更新檢查已禁用',
settings_dropdown_conversation: '對話',
settings_dropdown_appearance: '外觀',
settings_dropdown_preferences: '偏好設定',
+5 -1
View File
@@ -684,7 +684,11 @@
<div class="settings-section-title" data-i18n="settings_section_system_title">System</div>
<div class="settings-section-meta" data-i18n="settings_section_system_meta">Instance version and access controls.</div>
</div>
<span class="settings-version-badge"></span>
<div style="display:flex;align-items:center;gap:8px;flex-shrink:0">
<span class="settings-version-badge"></span>
<button class="sm-btn" id="btnCheckUpdatesNow" onclick="checkUpdatesNow()" title="Check for updates now" data-i18n-title="settings_check_now" style="padding:4px 10px;font-size:11px;display:flex;align-items:center;gap:4px"><svg id="checkUpdatesSpinner" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:12px;height:12px;display:none" aria-hidden="true"><path d="M21 12a9 9 0 1 1-6.219-8.56"/><polyline points="21 3 21 9 15 9"/></svg><span id="checkUpdatesLabel" data-i18n="settings_check_now">Check now</span></button>
<span id="checkUpdatesStatus" style="font-size:11px;white-space:nowrap"></span>
</div>
</div>
<div class="settings-field">
<label for="settingsPassword" data-i18n="settings_label_password">Access Password</label>
+36
View File
@@ -2507,6 +2507,42 @@ function _applySavedSettingsUi(saved, body, opts){
if(typeof renderSessionList==='function') renderSessionList();
}
async function checkUpdatesNow(){
const btn=$('btnCheckUpdatesNow');
const label=$('checkUpdatesLabel');
const spinner=$('checkUpdatesSpinner');
const status=$('checkUpdatesStatus');
if(!btn||!label) return;
// Disable button, show spinner
btn.disabled=true;
if(spinner) spinner.style.display='';
if(label) label.textContent=t('settings_checking');
if(status) status.textContent='';
try {
const data=await api('/api/updates/check?force=1');
if(data.disabled){
if(status){status.textContent=t('settings_updates_disabled');status.style.color='var(--muted)';}
} else {
const parts=[];
if(data.webui&&data.webui.behind>0) parts.push('WebUI: '+data.webui.behind);
if(data.agent&&data.agent.behind>0) parts.push('Agent: '+data.agent.behind);
if(parts.length){
if(status){status.textContent=t('settings_updates_available').replace('{count}',parts.join(', '));status.style.color='var(--accent)';}
// Also trigger the update banner
if(typeof _showUpdateBanner==='function') _showUpdateBanner(data);
} else {
if(status){status.textContent=t('settings_up_to_date');status.style.color='var(--success)';}
}
}
} catch(e){
if(status){status.textContent=t('failed_colon')+e.message;status.style.color='var(--error)';}
} finally {
btn.disabled=false;
if(spinner) spinner.style.display='none';
if(label) label.textContent=t('settings_check_now');
}
}
async function saveSettings(andClose){
const model=($('settingsModel')||{}).value;
const modelChanged=(model||'')!==(_settingsHermesDefaultModelOnOpen||'');
+6
View File
@@ -1550,6 +1550,12 @@ main.main.showing-profiles > #mainProfiles{display:flex;}
.settings-section-title{font-size:18px;font-weight:600;letter-spacing:-.01em;color:var(--text);line-height:1.3;margin-bottom:4px;}
.settings-section-meta{font-size:13px;color:var(--muted);line-height:1.55;}
.settings-version-badge{display:inline-flex;align-items:center;padding:3px 8px;border-radius:999px;background:var(--surface);color:var(--muted);font-size:11px;font-weight:600;font-family:'SF Mono',ui-monospace,SFMono-Regular,Menlo,monospace;flex-shrink:0;align-self:flex-start;letter-spacing:.02em;}
#checkUpdatesBlock{display:inline-flex;align-items:center;gap:6px;font-size:12px;flex-shrink:0;align-self:flex-start;}
#checkUpdatesBlock .btn-tiny{padding:4px 10px;border-radius:8px;border:1px solid var(--border);background:var(--surface);color:var(--text);font-size:11px;font-weight:500;cursor:pointer;display:inline-flex;align-items:center;gap:5px;transition:border-color .15s,color .15s;}
#checkUpdatesBlock .btn-tiny:hover{border-color:var(--accent);color:var(--accent);}
#checkUpdatesBlock .btn-tiny:disabled{opacity:.5;cursor:default;}
#checkUpdatesBlock .btn-tiny .spinner-xs{width:12px;height:12px;border:2px solid var(--border);border-top-color:var(--text);border-radius:50%;animation:spin .6s linear infinite;display:none;}
#checkUpdatesStatus{font-size:11px;font-weight:500;white-space:nowrap;}
/* Each logical form row is a card surface. Stack with comfortable gap. */
#mainSettings .settings-field{margin-bottom:12px;padding:16px;border:1px solid var(--border);border-radius:12px;background:var(--sidebar);}