Dunkelmodus mit Umschalter in der Fensterleiste

Farbschema hell / dunkel / dem System folgen, umschaltbar per Klick, Stand wird in
server_settings.xml gemerkt (ServerSettings.Theme). Der Editor zeigt die Einstellung
automatisch als Auswahlfeld - kein Zusatzcode, weil sie ein Enum ist.

- App.axaml: 18 Farb-Token je Variante (Flaechen, Rahmen, Texte, Bedeutungsfarben,
  Zeilenfaerbung, Trading-Umschalter, Chat-Rollen). Die Standard-Steuerelemente stellt
  das FluentTheme selbst um.
- 22 fest verdrahtete Farben in 11 XAML-Dateien auf DynamicResource umgestellt - die
  wechseln damit von selbst mit.
- Umschalter (Sonne/Mond) sitzt in der gemeinsamen Fensterleiste, also auf JEDEM Fenster.

Der eigentliche Knackpunkt waren die Farben, die im Code gesetzt werden - die folgen dem
Thema NICHT von selbst:
- TradeRowPalette liefert jetzt Eigenschaften statt static readonly, loest also bei jedem
  Zugriff neu auf. Im Dunkeln gedaempfte Toene statt der hellen Pastelltoene, die dort
  blenden und den Text unlesbar machen wuerden.
- ChatEntry speichert die ROLLE statt eines fertigen Brush - dadurch stimmt der Verlauf
  nach dem Umschalten ohne Neuaufbau der Liste.
- Launcher-Umschalter, Dashboard-KPI und die Grid-Zeilenfarben zeichnen sich ueber
  ThemeManager.ThemeChanged neu.
- Das Terminal bleibt in beiden Schemata dunkel (Konsolen sind konventionell dunkel).

Nebenbei die Anzeige-Kultur gepinnt (stand ohnehin auf der Linux-Liste): auf Linux richtet
sie sich sonst nach LANG/LC_ALL, das unter systemd oft nicht gesetzt ist - dann faellt .NET
auf Invariant zurueck und aus '1.234,56 USDC' wird '1,234.56 USDC'. Maschinen-I/O laeuft
davon unabhaengig weiter invariant.

Smoke-UI prueft jetzt zusaetzlich, dass ALLE 18 Farben in BEIDEN Varianten aufloesen -
ein vertippter Ressourcenschluessel wuerde sonst still grau werden.

Verifiziert: Solution baut, 450 Tests gruen, --smoke-ui gruen (10 Fenster + Editor +
beide Farbschemata), App startet im gespeicherten Schema, Linux-Publish laeuft.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-08-06 22:41:25 +02:00
co-authored by Claude Opus 5
parent 669676ac60
commit 487ea4466d
23 changed files with 350 additions and 60 deletions
@@ -1,19 +1,24 @@
using System;
using Avalonia.Media;
using PolyTrader.App.Avalonia.Shell;
using PolyTrader.Modules.CopyTrading.Logic;
namespace PolyTrader.App.Avalonia.ViewModels
{
/// <summary>
/// Avalonia-Farbpalette zu den <see cref="TradeRowTint"/>-Kategorien. Die Schwellenlogik liegt
/// Farbpalette zu den <see cref="TradeRowTint"/>-Kategorien. Die Schwellenlogik liegt
/// (getestet) in <see cref="TradeRowColoring"/>; hier steht nur, wie die Kategorien aussehen.
/// Werte unverändert aus der WinForms-Oberfläche übernommen.
///
/// <para>Die Farben kommen aus den Themen-Ressourcen und werden bei JEDEM Zugriff aufgelöst
/// deshalb Eigenschaften statt <c>static readonly</c>. Nach einem Themenwechsel liefern sie
/// sofort die passende Variante; im hellen Schema die Pastelltöne der bisherigen Oberfläche,
/// im dunklen gedämpfte Entsprechungen (helle Pastelltöne würden dort blenden).</para>
/// </summary>
public static class TradeRowPalette
{
public static readonly IBrush Loss = new SolidColorBrush(Color.FromRgb(245, 200, 200)); // rot
public static readonly IBrush SmallWin = new SolidColorBrush(Color.FromRgb(212, 240, 212)); // hellgrün
public static readonly IBrush BigWin = new SolidColorBrush(Color.FromRgb(140, 214, 140)); // grün
public static IBrush Loss => ThemeManager.Brush("AppTradeLossBrush");
public static IBrush SmallWin => ThemeManager.Brush("AppTradeSmallWinBrush");
public static IBrush BigWin => ThemeManager.Brush("AppTradeBigWinBrush");
public static IBrush ForPnlPercent(decimal pnlPercent) => TradeRowColoring.ForPnlPercent(pnlPercent) switch
{
@@ -1,4 +1,5 @@
using Avalonia.Media;
using PolyTrader.App.Avalonia.Shell;
namespace PolyTrader.App.Avalonia.ViewModels
{
@@ -21,11 +22,24 @@ namespace PolyTrader.App.Avalonia.ViewModels
public string Message { get; init; } = string.Empty;
}
/// <summary>Ein Eintrag im Supervisor-Analyseverlauf (Frage oder Antwort).</summary>
/// <summary>Wer im Supervisor-Verlauf spricht bestimmt die Farbe der Kopfzeile.</summary>
public enum ChatRole { User, Agent, Error }
/// <summary>
/// Ein Eintrag im Supervisor-Analyseverlauf. Speichert die ROLLE, nicht die Farbe dadurch
/// stimmt die Darstellung nach einem Themenwechsel ohne Neuaufbau der Liste.
/// </summary>
public sealed class ChatEntry
{
public string Header { get; init; } = string.Empty;
public string Text { get; init; } = string.Empty;
public IBrush HeaderBrush { get; init; } = Brushes.SteelBlue;
public ChatRole Role { get; init; } = ChatRole.User;
public IBrush HeaderBrush => Role switch
{
ChatRole.Agent => ThemeManager.Brush("AppChatAgentBrush"),
ChatRole.Error => ThemeManager.Brush("AppNegativeBrush"),
_ => ThemeManager.Brush("AppChatUserBrush")
};
}
}