Update-Pruefung gegen Gitea-Releases ergaenzen

Die App prueft ca. 60 s nach dem Start einmalig, ob auf dem Gitea-Server
ein neueres Release vorliegt. Bei einem Treffer erscheint eine
Systembenachrichtigung, und ein Symbol im Titelbalken bleibt sichtbar,
bis aktualisiert wird -- Klick auf beides oeffnet die Release-Seite im
Browser. Ueber die neue Einstellung "Nach Updates suchen" abschaltbar
(Standard: aktiv).
This commit is contained in:
Winkler, Stefan
2026-08-20 19:59:06 +02:00
parent e413a36506
commit bb311e76c2
15 changed files with 234 additions and 2 deletions
+23 -2
View File
@@ -74,6 +74,7 @@ Position wird gemerkt.
| Element | Bedeutung | | Element | Bedeutung |
|---|---| |---|---|
| Statuspunkt | Zeigt den Zustand der Datenquellen, siehe unten | | Statuspunkt | Zeigt den Zustand der Datenquellen, siehe unten |
| Pfeil-Symbol (nur bei neuem Release) | Öffnet die Release-Seite im Browser |
| Zahnrad | Öffnet die Einstellungen | | Zahnrad | Öffnet die Einstellungen |
| `` | Blendet das Fenster aus (Rückkehr über das Tray-Symbol) | | `` | Blendet das Fenster aus (Rückkehr über das Tray-Symbol) |
@@ -121,12 +122,19 @@ Das Zahnrad öffnet den Einstellungsdialog:
der Anmeldung. der Anmeldung.
- **Serverabfrage** — kann abgeschaltet werden, falls die Anzeige rein lokal - **Serverabfrage** — kann abgeschaltet werden, falls die Anzeige rein lokal
arbeiten soll (dann sind alle Werte Näherungen statt exakter Zahlen). arbeiten soll (dann sind alle Werte Näherungen statt exakter Zahlen).
- **Nach Updates suchen** — prüft rund eine Minute nach dem Start einmalig,
ob auf dem Projekt-Server eine neuere Version vorliegt. Falls ja,
erscheint eine Systembenachrichtigung, und ein Pfeil-Symbol im
Titelbalken bleibt sichtbar, bis aktualisiert wird. Klick auf
Benachrichtigung oder Symbol öffnet die Release-Seite im Browser. Diese
Einstellung wirkt erst beim nächsten Start.
- **Bezugsgrößen, Gewichtung, Wochenfenster** — fortgeschrittene Feineinstellungen - **Bezugsgrößen, Gewichtung, Wochenfenster** — fortgeschrittene Feineinstellungen
für den Fall, dass die Serverabfrage dauerhaft nicht zur Verfügung steht; für den Fall, dass die Serverabfrage dauerhaft nicht zur Verfügung steht;
im Normalbetrieb müssen Sie hier nichts anpassen. im Normalbetrieb müssen Sie hier nichts anpassen.
Änderungen wirken sofort, ein Neustart ist nicht nötig. `Strg`+`S` speichert, Änderungen wirken sofort, ein Neustart ist nicht nötig (Ausnahme: „Nach
`Esc` schließt den Dialog ohne zu speichern. Updates suchen", siehe oben). `Strg`+`S` speichert, `Esc` schließt den
Dialog ohne zu speichern.
## Tray-Menü ## Tray-Menü
@@ -154,6 +162,10 @@ und Beenden.
selbst profitiert automatisch davon. Das geschieht ausschließlich auf selbst profitiert automatisch davon. Das geschieht ausschließlich auf
Klick, nie automatisch im Hintergrund, und auch dabei wird kein Token Klick, nie automatisch im Hintergrund, und auch dabei wird kein Token
protokolliert. protokolliert.
- Ist „Nach Updates suchen" aktiviert, fragt die Anwendung rund eine Minute
nach dem Start einmalig den selbst gehosteten Gitea-Server
(`git.conatum.net`) nach der neuesten Versionsnummer ab — ohne
Zugriffstoken und ohne weitere Daten. Abschaltbar in den Einstellungen.
- Es findet keine Übertragung an Dritte statt. - Es findet keine Übertragung an Dritte statt.
## Deinstallation ## Deinstallation
@@ -188,3 +200,12 @@ Abschnitt „Installation“ oben.
**Nach einem Update sehe ich eine Benachrichtigung.** Das ist beabsichtigt: **Nach einem Update sehe ich eine Benachrichtigung.** Das ist beabsichtigt:
Die Anwendung erkennt neue Versionen automatisch und weist einmalig darauf Die Anwendung erkennt neue Versionen automatisch und weist einmalig darauf
hin. Ihre Einstellungen und Kalibrierung bleiben davon unberührt. hin. Ihre Einstellungen und Kalibrierung bleiben davon unberührt.
**Ich bekomme eine Benachrichtigung über eine neue Version / ein
Pfeil-Symbol erscheint im Titelbalken.** Das ist eine andere Meldung als
oben: Die Anwendung hat rund eine Minute nach dem Start auf dem
Projekt-Server ein neueres Release gefunden. Ein Klick auf Benachrichtigung
oder Symbol öffnet die Release-Seite im Browser — heruntergeladen oder
installiert wird nichts automatisch. Das Symbol bleibt sichtbar, bis eine
neuere Version installiert wurde. Abschaltbar über die Einstellung „Nach
Updates suchen".
+13
View File
@@ -276,6 +276,19 @@ npm run dist
`npm version` legt standardmäßig auch einen Git-Commit und -Tag an; ohne das `npm version` legt standardmäßig auch einen Git-Commit und -Tag an; ohne das
genügt es, `version` in `package.json` von Hand zu ändern. genügt es, `version` in `package.json` von Hand zu ändern.
### Update-Prüfung beim Gitea-Server
Zusätzlich zur lokalen Erkennung oben prüft die Anwendung ca. 60 s nach dem
Start einmalig aktiv, ob auf dem selbst gehosteten Gitea-Server
(`git.conatum.net`, Repo `swinks/claude-live-dashboard`) ein neueres Release
vorliegt (`GET .../releases/latest`, öffentlich ohne Authentifizierung). Bei
einer neueren Version erscheint eine native Systembenachrichtigung, und ein
Symbol im Titelbalken (zwischen Endpunkt-Status und Zahnrad) bleibt
sichtbar, bis aktualisiert wird. Ein Klick auf Benachrichtigung oder Symbol
öffnet die Release-Seite im Standardbrowser. Über die Einstellung „Nach
Updates suchen" lässt sich dieser Netzwerkaufruf beim Start abschalten
(`config.json:updateCheck.enabled`, Standard `true`).
## Release bauen ## Release bauen
Gebaut wird jeweils auf dem Zielsystem — electron-builder kann `.deb` nicht Gebaut wird jeweils auf dem Zielsystem — electron-builder kann `.deb` nicht
+4
View File
@@ -26,5 +26,9 @@
"_oauth": "Serverabfrage für exakte Werte. Standardmäßig aktiv; mit enabled:false abschalten. endpoint nur setzen, um den Standard zu überschreiben.", "_oauth": "Serverabfrage für exakte Werte. Standardmäßig aktiv; mit enabled:false abschalten. endpoint nur setzen, um den Standard zu überschreiben.",
"oauth": { "oauth": {
"enabled": true "enabled": true
},
"_updateCheck": "Prüft ca. 60 s nach dem Start einmalig beim Gitea-Server auf ein neueres Release. Standardmäßig aktiv; mit enabled:false abschalten.",
"updateCheck": {
"enabled": true
} }
} }
+1
View File
@@ -24,6 +24,7 @@ const SOURCE_LABEL = {
oauth: 'Server', oauth: 'Server',
app: 'App', app: 'App',
refresh: 'Token', refresh: 'Token',
update: 'Update',
}; };
let feedbackTimer = null; let feedbackTimer = null;
+5
View File
@@ -16,6 +16,7 @@
--good: #0ca30c; --good: #0ca30c;
--warning: #fab219; --warning: #fab219;
--critical: #d03b3b; --critical: #d03b3b;
--update: #9b6bd6;
--font: system-ui, -apple-system, "Segoe UI", sans-serif; --font: system-ui, -apple-system, "Segoe UI", sans-serif;
} }
@@ -228,6 +229,10 @@ dd[data-severity="critical"] .status-label {
color: var(--good); color: var(--good);
} }
.log-source[data-source="update"] {
color: var(--update);
}
.log-message { .log-message {
color: var(--text-secondary); color: var(--text-secondary);
line-height: 1.4; line-height: 1.4;
+29
View File
@@ -77,6 +77,11 @@ class Collector extends EventEmitter {
this.errors = Array.isArray(store.data.errors) ? store.data.errors : []; this.errors = Array.isArray(store.data.errors) ? store.data.errors : [];
this._localOk = true; this._localOk = true;
this._lastOauthError = null; this._lastOauthError = null;
// Für das Titelleisten-Symbol: bleibt für die gesamte Sitzung gesetzt,
// sobald ein neueres Release gefunden wurde — bewusst nicht gedrosselt
// wie die Notification (store.data.updateCheck.notifiedTag), das Symbol
// soll sichtbar bleiben, bis aktualisiert wird.
this._updateAvailable = null;
if (configError) this._note(configError, 'local'); if (configError) this._note(configError, 'local');
if (versionEvent) { if (versionEvent) {
const msg = const msg =
@@ -214,6 +219,24 @@ class Collector extends EventEmitter {
} }
} }
/**
* Hält ein gefundenes neueres Release fest — im Ereignisprotokoll (einmalig
* pro Tag, dedupliziert über _note()) und als Live-State fürs
* Titelleisten-Symbol (state.update, bleibt für die Sitzung gesetzt).
* Ausgelöst einmalig ca. 60 s nach dem Start (siehe index.js/update-check.js).
*/
noteUpdateAvailable(tagName, htmlUrl) {
this._updateAvailable = { tagName, htmlUrl };
this._note(`Neue Version verfügbar: ${tagName} (${htmlUrl})`, 'update');
this._recompute().catch(() => {});
}
/** Hält einen gescheiterten Update-Check fest — stört den Betrieb nie, nur Protokoll. */
noteUpdateCheckError(message) {
this._note(`Update-Prüfung fehlgeschlagen: ${message}`, 'update');
this._recompute().catch(() => {});
}
async _poll() { async _poll() {
if (this._polling) { if (this._polling) {
this._pollAgain = true; this._pollAgain = true;
@@ -411,6 +434,12 @@ class Collector extends EventEmitter {
local: { ok: this._localOk }, local: { ok: this._localOk },
oauth: { ok: !oauthError, text: this.oauth.status().text }, oauth: { ok: !oauthError, text: this.oauth.status().text },
}, },
// Titelleisten-Symbol: bleibt sichtbar, bis die Sitzung endet oder
// aktualisiert wird — bewusst kein „gesehen"-Zustand wie bei der
// Notification (store.data.updateCheck.notifiedTag).
update: this._updateAvailable
? { available: true, tagName: this._updateAvailable.tagName, htmlUrl: this._updateAvailable.htmlUrl }
: { available: false, tagName: null, htmlUrl: null },
errors: this.errors.slice(), errors: this.errors.slice(),
stats: { lastPollMs: this._lastPollMs }, stats: { lastPollMs: this._lastPollMs },
}; };
+48
View File
@@ -13,6 +13,7 @@ const { Store, loadConfig } = require('./store');
const { Collector } = require('./collector'); const { Collector } = require('./collector');
const { CONFIG_FILE, STATE_FILE, USER_DATA_DIR, ensureUserConfig } = require('./paths'); const { CONFIG_FILE, STATE_FILE, USER_DATA_DIR, ensureUserConfig } = require('./paths');
const { getAutostart, setAutostart: applyAutostart } = require('./autostart'); const { getAutostart, setAutostart: applyAutostart } = require('./autostart');
const { checkForUpdate } = require('./update-check');
const IS_LINUX = process.platform === 'linux'; const IS_LINUX = process.platform === 'linux';
@@ -29,6 +30,10 @@ const SETTINGS_HEIGHT = 660;
const ERRORS_WIDTH = 420; const ERRORS_WIDTH = 420;
const ERRORS_HEIGHT = 480; const ERRORS_HEIGHT = 480;
/** Wartezeit vor der einmaligen Update-Prüfung — verzögert, damit sie den
* Start nicht verlangsamt und nicht mit dem ersten Poll konkurriert. */
const UPDATE_CHECK_DELAY_MS = 60 * 1000;
let win = null; let win = null;
let settingsWin = null; let settingsWin = null;
let errorsWin = null; let errorsWin = null;
@@ -410,6 +415,10 @@ function registerIpc() {
if (!collector) return { ok: false, error: 'Anwendung startet noch' }; if (!collector) return { ok: false, error: 'Anwendung startet noch' };
return collector.refreshOauthToken(); return collector.refreshOauthToken();
}); });
ipcMain.on('open-release-page', () => {
const info = collector?.state?.update;
if (info?.available && info.htmlUrl) shell.openExternal(info.htmlUrl);
});
ipcMain.handle('settings:load', () => ({ ipcMain.handle('settings:load', () => ({
config, config,
@@ -509,6 +518,45 @@ async function main() {
} catch (err) { } catch (err) {
console.error('[collector] Start fehlgeschlagen:', err); console.error('[collector] Start fehlgeschlagen:', err);
} }
if (config.updateCheck?.enabled !== false) {
const updateTimer = setTimeout(() => {
checkForUpdate(app.getVersion())
.then((result) => {
console.log(result ? `[update-check] neue Version: ${result.tagName}` : '[update-check] keine neuere Version verfügbar');
if (!result || !collector) return;
// Titelleisten-Symbol: sofort sichtbar, unabhängig von der
// Notification-Drossel unten.
collector.noteUpdateAvailable(result.tagName, result.htmlUrl);
// Notification nur einmal pro neu erschienenem Tag — sonst würde sie
// bei jedem Start erneut aufpoppen, solange der Nutzer nicht
// aktualisiert (die App läuft typischerweise per Autostart täglich).
const alreadyNotified = store.data.updateCheck?.notifiedTag === result.tagName;
store.data.updateCheck = { notifiedTag: result.tagName };
store.save();
if (alreadyNotified || !Notification.isSupported()) return;
const notification = new Notification({
title: 'Neue Version verfügbar',
body: `${result.tagName} steht bereit. Klicken, um die Release-Seite zu öffnen.`,
icon: path.join(ROOT, 'assets', 'icon.png'),
});
notification.on('click', () => shell.openExternal(result.htmlUrl));
notification.show();
})
.catch((err) => {
console.warn('[update-check] fehlgeschlagen:', err.message);
if (collector) collector.noteUpdateCheckError(err.message);
});
}, UPDATE_CHECK_DELAY_MS);
// Darf den App-Exit nie blockieren — anders als collector._timers wird
// dieser einmalige Main-Prozess-Timer nicht von collector.stop() erfasst.
if (updateTimer.unref) updateTimer.unref();
} else {
console.log('[update-check] übersprungen (in den Einstellungen deaktiviert)');
}
} }
// Das Widget lebt im Tray weiter, auch wenn kein Fenster offen ist. // Das Widget lebt im Tray weiter, auch wenn kein Fenster offen ist.
+4
View File
@@ -30,6 +30,9 @@ const DEFAULT_STATE = {
errors: [], errors: [],
// Zuletzt gestartete App-Version, für die Neuinstallations-/Update-Erkennung. // Zuletzt gestartete App-Version, für die Neuinstallations-/Update-Erkennung.
appVersion: null, appVersion: null,
// Zuletzt per Notification gemeldeter Release-Tag — verhindert, dass
// dieselbe neue Version bei jedem Start erneut als Popup erscheint.
updateCheck: { notifiedTag: null },
}; };
class Store { class Store {
@@ -54,6 +57,7 @@ class Store {
window: { ...DEFAULT_STATE.window, ...(parsed.window || {}) }, window: { ...DEFAULT_STATE.window, ...(parsed.window || {}) },
oauth: { ...DEFAULT_STATE.oauth, ...(parsed.oauth || {}) }, oauth: { ...DEFAULT_STATE.oauth, ...(parsed.oauth || {}) },
oauthRefresh: { ...DEFAULT_STATE.oauthRefresh, ...(parsed.oauthRefresh || {}) }, oauthRefresh: { ...DEFAULT_STATE.oauthRefresh, ...(parsed.oauthRefresh || {}) },
updateCheck: { ...DEFAULT_STATE.updateCheck, ...(parsed.updateCheck || {}) },
errors: Array.isArray(parsed.errors) ? parsed.errors : [], errors: Array.isArray(parsed.errors) ? parsed.errors : [],
}; };
} catch (err) { } catch (err) {
+71
View File
@@ -0,0 +1,71 @@
'use strict';
/**
* Einmalige Update-Prüfung, ca. 60 s nach dem Start ausgelöst (siehe
* index.js). Fragt das öffentliche Gitea-API des eigenen Repos nach dem
* neuesten Release ab — kein Zugriffstoken nötig, der Endpunkt ist ohne
* Authentifizierung erreichbar (verifiziert per curl).
*
* Bewusst zustandslos und ohne Wiederholungslogik: Ein Check pro Sitzung
* genügt der Anforderung „einmalig prüfen". Persistenz (welches Release
* schon einmal per Notification gemeldet wurde) lebt bewusst außerhalb
* dieses Moduls, in index.js/store.js.
*/
const RELEASES_URL =
'https://git.conatum.net/api/v1/repos/swinks/claude-live-dashboard/releases/latest';
const ALLOWED_HOST = 'git.conatum.net';
const REQUEST_TIMEOUT_MS = 10000;
/**
* Vergleicht zwei MAJOR.MINOR.PATCH-Versionen ohne Suffixe.
* @returns {number} negativ wenn a<b, 0 wenn gleich, positiv wenn a>b
*/
function compareVersions(a, b) {
const pa = String(a).split('.').map(Number);
const pb = String(b).split('.').map(Number);
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
const diff = (pa[i] || 0) - (pb[i] || 0);
if (diff !== 0) return diff;
}
return 0;
}
/**
* Fragt das neueste Release ab und vergleicht dessen Tag gegen currentVersion.
* Drafts und Pre-Releases werden ignoriert.
* @param {string} currentVersion z. B. app.getVersion() (ohne führendes "v")
* @returns {Promise<{tagName: string, htmlUrl: string} | null>}
*/
async function checkForUpdate(currentVersion) {
const url = new URL(RELEASES_URL);
if (url.protocol !== 'https:' || url.hostname !== ALLOWED_HOST) {
throw new Error('Gitea-Endpunkt zeigt nicht auf den erwarteten Host');
}
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
try {
const res = await fetch(RELEASES_URL, {
method: 'GET',
signal: controller.signal,
headers: { Accept: 'application/json' },
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const json = await res.json();
if (json.draft || json.prerelease) return null;
const remoteVersion = String(json.tag_name || '').replace(/^v/, '').trim();
if (!remoteVersion) return null;
if (compareVersions(remoteVersion, currentVersion) <= 0) return null;
return { tagName: String(json.tag_name), htmlUrl: json.html_url };
} catch (err) {
throw new Error(err.name === 'AbortError' ? 'Zeitüberschreitung' : err.message);
} finally {
clearTimeout(timer);
}
}
module.exports = { checkForUpdate, compareVersions, RELEASES_URL };
+2
View File
@@ -24,4 +24,6 @@ contextBridge.exposeInMainWorld('dashboard', {
openSettings: () => ipcRenderer.send('open-settings'), openSettings: () => ipcRenderer.send('open-settings'),
/** Öffnet das Fehler-/Ereignis-Log. */ /** Öffnet das Fehler-/Ereignis-Log. */
openErrorLog: () => ipcRenderer.send('open-error-log'), openErrorLog: () => ipcRenderer.send('open-error-log'),
/** Öffnet die Release-Seite eines gefundenen neuen Updates im Browser. */
openReleasePage: () => ipcRenderer.send('open-release-page'),
}); });
+12
View File
@@ -47,6 +47,7 @@ const el = {
planLabel: $('plan-label'), planLabel: $('plan-label'),
staleBadge: $('stale-badge'), staleBadge: $('stale-badge'),
btnEndpointStatus: $('btn-endpoint-status'), btnEndpointStatus: $('btn-endpoint-status'),
btnUpdateAvailable: $('btn-update-available'),
btnHide: $('btn-hide'), btnHide: $('btn-hide'),
btnSettings: $('btn-settings'), btnSettings: $('btn-settings'),
btnExpand: $('btn-expand'), btnExpand: $('btn-expand'),
@@ -284,6 +285,15 @@ function renderEndpointStatus(s) {
`Serverabfrage: ${ep.oauth.text || '—'}`; `Serverabfrage: ${ep.oauth.text || '—'}`;
} }
/** Titelleisten-Symbol für ein gefundenes neues Release. */
function renderUpdateAvailable(s) {
const update = s.update || { available: false };
el.btnUpdateAvailable.hidden = !update.available;
if (update.available) {
el.btnUpdateAvailable.title = `Version ${update.tagName} verfügbar — Release-Seite öffnen`;
}
}
function renderWeek(week, burn) { function renderWeek(week, burn) {
renderMeter('week', week); renderMeter('week', week);
renderProjection(el.weekProjection, week.percent, burn.weekProjectedPercent, week.severity); renderProjection(el.weekProjection, week.percent, burn.weekProjectedPercent, week.severity);
@@ -397,6 +407,7 @@ function render(s) {
renderWeek(s.week, s.burn); renderWeek(s.week, s.burn);
renderSpend(s.spend); renderSpend(s.spend);
renderEndpointStatus(s); renderEndpointStatus(s);
renderUpdateAvailable(s);
if (expanded) { if (expanded) {
renderModels(s.block); renderModels(s.block);
@@ -462,6 +473,7 @@ el.btnExpand.addEventListener('click', () => applyExpanded(!expanded));
el.btnHide.addEventListener('click', () => window.dashboard.hide()); el.btnHide.addEventListener('click', () => window.dashboard.hide());
el.btnSettings.addEventListener('click', () => window.dashboard.openSettings()); el.btnSettings.addEventListener('click', () => window.dashboard.openSettings());
el.btnEndpointStatus.addEventListener('click', () => window.dashboard.openErrorLog()); el.btnEndpointStatus.addEventListener('click', () => window.dashboard.openErrorLog());
el.btnUpdateAvailable.addEventListener('click', () => window.dashboard.openReleasePage());
window.dashboard.onState(render); window.dashboard.onState(render);
+1
View File
@@ -17,6 +17,7 @@
<button class="icon-btn status" id="btn-endpoint-status" data-severity="ok" title="Endpunkt-Status" aria-label="Endpunkt-Status"> <button class="icon-btn status" id="btn-endpoint-status" data-severity="ok" title="Endpunkt-Status" aria-label="Endpunkt-Status">
<span class="status-icon" aria-hidden="true"></span> <span class="status-icon" aria-hidden="true"></span>
</button> </button>
<button class="icon-btn" id="btn-update-available" hidden title="Neues Release verfügbar" aria-label="Neues Release verfügbar"></button>
<button class="icon-btn" id="btn-settings" title="Einstellungen" aria-label="Einstellungen"> <button class="icon-btn" id="btn-settings" title="Einstellungen" aria-label="Einstellungen">
<svg viewBox="0 0 16 16" width="13" height="13" aria-hidden="true" focusable="false"> <svg viewBox="0 0 16 16" width="13" height="13" aria-hidden="true" focusable="false">
<path <path
+5
View File
@@ -28,6 +28,7 @@
--status-good: #0ca30c; --status-good: #0ca30c;
--status-warning: #fab219; --status-warning: #fab219;
--status-critical: #d03b3b; --status-critical: #d03b3b;
--status-update: #9b6bd6;
/* Kategoriale Slots (dunkle Stufen), fest je Modellfamilie vergeben */ /* Kategoriale Slots (dunkle Stufen), fest je Modellfamilie vergeben */
--series-1: #3987e5; --series-1: #3987e5;
@@ -152,6 +153,10 @@ body {
color: var(--text-primary); color: var(--text-primary);
} }
#btn-update-available {
color: var(--status-update);
}
/* ── Meter ───────────────────────────────────────────────────────────── */ /* ── Meter ───────────────────────────────────────────────────────────── */
.meters { .meters {
+5
View File
@@ -21,6 +21,7 @@ const el = {
clickThrough: $('clickThrough'), clickThrough: $('clickThrough'),
autostart: $('autostart'), autostart: $('autostart'),
autostartLabel: $('autostart-label'), autostartLabel: $('autostart-label'),
updateCheckEnabled: $('updateCheckEnabled'),
oauthEnabled: $('oauthEnabled'), oauthEnabled: $('oauthEnabled'),
oauthEndpoint: $('oauthEndpoint'), oauthEndpoint: $('oauthEndpoint'),
@@ -84,6 +85,9 @@ function fill(config, meta) {
el.autostartLabel.textContent = el.autostartLabel.textContent =
meta.platform === 'win32' ? 'Mit Windows starten' : 'Mit dem System starten'; meta.platform === 'win32' ? 'Mit Windows starten' : 'Mit dem System starten';
const updateCheck = config.updateCheck || {};
el.updateCheckEnabled.checked = updateCheck.enabled !== false;
const oauth = config.oauth || {}; const oauth = config.oauth || {};
el.oauthEnabled.checked = oauth.enabled !== false; el.oauthEnabled.checked = oauth.enabled !== false;
el.oauthEndpoint.value = oauth.endpoint || ''; el.oauthEndpoint.value = oauth.endpoint || '';
@@ -137,6 +141,7 @@ function collect() {
patch.opacity = Number(el.opacity.value) / 100; patch.opacity = Number(el.opacity.value) / 100;
patch.clickThrough = el.clickThrough.checked; patch.clickThrough = el.clickThrough.checked;
patch.updateCheck = { enabled: el.updateCheckEnabled.checked };
patch.oauth = { enabled: el.oauthEnabled.checked }; patch.oauth = { enabled: el.oauthEnabled.checked };
const endpoint = el.oauthEndpoint.value.trim(); const endpoint = el.oauthEndpoint.value.trim();
+11
View File
@@ -42,6 +42,17 @@
<span class="hint">Wird sofort übernommen, unabhängig vom Speichern.</span> <span class="hint">Wird sofort übernommen, unabhängig vom Speichern.</span>
</div> </div>
</div> </div>
<div class="row">
<label for="updateCheckEnabled">Nach Updates suchen</label>
<div class="control">
<input type="checkbox" id="updateCheckEnabled" />
<span class="hint">
Prüft rund eine Minute nach dem Start einmalig auf dem
Projekt-Server, ob eine neuere Version vorliegt.
</span>
</div>
</div>
</section> </section>
<section class="group"> <section class="group">