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
+
@@ -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
+