feat: add auto-update system with release checking, notification UI, and install-type-aware apply
- Abstract ReleaseProvider interface (Gitea impl, swappable for GitHub/GitLab) - Background UpdateService with periodic checks, debounce, dismissed version persistence - Install type detection (installer/portable/docker/dev) with platform-aware asset matching - Download with progress events, silent NSIS reinstall, portable ZIP/tarball swap scripts - Version badge pulse animation, dismissible banner with icon buttons, Settings > Updates tab - Single source of truth: pyproject.toml version via importlib.metadata, CI stamps tag with sed - API: GET/POST status, check, dismiss, apply, GET/PUT settings - i18n: en, ru, zh (27+ keys each)
This commit is contained in:
@@ -104,6 +104,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<div id="update-banner" class="update-banner" style="display:none"></div>
|
||||
<div class="container">
|
||||
<div class="tabs">
|
||||
<div class="tab-panel" id="tab-dashboard" role="tabpanel" aria-labelledby="tab-btn-dashboard">
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<button class="settings-tab-btn" data-settings-tab="backup" onclick="switchSettingsTab('backup')" data-i18n="settings.tab.backup">Backup</button>
|
||||
<button class="settings-tab-btn" data-settings-tab="mqtt" onclick="switchSettingsTab('mqtt')" data-i18n="settings.tab.mqtt">MQTT</button>
|
||||
<button class="settings-tab-btn" data-settings-tab="appearance" onclick="switchSettingsTab('appearance')" data-i18n="settings.tab.appearance">Appearance</button>
|
||||
<button class="settings-tab-btn" data-settings-tab="updates" onclick="switchSettingsTab('updates')" data-i18n="settings.tab.updates">Updates</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
@@ -206,6 +207,84 @@
|
||||
<!-- Rendered dynamically by renderAppearanceTab() -->
|
||||
</div>
|
||||
|
||||
<!-- ═══ Updates tab ═══ -->
|
||||
<div id="settings-panel-updates" class="settings-panel">
|
||||
<!-- Current version + status -->
|
||||
<div class="form-group">
|
||||
<div class="label-row">
|
||||
<label data-i18n="update.status_label">Update Status</label>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:0.5rem;margin-bottom:0.5rem;">
|
||||
<span data-i18n="update.current_version">Current version:</span>
|
||||
<strong id="update-current-version"></strong>
|
||||
</div>
|
||||
<div id="update-status-text" style="font-size:0.9rem;font-weight:600;margin-bottom:0.5rem;"></div>
|
||||
<div id="update-last-check" style="font-size:0.85rem;color:var(--text-muted);margin-bottom:0.3rem;"></div>
|
||||
<div style="font-size:0.85rem;color:var(--text-muted);margin-bottom:0.75rem;">
|
||||
<span data-i18n="update.install_type_label">Install type:</span>
|
||||
<span id="update-install-type"></span>
|
||||
</div>
|
||||
|
||||
<!-- Download progress bar -->
|
||||
<div style="display:none;margin-bottom:0.5rem;height:4px;background:var(--border-color);border-radius:2px;overflow:hidden;">
|
||||
<div id="update-progress-bar" style="width:0%;height:100%;background:var(--primary-color);transition:width 0.3s;"></div>
|
||||
</div>
|
||||
|
||||
<div style="display:flex;gap:0.5rem;">
|
||||
<button id="update-check-btn" class="btn btn-secondary" onclick="checkForUpdates()" style="flex:1">
|
||||
<span data-i18n="update.check_now">Check for Updates</span>
|
||||
<span id="update-check-spinner" class="spinner-inline" style="display:none"></span>
|
||||
</button>
|
||||
<button id="update-apply-btn" class="btn btn-primary" onclick="applyUpdate()" style="flex:1;display:none" data-i18n="update.apply_now">Update Now</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Release notes preview -->
|
||||
<div class="form-group" style="display:none">
|
||||
<div class="label-row">
|
||||
<label data-i18n="update.release_notes">Release Notes</label>
|
||||
</div>
|
||||
<pre id="update-release-notes" style="max-height:200px;overflow-y:auto;font-size:0.82rem;white-space:pre-wrap;word-break:break-word;padding:0.5rem;background:var(--bg-secondary);border-radius:var(--radius-sm);border:1px solid var(--border-color);"></pre>
|
||||
</div>
|
||||
|
||||
<!-- Settings -->
|
||||
<div class="form-group">
|
||||
<div class="label-row">
|
||||
<label data-i18n="update.auto_check_label">Auto-Check Settings</label>
|
||||
<button type="button" class="hint-toggle" onclick="toggleHint(this)" title="?">?</button>
|
||||
</div>
|
||||
<small class="input-hint" style="display:none" data-i18n="update.auto_check_hint">Periodically check for new releases in the background.</small>
|
||||
|
||||
<div style="display:flex;align-items:center;gap:0.5rem;margin-bottom:0.5rem;">
|
||||
<input type="checkbox" id="update-enabled">
|
||||
<label for="update-enabled" style="margin:0" data-i18n="update.enable">Enable auto-check</label>
|
||||
</div>
|
||||
|
||||
<div style="display:flex;gap:0.5rem;margin-bottom:0.5rem;">
|
||||
<div style="flex:1">
|
||||
<label for="update-interval" style="font-size:0.85rem" data-i18n="update.interval_label">Check interval</label>
|
||||
<select id="update-interval" style="width:100%">
|
||||
<option value="1">1h</option>
|
||||
<option value="6">6h</option>
|
||||
<option value="12">12h</option>
|
||||
<option value="24">24h</option>
|
||||
<option value="48">48h</option>
|
||||
<option value="168">7d</option>
|
||||
</select>
|
||||
</div>
|
||||
<div style="flex:1">
|
||||
<label for="update-channel" style="font-size:0.85rem" data-i18n="update.channel_label">Channel</label>
|
||||
<select id="update-channel">
|
||||
<option value="false">Stable</option>
|
||||
<option value="true">Pre-release</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary" onclick="saveUpdateSettings()" style="width:100%" data-i18n="update.save_settings">Save Settings</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="settings-error" class="error-message" style="display:none;"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
||||
Reference in New Issue
Block a user