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:
@@ -75,7 +75,7 @@
|
||||
|
||||
<Grid Grid.Row="1" RowDefinitions="*,*" ColumnDefinitions="*,*" Margin="0,12,0,0">
|
||||
<Border Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
|
||||
BorderBrush="#DDDDDD" BorderThickness="1" Margin="0,0,0,8">
|
||||
BorderBrush="{DynamicResource AppBorderBrush}" BorderThickness="1" Margin="0,0,0,8">
|
||||
<DockPanel>
|
||||
<TextBlock DockPanel.Dock="Top" Margin="8,6"
|
||||
FontWeight="SemiBold" Text="Equity-Kurve (kumulierter PnL)" />
|
||||
@@ -87,7 +87,7 @@
|
||||
</Border>
|
||||
|
||||
<Border Grid.Row="1" Grid.Column="0"
|
||||
BorderBrush="#DDDDDD" BorderThickness="1" Margin="0,0,4,0">
|
||||
BorderBrush="{DynamicResource AppBorderBrush}" BorderThickness="1" Margin="0,0,4,0">
|
||||
<DockPanel>
|
||||
<TextBlock DockPanel.Dock="Top" Margin="8,6"
|
||||
FontWeight="SemiBold" Text="PnL je Modul" />
|
||||
@@ -98,7 +98,7 @@
|
||||
</Border>
|
||||
|
||||
<Border Grid.Row="1" Grid.Column="1"
|
||||
BorderBrush="#DDDDDD" BorderThickness="1" Margin="4,0,0,0">
|
||||
BorderBrush="{DynamicResource AppBorderBrush}" BorderThickness="1" Margin="4,0,0,0">
|
||||
<DockPanel>
|
||||
<TextBlock DockPanel.Dock="Top" Margin="8,6"
|
||||
FontWeight="SemiBold" Text="PnL je Tag" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
@@ -73,6 +73,11 @@ namespace PolyTrader.App.Avalonia.Views
|
||||
|
||||
RefreshData();
|
||||
InitModulesTab();
|
||||
|
||||
// Die KPI-Farbe wird im Code gesetzt und wechselt daher nicht von selbst mit dem Thema.
|
||||
void OnTheme() => ApplyScope();
|
||||
Shell.ThemeManager.ThemeChanged += OnTheme;
|
||||
Closed += (_, _) => Shell.ThemeManager.ThemeChanged -= OnTheme;
|
||||
}
|
||||
|
||||
// ===== Daten laden / Scope =====
|
||||
@@ -150,9 +155,7 @@ namespace PolyTrader.App.Avalonia.Views
|
||||
|
||||
var pnl = this.FindControl<TextBlock>("kpiPnl")!;
|
||||
pnl.Text = $"{k.NetPnl:N2} USDC";
|
||||
pnl.Foreground = new global::Avalonia.Media.SolidColorBrush(
|
||||
k.NetPnl >= 0 ? global::Avalonia.Media.Color.FromRgb(0x2E, 0x7D, 0x32)
|
||||
: global::Avalonia.Media.Color.FromRgb(0xC6, 0x28, 0x28));
|
||||
pnl.Foreground = Shell.ThemeManager.Brush(k.NetPnl >= 0 ? "AppPositiveBrush" : "AppNegativeBrush");
|
||||
|
||||
this.FindControl<TextBlock>("kpiWin")!.Text = $"{k.WinRatePct:N1} %";
|
||||
this.FindControl<TextBlock>("kpiTrades")!.Text = k.TradeCount.ToString();
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
IsVisible="{Binding Icon, Converter={x:Static ObjectConverters.IsNotNull}}" />
|
||||
<StackPanel VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding Title}" FontWeight="{Binding FontWeight}" />
|
||||
<TextBlock Text="{Binding CheckMark}" FontSize="11" Foreground="#2E7D32" />
|
||||
<TextBlock Text="{Binding CheckMark}" FontSize="11" Foreground="{DynamicResource AppPositiveBrush}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
@@ -66,7 +66,7 @@
|
||||
</ItemsControl>
|
||||
|
||||
<TextBlock Name="lblPortInfo"
|
||||
Foreground="#996600"
|
||||
Foreground="{DynamicResource AppWarningBrush}"
|
||||
TextWrapping="Wrap"
|
||||
Text="Portierung läuft: Dashboard, Terminal, Einstellungen und die Modul-Fenster werden noch auf Avalonia umgestellt. Die Trading-Dienste laufen davon unabhängig." />
|
||||
</StackPanel>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Avalonia.Controls;
|
||||
@@ -62,10 +62,14 @@ namespace PolyTrader.App.Avalonia.Views
|
||||
e.Cancel = true;
|
||||
Dispatcher.UIThread.Post(_uiHost.RequestShutdown);
|
||||
};
|
||||
// Die Umschalterflaechen werden im Code gesetzt und folgen dem Thema nicht von selbst.
|
||||
ThemeManager.ThemeChanged += UpdateTradingToggles;
|
||||
|
||||
Closed += (_, _) =>
|
||||
{
|
||||
_statusTimer.Stop();
|
||||
_uiHost.OpenStateChanged -= OnOpenStateChanged;
|
||||
ThemeManager.ThemeChanged -= UpdateTradingToggles;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -106,17 +110,18 @@ namespace PolyTrader.App.Avalonia.Views
|
||||
Apply(this.FindControl<Button>("btnLiveTrading")!, "LiveTrading", _state.LiveTradingMode);
|
||||
Apply(this.FindControl<Button>("btnDemoTrading")!, "DemoTrading", _state.DemoTradingMode);
|
||||
|
||||
// Farbgebung aus der WinForms-Oberflaeche uebernommen (LightGreen/Orange/IndianRed).
|
||||
// Aussage wie in der WinForms-Oberflaeche (gruen/orange/rot); die konkreten Toene
|
||||
// kommen aus dem Thema, damit sie auf hellem wie dunklem Grund lesbar bleiben.
|
||||
static void Apply(Button btn, string label, TradingMode mode)
|
||||
{
|
||||
(string text, Color color) = mode switch
|
||||
(string text, string brushKey) = mode switch
|
||||
{
|
||||
TradingMode.Active => ($"{label} (AKTIV)", Colors.LightGreen),
|
||||
TradingMode.SellOnly => ($"{label} (SELL-ONLY)", Colors.Orange),
|
||||
_ => ($"{label} (DEAKTIVIERT)", Colors.IndianRed)
|
||||
TradingMode.Active => ($"{label} (AKTIV)", "AppToggleActiveBrush"),
|
||||
TradingMode.SellOnly => ($"{label} (SELL-ONLY)", "AppToggleSellOnlyBrush"),
|
||||
_ => ($"{label} (DEAKTIVIERT)", "AppToggleInactiveBrush")
|
||||
};
|
||||
btn.Content = text;
|
||||
btn.Background = new SolidColorBrush(color);
|
||||
btn.Background = ThemeManager.Brush(brushKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
</WrapPanel>
|
||||
|
||||
<TextBlock DockPanel.Dock="Top" Name="lblCurrencyNote" Margin="12,0"
|
||||
Foreground="#777777" FontSize="11" TextWrapping="Wrap" />
|
||||
Foreground="{DynamicResource AppMutedTextBrush}" FontSize="11" TextWrapping="Wrap" />
|
||||
|
||||
<DataGrid Name="gridMonthly" Margin="12" x:DataType="vm:MonthlyRow">
|
||||
<DataGrid.Columns>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<Button Name="btnTraderRefresh" Content="Aktualisieren" />
|
||||
<Button Name="btnTraderSave" Content="Speichern" />
|
||||
<TextBlock Name="lblTraderHint" VerticalAlignment="Center"
|
||||
Margin="12,0,0,0" Foreground="#777777" />
|
||||
Margin="12,0,0,0" Foreground="{DynamicResource AppMutedTextBrush}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
<GridSplitter Grid.Column="1" Background="#DDDDDD" />
|
||||
<GridSplitter Grid.Column="1" Background="{DynamicResource AppBorderBrush}" />
|
||||
|
||||
<DockPanel Grid.Column="2">
|
||||
<HeaderedContentControl DockPanel.Dock="Bottom" Header="Zugewiesene Accounts"
|
||||
@@ -147,7 +147,7 @@
|
||||
<TextBlock Text="Konto:" VerticalAlignment="Center" />
|
||||
<ComboBox Name="cbSettingsAccount" MinWidth="240" />
|
||||
<Button Name="btnSettingsSave" Content="Speichern" />
|
||||
<TextBlock Name="lblSettingsHint" VerticalAlignment="Center" Foreground="#777777" />
|
||||
<TextBlock Name="lblSettingsHint" VerticalAlignment="Center" Foreground="{DynamicResource AppMutedTextBrush}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
|
||||
@@ -93,6 +93,16 @@ namespace PolyTrader.App.Avalonia.Views.Modules
|
||||
LoadOpenTrades();
|
||||
LoadClosedTrades();
|
||||
LoadAccountList();
|
||||
|
||||
// Zeilenhintergruende werden in LoadingRow gesetzt und wechseln daher nicht von selbst
|
||||
// mit dem Thema. Ein Neuzuweisen der Quelle laesst die Zeilen neu erzeugen.
|
||||
void OnTheme()
|
||||
{
|
||||
gridOpen.ItemsSource = null; gridOpen.ItemsSource = _openTrades;
|
||||
gridClosed.ItemsSource = null; gridClosed.ItemsSource = _closedTrades;
|
||||
}
|
||||
Shell.ThemeManager.ThemeChanged += OnTheme;
|
||||
Closed += (_, _) => Shell.ThemeManager.ThemeChanged -= OnTheme;
|
||||
}
|
||||
|
||||
// ===== Master-Trader =====
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<TextBlock Text="Konto:" VerticalAlignment="Center" />
|
||||
<ComboBox Name="cbAccount" MinWidth="240" />
|
||||
<Button Name="btnRefresh" Content="Aktualisieren" />
|
||||
<TextBlock Name="lblStatus" VerticalAlignment="Center" Foreground="#777777" />
|
||||
<TextBlock Name="lblStatus" VerticalAlignment="Center" Foreground="{DynamicResource AppMutedTextBrush}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
<Border Classes="toolbar" DockPanel.Dock="Top">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Button Name="btnSettingsSave" Content="Speichern" />
|
||||
<TextBlock Name="lblSettingsHint" VerticalAlignment="Center" Foreground="#777777" />
|
||||
<TextBlock Name="lblSettingsHint" VerticalAlignment="Center" Foreground="{DynamicResource AppMutedTextBrush}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<controls:SettingsEditor Name="editorSettings" />
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<TextBlock Text="Modell:" VerticalAlignment="Center" />
|
||||
<TextBox Name="tbModel" MinWidth="240" Watermark="(Standardmodell)" />
|
||||
<Button Name="btnClearChat" Content="Verlauf leeren" />
|
||||
<TextBlock Name="lblChatStatus" VerticalAlignment="Center" Foreground="#777777" />
|
||||
<TextBlock Name="lblChatStatus" VerticalAlignment="Center" Foreground="{DynamicResource AppMutedTextBrush}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
<GridSplitter Grid.Column="1" Background="#DDDDDD" />
|
||||
<GridSplitter Grid.Column="1" Background="{DynamicResource AppBorderBrush}" />
|
||||
|
||||
<ScrollViewer Grid.Column="2">
|
||||
<SelectableTextBlock Name="txtDossier" Margin="12" TextWrapping="Wrap"
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Threading;
|
||||
using PolyTrader.App.Avalonia.ViewModels;
|
||||
using PolyTrader.Core.Analytics;
|
||||
@@ -86,7 +85,7 @@ namespace PolyTrader.App.Avalonia.Views.Modules
|
||||
var btn = this.FindControl<Button>("btnSend")!;
|
||||
var status = this.FindControl<TextBlock>("lblChatStatus")!;
|
||||
|
||||
AddChat("Du", question, Brushes.SteelBlue);
|
||||
AddChat("Du", question, ChatRole.User);
|
||||
tbQuestion.Text = string.Empty;
|
||||
btn.IsEnabled = false;
|
||||
status.Text = "Der Supervisor denkt nach …";
|
||||
@@ -102,13 +101,13 @@ namespace PolyTrader.App.Avalonia.Views.Modules
|
||||
|
||||
var result = await _agent.AskAsync(question, model, progress, profile);
|
||||
|
||||
AddChat($"Supervisor ({profile?.Name ?? "Allgemein"})", result.Answer, Brushes.SeaGreen);
|
||||
AddChat($"Supervisor ({profile?.Name ?? "Allgemein"})", result.Answer, ChatRole.Agent);
|
||||
status.Text = $"{result.ToolInvocations.Count} Tool-Aufruf(e) · " +
|
||||
$"{result.PromptTokens + result.CompletionTokens} Token.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AddChat("Fehler", ex.Message, Brushes.Firebrick);
|
||||
AddChat("Fehler", ex.Message, ChatRole.Error);
|
||||
status.Text = "Anfrage fehlgeschlagen.";
|
||||
_logger.Error($"Supervisor-Anfrage fehlgeschlagen: {ex.Message}");
|
||||
}
|
||||
@@ -119,9 +118,9 @@ namespace PolyTrader.App.Avalonia.Views.Modules
|
||||
}
|
||||
}
|
||||
|
||||
private void AddChat(string who, string text, IBrush brush)
|
||||
private void AddChat(string who, string text, ChatRole role)
|
||||
{
|
||||
_chat.Add(new ChatEntry { Header = $"[{DateTime.Now:HH:mm:ss}] {who}", Text = text, HeaderBrush = brush });
|
||||
_chat.Add(new ChatEntry { Header = $"[{DateTime.Now:HH:mm:ss}] {who}", Text = text, Role = role });
|
||||
this.FindControl<ScrollViewer>("chatScroll")!.ScrollToEnd();
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
<GridSplitter Grid.Column="1" Background="#DDDDDD" />
|
||||
<GridSplitter Grid.Column="1" Background="{DynamicResource AppBorderBrush}" />
|
||||
|
||||
<controls:SettingsEditor Name="editorAccount" Grid.Column="2" />
|
||||
</Grid>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
Text="Offene Orders und laufende Jobs werden geordnet heruntergefahren. Das kann einen Moment dauern. Die Schaltfläche ist kurz gesperrt, damit das Beenden nicht versehentlich ausgelöst wird." />
|
||||
|
||||
<TextBlock Name="lblCountdown"
|
||||
Foreground="#B71C1C"
|
||||
Foreground="{DynamicResource AppNegativeBrush}"
|
||||
Text="Beenden möglich in 10 s …" />
|
||||
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Spacing="8">
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<Button Name="btnCopyAll" Content="Alles kopieren" />
|
||||
<TextBlock Text="Level:" VerticalAlignment="Center" />
|
||||
<ComboBox Name="cbLogLevel" MinWidth="150" />
|
||||
<TextBlock Name="lblLiveStatus" VerticalAlignment="Center" Foreground="#777777" />
|
||||
<TextBlock Name="lblLiveStatus" VerticalAlignment="Center" Foreground="{DynamicResource AppMutedTextBrush}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
<TextBlock Text="Text:" VerticalAlignment="Center" />
|
||||
<TextBox Name="tbViewerText" MinWidth="220" />
|
||||
<Button Name="btnViewerLoad" Content="Laden" />
|
||||
<TextBlock Name="lblViewerStatus" VerticalAlignment="Center" Foreground="#777777" />
|
||||
<TextBlock Name="lblViewerStatus" VerticalAlignment="Center" Foreground="{DynamicResource AppMutedTextBrush}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user