WebUI: Hellmodus, Supervisor-Verlauf leeren, API-Status beschriftet

Umsetzung von #566.

Hell-/Dunkelmodus
Das WebUI hatte nur eine dunkle Palette. Jetzt gibt es "System", "Hell" und
"Dunkel" als Umschalter in der Kopfleiste. Die Wahl gilt je Browser; ohne
gespeicherte Wahl folgt das WebUI dem Betriebssystem. data-theme auf <html>
ueberstimmt prefers-color-scheme in beide Richtungen. Ein kleines Skript im
<head> setzt die Wahl vor dem ersten Zeichnen, sonst blitzte beim Laden kurz
das falsche Schema auf; die Login-Seite macht es genauso.

Kontrast der hellen Palette auf Weiss:
- Text 8:1
- gedaempfter Text 6,3:1
- Akzent, Gewinn, Verlust und Warnung je mindestens 4,5:1

Fuenf feste Farben haetten im Hellmodus gebrochen und sind jetzt Tokens:
- #11111b als Schrift auf Akzentfarbe (Primaerknopf, Profil-Hover, aktive
  Waehrung): im Hellmodus dunkel auf dunklem Blau.
- rgba(255,255,255,0.02 bis 0.08) fuer Zeilen-Hover und Trennlinien: auf Weiss
  unsichtbar.

Das Terminal bleibt in beiden Modi dunkel und bringt seine Palette lokal mit.
Es hatte einen festen dunklen Hintergrund, erbte aber var(--text), und das
waere im Hellmodus dunkle Schrift auf Schwarz geworden. Die uebrigen festen
Farben sind halbtransparente Toenungen und Schatten, die auf beiden Gruenden
funktionieren.

Supervisor
"Verlauf leeren" wie im Desktop. Geleert wird nur die Unterhaltung im Browser,
die Begruessung kommt zurueck, gespeicherte Berichte bleiben erhalten.

Statusanzeige
Der WSS-Punkt ist jetzt beschriftet, "WSS aktiv" oder "Polling", wie
"API: ..." in der Statusleiste des Desktops. Dazu kommt "Module x/y".

Reine Oberflaeche, keine Aenderung am Server. Die Pruefung erfolgt im Browser
auf der VM.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-09-13 14:24:23 +02:00
co-authored by Claude Opus 5
parent bc96807d81
commit 30891c1bce
4 changed files with 152 additions and 7 deletions
+58
View File
@@ -6,6 +6,8 @@ let allLogs = [];
// Init on DOM ready
document.addEventListener('DOMContentLoaded', () => {
applyThemeChoice(currentThemeChoice());
supChatWelcome = document.getElementById('supChatMessages')?.innerHTML ?? '';
loadUpdateStatus(false); // #565: Banner im Dashboard; der Server haelt das Ergebnis vor
document.addEventListener('keydown', e => {
if (e.key === 'Escape' && document.getElementById('modalStopTrading')?.style.display === 'flex') closeStopTradingModal();
@@ -53,6 +55,51 @@ function switchView(viewName) {
}
}
// ==================== Farbschema und Supervisor-Verlauf (#566) ====================
const THEME_KEY = 'polytrader-theme';
const THEME_ORDER = ['system', 'light', 'dark'];
const THEME_LABELS = { system: '◐ System', light: '☀ Hell', dark: '☾ Dunkel' };
/** Die Wahl gilt je Browser. Ohne gespeicherte Wahl folgt das WebUI dem Betriebssystem. */
function currentThemeChoice() {
try {
const t = localStorage.getItem(THEME_KEY);
return t === 'light' || t === 'dark' ? t : 'system';
} catch (e) {
return 'system';
}
}
function applyThemeChoice(choice) {
if (choice === 'system') document.documentElement.removeAttribute('data-theme');
else document.documentElement.setAttribute('data-theme', choice);
try {
if (choice === 'system') localStorage.removeItem(THEME_KEY);
else localStorage.setItem(THEME_KEY, choice);
} catch (e) { /* ohne Speicher gilt die Wahl bis zum Neuladen */ }
const btn = document.getElementById('btnTheme');
if (btn) {
btn.textContent = THEME_LABELS[choice];
btn.title = `Farbschema: ${THEME_LABELS[choice].slice(2)} – klicken zum Wechseln`;
}
}
function cycleTheme() {
const next = THEME_ORDER[(THEME_ORDER.indexOf(currentThemeChoice()) + 1) % THEME_ORDER.length];
applyThemeChoice(next);
}
let supChatWelcome = '';
/** Leert nur die Unterhaltung im Browser. Gespeicherte Berichte (sup_reports) bleiben unberührt. */
function clearSupChat() {
const area = document.getElementById('supChatMessages');
if (area) area.innerHTML = supChatWelcome;
}
// ==================== Kopfleiste: Aufklappmenues (#541) ====================
/**
@@ -159,6 +206,9 @@ async function refreshNavModules(modules) {
const isActive = new Map(modules.map(m => [m.name.toLowerCase(), m.isActive]));
const lblModules = document.getElementById('lblModules');
if (lblModules) lblModules.textContent = `Module ${modules.filter(m => m.isActive).length}/${modules.length}`;
document.querySelectorAll('.header-nav [data-module]').forEach(item => {
const off = isActive.get(item.dataset.module.toLowerCase()) === false;
item.disabled = off;
@@ -204,6 +254,14 @@ async function refreshDashboard() {
// Status indicators
document.getElementById('dotAlchemy').className = data.alchemyHealthy ? 'dot online' : 'dot offline';
// #566: beschriftet wie "API: WSS aktiv / Polling" in der Statusleiste des Desktops.
const lblAlchemy = document.getElementById('lblAlchemy');
if (lblAlchemy) {
lblAlchemy.textContent = data.alchemyHealthy ? 'WSS aktiv' : 'Polling';
lblAlchemy.parentElement.title = data.alchemyHealthy
? 'Signale kommen live über den Alchemy-WebSocket'
: 'Kein WebSocket – Signale werden abgefragt (langsamer)';
}
document.getElementById('dotVpn').className = data.vpnConnected ? 'dot online' : 'dot offline';
document.getElementById('dotWatchdog').className = data.watchdogActive ? 'dot online' : 'dot offline';