diff --git a/src/PolyTrader.Web/wwwroot/css/app.css b/src/PolyTrader.Web/wwwroot/css/app.css index 77b94f3..3588649 100644 --- a/src/PolyTrader.Web/wwwroot/css/app.css +++ b/src/PolyTrader.Web/wwwroot/css/app.css @@ -13,6 +13,59 @@ --negative: #f38ba8; --warning: #f9e2af; --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; + /* #566: Tokens fuer Stellen, die bisher feste Farben hatten und im Hellmodus brechen wuerden. */ + --on-accent: #11111b; + --row-hover: rgba(255, 255, 255, 0.02); + --hairline: rgba(255, 255, 255, 0.08); + --hairline-weak: rgba(255, 255, 255, 0.04); + color-scheme: dark; +} + +/* Hellmodus (#566). Die Palette folgt dem Betriebssystem, solange im Browser nichts gewaehlt ist; + data-theme="light"/"dark" auf ueberstimmt das in beide Richtungen. + Kontrast auf Weiss: Text 8:1, gedaempfter Text 6,3:1, Akzent, Gewinn, Verlust und Warnung je >= 4,5:1. */ +@media (prefers-color-scheme: light) { + :root:not([data-theme="dark"]) { + --bg: #eff1f5; + --surface: #ffffff; + --surface-alt: #e6e9ef; + --card: #ffffff; + --border: #ccd0da; + --divider: #bcc0cc; + --text: #4c4f69; + --muted: #5c5f77; + --accent: #1e66f5; + --accent-soft: rgba(30, 102, 245, 0.12); + --positive: #2e7d32; + --negative: #c11c3c; + --warning: #9a5b00; + --on-accent: #ffffff; + --row-hover: rgba(0, 0, 0, 0.035); + --hairline: rgba(0, 0, 0, 0.08); + --hairline-weak: rgba(0, 0, 0, 0.05); + color-scheme: light; + } +} + +:root[data-theme="light"] { + --bg: #eff1f5; + --surface: #ffffff; + --surface-alt: #e6e9ef; + --card: #ffffff; + --border: #ccd0da; + --divider: #bcc0cc; + --text: #4c4f69; + --muted: #5c5f77; + --accent: #1e66f5; + --accent-soft: rgba(30, 102, 245, 0.12); + --positive: #2e7d32; + --negative: #c11c3c; + --warning: #9a5b00; + --on-accent: #ffffff; + --row-hover: rgba(0, 0, 0, 0.035); + --hairline: rgba(0, 0, 0, 0.08); + --hairline-weak: rgba(0, 0, 0, 0.05); + color-scheme: light; } * { @@ -378,7 +431,7 @@ body { .btn-primary { background: var(--accent); - color: #11111b; + color: var(--on-accent); } .btn-primary:hover { opacity: 0.9; } @@ -431,11 +484,24 @@ tr:last-child td { } tr:hover td { - background: rgba(255, 255, 255, 0.02); + background: var(--row-hover); } /* Terminal / Log Viewer */ +/* Das Terminal bleibt in beiden Modi dunkel (#566): Die Level-Farben sind fuer dunklen Grund gewaehlt, + und ein helles Terminal liesse sich zwischen den farbigen Leveln schlechter lesen. Die Palette steht + deshalb hier lokal - sonst erbte es im Hellmodus dunkle Schrift auf seinem dunklen Hintergrund. */ .terminal-window { + --text: #cdd6f4; + --muted: #a6adc8; + --accent: #89b4fa; + --positive: #a6e3a1; + --negative: #f38ba8; + --warning: #f9e2af; + --border: #313244; + --surface-alt: #252538; + --card: #2a2b3f; + color-scheme: dark; background: #11111b; border: 1px solid var(--border); border-radius: 10px; @@ -631,7 +697,7 @@ tr:hover td { display: flex; gap: 6px; padding: 4px 0; - border-bottom: 1px solid rgba(255, 255, 255, 0.04); + border-bottom: 1px solid var(--hairline-weak); } .widget-alert-entry .time { @@ -701,7 +767,7 @@ tr:hover td { .btn-profile:hover { background: var(--accent); - color: #11111b; + color: var(--on-accent); } .badge-active { @@ -1053,7 +1119,7 @@ tr:hover td { gap: 12px; margin-bottom: 8px; padding-bottom: 4px; - border-bottom: 1px solid rgba(255, 255, 255, 0.08); + border-bottom: 1px solid var(--hairline); font-size: 11px; color: var(--muted); } @@ -1125,7 +1191,7 @@ tr:hover td { .currency-pill.active { background: var(--accent); - color: #11111b; + color: var(--on-accent); } .markdown-pre { diff --git a/src/PolyTrader.Web/wwwroot/index.html b/src/PolyTrader.Web/wwwroot/index.html index 5048152..efeaebb 100644 --- a/src/PolyTrader.Web/wwwroot/index.html +++ b/src/PolyTrader.Web/wwwroot/index.html @@ -5,6 +5,13 @@ PolyTrader Web +
@@ -66,7 +73,7 @@
- WSS + WSS
@@ -76,6 +83,11 @@ Watchdog
+ +
+ Module – +
+
@@ -792,6 +804,8 @@ + +
diff --git a/src/PolyTrader.Web/wwwroot/js/app.js b/src/PolyTrader.Web/wwwroot/js/app.js index 43e6b1f..dc8610a 100644 --- a/src/PolyTrader.Web/wwwroot/js/app.js +++ b/src/PolyTrader.Web/wwwroot/js/app.js @@ -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'; diff --git a/src/PolyTrader.Web/wwwroot/login.html b/src/PolyTrader.Web/wwwroot/login.html index 58834af..daa3898 100644 --- a/src/PolyTrader.Web/wwwroot/login.html +++ b/src/PolyTrader.Web/wwwroot/login.html @@ -5,6 +5,13 @@ PolyTrader — WebUI Login +