Phase 3: Avalonia-Bedienhuelle fuer Windows und Linux
Neues Projekt Predictalytics.Shell (Avalonia 12.1.1, net10.0) als plattformuebergreifender Ersatz fuer den WinForms-Host. Setzt auf dem in Phase 2 extrahierten Predictalytics.Hosting auf. Oberflaeche: - MainWindow mit Menue, Werkzeugleiste, Terminal-Tab, Einstellungen-Tab und Statusleiste. Die feste Fenstergroesse von 1886x1088 ist aufgeloest. - Terminal als ListBox ueber ObservableCollection mit Einfaerbung nach Loglevel. Ringpuffer statt des bisherigen kompletten Leerens bei 500 Zeilen. - Einstellungsansicht handgeschrieben als Ersatz fuer den PropertyGrid, den es in Avalonia nicht gibt: vier Gruppen, Passwortfelder, mehrzeiliges Textfeld fuer die Egress-Kanaele, ComboBox fuer den SSL-Modus. Gespeichert wird explizit statt bei jeder Einzeloperation. - LicenseWindow ersetzt den WinForms-LicenseDialog. - Dialogs.cs als schlanker Ersatz fuer MessageBox.Show, ohne Drittanbieter. Headless-Modus (--headless) im selben Binary: fuer Linux-Server ohne Desktop-Session. Die Argumentauswertung steht vor jeder Avalonia- Initialisierung, sonst stirbt der Prozess ohne X11 bevor der Schalter greift. Lizenz ueber PREDICTALYTICS_LICENSE_KEY statt Dialog, SIGTERM und SIGINT ueber PosixSignalRegistration fuer sauberes systemd-Stop. Exit-Codes: 0 planmaessig, 2 keine nutzbare Lizenz, 3 Fehler. Plattformdetails: Schrift-Fallbackkette fuers Terminal, Avalonia.Fonts.Inter als mitgelieferte UI-Schrift, UTF-8 fuer die Windows-Konsole im Headless-Modus. Die Avalonia-Basisklasse wird als global::Avalonia.Application angegeben: innerhalb von Predictalytics.* loest der kurze Name auf den eigenen Namespace Predictalytics.Application auf. Build: 0 Fehler, 0 Warnungen. Tests: 100 bestanden. Verifiziert: --headless laeuft bis zur Lizenzschranke und beendet sich mit Exit-Code 2; GUI startet und zeigt das Lizenzfenster. Das Hauptfenster konnte nicht geprueft werden, weil die hinterlegte Lizenz abgelaufen ist. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="using:Predictalytics.Shell.ViewModels"
|
||||
xmlns:hosting="using:Predictalytics.Hosting"
|
||||
xmlns:sql="using:MySqlConnector"
|
||||
xmlns:sys="using:System"
|
||||
x:Class="Predictalytics.Shell.Views.MainWindow"
|
||||
x:DataType="vm:MainWindowViewModel"
|
||||
Title="Predictalytics Analytics — Backend Server"
|
||||
Width="1400" Height="900"
|
||||
MinWidth="900" MinHeight="600"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
|
||||
<Window.Resources>
|
||||
<!-- Monospace mit Fallback-Kette: Cascadia gibt es unter Linux meist nicht. -->
|
||||
<FontFamily x:Key="TerminalFont">Cascadia Code,DejaVu Sans Mono,Liberation Mono,Consolas,monospace</FontFamily>
|
||||
</Window.Resources>
|
||||
|
||||
<Window.Styles>
|
||||
<Style Selector="TextBlock.label">
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
<Setter Property="Margin" Value="0,0,12,0" />
|
||||
</Style>
|
||||
<Style Selector="TextBlock.hint">
|
||||
<Setter Property="Opacity" Value="0.6" />
|
||||
<Setter Property="TextWrapping" Value="Wrap" />
|
||||
<Setter Property="FontSize" Value="11" />
|
||||
<Setter Property="Margin" Value="0,2,0,0" />
|
||||
</Style>
|
||||
<Style Selector="HeaderedContentControl.group">
|
||||
<Setter Property="Margin" Value="0,0,0,20" />
|
||||
</Style>
|
||||
</Window.Styles>
|
||||
|
||||
<DockPanel>
|
||||
|
||||
<!-- ─── Menü ─── -->
|
||||
<Menu DockPanel.Dock="Top">
|
||||
<MenuItem Header="_Datei">
|
||||
<MenuItem Header="_Beenden" Click="OnExitClick" />
|
||||
</MenuItem>
|
||||
<MenuItem Header="_Ansicht">
|
||||
<MenuItem Header="Log-Ordner öffnen" Command="{Binding OpenLogFolderCommand}" />
|
||||
<MenuItem Header="WebUI öffnen" Command="{Binding OpenWebUiCommand}" />
|
||||
</MenuItem>
|
||||
<MenuItem Header="_Entwicklung">
|
||||
<MenuItem Header="Märkte synchronisieren" Command="{Binding SyncMarketsCommand}" />
|
||||
<MenuItem Header="Datenbank aktualisieren" Command="{Binding UpdateDatabaseCommand}" />
|
||||
<Separator />
|
||||
<MenuItem Header="Alle Trader neu berechnen…" Command="{Binding RecalculateAllCommand}" />
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
|
||||
<!-- ─── Werkzeugleiste ─── -->
|
||||
<Border DockPanel.Dock="Top" Padding="8" BorderThickness="0,0,0,1"
|
||||
BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Button Content="{Binding ServerButtonText}" Command="{Binding ToggleServerCommand}" MinWidth="150" />
|
||||
<Button Content="{Binding WebserverButtonText}" Command="{Binding ToggleWebserverCommand}" MinWidth="170" />
|
||||
<ProgressBar IsIndeterminate="True" Width="120" VerticalAlignment="Center"
|
||||
IsVisible="{Binding IsBusy}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- ─── Statusleiste ─── -->
|
||||
<Border DockPanel.Dock="Bottom" Padding="8,4" BorderThickness="0,1,0,0"
|
||||
BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}">
|
||||
<Grid ColumnDefinitions="*,Auto,Auto">
|
||||
<TextBlock Grid.Column="0" Text="{Binding StatusText}" VerticalAlignment="Center" />
|
||||
<TextBlock Grid.Column="1" Text="{Binding DbSizeText}" Margin="16,0" VerticalAlignment="Center" />
|
||||
<TextBlock Grid.Column="2" Text="{Binding BuildVersionText}" VerticalAlignment="Center" Opacity="0.7" />
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- ─── Inhalt ─── -->
|
||||
<TabControl>
|
||||
|
||||
<TabItem Header="Terminal">
|
||||
<ListBox Name="LogList"
|
||||
ItemsSource="{Binding LogLines}"
|
||||
Background="#0F0F14"
|
||||
FontFamily="{StaticResource TerminalFont}"
|
||||
FontSize="12"
|
||||
SelectionMode="Multiple">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:LogLine">
|
||||
<TextBlock Text="{Binding Text}"
|
||||
Foreground="{Binding Foreground}"
|
||||
TextWrapping="NoWrap" />
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Einstellungen">
|
||||
<Grid RowDefinitions="*,Auto">
|
||||
<ScrollViewer Grid.Row="0" Padding="20">
|
||||
<StackPanel MaxWidth="760" HorizontalAlignment="Left"
|
||||
DataContext="{Binding Options}" x:DataType="hosting:PredictalyticsOptions">
|
||||
|
||||
<!-- Webserver -->
|
||||
<HeaderedContentControl Classes="group" Header="Webserver">
|
||||
<StackPanel Spacing="12" Margin="0,8,0,0">
|
||||
<Grid ColumnDefinitions="200,*">
|
||||
<TextBlock Grid.Column="0" Classes="label" Text="Port" />
|
||||
<NumericUpDown Grid.Column="1" Value="{Binding WebserverPort}"
|
||||
Minimum="1" Maximum="65535" FormatString="0" HorizontalAlignment="Left" Width="160" />
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="200,*">
|
||||
<TextBlock Grid.Column="0" Classes="label" Text="Bind-Adresse" />
|
||||
<StackPanel Grid.Column="1">
|
||||
<TextBox Text="{Binding WebserverHost}" Width="260" HorizontalAlignment="Left" />
|
||||
<TextBlock Classes="hint"
|
||||
Text="'localhost' = nur lokal erreichbar. '0.0.0.0' macht die API von außen erreichbar — dann unbedingt absichern." />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="200,*">
|
||||
<TextBlock Grid.Column="0" Classes="label" Text="Datenbank-Debug" />
|
||||
<CheckBox Grid.Column="1" IsChecked="{Binding DbConnectionDebug}"
|
||||
Content="Ausführliche Verbindungsinfos im Terminal" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</HeaderedContentControl>
|
||||
|
||||
<!-- Datenbank -->
|
||||
<HeaderedContentControl Classes="group" Header="Datenbank">
|
||||
<StackPanel Spacing="12" Margin="0,8,0,0">
|
||||
<Grid ColumnDefinitions="200,*">
|
||||
<TextBlock Grid.Column="0" Classes="label" Text="Server" />
|
||||
<TextBox Grid.Column="1" Text="{Binding DbServer}" />
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="200,*">
|
||||
<TextBlock Grid.Column="0" Classes="label" Text="Datenbank" />
|
||||
<TextBox Grid.Column="1" Text="{Binding DbName}" />
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="200,*">
|
||||
<TextBlock Grid.Column="0" Classes="label" Text="Benutzer" />
|
||||
<TextBox Grid.Column="1" Text="{Binding DbUser}" />
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="200,*">
|
||||
<TextBlock Grid.Column="0" Classes="label" Text="Passwort" />
|
||||
<TextBox Grid.Column="1" Text="{Binding DbPassword}" PasswordChar="•" />
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="200,*">
|
||||
<TextBlock Grid.Column="0" Classes="label" Text="SSL-Modus" />
|
||||
<StackPanel Grid.Column="1">
|
||||
<ComboBox SelectedItem="{Binding DbSslMode}" Width="200" HorizontalAlignment="Left"
|
||||
ItemsSource="{x:Static vm:OptionSources.SslModes}" />
|
||||
<TextBlock Classes="hint"
|
||||
Text="Bei entfernten Servern 'Preferred' empfohlen. 'None' überträgt unverschlüsselt." />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</HeaderedContentControl>
|
||||
|
||||
<!-- Watchdog -->
|
||||
<HeaderedContentControl Classes="group" Header="Watchdog">
|
||||
<StackPanel Spacing="12" Margin="0,8,0,0">
|
||||
<Grid ColumnDefinitions="200,*">
|
||||
<TextBlock Grid.Column="0" Classes="label" Text="Aktiv" />
|
||||
<CheckBox Grid.Column="1" IsChecked="{Binding WatchdogEnabled}"
|
||||
Content="Heartbeats senden (Dead-Man's-Switch)" />
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="200,*">
|
||||
<TextBlock Grid.Column="0" Classes="label" Text="Server-URL" />
|
||||
<TextBox Grid.Column="1" Text="{Binding WatchdogUrl}" />
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="200,*">
|
||||
<TextBlock Grid.Column="0" Classes="label" Text="API Key" />
|
||||
<TextBox Grid.Column="1" Text="{Binding WatchdogApiKey}" PasswordChar="•" />
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="200,*">
|
||||
<TextBlock Grid.Column="0" Classes="label" Text="Source" />
|
||||
<TextBox Grid.Column="1" Text="{Binding WatchdogSource}" />
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="200,*">
|
||||
<TextBlock Grid.Column="0" Classes="label" Text="Instanz" />
|
||||
<TextBox Grid.Column="1" Text="{Binding WatchdogInstance}" />
|
||||
</Grid>
|
||||
<Grid ColumnDefinitions="200,*">
|
||||
<TextBlock Grid.Column="0" Classes="label" Text="Intervall (Sek.)" />
|
||||
<NumericUpDown Grid.Column="1" Value="{Binding WatchdogIntervalSeconds}"
|
||||
Minimum="15" Maximum="3600" FormatString="0" HorizontalAlignment="Left" Width="160" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</HeaderedContentControl>
|
||||
|
||||
<!-- Egress -->
|
||||
<HeaderedContentControl Classes="group" Header="Egress (Proxy / Quell-IP)">
|
||||
<StackPanel Spacing="8" Margin="0,8,0,0">
|
||||
<TextBlock Classes="hint"
|
||||
Text="Ein Kanal pro Zeile im Format: id|type|value Beispiel: prox-1|Proxy|http://user:pass@proxy:8080 ip-1|SourceIp|192.168.1.100" />
|
||||
<TextBox Text="{Binding EgressChannelsText}"
|
||||
AcceptsReturn="True"
|
||||
TextWrapping="NoWrap"
|
||||
MinHeight="120"
|
||||
FontFamily="{StaticResource TerminalFont}"
|
||||
FontSize="12" />
|
||||
</StackPanel>
|
||||
</HeaderedContentControl>
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
<Border Grid.Row="1" Padding="20,12" BorderThickness="0,1,0,0"
|
||||
BorderBrush="{DynamicResource SystemControlForegroundBaseLowBrush}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<Button Content="Speichern & übernehmen" Command="{Binding SaveSettingsCommand}" />
|
||||
<TextBlock VerticalAlignment="Center" Opacity="0.6"
|
||||
Text="Port und Datenbank wirken beim nächsten Start des Webservers." />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
Reference in New Issue
Block a user